Skip to content

feat(notes): highlight a selection note's words, found again after a reprocess - #659

Merged
Valtora merged 7 commits into
Valtora:mainfrom
thebeline:pr/notes-b3d-highlights
Oct 23, 2026
Merged

Valtora merged 7 commits into
Valtora:mainfrom
thebeline:pr/notes-b3d-highlights

Conversation

@thebeline

@thebeline thebeline commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request

Description

Addresses #488 (B3d). The processed transcript marks a selection note's words, refound after a reprocess: underlined on a light tint, dotted where they changed (original quote in the title). Lost words aren't marked; the note line strikes the quote and shows the span's words now. Marked words, or a saved note's new pencil, open #613's popover to edit or delete; a 409 shows the current note, keeping the typed text. Unconfirmed edits arm the leave warning. No hover link from mark to note line.

Decision record. Context: a reprocess replaces every utterance; only the note's span and quote survive. Rule: resolved at read time, never written back: within 2 s of the span, ignoring case and punctuation, the run with fewest word edits wins (end words may be part words, as a mid-word drag stores them), ties to whole words, then the nearest; fuzzy from 0.5. Alternatives: time only (silently marks other words), context columns (a migration), re-anchoring at finalize (a second note writer). Trade-off: on the tuning fixtures (Lords, London, Seattle; not held out), 95% of 7,500 sampled selections mark the right words, 1.7% wrong ones (mostly repeated one-word quotes); of 3,000 drags inside words, across lines or shortened, 97% and 1.3%. Figures in the commits.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Checks run

  • Python: none changed; check_whitespace.py, validate_docs.py, check_file_size.py pass.
  • Frontend: npm run lint, check:contrast, test and build, at current main.

Migration impact

  • No database migration.

Documentation impact

  • docs/USAGE.md and docs/ARCHITECTURE.md (highlight invariant).

Security impact

  • No security-sensitive change.

Manual verification

New tests fail on main unless they guard what it already does; all 50 mutants fail one. A production build on a faked API (synthetic data) passed 19 Chromium checks, incl. a mid-word drag and Pixel 7; a real-phone pass is to come.

Screenshots (if relevant)

Exact (solid) and changed (dotted) words
Words no longer found
Editing from the marked words
A change made elsewhere: the draft is kept
Dark theme

…reprocess

A selection note keeps the span its words were selected at and the words
as the user saw them. The processed view now finds those words in the
transcript as it reads now and marks them, tinted and underlined: solid
where found exactly, dotted where found with changes (the original words
in the title). The underline keeps a mark visible on a bubble of the
tint's own colour. When they can't
be found near the note's time nothing is marked, and the note line strikes
the quote through, says the words changed, and quotes what the span reads
now. A click on the tinted words, or the new pencil on a saved note's line,
opens the note popover to edit the note or delete it, each write against
the revision shown; a 409 shows the note as it is now and keeps the text
typed, so a second Save replaces it knowingly.

Decision record (anchoring rule)

Context: a reprocess deletes every utterance and finalize supersedes the
live ones, so nothing in the transcript keeps a stable id or offset. The
audio clock is what survives: the note's start_ms/end_ms.

Rule: resolved at read time, never written back. Look only among words
timed within 2 s of the stored span; compare case-folded with punctuation
stripped; take the run with the fewest word edits from the quote
(approximate substring matching), ties to the run nearest the span's
middle. All words equal is exact, 1 - edits/words >= 0.5 is fuzzy, less is
unanchored. Placement still reads only the stored span, and prompts keep
the stored quote.

Alternatives: time span only (option C) would tint whatever words now sit
there, silently; prefix/suffix context columns (E) are a migration for a
gain only on repeated phrases, which the time tie-break already handles;
re-anchoring at finalize and writing back (F) would make the pipeline a
second writer of notes, bumping revisions under users' open edits.

Trade-offs: the margin and floor come from 7,500 selections made the way
the client makes them, on live and processed text of three benchmark
recordings, resolved on their final and reprocessed text. At 2 s and 0.5:
78% exact, 19% fuzzy, 4% unanchored; 95% marked on the right words, 1.5%
on wrong ones. A 5 s margin finds 0.2% more and marks 0.4% more wrongly.
A 0.6 floor (the design's 0.7 was a guess) refuses 1.6% more, 113 of
117 of them right, mostly two-word quotes with one word changed; 0.4 adds
0.1%. 92 of the 110 wrong marks are one-word quotes; the clear cases are a
word said twice within a few seconds, where the neighbouring copy is
marked.
…it is unsent

A selection's quote keeps just the characters chosen (B3c), so a drag
that starts or ends inside a word, ordinary in Firefox and Safari, stores
"ousand for the ve". The resolver compared whole words only, so such a
note read "Words changed since this note" straight after it was saved.
Now the quote's first word also matches the end of a word, its last word
the start of one, and a one-word quote any part of one, with no edit
counted. A whole word wins a tie over a part word, so "the" lands on "the"
rather than inside "breathe". The stored quote is unchanged.

Editing or deleting a saved note now holds the page as an unsent note
does: from Save or Delete until the write is confirmed or the editor is
closed, a refused write still open included. The hold lives in memory
beside the outboxes (holdPendingNoteWrite); nothing retries it. A refused
write puts focus back in the box, as disabling the pressed button dropped
it to the page, and the reason is announced as an alert.

The note underline (action-text on action-tint) is now in the contrast
gate at 3:1; it is what marks the words on a light card.

Calibration, margin and floor frozen at 2 s and 0.5. All figures are on the
dev fixtures (Lords, London, Seattle) the margin and floor were tuned on,
not on held-out data. On the 7,500 tuning selections the part-word rule
leaves 94.9% marked right; wrong marks go from 1.5% to 1.7%, and unanchored
from 3.7% to 3.5%. On 3,000 drags added after the freeze (inside words,
over two or three lines, and over 2,000 characters, so shortened), right
goes from 84.9% to 97.2%, unanchored from 14.8% to 1.5%, and wrong from
0.3% to 1.3%. Drags inside one line go from 10% to 83% exact. The wrong
marks are part-word quotes of one to three words, mostly one-word ones that
land on a neighbouring word holding the same letters. Limiting a one-word
part match to the note's own span cut those from 12.8% to 8.4% of one-word
drags but lost 12 points of right ones, so it is not done.
@thebeline
thebeline marked this pull request as ready for review October 23, 2026 16:59
@thebeline
thebeline requested a review from Valtora as a code owner October 23, 2026 16:59
wordsChanged built a map of every line for each unanchored note each
time a note line rendered, and note lines render on every playback
tick. The text is now read inside the memo with the marks, so a tick
only looks it up.
…essed

A click on another note's marked words, or its pencil, replaced the open
editor: the text typed in it was lost, and a save of it still on its way
let go of the page. An editor holding typed text now stays until it is
saved or cancelled; one left as it opened closes on the press as before,
and the words pressed open theirs.
USAGE read as though any change typed into a note's editor made the page
ask before closing. The hold starts when the change or delete is sent,
as ARCHITECTURE.md says, so the guide now says that.
@Valtora

Valtora commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Merged main (ebf8797, #658) into the branch in a65f719. Clean merge, and the diff against main is still only this PR's files.

@Valtora

Valtora commented Oct 23, 2026

Copy link
Copy Markdown
Owner

2ecad25 memoises what an unanchored note's span now reads. wordsChanged built a map of every line for each such note on every render of a note line, and note lines render on every playback tick, so a long meeting after a reprocess paid that four times a second. It is now worked out in the memo with the marks and looked up on a tick. Test in useNoteHighlights.test.ts.

@Valtora

Valtora commented Oct 23, 2026

Copy link
Copy Markdown
Owner

742c7d3 keeps an edit being typed when another note's marked words or pencil are pressed. Before, the open editor was replaced, so the typed text was lost and a save still on its way let go of the page. An editor left as it opened still closes on the press and the words pressed open theirs, as with a new note. Test added to TranscriptSection.highlights.test.tsx, and one line in USAGE.md.

@Valtora

Valtora commented Oct 23, 2026

Copy link
Copy Markdown
Owner

ab656b5 is a docs fix. USAGE.md read as though typing a change made the page ask before closing, but the hold starts at Save note or Confirm delete, as ARCHITECTURE.md says. The guide now says that.

@Valtora Valtora left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at ab656b5. The resolver follows the rule agreed on #488, the edit, delete and 409 paths keep the typed text and the revision right, and lint, contrast, the full vitest run and the build all pass with main merged in. The new underline pairing measures 4.52:1 at its lowest, against the 3:1 floor. I pushed three small fixes, each with its own comment above.

@Valtora
Valtora merged commit d8ab848 into Valtora:main Oct 23, 2026
17 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.

2 participants