Skip to content

feat(tui): trial an anchored composer in fullscreen mode on nightly - #4221

Open
abhinav-phi wants to merge 1 commit into
apache:mainfrom
abhinav-phi:feat/tui-fullscreen-anchored-composer
Open

feat(tui): trial an anchored composer in fullscreen mode on nightly#4221
abhinav-phi wants to merge 1 commit into
apache:mainfrom
abhinav-phi:feat/tui-fullscreen-anchored-composer

Conversation

@abhinav-phi

Copy link
Copy Markdown
Contributor

Summary

Implements the nightly trial proposed in #4136: the TUI can now run fullscreen (alternate screen) with an anchored composer — the prompt, pending queue, activity strip, and status line stay pinned to the bottom of the screen while the transcript scrolls in an application-owned viewport. On nightly builds the fullscreen path is the default; release builds keep today's terminal-scrollback renderer. MAKA_TUI_FULLSCREEN=1 opts any build in, MAKA_TUI_FULLSCREEN=0 opts a nightly build out.

This builds on the capability analysis in Discussion #3879: the pinned @earendil-works/pi-tui@0.84.2 already ships TuiAltScreen (alternate-screen viewport with mouse, selection, search, and hyperlink support), so this is a mode switch inside the existing dependency — no renderer fork, no upstream rewrite.

What changes

  • packages/cli/src/tui-fullscreen.ts (new) — the experiment switch and the unread-output logic:
    • resolveTuiFullscreen() resolves the mode with precedence: explicit setting (embeddings/tests) → MAKA_TUI_FULLSCREEN env override → build-channel default. Nightly is detected from the CLI package version using the Product Nightly identity format (0.2.0-dev.<run>.<YYYYMMDD>).
    • UnreadOutputCounter — counts transcript lines appended while the user is scrolled away from the bottom; cleared on return to the bottom, never negative on content shrink.
    • openExternalUrl() — a best-effort platform opener (open / xdg-open / cmd /c start) for OSC 8 hyperlink clicks.
  • packages/cli/src/pi-tui-layout.ts — the fullscreen layout pieces:
    • MakaTranscriptScrollView — a ScrollView (follow-end, primary, chaining overscroll, transient scrollbar) that computes the unread count at its layout pass — the one point in each frame where scroll state is fresh — and requests a catch-up frame when the rendered count lags, so the indicator settles deterministically.
    • MakaTranscriptDocumentComponent — renders the full transcript document inside the scroll view and exposes its line count.
    • MakaFullscreenChromeComponent — the anchored bottom chrome (unread indicator, activity strip, pending queue, editor, status line) with the same editor/autocomplete row-budget fixed-point as the main-screen layout, plus a minimum reserved transcript row.
  • packages/cli/src/pi-tui-runner.ts — when fullscreen is on, constructs TuiAltScreen with mouse: true and the URL opener, mounts the VStack layout root (scrolling transcript + intrinsic-height chrome), and wires the composer; the main-screen layout and its clear-on-shrink protection are skipped entirely.
  • packages/cli/src/skill-highlight-editor.ts — adds an onUserTextChanged hook fired after any input that actually changes the editor text; fullscreen uses it to re-anchor the transcript to the newest output while typing.
  • packages/cli/src/cli-core.ts / runtime-host-tui-command.ts — thread the CLI package version to the runner so the channel default can be resolved.

The fullscreen experience

  • Anchored composer: wheel scrolling, PageUp/PageDown, and Ctrl+Shift+↑/↓ (semantic prompt jumps) scroll the transcript; the editor and status line never move.
  • Reading position preserved: while scrolled away, new model/tool output streams in behind the reading position without moving the viewport; the view only follows the tail while already at the bottom.
  • Unread indication: an accent line (↓ N new lines — End to jump to latest) appears between the transcript and the composer, counting lines appended while away; End (or scrolling back down) clears it.
  • Selection & copy: drag-select with the mouse, copy via OSC 52 (pi-tui's default), auto-scroll selection past the viewport edges, wheel-through selection.
  • Search: Ctrl+Shift+F searches the rendered transcript with next/previous match navigation.
  • Links: OSC 8 hyperlinks in assistant markdown become click-to-open.
  • Terminal history trade-off: during the session the transcript lives in the app viewport instead of native scrollback; when the TUI exits, pi-tui restores the main buffer and prints the final document, so the last screen survives into the shell.

Behavior decisions (mapped to the issue's evaluation questions)

  1. Is returning to the bottom before typing a meaningful problem? With an anchored composer the prompt is always visible and usable, so the "scroll all the way back to type" problem no longer exists in fullscreen.
  2. Does the anchored composer improve reading-while-working? Yes mechanically: reading position is never displaced by streaming output, and the unread indicator + End jump make catching up one keystroke. Whether it materially helps is exactly what nightly feedback should answer.
  3. Is application-owned selection/copy acceptable? pi-tui's drag selection + OSC 52 covers the common path; the known cost is that terminal-native selection no longer reaches TUI content and OSC 52 support varies by terminal. This PR makes no further customization — nightly feedback decides.
  4. Is losing terminal-native scrollback acceptable? During a session, yes for the trial; after exit, the final document is printed back to the main buffer. The evaluation should specifically probe very long sessions and terminal scrollback-dependent workflows.
  5. Status line placement: it stays anchored with the composer (simplest, one screen-painting model). Collapsing or scrolling it with the transcript are alternatives the experiment can revisit.
  6. Typing while reading older content: the first keystroke that changes the draft re-anchors the transcript to the newest output. The composer and its autocomplete live at the bottom, so composing from the middle of history would be blind; restoring the prior reading position after submit is a possible refinement if nightly users ask for it.

Enabling the experiment

Build Default Override
Nightly (0.2.0-dev.*) fullscreen on MAKA_TUI_FULLSCREEN=0 to opt out
Release (0.2.0) main screen (unchanged) MAKA_TUI_FULLSCREEN=1 to opt in

This satisfies the issue's non-goals by construction: fullscreen is not the stable default on release builds, there is no permanent user-facing mode toggle (only the experiment env var, the same pattern as MAKA_RUNTIME_SAFE_BOUNDARY_RESUME), and the upstream renderer is untouched.

Exit criteria — what to try and report on nightly

  • Reading position while a turn streams tool output; whether the unread count matches expectations.
  • Selection, copy, and paste against native terminal behavior (Windows Terminal, iTerm2, Kitty, Ghostty, tmux).
  • Resizing mid-session and mid-turn (layout rebuilds each frame; small terminals keep ≥1 transcript row and the editor minimum).
  • Very long sessions (thousands of transcript lines) and wheel-scroll smoothness.
  • End/Home note: in the alternate screen these keys jump to the bottom/top of the transcript (pi-tui's intentional shadowing of the editor's line navigation); Ctrl+E/Ctrl+A still move the cursor.
  • Rollback is one env var (MAKA_TUI_FULLSCREEN=0), so a bad nightly can be worked around without a revert.

Testing

  • packages/cli/src/__tests__/tui-fullscreen.test.ts (new, 24 tests): the mode-resolution matrix (setting/env/channel), unread-counter semantics (growth while away, clear at bottom, shrink safety, reset), indicator copy, chrome sizing/reserved rows/activity-separator, and full renderLayoutFrame integration frames — composer anchoring, follow-end behavior, reading-position preservation under growth, and the exact two-frame unread convergence (including that a settled count requests no further frames).
  • packages/cli/src/__tests__/pi-tui-runner.test.ts (+2 runner-level tests on the FakeTerminal harness): a tall resumed session proves wheel-up keeps the composer anchored while older content scrolls in and typing re-anchors to the newest output; and a four-way gating run (release/nightly × env override) asserts which renderer actually starts.
  • biome check, ASF header check, and tsc typecheck across all workspaces pass. The CLI suite matches the pre-existing main baseline on this machine (the only divergent test was a flaky MCP-child process test that passes in isolation and is untouched by this change).

Rollout

Nightly builds pick this up automatically; the issue's exit criteria (documented feedback on reading position, unread indication, selection/copy, terminal history, resizing, small terminals) then decide whether the mode is stabilized, revised, or dropped.

…pache#4136)

Opt the TUI into an alternate-screen fullscreen renderer on nightly
builds: the transcript scrolls in an application-owned viewport while
the composer, activity strip, pending queue, and status line stay
anchored to the bottom of the screen. MAKA_TUI_FULLSCREEN=1 opts a
release build in; =0 opts a nightly build out.

- TuiAltScreen with mouse wheel scrolling, drag selection + OSC 52
  copy, transcript search (Ctrl+Shift+F), and clickable OSC 8 links
- primary ScrollView follows the newest output and preserves the
  reading position while scrolled away
- an unread indicator counts lines appended while away and clears on
  return; typing re-anchors to the newest output
- app-owned viewport disables the main-screen scrollback entry freeze,
  so expansion toggles retarget every entry
@github-actions github-actions Bot added the effort/XL Over 1000 readable lines label Aug 29, 2026

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex-assisted review performed under the maintainer-approved review workflow.

Reviewed exact head 285c6039155929b616542f36e4cb802ca02571d5. This change enables the alternate-screen TUI by default for nightly builds, threads build identity into the runner, adds a transcript ScrollView with anchored editor/status chrome and unread tracking, and enables mouse selection/search/link activation. I inspected the complete 8-file diff, the version-selection path, layout/scroll state, editor input routing, hyperlink activation, terminal teardown, and the added tests.

I found one P1 security issue in the Windows hyperlink opener; see the inline comment.

Validation: the exact-head maka-agent suite passed 659/659; a clean synthetic merge with current main at d2346707d65144682d45e905a378ee57be469769 also passed 659/659. Biome on all changed files, ASF header validation, and git diff --check passed. GitHub currently exposes only the successful PR-effort label check, not the repository test workflow.

Not independently verified: native Windows execution, real-terminal OSC 52 selection behavior, and long-session performance. This feature-level rollout still requires human product/merge judgment after the security issue is fixed.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

return;
}
if (platform === 'win32') {
spawn('cmd', ['/c', 'start', '', url], {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Do not pass model-authored link targets through cmd.exe. Assistant Markdown is rendered as OSC 8 with the raw href, and a click forwards that value here. With windowsVerbatimArguments: false, an argument such as https://example.com/?x=1&calc.exe is not quoted merely because it contains &, so cmd /c can interpret &calc.exe as another command. That makes a displayed assistant link a click-triggered command-execution path on Windows. Please restrict accepted protocols (the desktop already allows only http:, https:, and mailto:) and use a platform opener that does not parse the target as shell syntax; add a Windows-focused regression covering &, |, %, quotes, and rejected schemes.

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

Labels

effort/XL Over 1000 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants