Skip to content

feat: local version history — automatic snapshots, diff & restore#10

Merged
ijbo merged 1 commit into
mainfrom
feat/version-history
Jul 4, 2026
Merged

feat: local version history — automatic snapshots, diff & restore#10
ijbo merged 1 commit into
mainfrom
feat/version-history

Conversation

@ijbo

@ijbo ijbo commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What

Local version history — the classic must-have of serious editors, fully client-side. Snapshots of every workspace file are captured automatically as you edit and stored in an IndexedDB ring buffer; a History panel (right-click a file in the sidebar) shows a timeline with a GitHub-style line diff vs the current document, one-click Restore (with a pre-restore safety snapshot, so restores are undoable) and Copy.

Zero new dependencies — the line diff is ~90 lines of hand-rolled LCS with prefix/suffix trimming, matching the project's zero-dep ethos.

How it works

  • Capture policy: first save of a file → snapshot; then every ≥3 min of editing, or immediately when ≥400 chars change. Identical content never re-snapshots.
  • Hooks at the two authoritative save choke points: saveToLocalStorage (keystroke autosave) and setFileContent (file-switch saves). All calls guarded — the app behaves identically if the module fails to load.
  • Storage: IndexedDB textagent-history, split into meta (tiny records for listing/pruning) and content (loaded on demand).

Hardened via adversarial multi-agent review (9 confirmed findings fixed pre-merge)

  • meta/content store split — listing and pruning never deserialize document bodies (was: every capture re-read all snapshot contents)
  • Byte budgets — 25 MB/file + 50 snapshots/file + 400 global caps; docs >5 MB are never snapshotted (quota safety)
  • Diff rendering collapses unchanged runs to 3-line context and hard-caps rendered rows — no DOM blowup / tab freeze on huge documents
  • Restore reads authoritative content — disk-folder and single-linked files are read from disk (new disk.readSingleFile), not the stale localStorage cache, for both the diff and the safety snapshot
  • Honest failure reporting — a quota-failed restore shows an error toast instead of a false success
  • File delete purges its history — "cannot be undone" now includes snapshots; no sensitive retention, no orphans evicting live history via the global cap

Testing

  • New 9-test Playwright spec: API surface, IDB snapshot storage, diff correctness (adds/dels/sames exact), panel open/close, full restore round-trip incl. safety snapshot, identical-content dedupe, context-menu item, history purge.
  • Verified visually at 1280×800 (timeline, delta badges, +2/−1 diffstat, GitHub-style diff, Copy/Restore).
  • Smoke suite green (one pre-existing view-mode flake passes in isolation); build clean.

🤖 Generated with Claude Code

Automatic IndexedDB snapshots of every workspace file (first save / 3-min
interval / 400-char delta), a History panel (sidebar right-click) with a
hand-rolled LCS line diff vs the current document, one-click restore with a
pre-restore safety snapshot, and Copy-to-clipboard. Zero new dependencies.

Hardened per adversarial multi-agent review (9 confirmed findings fixed):
- meta/content split in IDB so listing & pruning never deserialize bodies
- per-file byte budget (25MB) + count caps (50/file, 400 global) + >5MB skip
- diff rendering collapses unchanged runs to 3-line context and caps rows,
  preventing DOM blowups on large documents
- restore reads the AUTHORITATIVE current content (disk / single-linked file,
  not the localStorage cache) for diffs and safety snapshots
- restore reports honestly: quota failures show an error, never a false
  success toast
- deleting a file purges its history (no sensitive retention, no orphan
  eviction of live history)

Tests: 9-test Playwright spec (API, IDB storage, diff correctness, panel,
restore round-trip incl. safety snapshot, dedupe, purge); smoke green;
visual verify at 1280x800.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ijbo ijbo merged commit 0b9fead into main Jul 4, 2026
4 checks passed
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