Unify terminal context menus and add helper terminals - #573
Conversation
AlertManager drops helper Sessions at its own entry points, so hosts mark an id once instead of guarding every alert call. helper-terminal.ts owns the single status/inspection poll and the promotion transaction; Wall keeps one close gate and one replaceable-shell predicate. The context view owns action errors, cycles focus through stepFocus, and abbreviates directories through cwdDisplay. Removes the connect-port chain and TodoAlertDialog, orphaned by the terminal context. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMqp3wzW3DDRrKBjdScFvG
The fake, Tauri, browser-dev, and VS Code hosts tell AlertManager which ids are helpers at spawn, listing, and promotion; the per-call guards and the three private id sets go. The fake adapter keeps one record per demo shell, and the VS Code PTY manager shares one correlated child round trip. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMqp3wzW3DDRrKBjdScFvG
One command validator, one strict-inspection throw, the module-level path import, and a settings read only for the settings operation. The replay buffer keeps chunks instead of re-copying 200 KB per write, and only the standalone host enables it: VS Code's extension host already buffers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMqp3wzW3DDRrKBjdScFvG
Host specs keep a one-line pointer for terminal context operations instead of restating the rule; alert, terminal-state, and terminal-context name the new owners. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HMqp3wzW3DDRrKBjdScFvG
Deploying mouseterm with
|
| Latest commit: |
ca86651
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://2b508937.mouseterm.pages.dev |
| Branch Preview URL: | https://new-right-click-a.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
Read the whole diff plus the merged-tree versions of helper-terminal.ts, TerminalContext*.tsx, Wall.tsx, pty-core.js, pty-manager.ts and message-router.ts. The helper lifecycle itself reads well — the conservative-inspection rule is applied consistently (busy: null → work, missing PTY in the process table → work, promoting → work), the promotion/rollback ownership handoff is genuinely covered by helper-terminal.test.ts, and the sidecar's validHelperOwner and openDirectory validation are tight. Four things below.
The one I'd most want addressed is the helper status poll: it never stops when the context closes, so each retained helper keeps issuing a host process-table inspection every 2s for the rest of the session. On Windows that is a synchronous powershell.exe spawn inside the sidecar process, which is the same process pumping every PTY's output.
Two smaller notes that don't fit on a diff line:
onKillinWall.tsxno longer callsexitTerminalMode()synchronously — it moved insideconfirmSource(), which for a helper-owning source runs only afterhelperBlocksCloseresolves. On the standalone host that inspection can take up to the 10spty_contexttimeout, so pressingxin passthrough now leaves the wall in passthrough for that whole window before anything visible happens.docs/specs/terminal-context.md→ Global autorun setting says the command is "at most 4096 characters, excluding CR, LF, and NUL". The hosts enforce all three (validCommand), but the Modify input only setsmaxLength={4096}— a pasted multi-line value reaches the host and comes back as a thrown error rather than being rejected at the field. Fine if that's the intent; worth a line in the spec if so, since the spec reads as one rule over both.
|
Addressed the review in cd4b7e3 and replied on each thread. The two general notes are covered too: Also corrected chunk-boundary trimming in Standalone's replay buffer, with a regression test preserving the complete 200,000-character tail. The nine stale Storybook interactions were fixed in ae83694; its automated visual regression run passed. Full |
dormouse-bot
left a comment
There was a problem hiding this comment.
Re-reviewed the increment. All five earlier threads are genuinely addressed: setHelperVisible gates both the 100ms tick and the process inspections (with the hidden-invalidation and pending-creation regressions to pin it), the focus-trap hook and pointInConvexPolygon are gone with no dangling references, the VS Code router now posts pty:exit for a rejected helper, and the Explorer path acknowledges launch rather than exit. The replay-trim correction is right too — slice(session.chars - REPLAY_CHARS) can never over-slice, because the loop stops precisely when chars - REPLAY_CHARS < chunks[0].length.
One thing the pause introduced, inline below: the status recomputed on reopen is never published, so TerminalContext can render the status the helper had when it was hidden.
standalone/sidecar/helper-terminal.test.js passes locally (8/8); the vitest suites need an install this checkout doesn't have, so I'm relying on CI for those.
|
The two general notes from my review check out on cd4b7e3. The five diff-line threads and the increment are answered in the review above. |
The context floats over its whole leaf, so it now renders through TerminalLeafOverlay instead of a portal out of TerminalPanel; this drops the sourceElement prop and the render-time ref read, and covers a context opened in the same commit its panel mounts. The context value splits into `id` (the Session holding input, null while exiting) and `mounted` (what the leaf renders), so consumers need no `closing` special case. TerminalContextView owns its element's DOM work: focus on open, the outside-press listener, and the exit snapshot (reads batched before writes). The origin clamp, pinhole and full clips, and the corner radius live once in theme.css; the surface derives from modalSurface; close gates on the house motionIsInstant() outside the state updater. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TnxkQNxVEbXkYJNjBbLn5D
dormouse-bot
left a comment
There was a problem hiding this comment.
Reviewed the five commits since 445fb1c — the move into the Lath leaf overlay, the clip-path reveal/dismiss, the copy and "Opening…" feedback, and the unfocused-window highlight suppression. Moving the context out of TerminalPanel's clipping box is a clean win: it drops the onMouseDown stopPropagation the old placement needed, and the mounted / id split (identity for the leaf, input-owner for everyone else) keeps the exit from leaking into the wall's keyboard state. Re-read helper-terminal.ts too — splitting setHelperVisible into its own [id, closing] effect still arms watchHelper before openHelper's first await resolves, so the retained-helper path is unaffected.
Two things below.
The one I'd most want addressed is the reveal animation's Chromatic exposure. Every other keyframe animation in the wall is either triggered by a play function or has a cfg static substitute; this is the first one that runs on mount over Chromatic-snapshotted content, and it is gated only by prefers-reduced-motion — motionIsInstant() already covers the dismissal but not the entrance.
One smaller note that doesn't fit on a diff line: the detail dialog renders outside .terminal-context-content, so during the first 160ms of a reveal it paints at full opacity over content still at opacity 0. Only reachable through initialDetail, which is exactly what the TitleSources / ModifyCommand / ResetHelper stories set — so it compounds the first finding rather than standing on its own.
Happy to push either fix if you'd rather not hand-apply them.
dormouse-bot
left a comment
There was a problem hiding this comment.
ca86651 closes both threads and the general note. Verified the Chromatic path end to end (isChromatic() → cfg.layout.animate = false at module scope → motionIsInstant() → no terminal-context-enter, so neither the clip-path reveal nor the now-scoped content fade can match), and the focus fix keeps the launch button in the tab order while suppressing the repeat click. Nothing new in the increment.
Automated checks are green on this head: Build & Test, Visual Regression Tests, Standalone Smoketest, both Standalone Platform Checks, Webview Boot Smoketest, relay, Cloudflare Pages, Storybook Publish. UI Tests is the Chromatic baseline gate — 16 changes awaiting your accept, which only you can clear.
Right-clicking a terminal's header, alert indicator, or uncaptured terminal body now opens one terminal context panel. It shows the surface identifier, title provenance, directory actions, port actions, and source alert controls, with a real helper terminal filling the remaining space.
The helper starts lazily in the source directory and runs the global default command (
git statusinitially). Untouched idle helpers refresh on reopening; any user input preserves the session until an explicit Reset. Promote moves the same PTY and xterm into a regular split, preserving scrollback and running editors. Running helper work blocks closing its source and is revealed for the user to stop. Helpers stay outside alerts,dor, and Pocket until promotion.Port actions open the system browser, an iframe, or an embedded/popped-out agent browser. Browser targets are reused per source and port. Shared compact switches and subtle labeled actions match the approved Storybook baseline.
Includes the native host protocol, recovery and process inspection, lifecycle regression tests, and updated specs. The presentation is shared by the live menu and Storybook state gallery.
Validation:
pnpm testandpnpm build.dor ab: autorun, preserved input, background-job close protection, idle parent/helper closure, and promotion with unsaved editor contents intact.