Skip to content

app: history strip slices are 1:1 crops of the frame - #656

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

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

Conversation

@foxnne

@foxnne foxnne commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Builds on #653 (base app/history-slice-change; retarget to main once it lands).

What changes

The history strip's slices are crisp: each step's picture is a crop of the pane at its own pixels, the strip's size, centred on where that step changed the document, and the strip draws it one pixel for one (Retina-aware, nearest sampling, whole-pixel placement). Before, a ~384x192 thumbnail was stretched into the strip, which blurred text past reading.

  • HistoryFrames.take copies the pane twice on the GPU: a small copy (384x192, as before) for finding the change, and the whole pane at 1:1 into a target kept while the history keeps moving (a held undo or a scrub reuses it; it is let go of the first frame no picture is asked for). The next frame, land reads back the small one, finds the change's box (columns from app: each step's history slice shows where it changed #653's prints, plus rows the same way: prints), and crops the strip's size round it out of the whole copy (history_transport.cropAt, FrameTarget.copyRect). Only the crop is kept.
  • FrameTarget gains snapshotInto (the last frame into a caller's kept target) and copyRect (a rect of a texture into a new target); snapshotTarget is built from the same pieces.
  • A step with no visible change is cropped round the document's content (contentOf), else the pane's middle. (The brief said the pane's middle; content first keeps a small document's text in the strip rather than empty pane.)
  • HistoryTransport.stripPixels gives the strip's full-grown size in physical pixels, at its widest (no export button); paintStrip draws each picture centred in its column with history_transport.oneToOne, cut to the column, never scaled. The content-framing rules (frameContent, contentUnion, sliceAt) are gone.

Measured (Debug, macOS, one quiet sandbox run, text document, 15 edits/undos through the control socket, devtools.stats): the capture (history.frame, both GPU copies encoded) 0.30–0.44 ms max per move; the landing (history.frame.crop: readback of the small copy, prints, crop) 1.6–2.9 ms max, once per move, nothing on other frames. The readback was already there before this change (history.frame.content).

Memory: per step, the crop (strip w x h x 4 bytes; 600x104 on a Retina screen with a full-width bar = 250 KB) plus prints ((384 + 192) x 4 = 2.3 KB). cap stays 48: about 12 MB, under the old ~14 MB. Transient: one whole-pane copy for one frame per history move.

SDK impact

  • Core-only or additive: reaches plugins at the next SDK release (FrameTarget.copyRect added in the backend's glass; no fingerprint move expected)

Verified

  • macOS: unit tests; built and run once in a quiet sandbox to measure the capture (above). The strip itself not yet looked at by eye: it needs a scrub, which is the person's pointer.
  • Windows:
  • Linux:
  • Web:

Unit tests: cropAt (round the change, held inside, smaller picture), oneToOne (1:1, centred, cut, whole pixels), row+column change box from prints, changedSpan.

Follow-ups

  • A stop with no picture of its own still shows the nearest stop's picture (as before), which now means that stop's crop round its change. Left as is.
  • A step with no comparison (the first picture, or after the pane is resized) is cropped round the content's middle; for a document filling the pane that is its middle.

A picture taken at one display scale and shown at another is still drawn 1:1 (so smaller/larger than the strip); it is replaced at the next move.

🤖 Generated with Claude Code

@foxnne
foxnne force-pushed the app/history-slice-crisp branch 2 times, most recently from e0ac0c3 to ef8e7e4 Compare October 19, 2026 17:27
@foxnne
foxnne marked this pull request as ready for review October 19, 2026 17:27
Base automatically changed from app/history-slice-change to main October 19, 2026 18:01
Each step's picture is now a crop of the pane at its own pixels, the
strip's size, centred on where the step changed the document, and the
strip draws it one pixel for one. Before, a 384x192 thumbnail was
stretched into the strip, which blurred it past reading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne force-pushed the app/history-slice-crisp branch from ef8e7e4 to 871087e Compare October 19, 2026 18:15
@foxnne
foxnne added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit 58cdcb1 Oct 19, 2026
11 checks passed
@foxnne
foxnne deleted the app/history-slice-crisp branch October 19, 2026 18:43
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