Skip to content

app: each step's history slice shows where it changed - #653

Merged
foxnne merged 1 commit into
mainfrom
app/history-slice-change
Oct 19, 2026
Merged

foxnne merged 1 commit into
mainfrom
app/history-slice-change

Conversation

@foxnne

@foxnne foxnne commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

No plan; the maintainer's idea.

What changes

The history strip (the transport's film of a document's pictures) gave each step the stretch of its picture under its own knob, so the strip read as one picture of the document building up left to right. Now each step's slice stands round where that step changed the document, so the strip reads as the actions themselves, vertically sliced: a stroke in the corner shows the corner, the next stroke across the canvas shows that.

How (host-only, owner-agnostic):

  • HistoryFrames.land already reads each picture back once, when it lands, for its content box. It now also takes a print of each column (columnPrints: a hash of the column's pixels, leaving out the bottom and right bands as contentOf does, so a scrollbar moving is not the change) and compares it with the picture of the step below (history_transport.changedSpan). The middle of the changed column span is kept on the picture (Picture.change).
  • moved now takes the entry under the top too, so a picture knows which one it is compared with. An undo or a scrub retakes a step's picture against its own predecessor, not whatever landed last.
  • Two pictures are compared only when taken from the same size of pane (the bar's height and the pane's size move the crop, and the rescale alone would differ everywhere); a retake keeps its old change only from the same view.
  • paintStrip centres each fixed-width slice on that middle, kept inside the picture (history_transport.sliceAt). A change wider than a slice uses its middle. A step with no visible change, whose predecessor has no picture, or that borrows a neighbour's picture (it has none of its own), keeps today's slice; a picture taken again with nothing to compare with keeps what it knew.

Computed once per landed picture, never per frame. Cost: the column prints of a 384x192 picture (the largest HistoryFrames takes) are 17 µs ReleaseFast and 111 µs Debug (M-series, 2000-run average, measured with a standalone copy of columnPrints), on a frame that already reads the picture back; plus 1.5 KB per picture (48 kept at most). Inside the existing history.frame.content profile zone.

Later seam: an owner-reported change region per step (a HistoryLine entry carrying the rect it touched), which the strip would prefer over the pixel diff: exact for changes that scroll or that the picture can't see. It is an SDK change and would move the fingerprint, so it is left for when an owner needs it; Picture.change is where it would land.

SDK impact

  • None
  • Core-only or additive: reaches plugins at the next SDK release
  • Fingerprint moved: recorded in sdk/src/version.zig, sdk_version untouched, PR labelled sdk
  • SDK release: bumps sdk_version, lists the sdk PRs since the last sdk-v* tag

Verified

Unit tests on synthetic images: a stroke added across columns 20-25 gives exactly that span, no change gives none, a change only in the scrollbar band gives none; the slice centres on the change, held inside the picture at both edges, and a change wider than a slice uses its middle.

  • macOS: gates green locally (check-fmt, zig build, test, test-integration 81/81 steps, check-web). One sandbox launch (own profile, --quiet): four text.inserts at different columns of a text file recorded four steps and their pictures landed without errors. The strip itself grows only under the person's pointer or held keys (the control server's undo does not stir it), so it was not seen; worth a look by hand.
  • Windows:
  • Linux:
  • Web:

Follow-ups

Known limits of a pixel diff (found in review, left for the owner-reported seam): a step that also scrolls or pans the view changes every column, so its slice centres; a caret or hover drawn in only one of the two pictures widens the span; a step's change is measured once, when its picture lands, so a predecessor landing later does not revisit it.

🤖 Generated with Claude Code

The history strip gave each step the stretch of its picture under its own
knob, so the strip read as one picture building up left to right. Now each
step's slice stands round the columns its picture changed from the step
before's, so the strip reads as the actions themselves, sliced. A step
that changed nothing visible keeps the old slice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the app/history-slice-change branch from 8057f49 to eb9ac23 Compare October 19, 2026 16:54
@foxnne
foxnne marked this pull request as ready for review October 19, 2026 16:54
@foxnne
foxnne enabled auto-merge October 19, 2026 16:56
@foxnne
foxnne added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit 2e98833 Oct 19, 2026
11 checks passed
@foxnne
foxnne deleted the app/history-slice-change branch October 19, 2026 18:01
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.

1 participant