v1.6: diff-on-reload (track-changes review surface)#7
Merged
nonatofabio merged 5 commits intomainfrom Apr 29, 2026
Merged
Conversation
Foundation for v1.6 diff-on-reload. DocumentStore now carries a per-tab
lastSyncedText alongside rawText: when external edits land via the file
watcher, rawText updates but lastSyncedText holds the user's last
reviewed version, so a diff can be computed.
New surface:
hasInFlightDiff — true when lastSyncedText != rawText
acceptAllChanges() — promotes rawText to baseline, no file change
rejectAllChanges() — writes baseline back to disk, the in-place
watcher rewrite is a no-op (rawText already
matches), so we don't loop
setLastSyncedText(_:) — JS-side per-chunk accept; new baseline
setRawText(_:) — JS-side per-chunk reject; writes through
Sidecar gains an optional lastSyncedText (omitted when there's no
in-flight diff, so v1.5 sidecars round-trip cleanly). Closing a tab
or app mid-review restores the review state on reopen.
Loaded after the existing markdown-it / highlight.js / katex / mermaid chain in reader.html. Exposes window.Diff with diffLines / diffWords, which the diff render layer uses to walk lastSyncedText vs rawText.
mindleLoad now takes an optional third argument — the diff baseline.
When the baseline differs from the current text, the JS pipeline:
1. Walks Diff.diffLines(baseline, current) and groups consecutive
+/- segments into chunks (each with start/end positions in both
baseline and current).
2. Builds a markdown source that interleaves unchanged regions of
'current' with raw-HTML chunk wrappers around each change.
3. Pre-renders each chunk's before/after through markdown-it (so
lists, headings, code fences inside a chunk look right) and
wraps them in styled <div> blocks with ✓ Keep / ✗ Revert chips.
4. Attaches click handlers that compute a new baseline (accept) or
a new current text (reject) and post to Swift via the
diffSetLastSynced / diffSetCurrent message channels.
CSS adds the chunk chrome — accent-bordered card, soft red strikethrough
for the deleted block, soft green wash for the added block, small
serif-friendly buttons. The print pipeline (PDF export) hides the
deleted blocks and chrome entirely so exported docs reflect the
accepted text only.
WebReaderView: - Watches lastSyncedText alongside rawText so a baseline-only change triggers a re-render. Live-reload preservation still applies. - Passes the baseline as mindleLoad's third arg (or null when there is no in-flight diff, so the JS hot path stays cheap). - Adds diffSetLastSynced and diffSetCurrent message handlers; per-chunk accept/reject in JS lands in DocumentStore.setLastSyncedText / setRawText. MindleCommands: - Edit menu gains 'Keep All Changes' (⌘⌥⏎) and 'Revert All Changes' (⌘⌥⌫). Both disabled when there's no in-flight diff. Useful when the agent's revision is bulk-correct or bulk-wrong and the user doesn't want to chip through every chunk.
After a Keep / Revert, snap the next-render scroll position to where the chunk used to live so the user gets visible confirmation that the action landed. Without this, removing the chunk's chrome collapses the doc and preserveScroll lands you in the wrong place — the change landed but felt invisible. Also removes the v1.6 instrumentation that helped diagnose the issue (NSLog calls on accept/reject paths, a JS dlog channel + the matching WKScriptMessageHandler). Pure cleanup — no behavior change beyond the scroll.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
v1.6 lands the headline UX leap from the v2 roadmap: when an external edit hits the active file (vim, an agent, anything), Mindle renders the change as a Word-style track-changes overlay you can accept or reject — per chunk, or whole-document.
This is the foundation the v2.0 MCP loop sits on. Today it's already useful on its own — anyone editing the file in vim, Cursor, or Claude Code outside Mindle gets visible change tracking.
What changed
Snapshot model (Swift) —
DocumentStorenow carries a per-tablastSyncedTextalongsiderawText. When the file watcher fires after an external edit,rawTextupdates butlastSyncedTextholds the user's last reviewed version, so the JS layer can compute and render the diff. The sidecar persistslastSyncedTextonly when there's an in-flight review (so v1.5 sidecars round-trip clean), letting an unfinished review survive close and reopen.Diff render (JS + CSS) —
mindleLoadtakes an optional third argument: the baseline. When it differs from the current text, the pipeline:Diff.diffLines(baseline, current)(jsdiff vendored atResources/web/vendor/diff.min.js) and groups consecutive +/- segments into chunks with start/end positions in both texts.The print pipeline strips the chunk chrome, so PDF export reflects accepted text only.
Round-trip (Swift ↔ JS) — clicking ✓ Keep posts a new baseline back to Swift via
diffSetLastSynced; clicking ✗ Revert posts a new current text viadiffSetCurrent, which Swift writes through to disk. Each click triggers a re-render of the now-shrunk diff. No partial state in JS — Swift owns all text.Whole-doc shortcuts — Edit menu gains Keep All Changes (⌘⌥⏎) and Revert All Changes (⌘⌥⌫). Disabled when there's no in-flight diff. Useful when the agent's revision is bulk-correct or bulk-wrong.
Test plan
.mdfile, edit it externally (echo "..." >> file.mdor save in vim) — diff chunks render with green/red wash and chips.Out of scope (v1.6.x or later)
Part of v2.0 roadmap.