You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Answers "can the LevelCode AI panel move to the centre?" — by dragging, no, and
that is not our choice: `ViewContainerLocation` is Sidebar | Panel | AuxiliaryBar
and nothing else, so a VIEW can go left or to the bottom but never into the
editor grid. Explorer and Terminal cannot either. Only EDITORS live in the middle.
So the centre needs a WebviewPanel rather than a WebviewView: a real tab that
splits, moves between groups, and drags to another window. `AI: Open Chat in
Editor`.
It is a MOVE, not a mirror. Two live surfaces would mean fanning out every
post(), making every handler idempotent, and shipping a UI that can disagree with
itself; one live surface keeps a single source of truth and is what "open in
editor" means to a user. The sidebar hands its slot to a small card offering
"Bring it back".
Three things carried the design:
**The listener survives an html swap.** onDidReceiveMessage binds to the WEBVIEW,
not the document, and the sidebar's html is swapped between the chat and the
hand-off card. So wire() (register the handler, once) is split from makeLive()
(become the live surface, load the chat). Wiring on every swap would stack
handlers and double-send every message.
**The transcript is DOM state.** A hand-over would land you in an empty chat
holding a conversation the model still remembers. Every transition arms a replay
of the live session's turns, consumed on `ready` — the earliest a fresh webview
can receive anything — and cleared in the same step so it cannot repeat on a
later reload.
**One restore path.** "Bring it back" disposes the panel rather than restoring
the sidebar itself, so closing the tab and clicking the button run identical
code. A sidebar that was never resolved is revealed instead of written to.
The replay reuses the sessionResumed renderer rather than a second one, now
labelled by the caller: a move says "Moved to the editor", not "Resumed" — which
would claim the session had been reloaded from disk when nothing of the sort
happened. A real resume passes no tag and still reads "Resumed".
Tests: 12 in test/chatSurface.test.js, read out of the shipped extension.js the
way mcpManage does, because every failure mode here is state and invisible in a
diff. Verified non-vacuous — each bypass is a bug someone could plausibly write:
wire inside makeLive (duplicate handlers) 2/12
drop the already-open guard (two panels) 1/12
replay not cleared (stacking transcripts) 5/12
reattach restores directly (two paths) 7/12
hard-code "Resumed" (a move mislabelled) 10/12
drop the empty-transcript guard 6/12
33 suites green.
0 commit comments