Add terminal title candidate diagnostics and CWD-aware Storybook coverage#59
Draft
Add terminal title candidate diagnostics and CWD-aware Storybook coverage#59
Conversation
Short-circuit no-op semantic events in the reducer and skip listener notification when state is unchanged so prompt/CWD/title events on busy shells stop triggering Baseboard and TerminalPaneHeader re-render storms. Memoize the all-pane-states array in those components so the per-render allocation is amortized. Clean up replay parsers and pane state on natural pty:exit in the vscode and tauri adapters. Collapse the triplicated "derived === 'shell' && X !== '<unnamed>'" ternary into a shared resolveDisplayPrimary helper, export DEFAULT_COMMAND_TITLE and a new UNNAMED_PANEL_TITLE constant, and replace the magic strings across the components and lifecycle. Hoist the duplicated getCwd().then(fillTerminalProcessCwd) into a helper. Add getSessionIdByPtyId so terminal-state-store no longer iterates the registry directly. Unify parseOsc133 and parseOsc633 prompt boundary cases via a parsePromptBoundary helper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop defensive null guards on the non-optional titleCandidates field, collapse the seed-helper indirection in createTerminalPaneState, and swap sort-pick-first for linear max-scans across the candidate accessors. Stabilize the popover dismissal effect on a boolean so the window listeners stop re-registering on every rect change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
cad20e4
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://b5be12a0.mouseterm.pages.dev |
| Branch Preview URL: | https://codex-auto-title-and-cwd.mouseterm.pages.dev |
Why: getCwd() resolves async after spawn; if the session is disposed before then, updateCwdIfAllowed would resurrect a phantom pane state that no listener cleans up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why: the rename input now defaults to the derived header label (e.g. <idle> or <unnamed>), so pressing Enter without editing would pin the sentinel as a permanent user title that overrides everything. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why: the previous regex matched any line ending in $/#/%/>, which fires on TUI output (lazygit alt-screen render, progress lines like "step 1: 95% complete") and flickers the pane between running and idle. Now we strip alt-screen spans, require the prompt to start on a fresh line, and demand a path/user context signal before treating a trailing $/#/%/> + space as a returned prompt. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why: idleLabel never reads the {shellName} options it accepts. The
_options parameter is dead weight that obscures the function's
contract; either implement it or remove it. Removing for now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Why: pty:replay used to forward raw bytes to xterm; it now runs the protocol parser to extract CWD/prompt/title state, which means OSCs recognized by the parser are silently stripped. Future readers adding xterm-side OSC handling need to know this happens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
collectTerminalSemanticEvents already sets updatedAt in stream order, so the inline Date.now() is dead-write. Use 0 and document the contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The parser intentionally drops everything after the first unescaped `;`, matching VS Code shell integration's encoding (semicolons inside the command must be escaped as \x3b). Emitters that don't follow that encoding will see truncated commands; this is by design. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The heuristic is bounded to user_input fallback commands, so false negatives just mean the header doesn't auto-clear; explain that this is preferable to false positives that would flip a real running command back to idle prematurely. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both functions now accept title?: string | null, and restoreTerminal trims its input before checking against the UNNAMED_PANEL_TITLE sentinel — matching resumeTerminal's behaviour and avoiding pinning whitespace-only saved titles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live pty:data is pre-parsed by the extension host, so the parser only needs to run on the one-shot pty:replay buffer. Instantiate it transiently inside the message handler instead of keeping a per-id map that's never read after init. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The appTitleForPane resolver returns the alert manager's current OSC 9 notification body, which previously bypassed the same staleness check that activeTerminalTitle applies to title candidates. An OSC 9 emitted before the current command would leak through as the header label. Now: if titleCandidates.osc9 exists and predates currentCommand.startedAt, ignore the appTitle and fall back to the command's own display label. When there is no osc9 candidate (notification injected without going through the parser), trust appTitle to preserve legacy behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two cases to vscode-adapter.test.ts: - pty:replay runs raw data through TerminalProtocolParser, forwards semantic events (e.g. OSC 7 CWD) to applyTerminalSemanticEventsByPtyId, and emits the OSC-stripped visible data to replay handlers. - terminal:semanticEvents passes the host-parsed events straight through under the same PTY id. Both paths were uncovered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vscode-ext has no test runner configured, so cover the contract from the lib side: parse a PTY chunk through the same TerminalProtocolParser + collectTerminalSemanticEvents pipeline message-router.ts uses, JSON round-trip the result (proxy for structured-clone), and dispatch it through VSCodeAdapter. Asserts the webview applies exactly what the host emitted, end to end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setTerminalUserTitle now returns { accepted, reason? } so the rename
flow can distinguish accepted, empty, and reserved-sentinel inputs.
Wall.onFinishRename forwards that result; TerminalPaneHeader anchors
an auto-dismissing warning popover under the input when it's rejected.
Also adds a TerminalPaneHeader Storybook story
("RenameRejectedReserved") that submits "<idle>" and shows the
warning, plus a layout.md note documenting the new behaviour.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…alert iTerm2.md had three unrelated concerns (iTerm2 identity, notification machinery, and a registry of every OSC parsed). vscode.md mixed VS Code-specific behavior with adapter-agnostic transport. - OSC.md (new): every supported OSC plus iTerm2 identity and known- unimplemented sequences. Single source for parsing-location and pty:data strip semantics. - transport.md (new): adapter-agnostic PTY lifecycle, message protocol, and persisted-session types shared across VS Code, standalone, and fake adapters. - alert.md: absorbs notification machinery (OSC 9/9;4/99/777/BEL, ActivityNotification model, text handling, security, scenarios, verification checklist). - vscode.md: trimmed to VS Code-specific layer (manifest, persistence flow, theme, CSP, build, dream-architecture commands). - terminal-state.md: header cross-ref points at alert.md and OSC.md. - iTerm2.md: deleted. Also adds SPEC-CONFLICTS.md capturing a prior audit; this commit closes items #3 (OSC 9 timing) and #4 (pty:data strip semantics) from it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the iTerm2.md entry with OSC.md (registry/identity) and transport.md (adapter-agnostic protocol). Expands alert.md to mention the folded-in notification machinery, trims vscode.md to its VS Code-specific layer with a pointer to transport.md, and drops stale "soft/hard" TODO wording. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The docs/specs reorganization in 096a3d5 closed: - #3 (OSC 9 title-override timing stated three ways) — iTerm2.md is gone; terminal-state.md is the single canonical source. - #4 (pty:data strip semantics vs "the same streaming parser") — OSC.md is the single source for parsing-location rules. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #1 (ShellActivity 5 kinds vs. DerivedHeader.status 4 values). DerivedHeader.status and DerivedHeader.exitCode were dead in production: only consumers were a Storybook debug badge and the headerStatus() helper that produced them. The real header (TerminalPaneHeader.tsx, Baseboard.tsx) reads only .primary and .secondary. Drop both fields. While reshaping the header rules, also collapse "freshly finished" panes onto <idle>: - A finished command no longer keeps lastCommand.displayCommand in the header until the next prompt. The header returns to <idle> immediately. Exit code, just-finished context, and TODO/notification detail still flow through the alert/TODO machinery, which is the surface designed for that. - This simplifies headerPrimary, isAppTitleFresh, activeTerminalTitle, and cwdForHeader: they only branch on currentCommand, not on finished + lastCommand. - Disambiguator rules collapse from "running and finished use cwdAtStart, idle uses pane.cwd" to "running uses cwdAtStart, everything else uses pane.cwd." Status grouping keeps its 4 buckets (unknown | idle | running | finished) via an inline statusBucket() helper; the prompt/editing → idle projection is now documented explicitly in terminal-state.md. Storybook debug badge updated to read pane.activity.kind / pane.activity.exitCode directly. All 402 lib tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #2. layout.md was restating the full title priority chain, the disambiguator formula, and the OSC-channel list — all of which already live in terminal-state.md. Replace those paragraphs with a one-way delegation pointer. Layout keeps the rendering concerns it owns (truncation, click/right-click, secondary label visual treatment) and defers semantics to terminal-state.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #5. Dead in production (no producer outside the type definition): - CommandRunSource: 'foreground_process', 'title' - TerminalTitleSource: 'notification', 'profile', 'derived' Removed from both the TypeScript types and the spec, plus the exhaustive titleSourceLabel switch and the HEADER_APP_TITLE_SOURCES constant. One Storybook story was using 'derived' as a placeholder title source — switched to 'osc0'. Live but undocumented in the spec: - CwdSource 'manual': cwdFromManualPath(), used by session restore. - TerminalTitleSource 'user': setTerminalUserTitle(), inline rename UI. Both now have explicit production rules in terminal-state.md so readers can reason about where each value comes from. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-resolved by the prior commits: cwdForHeader in terminal-state.ts collapsed to "running uses cwdAtStart, everything else uses pane.cwd" (da25a4c), and layout.md now delegates the disambiguator rule to terminal-state.md (2aaa16c). No remaining duplication or mismatch — every kind, including unknown, is covered by the "everything else" branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Auto-resolved: dead enum values (notification/profile/derived) were removed in fe09ab4, and layout.md was rewritten in 2aaa16c to delegate the channel list to terminal-state.md instead of enumerating it. The type now has exactly the six sources the popup shows, and layout.md no longer hardcodes a list — it shows whatever the canonical type defines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #8. Both layout.md and transport.md now describe the resume seeding flow in terms of setTerminalUserTitle() — the canonical helper that rejects reserved sentinels (<idle>, <unnamed>). This is what the code actually does (resumeTerminal/restoreTerminal in terminal-lifecycle.ts), and it removes the prior wording divergence where transport.md said "non-unnamed" and layout.md said nothing about a filter at all. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #9. The reducer in terminal-state.ts unconditionally clears pendingCommandLine on promptStart and promptEnd — there's no actual "staleness" condition, the spec wording was just imprecise. Replace "clears stale pending command-line fallback" with the concrete rule: a fresh prompt boundary drops any pending input that was not yet consumed by a commandStart, because a fresh prompt is the unambiguous signal that no command is in flight. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #10. The prior wording mixed a numbered fallback list ("OSC > process > manual > null") with a separate caveat ("process may fill null or replace manual/restored, but not OSC"). Read together they were ambiguous about manual-vs-process tiebreaks. Replace with one rule per source, matching what updateCwdIfAllowed() in terminal-state-store.ts actually enforces: - OSC always wins (only a later OSC can replace it). - process updates only when current source is null/manual/process. - manual is the initial seed and is replaceable by any later source. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolves SPEC-CONFLICTS #11 — the final item. Add an explicit note to terminal-state.md's title-candidate table so a reader does not have to infer that "OSC 9" in the candidate row means only the message form, not the progress form. The progress form (OSC 9;4) carries no text and is fully specified in alert.md. With every audit item now closed (#1 through #11), SPEC-CONFLICTS.md is removed; the audit lived as a working document and has served its purpose. The resolution trail is preserved in commits 096a3d5, b5896bb, da25a4c, 2aaa16c, fe09ab4, 8a79062, 0a437c5, c4fc723, 9cbb0a4, and 1cf31b6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Testing
mouseterm-libbuild and focused test suite successfully.