Skip to content

feat: persist viewed files across page reloads - #35

Open
adamward459 wants to merge 1 commit into
nilbuild:mainfrom
adamward459:feat/persist-viewed-files
Open

feat: persist viewed files across page reloads#35
adamward459 wants to merge 1 commit into
nilbuild:mainfrom
adamward459:feat/persist-viewed-files

Conversation

@adamward459

Copy link
Copy Markdown

Problem

Marking a file as viewed collapses it, but the state lives only in React state (useState in diff-page.tsx). A browser refresh expands every file again.

It's also lost on any live diff refetch — the collapse set is rebuilt from scratch whenever the diff object identity changes, and reviewedFiles was never an input to that rebuild.

This bites hardest in the common loop: review → edit code → refresh → everything you already went through is open again.

Approach

Viewed paths are stored in localStorage, keyed by repo root and ref, capped to the ten most recently used refs so the store can't grow without bound. Keyed by repo root rather than origin because ports get recycled between repos.

Each mark is stored alongside a fingerprint of that file's diff. On load the fingerprint is recomputed and the mark kept only if it still matches — so a file you edited since reviewing it expands again, while untouched files stay collapsed. Same behaviour as GitHub's "viewed" checkbox.

The fingerprint covers file identity, status, and every add/delete line. It deliberately excludes context lines, hunk headers and line numbers, so an unrelated edit elsewhere in the file doesn't unmark a hunk you already reviewed, and trims content so toggling the hide-whitespace filter doesn't churn marks.

One behaviour change worth flagging: viewed now takes precedence over a file having comments, which previously force-expanded it. Ticking the box reads as an explicit "I'm done here".

Changes

  • packages/ui/src/lib/viewed-storage.ts (new) — fingerprinting, reconciliation, and the LRU-capped store, as pure functions
  • packages/ui/tests/viewed-storage.test.ts (new) — 25 tests
  • packages/ui/src/components/diff/diff-page.tsx — restore on load, persist on change, comment-precedence fix (+32 / -2)

Testing

npm test — 100/100 passing. New tests cover fingerprint stability (identical file, indentation-only change, context/line-number shift) and sensitivity (edited line, rename, status change), reconciliation, LRU eviction, and storage round-trip including corrupt JSON and version mismatch.

Eviction orders by a monotonic write counter rather than Date.now(), since several writes can land in the same millisecond and tie.

Manual check: mark two files viewed, edit one, refresh — the edited one expands, the other stays collapsed.

Marking a file as viewed collapsed it, but the state lived only in React
state, so a browser refresh expanded every file again. It was also lost on
any live diff refetch, since the collapse set is rebuilt from scratch
whenever the diff object changes.

Viewed paths are now stored in localStorage keyed by repo root and ref,
capped to the ten most recently used refs so the store can't grow without
bound.

Each mark is stored alongside a fingerprint of the file's diff. On load the
fingerprint is recomputed and the mark is kept only if it still matches, so
a file you edited since reviewing it expands again while untouched files
stay collapsed. The fingerprint covers file identity, status, and every
add/delete line, excluding context lines, hunk headers and line numbers so
an unrelated edit elsewhere in the file doesn't unmark a reviewed hunk.
Content is trimmed so toggling the hide-whitespace filter doesn't churn
marks.

Viewed now also takes precedence over a file having comments, which
previously force-expanded it.
@adamward459

Copy link
Copy Markdown
Author

Test video:

2026-08-15.at.22.40.25.mp4

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