Commit Graph
8512 Commits
Author SHA1 Message Date
Stefan Haller 150dfadf9b Acknowledge editor clicks in the diff
Non-suspending graphical editors can take a moment to reach the
foreground, leaving a modified click with no visible response. Briefly
reverse the clicked row's selection bar after launching the editor so
the registered action is apparent.

Arm the flash only for a resolved diff row, keep newer clicks safe from
stale timers, and rely on suspension to clear the transient state before
terminal editors take over.
2026-10-05 11:55:14 +02:00
Stefan Haller 7aff6d7eae Give views a transient line flash
Actions that hand control to another application need visible
acknowledgement without moving a view's cursor or replacing renderer
colors. Reverse the narrow selection bar independently of selection
state, and clear transient flashes whenever the terminal UI suspends.
2026-10-05 11:55:14 +02:00
Stefan Haller 6990c06880 Open a clicked diff line in the editor
Clickable renderer gutters are small and unavailable on some diff rows.
Make the whole row an editor target without changing focus or selection,
including while a popup is focused.

Use both Alt and Shift because terminal mouse protocols do not deliver
either modifier consistently across Ghostty, iTerm2, and VS Code.
2026-10-05 11:55:14 +02:00
Stefan Haller 9fdefc418d Keep mouse gesture modifiers stable
Bindings match modifiers exactly, so a modified press must not turn into
an unmodified drag or release halfway through the gesture. Capture the
press-time modifiers once and carry them until the button is released.

This also makes unbound modified clicks no-ops instead of silently
invoking plain-click behavior.
2026-10-05 11:55:14 +02:00
Stefan Haller 345191a16f Share diff-line editing with mouse actions
The selected-line keybinding and a modified click need the same path
from a rendered diff row to the editor. Give that operation an explicit
view-line argument before adding the mouse gesture.
2026-10-05 11:55:14 +02:00
Stefan Haller ef5f0dccfb Let mouse bindings work behind focused popups
Mouse events on views behind a popup are normally swallowed before their
bindings can run. Add an explicit early-dispatch opt-in for actions that
should remain live there, matching the phase where hyperlink clicks
already run.
2026-10-05 11:55:14 +02:00
Stefan Haller 23c0b40fce Separate redraws from view-line invalidation
A view can need repainting even when its cached wrapping is still valid.
Track that state independently so content-only flushes do not overload
tainted, whose only job is to request a viewLines rebuild.
2026-10-05 11:55:14 +02:00
Stefan Haller dc6ea4d515 Remove unused per-line highlighting
View.SetHighlight has no production callers.
2026-10-05 11:55:14 +02:00
Stefan HallerandClaude Opus 5 15532b74b9 Cleanup: remove error return value from Gui.SetRune, Gui.draw() et al
These always returned nil.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:55:14 +02:00
Stefan Haller 2aac999354 Remove the staging and patch-building panels (#6040)
9th PR in the stack of PRs towards staging hunks directly from the main
view. This one is stacked on #6039.

Everything the staging and patch-building panels did now happens in the
main view, so this PR removes them.

`enter` on a file, in the files panel or the commit files panel, focuses
the main view at that file's diff. Double-clicking the file row does the
same. Everything else about the flow is unchanged; there is simply no
separate panel to be in.

**Config changes, both migrated automatically:**

- `gui.wrapLinesInStagingView` → `gui.wrapLinesInDiffView`
- `gui.useHunkModeInStagingView` → `gui.useHunkModeInDiffView`

Notable change: `wrapLinesInStagingView` used to affect only the staging
panel, but not the normal diff view. The new `wrapLinesInDiffView`
affects the main view always, whether focused or not; I assumed this is
probably the desired behavior, but I don't use the option myself, so I
can't tell for sure.

**One thing to know if you use custom commands:** `staging`,
`stagingSecondary` and `patchBuilding` are no longer context names. If a
custom command still names one in its `context` field, that is now
reported as a config error which you need to fix manually.

The context size can now be changed while a patch is being built. The
refusal existed because the old patch explorer kept track of which lines
are included in the patch by their line indices in the diff, which would
change when changing the context size. The new mechanism doesn't have
this problem, because it keeps track of patch lines by their line
identity, which doesn't change.
2026-10-05 11:55:10 +02:00
Stefan Haller 550d4011cc Rename ctrl+o description to "Copy selected diff lines to clipboard"
In the staging panel it used to copy the selected text verbatim, but in
the focused main view it copies the diff lines (that's also what the
toast says), and now that the staging panel is gone, we can change the
text to make it more specific.
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot c04c192659 Remove the last staging-panel language from tests
Generic range assertions and migrated commit tests should describe the views
they still exercise. Drop stale explorer wording so failures and test intent
no longer point contributors at UI that does not exist.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 2bc2a59ad4 Document line actions in the focused diff
Users no longer enter separate staging or patch-building panels. Describe file
entry, pane switching, staging, and custom patch construction where those
actions now happen, and remove the deleted package from the developer guide.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandClaude Opus 5 0b47b0013a Let wrapLinesInDiffView govern the two main panes
Working in a diff used to mean the staging view, and this option said
whether the long lines there were wrapped. The main view does that work
now, and the option has had nothing to govern since the staging view went
away. Both panes wrap whatever they are given.

Make the option their wrap setting instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot aba33ad93b Name diff options after the view that now uses them
Hunk defaults and wrapping no longer belong to a staging panel. Rename both
public keys with automatic migration, and remove panel-era English strings so
config, UI, schema, and generated docs describe the surviving interface.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 5d72d7da99 Allow context changes while building a custom patch
The focused diff addresses patch lines by identity, so changing how much
context is rendered cannot invalidate an active patch. Remove the
explorer era refusal and prove that another line can still be toggled
correctly after the rerender.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 9ddd5fc65f Remove the staging and patch-building panel shells
Nothing routes to the explorer contexts after line actions and file entry
moved into the normal main pair. Delete their contexts, views, main-pair
wiring, discovery API, test drivers, and selection-state package so the
surviving diff view is the only implementation.

Their names go from the contexts a custom command may bind to as well, so
that a config still naming one is reported as the config error it now is
rather than taken as a context we simply failed to find.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot c455d91502 Remove the explorer behavior behind the retired panels
Focused main views now own selection, staging, patch construction, copying,
and context-size rerenders. Remove the parallel controller/helper stack and
its refresh scopes, while retaining custom-patch reset as mode behavior.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandClaude Opus 5 1c0624577b Move the diff-copy helper out of the explorer controller
The next commit deletes the controller this lives in, while the helper
itself is still wanted: it is what makes a copied selection paste
straight into code. Move it first, so that the deletion is a deletion.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot a825c5f7a7 Open file diffs in the focused main view
Enter and double-click should take users to the diff where line actions now
live, whether the file belongs to the working tree or a commit. Share the
existing focus, raw-fallback, and selection setup while retaining directory,
submodule, and conflict-specific behavior.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot c38560bfa2 Move the last explorer workflows onto the focused diff
Historical file removal and the staging/custom-patch demos still entered
the old panels even though their line actions already exist in the main
diff. Preserve their prompts, timing, and downstream results while changing
only the interaction surface.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 0902d0a5ea Drop test steps owned by the retiring explorers
The generic filter and range-selection suites already exercise their state
machines on surviving views, while focused-diff tests own range selection
and drag autoscroll. Remove only the repetitions that enter an explorer,
along with the staging-panel screen-mode test whose UI no longer survives.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan Haller f1232bbfd9 Migrate range select tests to focused main view 2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 59df93bfd5 Exercise commit and stash flows through the focused diff
Line staging remains part of several broader workflows: committing staged and
unstaged changes, bypassing hooks, and stashing only the staged part of a
file. Drive those selections through the main diff pair while preserving
their commit, focus, and exact stash-content assertions.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 4c0358d27f Build mixed custom patches from the focused diff
The broad regression for combining a whole file, a hunk, a range, and
individual lines must survive the patch-building panel. Keep the side panel
for the explicit whole-file operation and drive every content selection
through the commit's focused diff.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot a6b5dcdfdb Keep partial rename patches on the focused diff
Selecting a rename's content changes must remain distinct from selecting
the file operation itself. Build and remove that partial patch through
the focused commit diff, and keep asserting that the rename stays in its
source commit after the content change is taken out.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot f3e98ed222 Manage patch lifetime from the focused diff
Removing a whole or partial patch from its source commit, and replacing
a patch when the user selects another commit, must not depend on
entering a patch-building panel. Keep those state transitions covered
through the diff that now owns patch selection.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 8bfd86ca2c Build applied patches from the focused diff
Applying and reverse-applying custom patches must keep their staging,
dirty-worktree, and conflict behavior after the patch-building panel is
removed. Select the source lines in commit diffs while retaining every
assertion about the resulting working tree and conflict resolution.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot b45df1b612 Keep historical diff actions off the explorers
Editing a line whose working-tree position shifted, discarding part of
an added file, and giving up a patch by escaping are all behavior worth
retaining. Drive them through the focused commit diff so their coverage
no longer depends on the patch-building panel.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 41fd1d1c86 Move partial-patch destination tests onto the commit diff
Line-level patch moves must keep their behavior for added and deleted
files, adjacent additions, stacked branches, and conflicting earlier
destinations. Select those lines directly in the focused diff so only
the intended changes move and file-level metadata stays with the source
when appropriate.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 78d0898911 Move whole-patch destination tests onto the commit diff
Moving a complete patch must preserve each file operation whether its
destination is a new commit before or after the source, or an existing
commit earlier or later in history. Build those patches as ranges over
the focused diff so the coverage survives removal of directory toggling
in the patch builder.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 3c36c066ed Keep patch-move conflict coverage on the focused diff
Conflict and dirty-worktree handling happen after a custom patch is
built, but their tests must no longer rely on either explorer to build
it. Select whole and partial patches in the commit diff while preserving
the rebase-conflict and stash-restoration assertions.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 5ab45d93d1 Move index-operation edge cases onto the focused diff
Patch movement still needs coverage for partial modifications, adjacent
additions, custom diff settings, and selections spanning files after the
patch-building panel goes away. Drive each through diff-line identities
while keeping the resulting commit and index assertions unchanged.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot b75a5930a9 Preserve whole-file operations for added and deleted files
A focused diff must replace the patch builder's whole-file toggle
without losing file creation or deletion metadata. Ordinary modifications
and renames must remain line-level selections even when every changed
content line is selected.

Ask the patch builder's canonical raw diff whether it consists of one
hunk containing only additions or only deletions and no context. Use the
whole-file operation only when the selection also covers every change.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 9f6270692a Expose the missing whole-file semantics in the focused diff
The patch-building panel could move an added file as a file, while
selecting every visible change through the focused diff currently moves
only its content and leaves the empty file behind. Keep the intended
index state beside the current one so the replacement flow demonstrates
that gap before it is fixed.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot abf24eaa74 Exercise patch-move selection recovery through the diff
The commit rewrite triggered by moving a custom patch must keep the
user's place without relying on the patch-building panel. Build the
patch and leave the affected range selected in the focused diff so the
regression test follows the UI that survives this branch.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot aeaaf3df57 Keep whole-file discard coverage on the surviving diff view
Removing the staging panel must not lose the handoff that happens when a
file's final change disappears. Drive that behavior through the focused
main view so the test continues to require focus and selection to follow
the files panel onto the next diff.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandGitHub Copilot 2be96483fa Let focused diff tests own shared staging behavior
Keeping the same staging interactions covered through the explorer would
pin the suite to the panel that this branch removes. The focused main
view now owns line, range, hunk, navigation, search, context-size, and
rapid-input coverage.

Co-Authored-By: GitHub Copilot <copilot@github.com>
2026-10-05 11:44:42 +02:00
Stefan HallerandClaude Opus 5 fd70aee09b Remove the hint about hunk selection being the default
The hint was raised on entering the staging panel, once, to explain that
the selection mode there had changed and how to get the old one back. That
panel is gone, and with it the moment the hint was tied to; what is left is
a string nothing prints and a flag nothing reads.

The advice it carried is not lost: the option it names is documented, and
the key that switches modes is on screen while a diff is focused.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:44:42 +02:00
Stefan Haller 65dcff32e5 Build custom patches directly from a commit's diff (#6039)
8th PR in the stack of PRs towards staging hunks directly from the main
view. This one is stacked on #6035.

The same idea as the PR below it, for commits. `space` over a commit's
diff toggles the selected lines into the custom patch, and `d` removes
them from the commit.

It works from all five panels that show a commit's diff: commits, commit
files, sub-commits, stash and the reflog. The reflog and the stash never
had a patch-building view at all; they have one now.

- A gutter of checkmarks down the left of the diff shows which lines are
in the patch. The marks are worked out again wherever a pane's content
settles, so a renderer switch or a context-size change doesn't leave
them on the wrong rows.
- The lower pane previews the patch through your diff renderer, rather
than as plain text. The patch is materialized into two temp trees and
rendered as a real diff, so a renderer shows it the way it shows
everything else, and lines can be taken back out of it from that pane by
pointing at them.
- `d` is disabled, with a reason given, where the commit can't be
rewritten — the stash, sub-commits of another branch, mid-rebase — and
in the preview pane.
- Your selection survives the commit being rewritten under it. Moving a
patch out of a commit, or undoing that, used to leave a stale selection
painted over the new diff.

The `space` and `d` descriptions now depend on which kind of diff you
are looking at: "Stage" and "Discard" over the working tree, "Toggle
lines in patch" and "Remove lines from commit" over a commit.
2026-10-05 11:44:39 +02:00
Stefan HallerandClaude Opus 5 1e58904a93 Take lines back out of the custom patch from the pane showing it
Space in the pane previewing the patch had nothing to act on: everything
there is in the patch already, so it can only mean taking those lines back
out, in the way that space in the staged side of the working tree's diff
only unstages.

A line of the patch can't be found by its number: the patch renumbers
whatever follows a change it leaves out, so a line of it names a line of
the commit's diff that isn't the one shown. A line is identified instead
by its position among its file's changes, counted in the diff the patch is
shown as. That is the same position it has among the changes the patch
holds for the file, since both are in the order the file has them. This
holds however the rendering came out, so a renderer that groups a hunk's
deletions before its additions, or leaves a change out altogether, no
longer matters.

The pane's rows are stated in terms of the trees the patch was
materialized into, so the paths come back to the repo's own before
anything is looked up in them. This also lets the lines there be copied
and edited, as the lines of a commit's diff can be.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 b6ce478d46 Materialize the custom patch so the diff renderer can show it
The pane beside a commit's diff showed the patch being built from it by
assembling the text itself. That text could not be handed to a diff
renderer the way a diff can: a stdin filter might have coped, but a tool
that diffs two files could not. Its idea of how much context to show
around a hunk was also its own rather than git's.

Materialize the patch instead: write each of its files as it is before the
patch into one tree and as it is after into another, and let git diff the
two trees. The patch becomes a diff of real files, rendered by whatever
renders the rest of them, with git's own context around it. Its lines can
then be pointed at; taking them back out of the patch will need that.

The trees are named a and b, so that with git's own prefixes suppressed
the paths read like an ordinary diff's over the repo's own paths. They are
written when the patch changes rather than when it is shown, the patch
builder counting its own versions for that, and they go away with the
patch.

A renamed file is materialized under the name the patch expects to find it
under. Where the patch carries the rename, that is the name the file had
before, so the rename comes out as a rename. git names the trees
themselves in the two rename lines, having only the two paths to go by.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 770f788ef8 Mark the lines of a commit's diff that are in the custom patch
A patch built from what is on screen has to show what is in it, over the
diff those lines were taken from — which may be a diff renderer's
rendering of it, whose bytes are none of ours to touch.

So the marks are drawn in a gutter over the content, from the lines the
patch holds rather than from where they were drawn last. They are worked
out again whenever a pane's content settles and whenever the patch
itself changes. Working them out as the content settles keeps them right
across a renderer switch, a change of context size, or a walk through
the commits.

They are shown whenever the main view shows the diff the patch is built
from, whether or not it has the focus. The pane beside the diff previews
the patch all the while, and marks that came and went with the focus
would look like a bug while browsing. A diff of any other commit gets
none.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 4d23c06eb7 Build a custom patch from a commit's diff, and discard lines from it
The five panels that show a commit's diff now offer on it what the patch
explorer offers on the one file it can show at a time: space takes the
selected lines into the custom patch being built, or back out of it again,
and d takes them out of the commit itself.

So a patch can be built straight from the diff already on screen, over
several files at once and without entering the commit's files first — and
from a stash entry or a reflog entry too, whose diffs the explorer could
reach but which had no patch preview of their own to show for it.

The two arrive together because they are one answer to what a commit's diff
offers, and because d needs exactly what space builds: a patch of the
selection, which the rebase then removes from the commit. Which of the two
space is doing is decided by the first selected line, as toggling a file in
the commit files panel is, and the selection then moves past the lines
dealt with — they are still in the diff, a patch leaving the commit alone,
so a second press would otherwise take them straight back out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 a19fd2bdc0 Say which change line is meant in one way
Which line of a file a diff row is, in the terms a patch is built in, is
asked in two places already — staging a selection and editing a hunk — and
is about to be asked in a third, of a commit's diff rather than the working
tree's. The rule is the same one everywhere: an addition is where it sits in
the new version of the file, a deletion where it sat in the old one.

So the row is asked for its identity and the patch package for the lines
with those identities, instead of walking the diff working the identities
out again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 b5dd073056 Keep the diff selection when a commit is rewritten under it
Removing lines from a commit, moving a custom patch out of one, undoing
either — none of it goes through the focused main view, so a selection over
that commit's diff was left where it was, painted over a rendering of a diff
that no longer exists.

Every render now asks whether it is showing a different diff than the one on
screen, which is what a rewritten commit looks like, and puts the selection
back on the change that has taken its place — the same reveal an action in
the view does for itself. It stands down for a render of the same diff,
where a selection, perhaps a range still being made, is exactly right as it
is, and for one that something more precise is already waiting to place.

The key a render is remembered under moves ahead of anything that might
change the command's arguments, so that it says which diff is being
rendered and nothing else.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 b5fa914bf6 Move the post-action reveal onto the diff-line helper
It is built entirely on that helper, and the render chokepoint that is about
to want it lives in the gui package, which cannot reach a controller.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 c337c96d09 Give the panels showing a commit's diff one voice about it
Five panels show a commit's diff in the focused main view — one file of it
from the commit files panel, the whole of it from the commits, sub-commits,
stash and reflog panels — and each answered for that diff itself, in the
same words.

Have them share one object instead, told only which diff it is, so that what
follows has a single place to say what can be done to a commit's diff and
needs saying once rather than five times.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 a02b0c7106 Let the patch builder be told which lines by their identity
A patch is built in terms of where a line sits in the file's diff, which is
what the patch explorer has to hand. The main view doesn't: a row of it
resolves to a line of a file, and what index that line has depends on how
much of the diff has been read and how a diff renderer chose to lay it out.

So let the two meet at the patch builder's edge, in the identity of a change
line — its number on the side it belongs to — leaving the main view to speak
only of lines it can see.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00
Stefan HallerandClaude Opus 5 394d91d147 Draw a gutter of inclusion marks over a view's content
The lines of a commit's diff that are in the custom patch being built have
to be shown as such over whatever a diff renderer made of that diff, whose
bytes we can't touch — and mustn't, since everything we know about a row is
keyed by where it sits in the content.

So the marks are a decoration drawn over the content instead: a column
reserved at the left of every line, blank but for the lines that are in,
with the content shifted along past it. Nothing about the content changes
but the width it has to wrap in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-05 11:33:11 +02:00