[UI] Show uncommitted work as a row in the stack - #42
Merged
Conversation
This was referenced Jul 26, 2026
KCaverly
force-pushed
the
ct/diff_view/303beec7
branch
from
July 26, 2026 19:23
b591f18 to
8644387
Compare
KCaverly
force-pushed
the
ct/diff_view/64abe23f
branch
from
July 26, 2026 19:23
bb0a433 to
ae19b26
Compare
The diff viewer lists untracked files by name in its file index but shows nothing of what is in them, because `git diff HEAD` never reports a file git isn't tracking. For a brand-new file that leaves the most interesting part invisible — the contents are the whole change. DiffUntracked diffs each path against os.DevNull with `git diff --no-index`, which renders it as a whole-file addition beneath an ordinary `diff --git` header. That shape matters: the diff builder indexes those headers for its file-jump keys and colours the + lines, so new files behave like tracked ones with no styler changes. Binary files collapse to git's one-line notice rather than inlining their bytes. --no-index follows diff(1) and signals "the inputs differ" with exit 1, which Git treats as failure. Rather than a second runner, Git now delegates to an internal git() that takes one forgivable exit code; -1 (accept only 0) keeps every existing caller's behaviour byte-identical. Only an ExitError is forgiven, so a timeout or a missing binary still fails. Per-path errors are skipped rather than propagated. An untracked file is by definition outside git's control and can vanish between the status call that listed it and this one; losing every other file's diff to that race would be the wrong trade. Caller-driven path list: each file costs a subprocess, so the cap belongs with the caller that knows what it is willing to render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> ct-stack-id: 303beec7
The stack screen could only show you commits, so the work in progress — the thing most likely to be moving while you are looking at it — was the one part of the worktree the preview pane could not reach. Reading it meant leaving for the deck's diff viewer and its separate `u` scope toggle. Uncommitted work now appears as a synthetic row trailing the commits, and behaves like any other: it takes the cursor, drives the preview pane, and windows with the rest. Commits are bottom-first, so it sits last, which is where it belongs — above the tip. The cursor now indexes a row list rather than status.Commits directly, which is what makes that possible. clampStackCursor counts rows for the same reason: clamping to the commit count would knock the cursor off the last row on every passive refresh. Contents are staged and unstaged together, matching what the deck already counts as uncommitted, plus untracked file contents — `git diff HEAD` never reports those, so a brand-new file would otherwise show as a name with nothing in it. Untracked rendering is capped at 20 files, well clear of a realistic worktree, and the surplus is named in the section rule rather than silently dropped. Caching splits by row kind. A commit's patch is immutable, so it is fetched once and reused. Uncommitted work is the opposite — it changes under the screen, which is the whole reason to watch it — so landing on the row always revalidates, keeping the previous patch on screen while the new one is in flight. Only the first landing shows "loading…", and the scroll position carries across, or the row would be unreadable while an agent writes to the worktree. The row is hidden when the tree is clean rather than sitting there dead, and o degrades to a flash: uncommitted work has no PR and cannot have one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> ct-stack-id: 64abe23f
KCaverly
force-pushed
the
ct/diff_view/303beec7
branch
from
July 26, 2026 19:30
8644387 to
88bf444
Compare
KCaverly
force-pushed
the
ct/diff_view/64abe23f
branch
from
July 26, 2026 19:30
ae19b26 to
05c1cfd
Compare
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.
The stack screen could only show you commits, so the work in progress — the
thing most likely to be moving while you are looking at it — was the one
part of the worktree the preview pane could not reach. Reading it meant
leaving for the deck's diff viewer and its separate
uscope toggle.Uncommitted work now appears as a synthetic row trailing the commits, and
behaves like any other: it takes the cursor, drives the preview pane, and
windows with the rest. Commits are bottom-first, so it sits last, which is
where it belongs — above the tip.
The cursor now indexes a row list rather than status.Commits directly, which
is what makes that possible. clampStackCursor counts rows for the same
reason: clamping to the commit count would knock the cursor off the last row
on every passive refresh.
Contents are staged and unstaged together, matching what the deck already
counts as uncommitted, plus untracked file contents —
git diff HEADneverreports those, so a brand-new file would otherwise show as a name with
nothing in it. Untracked rendering is capped at 20 files, well clear of a
realistic worktree, and the surplus is named in the section rule rather than
silently dropped.
Caching splits by row kind. A commit's patch is immutable, so it is fetched
once and reused. Uncommitted work is the opposite — it changes under the
screen, which is the whole reason to watch it — so landing on the row always
revalidates, keeping the previous patch on screen while the new one is in
flight. Only the first landing shows "loading…", and the scroll position
carries across, or the row would be unreadable while an agent writes to the
worktree.
The row is hidden when the tree is clean rather than sitting there dead, and
o degrades to a flash: uncommitted work has no PR and cannot have one.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Stack (bottom → top):