Commit graph

8234 commits

Author SHA1 Message Date
Stefan Haller d355cb58a3 Session notes: selection-preserve net across commit rewrites (§21.33)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 3375637d93 Drop the bespoke commit-discard selection reveal
Discarding lines from a commit via the focused main view installed its own
revealSelectionAfterPrimaryAction before the rebase. That rebase rewrites
the commit, so the selection-preserve net now re-establishes the selection
as the diff re-renders — with the same anchor (the selection's first line),
making this install redundant. Files-discard keeps its own reveal: its diff
command is stable, so the net never fires there, and it has staging's
focus-follow to the secondary pane besides.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller fdb16a1c7e Preserve the focused main view's selection across commit rewrites
Dropping a hunk with `d` re-establishes the focused main view's selection
on the next surviving change, which feels great. But other operations that
rewrite the commit under the focused main view — moving a custom patch out
into the index, undoing right after a discard or a patch move — don't run
through the focused-main-view action handlers, so nothing was preserving
the selection. The stale gocui selection was left painted over the new
content, often as a large, now-meaningless range.

Rather than teach every such command to capture and restore the selection
(move-patch, undo, redo, and any future one), the focused main view now
preserves it itself, by its change-line ordinal, as the diff re-renders —
the command-agnostic counterpart of revealSelectionAfterPrimaryAction. The
diff side panels call it from their render-to-main before triggering the
render, so the restore rides the re-render.

It stands down unless the focused main view is current and shows a
selection, no precise restore is already pending (escape / post-stage
reveal / context-size place the selection more precisely), and the diff
command is actually changing. That last gate matters: a plain background
refresh re-renders the same commit's diff unchanged, and there the
selection — range and all — must be left alone; only a command change
(e.g. a rebase rewriting the commit's hash) means the content moved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 49e2fbc076 Session notes: drop commit SHAs from §21.32
SHAs go stale on every history rewrite of this throwaway branch; describe
commits by what they did instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller b3ac8fc259 Session notes: correct the patch-explorer claim for the driver fix (§21.32)
The driver fix is a no-op for the old staging panel, not a tightening: that
panel anchors its drag at the click on mouse-down, so it never exposed the
bug.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 0f99ba625a Session notes: first-drag-event driver fix (§21.32)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 6481ee615a Session notes: drag-to-range anchoring fix (§21.32)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 96fa098845 Anchor a drag-selection at the clicked line in the focused main view
Click-and-drag in the focused main view turns a hunk selection into a range,
but it was anchored at the change block's far end — where selecting a hunk
leaves the range anchor — rather than the clicked line. And on a context
line, where the click leaves no range anchor at all, dragging just moved the
single selected line instead of opening a range. Remember the line each
mouse-down lands on (the click can show a whole hunk, so it can't be read
back from the view) and, as the drag proceeds, anchor the range there while
the cursor end follows the mouse as gocui already moves it. Works for a click
that focuses the view and for one while it's already focused, on change and
context lines alike.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 79ce7f3382 Session notes: two hunk-mode click/pager bug fixes (§21.32)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 3e4207d7fa Preserve a whole selection's extent across a pager or context-size change
When the diff re-renders for a new pager (or a changed context size),
PreserveDiffPositionOnRerender restored the cursor by patch identity but left
the selection's other end — the range anchor — pinned to its old view line. A
pager that restructures the diff (delta side-by-side is the clearest case)
then left the selection spanning the wrong range of patch lines. Remember the
far end by patch identity too and put it back the same way, so the selection
covers the same lines however the new pager lays them out. One mechanism now
restores both ends, covering hunk and range selections alike; a single-line
selection still needs only the cursor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller e03f4b1321 Carry hunk mode into the other pane on a cross-pane click
Clicking the staged/unstaged pane you weren't focused on selected a single
line the first time, even in hunk mode, because that pane's select mode was
still its default until it had been focused once (tabbing to it and back was
the workaround). Seed the clicked pane's mode from the one we're leaving so
the very first click there behaves like every later one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 0aab64e362 Session notes: hunk-on-click UX tweaks in the focused main view (§21.32)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller d80802c1f9 Keep hunk mode when clicking another hunk in the focused main view
Once in hunk mode, clicking another change line reset the selection to a
single line, so you had to press `a` again for each block you wanted to
stage (a behaviour the main view inherited from the staging and patch-
building panels). Preserve hunk mode across clicks instead: a click on a
change line re-selects that whole block, while a click on context — or any
click when we weren't in hunk mode — drops to a single line, where the click
points precisely (e.g. to edit it).

The two click handlers shared an identical selection body, so unify them
into one helper and make the change in a single place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 8395658e75 Select the clicked hunk when clicking into the main view in hunk mode
Focusing the main view by keyboard already selects a whole change block in
hunk mode; a click, though, only ever placed a single-line selection, so the
common "click the diff to stage this block" gesture still needed a follow-up
`a`. Now a click on a change line selects that line's block too. A click on
context still selects just that line, since the click points at it precisely
(e.g. to edit it with `e`).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 6ac8c47091 Try another solution for the fight over the bg color (delta vs. selection) 2026-08-08 12:59:00 +02:00
Stefan Haller 3f3139de71 Spec: add diff-so-fancy as a third reference implementation
§10 now lists diff-so-fancy alongside delta and difftastic: the same #2 case as
delta's default (strips +/- markers, conveys side by color) but a line-oriented
Perl filter, unified-only, and -- the one wrinkle worth stating in the spec --
it strips terminal escapes from its content, so the record is prepended to the
line rather than embedded. Fix the now-stale "all three"/"both pagers" counts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 9273bb7e23 Design notes: diff-so-fancy emitter prototype (§12)
Record the third pager emitter (diff-so-fancy, commit c397cd6 on its
prototype-osc-metadata branch): the simplest of the three -- a line-oriented
Perl filter, unified single-column only, the same #2 category as delta's
default. Captures the diff-so-fancy-specific findings: sanitize_display strips
OSC so the record is prepended not embedded; the path is derived from
$file_1/$file_2 (not $last_file_seen, which is empty for a noprefix deletion);
combined diffs skipped; counters are file-scope globals so a chunk-spanning hunk
keeps counting; classification mirrors strip_leading_indicators so the
no-newline marker is correctly skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller fc134d643f Session notes: patched the three pager emitters + the spec (§21.31) 2026-08-08 12:59:00 +02:00
Stefan Haller a6fedd0255 Spec: define the version-only handshake record
Add §4.4: a conforming pager emits a version-only OSC 1717 record (no further
fields) as its first output, so a host can probe it on an empty diff — which
emits no per-line records — and tell "speaks the protocol" apart from
"unsupported pager", content-independently. Used by the focused main view's
raw-diff fallback to decide whether a pager's output is actionable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 40c035fd79 Session notes: detection reworked observe → probe (§21.30) 2026-08-08 12:59:00 +02:00
Stefan Haller c0ee7de776 gocui: swallow the version-only diff-metadata handshake OSC
A metadata-aware pager emits a version-only OSC 1717 record (no fields) as its
first output, to announce it speaks the diff-line-metadata protocol so lazygit
can probe for it. It isn't per-line metadata, so on a real render it must be
swallowed whole rather than lingering in the accumulator and attaching to the
following diff-header line (or, if newline-terminated, producing a phantom blank
line). Drop any OSC 1717 payload with no fields at its terminator; per-line
payloads always have fields, so they're kept.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller f3ef02556a Session notes: (D) raw-diff fallback for unsupported pagers done (§21.29) 2026-08-08 12:59:00 +02:00
Stefan Haller ae1ab5ca1e Extend the raw-diff fallback to the commit/stash/patch-building panels
Wire the remaining diff panels — local commits, sub-commits, commit files,
stash, and reflog — into the focused main view's raw-diff fallback, the same way
the files panel already is: each computes DiffMainViewShouldRenderRaw and renders
its diff raw (no pager) when focused under a pager whose output we can't resolve,
so its selection stays toggleable into a custom patch (or, for reflog, navigable
and copyable).

The commit-diff panels build their diff through the diff helper, which can't
reach the staging helper, so the panel computes renderRaw and passes it in.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 10f41bed50 Thread ignoreExternalDiff through the remaining diff-cmd builders
Prep for extending the focused main view's raw-diff fallback beyond the files
panel. ShowCmdObj, ShowFileDiffCmdObj, ShowStashEntryCmdObj and DiffCmdObj gain
the ignoreExternalDiff arg WorktreeFileDiffCmdObj already has — forcing git's own
coloured diff regardless of a configured external diff command. All callers pass
false, so behaviour is unchanged.

Also lift the pty-vs-command task choice out of the files controller into
types.NewMainViewDiffTask(WithPrefix), so the commit/stash/patch-building panels
(whose diffs are built in the diff helper, which can't reach the staging helper)
can select the same raw-fallback task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 2a69d4c140 Fall back to the raw diff when focusing a main view under an unresolvable pager
The focused main view is the staging surface, so it has to resolve the diff it
shows to patch-space. A pager that restructures the diff without emitting our
metadata (stock delta-default, plain difftastic, `cat -n`) produces output the
buffer parser can't read, which would leave its diff unstageable.

When such a diff is focused, re-render it raw — git's own colour, no pager — the
same content a no-pager setup shows, which the buffer parser handles. Browsing
keeps the pretty pager output; only focusing to act switches to raw.

Whether the pager is usable is decided by probing it: run it on empty input and
look for the version-only OSC 1717 handshake a metadata-aware pager emits first.
This is a pager-level, content-independent fact (a binary file, which has no
change lines under any pager, can't mislead it) and it's known before we render,
so we never render pretty only to discover mid-flight that we should have rendered
raw. The verdict is cached per pager (reset when the pager changes). No PTY is
needed — git needs a terminal to decide to invoke a pager, but the pager emits the
handshake regardless. A git-config external diff driver (useExternalDiffGitConfig)
is chosen per file via .gitattributes and a single diff can mix drivers, so
there's no one pager to probe; it's treated as unsupported (always raw).

Bypassing the pager needs two things, since a pager reaches the diff by two
routes: an external diff command (suppressed in the cmd via the new
ignoreExternalDiff arg, keeping git's colour, unlike plain) and a stdin pager
(GIT_PAGER, applied by the pty task — so the raw render uses a plain command
task instead).

This wires the files panel; the commit/stash/patch-building panels follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 482acff66d Session notes: (C) ctrl+o copy done (§21.28)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller f0a997705a Copy the selected diff line(s) from the focused main view
`ctrl+o` in the focused main view copies the selection to the clipboard,
mirroring the staging view's copy. Unlike stage / discard, copying is the same
for every diff panel — it just reads the text shown — so it's a direct
MainViewController command rather than a FocusedMainViewActions method. That
keeps it free of per-panel duplication and lets it work over panels that have
no actions, notably the reflog.

The +/- prefix handling is metadata-aware where it matters: with no pager the
main view shows the raw diff, so a homogeneous selection has its +/-/space
column stripped (dropDiffPrefix) to ease pasting into code. With a pager
configured the rendered lines carry no such column, so stripping a leading
character would eat real content — copy verbatim instead. (Refining the raw
case to classify header lines via the diff-line metadata, fixing the
'--- a/file' edge case noted in 159bbb0825, is left for later.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller a0af1b1d9a Session notes: discard bug fixes + selection visibility (§21.27)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 252a26cbc4 Hide the focused main view's selection when there's no diff to act on
The focused main view always showed a selection while it held focus, even when
its content was a placeholder ("No changed files", a merge-conflict message)
rather than a diff — so pressing a navigation key conjured a highlighted line
over "No changed files", and discarding the last change (or changes vanishing
outside lazygit) left a stale selection behind.

A selection should only show when there are change lines to act on. Two
moments establish or change the content:

- Focus: showInitialDiffSelection now leaves the selection off when the view
  has no change lines (ViewHasChangeLines), rather than highlighting a stray
  line at the top.
- Render: the side panel's render-to-main is where it decides between a diff
  and a placeholder, so that's where the selection's visibility is set —
  updateFocusedMainViewSelectionVisibility shows it only on the focused pane
  and only when a diff is being rendered. This covers the refresh cases that
  focus can't: discarding the last change, and changes disappearing or
  reappearing outside lazygit, all hide or restore the selection on the next
  refresh. (Focusing reuses the already-rendered content rather than
  re-rendering, which is why focus needs its own check.)

Wired into the files panel, where placeholders occur; the commit panels always
render a diff. Adds a SelectionIsShown/SelectionIsHidden test assertion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 26dcab23ae Session notes: (B) d discard a hunk done (§21.26)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller e9c4425df6 Discard the selected diff line(s) from the focused main view
Wire the focused main view's `d` binding to a DiscardSelection action on the
side panel beneath, the discard counterpart of the primary action. As with
stage/toggle, what discard means is the panel's business:

- the files panel discards from the working tree, mirroring the staging
  view's `d` — reverse-apply not cached on the unstaged side (destructive, so
  confirmed), reverse-apply cached on the staged side (i.e. unstage);
- the commit panels (commit files, and the whole-commit diff of the local
  commits / sub-commits / stash panels) remove the selected lines from the
  commit via a rebase, mirroring the patch builder's "discard lines from
  commit" — building a one-off patch from the selection and deleting it.

The commit-discard backend and its guard are shared free functions in
patch_building_from_main_view.go, paralleling the patch toggle. Discarding
from a commit is only possible on a local branch, so DiscardSelection grows a
disabled-reason companion on the FocusedMainViewActions interface: the `d`
binding is greyed with a reason on stash and other-branch sub-commits (never
rebaseable) and while a rebase is in progress, exactly as the patch builder
greys it. The files panel is always available (a zero-context diff is the one
error it reports inline, matching the staging view).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 99f80b1bc8 Generalize FilesController.stageDiffLines to applyDiffLines
The focused main view's discard command (coming next) needs to read either
side's diff and apply a patch with an arbitrary direction and target: discard
from the working tree reads the unstaged diff but applies a reverse patch that
is not cached. stageDiffLines couldn't express that — it used a single reverse
flag both to pick which diff to read and to reverse the apply, and hardcoded
Cached: true.

The read side and the apply direction are independent (they coincide only for
staging and unstaging), so split them: applyDiffLines now takes sourceCached
(which diff to read) and a git_commands.ApplyPatchOpts (how to apply). Renamed
from stageDiffLines since it now covers stage, unstage, and discard. Staging
passes the same values as before, preserving behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 2d5d149f5e Session notes: (A) dispatch refactor done (§21.25)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 88dae2f2c6 Collapse the focused-main-view handler channels into one
Each command a side panel handles in the focused main view needed its own
delegation channel: a HasKeybindings getter, an IBaseContext Add method, a
BaseContext field + getter, a baseController nil default, and an attach.go
registration — roughly five touch points per command. With three commands
(click, stage, toggle-patch) that was already a lot of boilerplate, and it
doesn't scale to the discard / copy commands coming next.

Replace the three channels with one: a side panel exposes a single
FocusedMainViewActions interface via GetFocusedMainViewActions (nil when its
diff offers no actions), and the controllers implement that interface
directly. The stage and toggle-patch handlers, already unified to a plain
error return, become one PrimaryAction method whose meaning is the panel's
business (stage for the files panel, patch toggle for the commit panels);
the click handler becomes OnClick. MainViewController is now a thin
dispatcher: fetch the actions from the panel beneath, call the method.
Adding a command is now one interface method, an implementation in the two
or three controllers, and a keybinding — no plumbing.

The toggle-patch channel did double duty as the "this panel builds a custom
patch" signal for the inclusion gutter (shown only beneath a patch-building
panel, not the staging files panel). Collapsing the channels removes that
proxy, so make the classification explicit on DiffMainViewContext, whose
marker method now returns a DiffMainViewType (none / staging / patch-building)
instead of being a bare marker. The gutter shows only beneath a panel whose
type is patch-building (commit files, local commits, sub-commits, stash).

Behavior-preserving.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 96b09bb41d Push the staging focus-follow into the side-panel handler
The focused main view's space binding delegates to the side panel beneath
it, but the two handlers were asymmetric: the patch-toggle handler did its
own re-render and re-established the selection itself, while the staging
handler returned a focusViewName so the dispatcher (stageRange) could do the
reveal-and-focus dance on its behalf. That split blocks collapsing the
per-command handler channels into one, since the two have different return
types.

Make staging match the toggle: GetOnStageFocusedMainView returns plain
error, and the FilesController handler does the post-staging reveal and pane
focus itself. The reveal/select-mode logic the two handlers shared (collapse
a range to a line, preserve the change-line ordinal across the re-render,
re-expand a hunk) is extracted into revealSelectionAfterPrimaryAction, with
mainContextForViewName resolving a main view name to its context. The
dispatcher is now uniform: read the selected range, hand it to whichever
handler the panel registered.

Behavior-preserving.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 1b02020022 Session notes: revised forward plan (§21.24) — drop steps 7/8, add commands + dispatch refactor + pager fallback
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 4fe5211da0 Session notes: crash fix for resetting a patch built from the commits main view
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller f4e180c128 Session notes: step 6c done (patch building from the whole-commit main view)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 86ce3ba798 Build a custom patch from the commits / sub-commits / stash main views
Until now space toggled lines into a custom patch only from the commit files
main view (the per-file diff). Register the same toggle handler on the commits,
sub-commits and stash panels, so a patch can be built straight from the
whole-commit (multi-file) diff their main view shows, without first diving into
the commit files panel.

The handler lives on SwitchToDiffFilesController, which is already bound to
exactly those three panels and already knows how to derive the patch target
(from/to/reverse/canRebase) for the selected ref — pulled out of enter() into a
shared canRebase helper and the reused FromAndToForDiff. The post-toggle refresh
is the cheap one: these panels have no per-file patch indicator to update, so we
just re-render their own main + secondary views rather than reloading the whole
commit list on every keystroke.

Sub-commits and stash didn't render the secondary patch view at all; give them
the same secondaryPatchPanelUpdateOpts the commits panel uses, and recompute the
inclusion gutter as their diff (re-)renders, so the cumulative patch and the
gutter track the toggle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 26b27ed69b Extract the patch-toggle back end out of CommitFilesController
The next commit adds patch building from the commits / sub-commits / stash
main views, which toggle the same way as the commit files main view; only the
patch target and the post-toggle refresh differ. Pull the panel-agnostic back
end (the toggle skeleton, the line toggle, the post-toggle reveal, the patch
filename mapping) into shared free functions so both controllers reuse it.

The skeleton now takes the patch target (from/to/reverse/canRebase) and a
refresh callback as parameters rather than reading them from the commit files
context and hardcoding Refresh({COMMIT_FILES}). The commit files handler passes
exactly what it did before, so behaviour is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller e074c40cc9 Session notes: first interactive feedback on the gutter (4 of 5 fixed)
Records the visibility model (gutter visible iff patch-active AND main
focused), the selection re-select after the split re-render, the
all-wrapped-segments marker, and the deferred pager-switch bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller c14d9432cb Session notes: toggle refreshes normally; scroll preserved for free
Records the fix for the §21.20 live-update gap: the patch toggle now
refreshes (updating the browser indicators + secondary patch summary +
the split), scroll/selection are preserved automatically because the
re-render uses the same diff command, and the gutter rides the same
re-render via the pipeline's own content-equality test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 115582e9bc Session notes: step 6 built — inclusion gutter + patch toggle (6a/6b)
Records what landed, the confirmed sync (no re-render) insight, what
needs interactive sign-off, the known limitations, and the 6c/step-7
next steps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 67ba1899b4 Build a custom patch from the focused main view of a commit's files
Pressing space in the commit-files focused main view now toggles the
selected line(s)/hunk into or out of the custom patch, instead of being a
no-op. It's the patch-building counterpart of working-tree staging from
the main view: the selection is resolved to change-line identities the
same way (ChangeLinesInViewRange), then mapped to the patch builder's
per-file line indices and added or removed.

The crux is that the commit's diff is unchanged by a toggle — only the
inclusion set changes — so unlike staging there's no async re-render to
ride. Membership is shown by an on-demand inclusion gutter: a reserved
left column painted with a marker on every change line currently in the
patch, recomputed synchronously after each toggle and on focusing the
main view, over the existing pager output. This is what lets patch
building work over a restructuring pager at all, where the old green
first-char overlay (which rewrites the rendered bytes) can't.

space routes to staging or patch building based on which handler the
panel beneath registers, via a new onTogglePatchFocusedMainView channel
(kept separate from staging because the post-action differs: sync gutter
repaint vs async reveal). Scoped to the commit-files panel for now;
commits/sub-commits/stash and the whole-commit multi-file diff follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 2fee7653f2 Extract stageRange from stageSelectedLine in the main view
Prep for routing space to custom-patch building when the panel beneath
the focused main view is a commits/commit-files panel. stageSelectedLine
becomes the router; the staging-specific body (async re-render + reveal)
moves to stageRange. Behavior-preserving.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 4c123f9414 Add identity-based line accessors to PatchBuilder
The patch builder works in patch-line-index space, which the patch
explorer can supply directly from its parsed-patch state. The focused
main view can't: it only knows a selection as diff-line metadata
identities (file line number + deletion?), and those indices differ
between the raw diff and however a pager renders it.

Add the conversion at the boundary so the main view stays in identity
space: PatchLineIndicesForLines maps identities to indices (for
toggling) and IncludedLineIdentities reports the included change lines
as identities (for the inclusion gutter to match rendered rows against).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 12523c8a20 Add an on-demand inclusion gutter to gocui views
A reserved left-hand column that draws a per-line marker glyph and shifts
the content right past it, for the custom-patch inclusion overlay (which
needs to show which change lines are in the patch over arbitrary pager
output, where it can't touch the rendered bytes).

It's pure draw-time decoration: the content buffer, and so the diff-line
metadata, click resolution and wrapping inputs, are untouched. The marker
is drawn on a line's first wrapped segment only, and the gutter narrows
the content wrap width while shown. Off by default; SetInclusionGutter
turns it on per view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 01882cf2fd Session notes: step 6 design — the on-demand inclusion gutter
Records the plan for patch-building from the main view: the sync (no
re-render) vs async (staging) split, the included-set mapping, the
user's gutter decisions (on-demand, inclusion-only, checkmark), the
draw-time gocui rendering approach, and the 6a/6b/6c decomposition.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-08 12:59:00 +02:00
Stefan Haller 22e1c82405 Session notes: prototype-branch callout; step 5 signed off; step 6 next 2026-08-08 12:59:00 +02:00
Stefan Haller 2421e19f5d Session notes: reveal reworked to preserve change-line ordinal 2026-08-08 12:59:00 +02:00