Skip to content

feat(review): add working-tree staging, hunk actions, and focused files-pane UX - #1021

Open
yusing wants to merge 27 commits into
modem-dev:mainfrom
yusing:main
Open

feat(review): add working-tree staging, hunk actions, and focused files-pane UX#1021
yusing wants to merge 27 commits into
modem-dev:mainfrom
yusing:main

Conversation

@yusing

@yusing yusing commented Sep 7, 2026

Copy link
Copy Markdown

Problem and user impact

Working-tree review could show a diff, but staging, unstaging, discarding, and stashing still required leaving the TUI. File-list navigation also competed with the review stream for the same keys.

This PR adds default Git working-tree actions while preserving the full multi-file review stream:

  • Tab switches the full Unstaged / Staged stream.
  • Space or file-row double-click stages remaining unstaged changes, or unstages a fully staged file. After a code-line click or [ / ], Space acts only on that hunk.
  • d confirms discard of the selected file or folder, either all changes or only unstaged changes where supported.
  • s stashes only the selected file or folder, preserving its staged/unstaged split and unrelated work.
  • e opens the selected source line in $EDITOR, mapping staged coordinates through later worktree edits; supported editors include micro.
  • Selectable folders apply actions to their visible projection: nested files in tree view, or the files in a compact group.
  • Focused-pane chrome makes keyboard ownership visible. Arrow keys and j/k navigate the focused files pane; Enter focuses its selected review and Escape returns to the files pane.
  • Git-style status colors and line counts remain visible across both tabs.

Keyboard and mouse actions share the existing command path. Built-in review shortcuts retain their meaning outside an actionable working-tree files pane.

Approach and ownership

Keep Pierre as the renderer and the shared review document as the semantic model. Optional VCS provider operations implement mutations behind hunkdiff/extension; useWorkingTreeActions owns host-side action targeting and AppHost owns write/reload/shutdown coordination. Sidebar selection still navigates the whole stream rather than creating a single-file review.

The bundled Git provider implements attested file and canonical byte-preserving hunk staging, scoped discard/stash (including stashFiles), and staged-to-worktree editor-line mapping. Provider commands remain in packages/hunk-git.

Working-tree inventory and mutation capabilities require API v25: stageHunk / unstageHunk, discardFile / stashFile, resolveWorkingTreeLine, inventory status and line counts, and stashFiles. Upstream API v20 supplies clipboard capabilities but does not supply these working-tree operations.

Sidebar extensions receive selectedEntryId, selectEntry, and toggleEntry. These default working-tree controls belong in the host, while provider-specific behavior stays behind the existing extension contract.

Review follow-up fixes

  • Read staging eligibility, file/folder targets, and hunk selection at dispatch time, including several terminal inputs arriving before a React commit.
  • Keep file and hunk scope transitions correct in both directions within one input burst.
  • Exclude extension workspace writes while a Git action owns its write-and-refresh boundary, and preserve the reverse exclusion and graceful-shutdown tracking.
  • Add real AppHost regressions for file/folder/hunk bursts and both write arrival orders, plus a raw-input PTY regression.
  • Preserve the latest extension dialog answer, note draft edits, discard/stash targets, and newer note hover during deferred UI work.
  • Window large unwrapped diffs before the first viewport measurement; support micro editor line jumps and improve Jujutsu source-error context.
  • Correct stale architecture paths, keep release summaries to one sentence, and fix the adapter formatting failure.

Upstream rebase and validation

Rebased onto the actual modem-dev/hunk/main commit 6d4440a, resolved conflicts, and pushed the rewritten branch with an explicit force-with-lease. GitHub now reports MERGEABLE at head c4a0785. The earlier pull against the fork's origin/main did not incorporate upstream; this rebase replaces that incorrect update.

Conflict resolution preserves upstream's shortened Git comparison titles, clipboard actions, history review metadata, and saved-view quit dialog alongside the PR's working-tree staging, selection, and pane-focus behavior. Test fixtures now include the upstream clipboard action, and capability documentation requires API v25 for working-tree operations rather than implying that upstream API v20 supports them. A targeted independent source inspection approved the integration.

Validated on Linux with Bun 1.4.2 and Node 24.20.0:

Command Result
bun run typecheck Passed
bun run format:check Passed
bun run deps:check Passed
bun run check:docs Passed
bun run check:changelog Passed
bun run test 4,087 passed, 40 skipped
bun test packages/hunk/src/ui/components/ui-components.test.tsx 96 passed
Focused provider, AppHost, and ExtensionPane command below 71 passed
bun test ./test/review-conformance 111 passed
bun run test:session-broker-node 4 passed
bun run test:tty-smoke 9 passed
bun run install:bin Passed

Focused rebase integration command:

bun test packages/hunk-git/src/index.test.ts packages/hunk/src/ui/AppHost.working-tree.test.tsx packages/hunk/src/ui/AppHost.workspace.test.tsx packages/hunk/src/ui/AppHost.review-metadata.test.tsx packages/hunk/src/ui/components/panes/ExtensionPane.test.tsx

The ordinary lint traversal is affected by a parent ignore rule. Explicit tracked-file lint passed on 993 files:

git ls-files '*.ts' '*.tsx' '*.js' '*.jsx' '*.mjs' '*.cjs' | xargs node_modules/.bin/oxlint --deny-warnings

Terminal-suite limitations

bun run test:integration completed with 175 passed, 1 skipped, 2 failed:

  • add-note affordance appears only after mouse movement in a real PTY
  • clicked add-note drafts can cancel and save with keyboard shortcuts

Both timed out looking for the add-note affordance. The subsequent complete notes-suite rerun passed all 21 tests:

bun test ./test/pty/notes.test.ts

The two exact cases also passed 20 executions on the new upstream base:

bun test ./test/pty/notes.test.ts --test-name-pattern 'add-note affordance appears only|clicked add-note drafts' --rerun-each 10

A broader upstream run, bun test ./test/pty/notes.test.ts --rerun-each 3, produced 56 passes and one failure in the existing CJK draft-frame assertion. Earlier validation on upstream 0b730465 reproduced the add-note hover timeout, current-line repaint assertion, and CJK draft assertion. These results do not establish that both latest timeouts reproduce on the latest upstream commit; the complete PTY run remains recorded as non-green, not hidden behind the successful rerun. No unrelated terminal behavior was changed to address these intermittent cases.

Platforms and visual evidence

  • Linux source, host, PTY, and real-TTY smoke coverage exercised.
  • macOS and Windows not re-verified; the macOS terminal-revocation case and unavailable Jujutsu/Sapling cases remain skipped.
  • No screenshots or video attached yet. Automated real-PTY coverage is not a substitute for PR visual evidence.

Non-goals and limitations

  • No Jujutsu/Sapling mutations, browser/session remote Git writes, single-file main-pane mode, or renderer replacement.
  • Binary, text-converted, and metadata-only changes retain file actions; hunk staging refuses unsupported patches.
  • Unknown editor line syntax is refused instead of guessing a location.
  • Stashing requires an initial commit.
  • Discard/stash shortcuts require the visible, focused files pane; otherwise built-in scrolling/sidebar behavior remains available.
  • Git actions retain the host exclusion guard through authoritative refresh, including after partial failures.

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

PR author is not in the allowed authors list.

@vercel

vercel Bot commented Sep 7, 2026

Copy link
Copy Markdown

@yusing is attempting to deploy a commit to the Modem Team on Vercel.

A member of the Team first needs to authorize it.

@benvinegar

Copy link
Copy Markdown
Member

@yusing This feels like a large UX change, with no screenshots/video etc. Did you consider building this as an extension instead?

(It's unlikely I'm going to merge this because it's not aligned with the project goals. The core is not trying to rebuild lazygit. But if you need more extension points to make that happen, that's worth discussing.)

https://hunk.dev/extensions

Expose both Git status sides through the public VCS and sidebar contracts. Stage or unstage exact files with Space and consecutive mouse double-clicks, preserve unrelated changes, and reconcile writes through reload and graceful shutdown. Add provider, command, component, and PTY coverage plus extension and keybinding documentation.
Apply byte-preserving canonical Git hunks with status and HEAD attestation. Keep unrelated hunks and worktree content intact, refuse text-converted patches, and integrate scoped Space and release-confirmed double-click actions through the provider contract.
Add exact-file confirmation and stash-message prompts with contextual keys and mouse parity. Preserve unrelated live changes and stash trees, partial staging, tracked modes, and recoverable published stashes when cleanup fails.
Track deliberate line selection separately from viewport seeding, fall back to the active hunk's first change, and map staged source addresses through later worktree edits. Guard asynchronous lookups by review generation and refuse unsupported editor line syntax.
Render index and worktree status columns with semantic colors, highlight
selected rows across the full sidebar width, and color fully staged
filenames. Add optional `statusCode` to the v21 extension API, expose
Git status codes, and document and test the new sidebar behavior and
theme contrast.
Hide shadowed key hints from help and menus, keep file quick actions
scoped to the focused sidebar, and fall back to file staging when hunk
staging is unavailable. Add coverage for pointer focus, shortcut
precedence, and working-tree behavior.
Track focus ownership between the files pane and the review stream so
pointer and command toggles move cursor ownership cleanly:
`↑`/`↓`/`j`/`k` now step files when the files pane is focused,
review focus stays in the diff stream, and `Space` stages the file when
a selected hunk is not stageable. Review-stream clicks now drop
files-pane quick-action scope, and focused panes are visually emphasized
with brighter frame and divider styling.

Centralize shortcut labeling for help and menus through first-match
command ownership so a command only advertises keys that are actually
reachable, preventing shadowed aliases from appearing while preserving
visible alternatives on the real owner. Updated command descriptions,
docs, and focused-pane/interaction tests to match the new focus model
and selection behavior.
Add a files-pane selection model that tracks folder rows alongside
files, and thread folder-aware entry IDs through sidebar rendering and
extension working-tree props. Folder and file rows are now
selectable/clickable with updated focus and highlighting, and the
focused entry is kept coherent across tree and compact sidebar modes.

Wire the new model into working-tree actions so `Space`, discard, and
stash now operate on the focused files-pane row, including folders.
Folder actions apply to all nested files in tree view or all files under
a compact group row, with updated prompts/messages and selection
behavior.

Bump extension VCS API version and expose
`selectedEntryId`/`selectEntry`/`toggleEntry`, plus a new provider
operation `stashFiles` so folder actions can stash multiple attested
files in one operation. The Git adapter now performs multi-file stashes
while preserving per-file staged-untracked semantics, with docs and
tests updated for the new behavior.
Add optional `stats` (`additions`/`deletions`) to working-tree file
inventory in API v23 and enrich git status rows from `git diff
--numstat` across both staged and unstaged sides, including
rename/copy-safe parsing and untracked/symlink/large-file count
handling, so sidebar +/- stays visible even when a file is off the
active review stream.

Preserve folder selection semantics by choosing a nested path on the
current tab when available so folder-stage/discard/stash actions do not
unexpectedly jump sides, and update sidebar rendering helpers, selection
tests, and docs/keybinding wording to reflect file-or-folder actions and
stable stats visibility across tabs.
Add `hunk.review.focusDiffPane` and `hunk.review.focusFilesPane` command
entries and App bindings so Enter focuses the selected file’s review
and Escape returns focus to the files pane, with enablement tied to
active pane state and selection kind. Normalize Escape key matching for
`escape`, `esc`, and raw `\u001b`, expose both actions in file
menu/help/keyboard docs, and extend command, menu, and interaction tests
(including mode handoff coverage with overrideable bindings) for the new
focus behavior.
Add micro editor line-jump support for the `e` shortcut, including
editor flags, documentation, and PTY coverage.
Keep large unwrapped diffs responsive on the first frame by applying
provisional row bounds before the terminal viewport is measured. Update
PTY coverage for split and stack modes, keyboard readiness, and shared
sidebar frame assertions.
@yusing

yusing commented Sep 7, 2026

Copy link
Copy Markdown
Author

Rebased onto modem-dev/hunk/main at 6d4440a and resolved the Git-provider, clipboard, and pane integration conflicts. Force-pushed with a lease to c4a0785; GitHub now reports MERGEABLE. The earlier fork-only rebase did not incorporate upstream and has been corrected. Updated the description with fresh validation: 4,087 unit tests pass, 71 focused integration tests pass, 9 TTY smoke tests pass; the full PTY run had two add-note timeouts, followed by a green 21-test notes rerun.

@yusing

yusing commented Sep 7, 2026

Copy link
Copy Markdown
Author
image

Sorry that it does not align with the project goal. My original idea was I love this UI, but I also want lazygit like git control on the hunk diff UI so I don't have to switch btw lazygit and hunk. I will try making it an extension and see how it goes.

@benvinegar

benvinegar commented Sep 7, 2026

Copy link
Copy Markdown
Member

My original idea was I love this UI, but I also want lazygit like git control on the hunk diff UI so I don't have to switch btw lazygit and hunk.

I appreciate that. Tell me what you need changed from the extension API (or add your own upstream contribs) and we'll make it possible.

You might also want to look at https://github.com/victor-software-house/hunk-history

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