feat(pager): static ANSI render for captured pager hosts#9
Merged
Conversation
fd3f44d to
b569e18
Compare
Launched as a captured pager (LazyGit, `git -c core.pager="dunk pager" log -p` — anything that pipes a custom pager into its own panel with `TERM=dumb`), dunk hit the app branch and OpenTUI attempted a full alt-screen render against a non-TTY pipe: broken or blank output. Add two `StartupPlan` outcomes — `static-diff-pager` and `passthrough` — and `isCapturedPagerHost(env)` detection. A known captured host gets a static, colored ANSI diff (checked before the generic non-TTY case so it fires even though the host's stdout is a non-TTY pipe); any other non-TTY/dumb pipe echoes the raw patch verbatim. `staticDiffPager.ts` is a thin adapter: it reuses the existing `loadAppBootstrap` → Pierre `buildStackRows` planning path, reads all colors from `themes.ts` (no `renderRows.tsx` change), skips syntax highlighting so a per-selection re-spawn stays snappy, and never renders the comment overlay. `main.tsx` drains via `Bun.write` before exit so large diffs aren't truncated. Behavior change: `<patch> | dunk pager` with a non-TTY stdout and no captured-host markers now echoes the patch instead of attempting a broken TUI. A real TTY still launches the interactive UI. Backports modem-dev/hunk#271, redesigned for dunk (Option B, expert design- and implementation-reviewed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b569e18 to
298cae7
Compare
Merged
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.
Context
Backport of modem-dev/hunk#271. PR 5 of an expert-gated backport series — the largest, so it had a dedicated expert design review before any code and an implementation review after.
Launched as a captured pager (LazyGit,
git -c core.pager="dunk pager" log -p— anything that pipes a custom pager into its own panel withTERM=dumb), dunk fell into theappbranch and OpenTUI attempted a full alt-screen render against a non-TTY pipe: broken or blank output.What was changed
StartupPlanoutcomes —static-diff-pagerandpassthrough— plusisCapturedPagerHost(env)(TERM=dumb+GIT_PAGER/LV=-c/LAZYGIT*). A known captured host is detected before the generic non-TTY case, so it fires even though its stdout is a non-TTY pipe; any other non-TTY/dumb pipe echoes the raw patch verbatim.src/ui/staticDiffPager.ts— a thin adapter (chosen over upstream'srenderRows.tsx → rowStyle.tsextraction, per expert design review). Reuses the existingloadAppBootstrap→ PierrebuildStackRowsplanning path, reads every color fromthemes.ts(zerorenderRows.tsxchanges), skips syntax highlighting so a per-selection re-spawn stays snappy, and never renders the comment overlay.main.tsxdrains viaawait Bun.write(Bun.stdout, …)before exit so a large diff isn't truncated. ANSI-colored git input is handled transitively (the shared loader already strips it before Pierre).AGENTS.md/CLAUDE.mdupdated: pager paths two → four,StartupPlannamed as the single enumeration.Behavior change
<patch> | dunk pagerwith a non-TTY stdout and no captured-host markers now echoes the patch verbatim instead of attempting a broken TUI (the previous code had no TTY guard and always tried to launch the app). A real TTY still launches the interactive UI; the TUI-path startup test was updated to declare its TTY requirement explicitly — this reflects the fix, it is not a weakened assertion (verified against the pre-change branch, which had no guard).Verification
bun run typecheck,bun run lint: clean.staticDiffPager.test.ts+startup.test.ts: 18 pass — serialization, theme-field drift guard vsstackCellPaletteacross all built-in themes, 6-hex validation across all themes, hunk-header toggle, no-overlay, empty changeset, per-arm captured-host detection (LAZYGIT*/GIT_PAGER/LV), and theTERM=dumb-gate false-positive guard (globalGIT_PAGERmust not force static in normal use).test/cli/entrypoint.test.ts: captured host → ANSI, never enters the alt screen; plain non-TTY → verbatim passthrough.src/core+src/ui: 333 pass. PTY: 21 pass. Real captured-host smoke shows a colored static diff; plain non-TTY shows verbatim passthrough; neither enters the alt screen.Out of scope
Upstream's
rowStyle.tsextraction fromrenderRows.tsx— deliberately not ported (would refactor dunk's diverged windowed renderer for no functional gain; expert-endorsed Option B). Syntax highlighting in static output (skipped for hot-path latency). Auto theme detection (separate evaluation).🤖 Generated with Claude Code