feat: replace the highlights prop with a history the view keeps - #15
Merged
Merged
Conversation
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
Splits the old
highlightsprop in two and gives the view a real undo history.Cuts 1.2.0.
highlightswas the value baked into the page at mount and a controlledstate prop that replayed whatever it was set to, while the ref mutated the same
state imperatively. Two writers, one value, so the component had to tell a
genuine restore from an echo of its own last report:
That guard was correct and it still leaked: building a clear → undo button on
top of it needed a stack in a ref, a flag to swallow the echo of a restore, and
reasoning about string identity, because React skips an effect when the prop
value has not changed. The Snack demo in this repo had all three. Nobody writes
that on the way to shipping a reader app.
Now:
initialHighlightsis the value the view mounts with, read once.setHighlights(payload)replaces the highlights of a mounted view;""clears them.
undo(),redo(),clearHistory(),getHistory()andonHistoryChangeexpose a history the page keeps itself, recorded at theone place every content change already passed through.
Two behaviours changed along the way, both of them bugs found while testing this
on device:
onErrorwithout touching the highlights already on screen. It used tocall
removeAllHighlights()first and fail afterwards, so a bad string costthe reader their highlights and left the history pointing at a state that was
no longer on screen.
animation. An undo restores something the reader has already seen; announcing
it as new misreads the gesture.
The history lives in the WebView runtime, beside the highlighter that owns the
state, and is bounded at 50 entries — each one is a whole serialized payload.
Type of change
fix— bug fix (no public API change)feat— new backward-compatible APIdocs/chore/ci/test— no runtime changehighlightsis removed, so code that passes it no longer compiles. Thechangelog opens with the migration. This ships as 1.2.0 rather than 2.0.0 by
decision: the package is ten days old with ~19 downloads a month, and the
upgrade note carries the break.
How was this tested?
yarn lint,yarn typecheckandyarn test(156, up from 151) pass. The newtests cover
setHighlights, the three history commands posting their messages,onHistoryChangemapping, a malformed history message falling back to defaultsinstead of propagating
NaN, and thegetHistoryround-trip.On an Android emulator, through the example app: highlight twice, undo and redo
step through correctly and the buttons disable themselves at each end; a
deliberately invalid payload surfaces
invalid_highlightand leaves thehighlights standing.
Two things landed after that session and have not been run on a device: the
entrance-animation guard and
clearHistory().Worth knowing: the history logic itself has no automated coverage. It lives in
the WebView runtime, which is a template string, so the tests can only assert
the messages that cross the bridge. That is where a
ReferenceErrorslippedpast TypeScript during this work.
Checklist
yarn lint,yarn typecheck, andyarn testall pass.docs/REFERENCE.mdgains Restoring,replacing and undoing, the props and callbacks tables, and the ref
entries; the README quick start uses the new shape.
BridgingNamesforundo,redo,clearHistoryandgetHistory, the handlers inutils.ts, andthe ref methods in
SelectableTextView.tsx.— N/A: this PR cutsCHANGELOG.mdupdated under## [Unreleased]the release, so the entries sit under
## [1.2.0] - 2026-09-24.I did not bump the version— N/A:bump: v1.2.0syncs the sixplaces
docs/VERSION-UPDATE.mdlists (versionCode2 → 3) plus the twocosmetic ones. Merging this arms
git tag v1.2.0, which publishes tonpm.
The Snack demo is updated in
snack/, but the published Snack still runs 1.1.0until its files and its pinned version are updated by hand after the release —
see
snack/README.md.