Skip to content

fix(chat): Chat view fixes — select and copy message text, keep the view across project switches - #1247

Merged
eneskirca merged 2 commits into
eneskirca:mainfrom
danny-acv2:fix/chat-view-select-and-persist
Oct 17, 2026
Merged

eneskirca merged 2 commits into
eneskirca:mainfrom
danny-acv2:fix/chat-view-select-and-persist

Conversation

@danny-acv2

@danny-acv2 danny-acv2 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Two small fixes to the ⌘M chat view, one commit each.

1. Message text could not be selected or copied

What broke

In the ⌘M chat view, dragging over a message highlighted nothing, so Cmd/Ctrl+C had nothing to copy. The CSS was fine — .term-chat__msgs already has user-select: text.

Why

With the composer focused, a press on the message list moves focus out of the composer (the list is not focusable). useClickToFocus reads that as a press on the node's own chrome — focusLossOutcome → 'reclaim' — and focuses the composer again one tick later. Focusing a textarea moves the document selection into it, so the drag-selection that press just started is cancelled.

Fix

  • focusLossOutcome gets a pressedInReadableText input and a new outcome, 'reclaim-on-release', for a press that landed on .term-chat__msgs.
  • The hook remembers the element that lost focus and decides on pointerup through the pure reclaimOnRelease:
    • plain click (nothing selected): the keyboard goes back to the composer, exactly as before;
    • a selection was made: it is left alone, and so is the keyboard, so Cmd/Ctrl+C copies it. Clicking the composer afterwards works as usual.
  • Every other focusLossOutcome case is unchanged. The header-drag reclaim is still immediate.
  • The click-to-focus paragraph in CLAUDE.md now describes this case.

2. The ⌘M view closed itself on a project switch

What broke

Open the chat view on a node, switch to another project and come back: the node is on the raw terminal again. A page reload does the same.

Why

mdMode lived only in React Flow's node data. It was in neither the load seam nor the save seam of state/workspace.ts (nodeStatesToFlow / flowToNodeStates), and a project switch rebuilds the canvas from the saved nodes.

Fix

  • Persist it as CanvasNodeState.mdMode, next to hideFanout, through both seams.
  • It is written only while the view is open, so a node that never opened it saves byte-for-byte as before.
  • Nothing in src/core filters node fields, so no core change is needed.
  • CardModal's view stays modal-local, as its comment intends.
  • The ⌘M bullet in CLAUDE.md notes that the view is persisted.

Tests

  • Focus (terminalFocusMode.test.ts, useClickToFocus.test.tsx): 46 pass, 5 of them new.
    • A pure case for 'reclaim-on-release' and its boundaries, plus reclaimOnRelease.
    • Three jsdom cases on a chat-shaped node: no reclaim while the press is still selecting; a press that selected text keeps the selection; a plain click hands the keyboard back to the composer on pointerup.
    • With the fix reverted ('reclaim' for every press on the node), 3 of the 5 fail, including both DOM selection cases.
  • Persistence (workspace.md-mode.test.ts, new): an open view survives a save/load round trip, and a closed one is not written. Removing the save line turns it red.
  • Typecheck: npm run typecheck is clean for both web and node.
  • Broader run: src/renderer/state/, useMdModeFocus and the focus suites give 1103/1104. The one failure is workspace.node-ids.test.ts › never repeats across a simulated module reload. It is timing-sensitive: it also fails on untouched main when the machine is loaded, and passes on this branch when run alone.
  • Field use: both fixes have been running for a day on a Server Edition install, where the selection fix was confirmed in the browser.

Three surfaces

  • Desktop and Server Edition: both changes are renderer-only and behave the same on both.
  • Mobile companion: not applicable. The phone has no ⌘M view and does not read mdMode.

🤖 Generated with Claude Code

danny-acv2 and others added 2 commits October 17, 2026 16:21
A press on the message list starts a drag-selection, but focus leaves the
composer on that press and click-to-focus reclaimed it one tick later
(`focusLossOutcome` → 'reclaim'). Focusing the textarea moves the document
selection into it, so the drag died: nothing could be highlighted, and
Cmd/Ctrl+C had nothing to copy.

A press on `.term-chat__msgs` now returns 'reclaim-on-release': the reclaim
waits for the pointerup and happens only if nothing was selected
(`reclaimOnRelease`), so a plain click still hands the keyboard back to the
composer while a selection survives to be copied.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`mdMode` lived only in React Flow's node data and was in neither the load
nor the save seam of `state/workspace.ts`, so switching projects (which
reloads the canvas from the saved nodes) or reloading the page brought
every open chat view back as the raw terminal.

Persist it as `CanvasNodeState.mdMode`, written only while open, so nodes
that never opened the view save exactly as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@eneskirca
eneskirca enabled auto-merge October 17, 2026 23:02
@eneskirca
eneskirca merged commit 7c38c5e into eneskirca:main Oct 17, 2026
5 checks passed
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