feat(terminal): render Claude/Codex transcripts beside the terminal; keep it mounted across swaps - #1489
Merged
Conversation
Swapping the terminal between top and bottom (or left and right) killed every running shell, taking programs like a Claude session with it. EditorArea rendered the panel in one of two child slots depending on panelPosition, so a top<->bottom or left<->right swap moved it to the other slot and React unmounted and remounted TerminalPanel. The panel's unmount disposes all sessions, which kills their PTYs; the remount then started fresh shells. Auto mode rarely hit it because bottom and right share a slot. The panel now always occupies one slot after the editor column; top and left reverse the flex axis instead, so no node moves and nothing remounts. Reordering keyed children instead would have made React move the editor column, which reloads its iframes and drops focus. The cost is that for top/left the Tab order reaches the panel after the editor while it is drawn before; the two are independent landmarks. A regression test swaps a stateful panel through every position and asserts one mount; a WebKit test measures that each position still draws the panel on its side of the editor.
A terminal cannot draw a Markdown table or a Mermaid diagram, so the replies where Claude Code or Codex answer with one arrive as pipes and source. With Settings > Terminal > "Automatic transcript rendering" on (off by default), VMark renders those replies beside the CLI. Binding: each shell gets an opaque token (VMARK_TRANSCRIPT_TOKEN). A SessionStart hook installed additively into ~/.claude/settings.json and ~/.codex/hooks.json records that CLI session's transcript path under the token, so every terminal follows its own session, never "the most recently modified transcript". Config is written only when the hook is missing, and invalid user config is refused rather than replaced. Disabling removes the enabled marker and every binding; closing or restarting a shell deletes its binding. Reading: terminal_transcript_read is bounded to the last 2 MiB, confined to canonical paths under the CLIs' session directories, returns only when the file changed, and treats a transcript announced but not yet written as waiting. Parsing keeps complete JSONL lines and the last 100 assistant messages. Rendering: Markdown becomes React elements (no raw HTML, links or remote images); Mermaid renders with strict security and SVG labels into a scriptless sandboxed iframe, then restores the editor's config. The region sits right of the CLI in a top/bottom panel and below it in a side panel, starts collapsed, and opens once per new reply that holds a table or diagram. A pressed-state chart toggle in the tab bar shows or hides it; hidden, it leaves the layout. Documented in terminal.md and settings.md in all ten locales.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two changes, one commit each.
fix(shell): keep the terminal mounted when it swaps sides
Swapping the terminal between top and bottom (or left and right) killed every running shell, including programs like a Claude session.
EditorArearendered the panel in one of two child slots depending on position, so a swap made React unmount and remountTerminalPanel, whose unmount disposes all PTYs. The panel now stays in one slot; top and left reverse the flex axis instead. This bug had been present since the swap control shipped (47228ef).feat(terminal): render Claude/Codex transcripts beside the terminal
New opt-in setting Settings → Terminal → Automatic transcript rendering (off by default). It renders the Markdown tables and Mermaid diagrams that a terminal cannot draw, beside the CLI.
VMARK_TRANSCRIPT_TOKEN. An additiveSessionStarthook in~/.claude/settings.jsonand~/.codex/hooks.jsonrecords that CLI session's transcript under the token, so each terminal follows its own session. Config files are written only when the hook is missing, and invalid config is refused, never replaced. Bindings are deleted when a shell closes or restarts, and all of them when the setting is turned off.terminal.mdandsettings.mdin all ten locales.Verification
pnpm check:predelta: all 46 gates passed.pnpm check:all: exit 0.