fix(edit-history): harden diff after review

Address findings from 2 review agents on the edit-history diff:

- Perf: diffWords is O(n*m); cap at 2000 tokens/side and fall back to a
  coarse whole-block replaced diff above that, so a very large multi-edit
  message can't freeze the main thread. Memoize the per-row diff in
  DiffText. (Added a unit test for the coarse fallback.)

- Perceivability (a11y/design): the added-word <ins> highlight was
  color-fill only, which is faint against the modal surface in the lotus
  themes. Add a Success.ContainerLine border + horizontal padding (so the
  rounded corners read as a chip) + box-decoration-break: clone for clean
  wrapping, so the "added" cue survives low fill contrast.

- Consistency: a media/no-body edit now renders "(no text)" in diff mode
  too (matched the toggle-off view; was blank).

- Softened the code comment's screen-reader claim (bare <ins>/<del> aren't
  announced by default).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 11:11:16 -04:00
co-authored by Claude Opus 4.8
parent 961789fd71
commit 90e6901a60
3 changed files with 36 additions and 4 deletions
+15
View File
@@ -10,6 +10,18 @@ export type DiffSegment = {
// spacing/newlines are both preserved when segments are re-joined.
const tokenize = (text: string): string[] => text.match(/\s+|\S+/g) ?? [];
// Above this many tokens per side the O(n*m) LCS table gets expensive/large, so
// we fall back to a coarse whole-block "replaced" diff. Message bodies this long
// are rare; the exact word diff isn't worth a multi-million-cell allocation.
const MAX_DIFF_TOKENS = 2000;
const coarseDiff = (oldText: string, newText: string): DiffSegment[] => {
const segments: DiffSegment[] = [];
if (oldText) segments.push({ type: 'removed', text: oldText });
if (newText) segments.push({ type: 'added', text: newText });
return segments;
};
/**
* Word-level diff of `oldText` → `newText` via a classic LCS. Returns segments
* in reading order: `equal` runs, `removed` runs (present only in old), and
@@ -19,6 +31,9 @@ const tokenize = (text: string): string[] => text.match(/\s+|\S+/g) ?? [];
export function diffWords(oldText: string, newText: string): DiffSegment[] {
const a = tokenize(oldText);
const b = tokenize(newText);
if (a.length > MAX_DIFF_TOKENS || b.length > MAX_DIFF_TOKENS) {
return coarseDiff(oldText, newText);
}
const n = a.length;
const m = b.length;