Skip to content

Unify terminal context menus and add helper terminals - #573

Merged
nedtwigg merged 23 commits into
mainfrom
new-right-click-a
Sep 6, 2026
Merged

Unify terminal context menus and add helper terminals#573
nedtwigg merged 23 commits into
mainfrom
new-right-click-a

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Sep 5, 2026

Copy link
Copy Markdown
Member

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 status initially). 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:

  • Full pnpm test and pnpm build.
  • Standalone frontend build and Rust check.
  • Real PTY checks through dor ab: autorun, preserved input, background-job close protection, idle parent/helper closure, and promotion with unsaved editor contents intact.

nedtwigg and others added 14 commits September 5, 2026 12:26
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
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 5, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: ca86651
Status: ✅  Deploy successful!
Preview URL: https://2b508937.mouseterm.pages.dev
Branch Preview URL: https://new-right-click-a.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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:

  • onKill in Wall.tsx no longer calls exitTerminalMode() synchronously — it moved inside confirmSource(), which for a helper-owning source runs only after helperBlocksClose resolves. On the standalone host that inspection can take up to the 10s pty_context timeout, so pressing x in 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 sets maxLength={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.

Comment thread lib/src/lib/helper-terminal.ts Outdated
Comment thread docs/specs/shortcuts.md Outdated
Comment thread lib/src/lib/ui-geometry.test.ts Outdated
Comment thread vscode-ext/src/message-router.ts Outdated
Comment thread standalone/sidecar/pty-core.js Outdated
@nedtwigg

nedtwigg commented Sep 5, 2026

Copy link
Copy Markdown
Member Author

Addressed the review in cd4b7e3 and replied on each thread. The two general notes are covered too: onKill now exits passthrough synchronously before inspection, and the spec explicitly assigns command validation to the host and requires rejected edits to retain the prior preference and show the error in Modify.

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 pnpm test and pnpm build pass on cd4b7e3. The new CI run is in progress.

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Comment thread lib/src/lib/helper-terminal.ts
@dormouse-bot

Copy link
Copy Markdown
Collaborator

The two general notes from my review check out on cd4b7e3. onKill calls exitTerminalMode() at Wall.tsx:1294, ahead of the helper lookup and the helperBlocksClose branch, so passthrough exits on the keypress instead of after the inspection round-trip. And docs/specs/terminal-context.md now carries Must enforce command validity at the host with the rejected-edit behavior, which stops the 4096/CR/LF/NUL rule above it from reading as a field-level constraint.

The five diff-line threads and the increment are answered in the review above.

nedtwigg and others added 5 commits September 5, 2026 18:58
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 dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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-motionmotionIsInstant() 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.

Comment thread lib/src/components/wall/TerminalContextView.tsx Outdated
Comment thread lib/src/components/wall/TerminalContextView.tsx Outdated

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

@nedtwigg
nedtwigg merged commit f4ebbd7 into main Sep 6, 2026
28 checks passed
@nedtwigg
nedtwigg deleted the new-right-click-a branch September 6, 2026 04:01
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