Skip to content

fix(chat): closing the chat tab closes it, instead of reopening on the right - #80

Closed
ndemianc wants to merge 1 commit into
developfrom
fix/closing-chat-tab-should-close-it
Closed

fix(chat): closing the chat tab closes it, instead of reopening on the right#80
ndemianc wants to merge 1 commit into
developfrom
fix/closing-chat-tab-should-close-it

Conversation

@ndemianc

Copy link
Copy Markdown
Contributor

Regression from #76, reported from a real build.

A move and a close both end in panel.dispose(), and onDidDispose treated them identically — revealing the sidebar every time. That was correct while the sidebar was home: surrendering the tab meant going home.

With chat.startLocation defaulting to editor it inverts into a trap. Closing the tab pops the panel open on the right, and there is no way to put the chat away at all — close the tab and the panel appears, close the panel and it returns with the next window.

The else branch was the sharp end. With no resolved sidebar view — the normal case now, since the chat opens centred and nobody opens the secondary sidebar — it called focusChatView() unconditionally, which is exactly what revealed the panel.

Dispose can't see the difference on its own, so moveChatToSidebar announces itself with a flag that dispose reads once and clears. A plain close now closes.

What still happens on a close

If a sidebar view is already resolved, it still becomes live — otherwise the conversation would be live nowhere while a resolved view sat there showing a stale hand-over card. Only the reveal is conditional; making a hidden view live costs nothing and is correct the moment the user opens it.

Two existing tests asserted the old behaviour

Updated deliberately, not mechanically:

  • "a sidebar that was never resolved is revealed rather than assumed" asserted the reveal on every close. That was the bug, written down as a requirement. It now covers the move.
  • The activeWebview invariant pinned an exact list of assignments, so adding a second reset read as a regression. It now states what it means: exactly one place points it at a live surface, and at least one releases it.

Guards

Each bypass-verified by reverting the fix:

bypass caught
resolved view force-opened on a plain close
the no-view reveal escaping the moving branch — the reported bug
the flag never cleared, so the next close reveals too
dispose unable to see a move at all
the flag set after dispose, so the move reads it stale
the resolved view never becoming live — chat live nowhere

The two other things reported alongside it

The untitled tab at startup is not ours. LevelCode has no workbench.startupEditor override anywhere in its source — that's the editor default or a user setting (workbench.startupEditor: "none" turns it off).

The ResizeObserver loop spam is INFO-level noise from VS Code's own webview harness — we register no ResizeObserver anywhere. I had a plausible mechanism (the empty state's ASCII logo sizes itself from container width with clamp(5px, 3.6cqi, 13px) inside a scrollable container, which can oscillate with the scrollbar) and could not reproduce it: a harness running the real stylesheet at 280/320/340/360px, with the clamp genuinely in its variable range, reported 1 resize callback and 0 loop errors at every width.

Fixing this bug removes the reported scenario anyway, since the sidebar view is no longer force-opened. I'm not shipping a speculative CSS change for something I haven't reproduced — if the spam survives this fix, it's worth its own investigation with that mechanism as the first suspect.

25 tests in chatSurface, 34 suites green.

…e right

Regression from #76, reported from a real build. A MOVE and a CLOSE both end in
panel.dispose(), and onDidDispose treated them identically — revealing the sidebar every
time. That was correct while the sidebar was home: surrendering the tab meant going home.

With chat.startLocation defaulting to `editor` it inverts into a trap. Closing the tab
pops the panel open on the right, and there is no way to put the chat away at all: close
the tab and the panel appears, close the panel and it returns with the next window. The
`else` branch was the sharp end — with no resolved sidebar view (the normal case now,
since the chat opens centred and nobody opens the secondary sidebar) it called
focusChatView() unconditionally, which is precisely what revealed the panel.

Dispose cannot see the difference on its own, so moveChatToSidebar announces itself with a
flag that dispose reads once and clears. A plain close now closes.

WHAT STILL HAPPENS ON A CLOSE: if a sidebar view is already resolved it still becomes live,
because otherwise the conversation would be live nowhere while a resolved view sat there
showing a stale hand-over card. Only the REVEAL is conditional — making a hidden view live
costs nothing and is correct the moment the user opens it.

Two existing tests asserted the old behaviour and were updated deliberately, not
mechanically:
  - "a sidebar that was never resolved is revealed rather than assumed" asserted the reveal
    on EVERY close. That was the bug, stated as a requirement. It now covers the move.
  - the activeWebview invariant pinned an exact list of assignments, so adding a second
    RESET read as a regression. It now says what it means: exactly one place points it at a
    live surface, and at least one releases it.

Guards, each bypass-verified by reverting the fix:
  - the resolved view force-opened on a plain close
  - the no-view reveal escaping the `moving` branch (the reported bug)
  - the flag never cleared, so the NEXT close reveals too
  - dispose unable to see a move at all
  - the flag set AFTER dispose, so the move reads it stale
  - the resolved view never becoming live, which would leave the chat live nowhere

NOT in this change, both reported alongside it:
  - The untitled tab at startup is not ours — LevelCode has no `workbench.startupEditor`
    override anywhere in its source. That is the editor default or a user setting.
  - The "ResizeObserver loop" console spam is INFO-level noise from VS Code's own webview
    harness; we register no ResizeObserver. I could not reproduce a loop: a harness running
    the real stylesheet at 280/320/340/360px, with the ASCII logo's clamp(5px, 3.6cqi, 13px)
    genuinely in its variable range, reported 1 resize callback and 0 loop errors. Fixing
    this bug removes the reported scenario anyway, since the sidebar view is no longer
    force-opened. Not shipping a speculative CSS change for something I have not reproduced.

25 tests in chatSurface, 34 suites green.
Copilot AI lite review requested due to automatic review settings August 16, 2026 17:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a regression introduced when the chat’s default start location moved to the editor: closing the chat editor tab was treated like a “move back to sidebar” and would forcibly reveal the secondary sidebar, making the chat effectively impossible to dismiss.

Changes:

  • Differentiate “move to sidebar” vs “close tab” by introducing a one-shot movingChatToSidebar flag that onDidDispose reads and clears.
  • Make sidebar reveal conditional on an intentional move, while still handing conversation state back to an already-resolved sidebar surface.
  • Update chatSurface tests to reflect the new close vs move semantics and tighten the activeWebview invariant.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
extensions/levelcode-ai/extension.js Adds close-vs-move detection and conditions sidebar reveal behavior accordingly.
extensions/levelcode-ai/test/chatSurface.test.js Updates and extends tests to pin the regression and the new intended behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +2324 to 2326
const moving = movingChatToSidebar;
movingChatToSidebar = false;
chatEditorPanel = undefined;
@ndemianc

Copy link
Copy Markdown
Contributor Author

Superseded by the editor-only chat: with the sidebar view removed entirely there is nothing to move to, so the close-versus-move distinction this added no longer exists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants