Skip to content

Adopt deferred silvery features in the retained TUI (notify, Find, native selection/copy) #1530

Description

@KvFxKaido

The silvery migration deferred several framework features that are now worth reconsidering. Fable (Windows surface) investigated what silvery@0.21.1 offers vs. what cli/silvery/ adopted; this issue is that analysis verified against the pinned exports and our wiring, corrected where I had direct experience, and reconciled with the deleted-ANSI-TUI restoration backlog (tracked in-conversation, not yet its own issue — the remaining items are config editor + masked key, reasoning modal, and a quick-win bundle) so the two tracks don't collide.

All API claims below were checked against node_modules/silvery/dist at 0.21.1 and against cli/silvery/.

The strategic point first: three lanes, not two overlapping lists

The silvery-adoption list and the restoration backlog are ~70% disjoint but collide hard at selection/copy, and the collision resolves in favor of adopting native:

  • Copy / selection (unified lane). The restoration item "port tui-selection.ts" (hand-rolled coordinate→text mapping + inverse-video + drag-release OSC 52) and Fable's "adopt useSelection + SemanticCopyProvider" are the same capability. Adopting silvery's native selection retires the hard part (coordinate mapping in a retained compositor). Don't port the ANSI-era math; let the framework own selection and put our logic only in enrichCopy. The dead /copy completer (restoration quick-win) and the existing transcript-copy.ts share this clipboard path — plan them as one track, with byte-honest truncation as the through-line.
  • New silvery capabilities (net-new, never in the old TUI): notify + window-title, Find, focus scopes, OSC 8 links.
  • Bespoke restorations (no silvery overlap): config editor + masked key, reasoning modal, observability warnings, citations row. Silvery has Form/Picker/Table, but VL v2 deliberately excludes them — these stay hand-rolled.

Adoption list (my recommended order)

1. notify + window-title — highest daily-use value, few lines

Push's core loop is "kick off a turn, walk away for minutes." Silvery ships notify (OSC 9/99, auto-detected per emulator w/ fallback), useTerminalFocused, and setWindowTitle/setWindowAndIconTitle — all confirmed in 0.21.1. Wire: turn completes while unfocused → desktop ping; repo · branch · activity in the title while running. Gate on useTerminalFocused so it never fires while you're watching (same don't-over-notify discipline as elsewhere). This subsumes the restoration "completion bell" — do notify, not a bare \x07.

  • Spike: verify graceful fallback on a terminal without OSC 9/99.

2. Find (ListView search prop) — cheap, missing capability

ListViewProps.search?: boolean | ListViewSearchConfig<T> (index-DnuadDNL.d.mts:1659). Our transcript already renders through ListView (surface.tsx:537) with a .text-bearing row model, so this is set a prop + supply getText, not a provider-stack spike. We have no transcript search today, and the alt-screen kills the terminal's native Cmd+F, so a long session is scroll-or-nothing. Match highlighting + n/N navigation come with it.

3. Native selection + SemanticCopyProvider — the unified copy lane

SemanticCopyProvider / createCopyProvider / createOsc52Backend / getInternalClipboard all confirmed. enrichCopy(event) gets the plain text + screen range and returns richer ClipboardData — exactly transcript-copy.ts's philosophy, but wired to real drag selection. Also brings an internal clipboard store (full-fidelity within-app paste, OSC 52 externally), which we lack.

HARD GATE (merge blocker): the clipboard write routes through our osc52Copy (byte-accurate cap + honest truncation report), NOT silvery's backend — unless createOsc52Backend is proven to report a dropped oversized payload. This is the exact #1474 bug (capping by String#length not bytes, reporting truncated: false on a payload the terminal silently dropped). Selection is the win; the write path stays ours until disproven.

Folds in the restoration /copy code|tool|last wiring (the completer advertises them; there is no handler — dead command today).

4. @silvery/test — coverage only

Pre-endorsed in the Retained-Mode decision doc as the way to drive the real onClick last inch (currently source-verified). Dev-deps only, no runtime change.

Decouple from the version pin. The Phase 0 doc names this as the condition for relaxing the exact 0.21.1 pin to a caret. Add the tests; keep the exact pin. A caret lets a silvery minor change rendering under us, and we've catalogued how many traps live one export away (see NOs below). Coverage and pin-relaxation are two decisions — don't merge them.

5. Focus scopes — deletes a workaround, but touches input routing

entry.tsx's bridgeSilveryCompletionKey is a raw-TTY Tab bridge that exists because silvery consumes Tab for focus traversal before useInput sees it. Adopting silvery focus scopes deletes the bridge and gets click-to-focus free. Medium effort; its own careful PR (input routing), not bundled.

Parked / additive

  • OSC 8 href on Text — clickable file paths / PR URLs in tool cards. Tiny, additive.
  • Diff component — first-class hunks + unified/split + gutter, but hardcodes $success/$error at module scope (law-2/3 conflict) and its split mode only earns its keep once panes land. Park until panes are real, and only if it themes into VL v2.

Explicit NOs (with reasons, so they don't get re-proposed)

  • TextShimmer — no. Evaluated this session while rebuilding the verb shimmer; the rejection is documented in theme.tsx. It's a whole-word binary flip (value > .5 ? high : low), not a band sweep, AND runs its own private 1200ms timer beating against our 150ms shared tick — a direct VL v2 law-8 violation. Reproduces neither the effect nor the timing model.
  • Inline / dynamic-scrollback mode — no. It graduates completed rows to terminal-owned scrollback, i.e. out of the compositor. Every click-to-expand tool card, the fold/group projection, re-highlighting a fence on theme switch, the diff cards — all require rows to stay live under React. Inline trades interactivity-on-history for native scrollback; for Push, whose history is interactive, that's a functional regression against recent work. Find + selection recover ~80% of what inline buys without touching the shell. Stays a reversible one-line door.
  • Component library (CommandPalette/Picker/Table/Tabs/TreeView/Form), Image/Kitty/Sixel (the subsystem that blocked Bun single-binary bundling in Phase 0), Prose/typography (our markdown.tsx is tuned to transcript needs) — deliberate taste decisions, not deferrals.

Suggested sequencing

notify + titleFindselection + SemanticCopyProvider (byte-cap gate) → @silvery/test (pin stays) → focus scopes. Copy/selection folds in the restoration /copy + selection items; notify absorbs the completion bell; config-editor and reasoning-modal proceed as independent hand-rolled restorations.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions