DIFF_DISPLAY_POLISH

Diff display polish: status + follow-up scope

Plan status (vs DIFF_VIEWER_POLISH_AND_TESTING.md)

StepItemStatusEvidence
1Store: previousRevisionContent, isNewestRevision, isOldestRevision, viewMode enum, clear-on-backdoneclient/src/stores/pageEditorStore.ts:62-63, 81-86, 391-410, 451-465, 541-560
2PageEditorView passes new propsdone (props consumed by PageRevisionView from store; revision view uses them)client/src/components/PageEditor/PageRevisionView.vue:15-23
3Three view modes, conditional buttons, “no changes” messagedonePageRevisionView.vue:64-93, 116-121
4Unit tests for PageRevisionViewdonePageRevisionView.test.ts:31-33, 52-155 (renamed viewMode, hidden-button cases, both diff modes)
5Store unit tests for new computeds + previousRevisionContentdonepageEditorStore.test.ts:1065-1251
6navigation.yml selectorsdoneclient/src/utils/navigation/navigation.yml:733-746
7Selenium: test_revision_diff_view (history pages)donelib/galaxy_test/selenium/test_history_pages.py:498-539
8Selenium: test_standalone_page_revision_diffdonelib/galaxy_test/selenium/test_pages.py:160-198
Verificationvitest + selenium suites runnable against abovedone (suites + selectors exist)same

All eight steps + verification have landed.

Outstanding gaps from the plan

New polish opportunities (not in the plan)

  1. Word/intra-line diff granularity in ProposalDiffView / PageRevisionView

    • Problem: line-level diff only — a one-word edit highlights whole lines red+green; reader has to eyeball the delta.
    • Fix: layer diffWordsWithSpace on top of Change[] for paired removed+added blocks; render <ins>/<del> spans inside the existing pre.diff-line.
  2. Section-aware diff in the revisions tab

    • Problem: revisions tab uses raw computeLineDiff, ignoring sectionDiff which the rest of the system relies on; long edits sprawl with no heading grouping.
    • Fix: optional “by section” toggle that reuses sectionDiff to chunk the revision diff like SectionPatchView, with each heading collapsible.
  3. Collapse unchanged context

    • Problem: when only one section changes in a long page, the diff shows hundreds of unchanged lines (diff-context).
    • Fix: collapse runs of >N (e.g. 3) context lines into a ”… N unchanged lines …” disclosure (jsdiff Change blocks where !added && !removed).
  4. Stale warning is tooltip-only

    • Problem: stale state in ProposalDiffView/SectionPatchView just disables buttons + sets a title=; users won’t see why Accept is greyed.
    • Fix: render a visible warning banner at the top of the diff (“Page changed since this suggestion — accept will overwrite N edits”) + a “show what diverged” link.
  5. Section accept/reject lacks per-section reject

    • Problem: SectionPatchView is opt-in (checkbox to accept) only; no way to mark a section “rejected” distinct from “not yet decided”, and no per-section undo after Apply.
    • Fix: tri-state per section (accept/reject/undecided) with a counter; reject just hides; Apply still uses accepted set.
  6. Diff stats not surfaced in revision list

    • Problem: PageRevisionList shows date + source only; cannot scan for “which revision was the big edit”.
    • Fix: precompute diffStats between adjacent revisions (lazy/on-hover) and show +12/-3 next to each row.
  7. No “compare arbitrary two revisions”

    • Problem: only “vs current” and “vs previous” are wired; comparing rev N to rev N+3 requires restoring or eyeballing.
    • Fix: add a “Compare with…” dropdown on the revision toolbar listing the other revisions; reuse currentChanges computation with the chosen baseline.
  8. Visual contrast / dark-mode hardening

    • Problem: diff-added/diff-removed colors are hard-coded (#1e7e34, #bd2130, rgba green/red) instead of theme tokens; dark mode + colorblind users underserved.
    • Fix: route through Galaxy CSS variables (--state-success-*, etc.) and add a non-color cue (left-border bar + +/- glyph already present, but bar would survive grayscale).
  9. Word-break: break-all on monospace

    • Problem: word-break: break-all on .diff-line (all three views) shatters URLs/identifiers mid-token.
    • Fix: overflow-wrap: anywhere or break-word, or horizontal scroll for code-ish runs.
  10. Large-diff perf / max-height inconsistency

    • Problem: ProposalDiffView caps .diff-content at max-height: 400px; PageRevisionView diff has no cap and renders one <pre> per line for the full doc (O(lines) Vue nodes).
    • Fix: virtualize via RecycleScroller (already used elsewhere in Galaxy) past a threshold; cap revision diff height too.
  11. No section-patch keyboard a11y / “Apply all” shortcut

    • Problem: BFormCheckbox is keyboard-reachable but no roving focus, no “A/N” shortcut for All/None, no aria-live for the acceptedCount updates.
    • Fix: add aria-live="polite" to header, label All/None buttons with accesskey, ensure tabindex order matches reading order.
  12. SectionPatchView matches sections by exact heading string

    • Problem: trivial heading edit (”## Results” -> ”## Results ”) yields a remove + add pair instead of an edit (per sectionDiff in sectionDiffUtils.ts:94).
    • Fix: normalize headings (trim + collapse whitespace) for the match map; keep raw heading for display.
  13. Per-section diff stats recomputed inline in template

    • Problem: SectionPatchView.vue:121-122 calls diffStats(sc.changes) twice per row each render.
    • Fix: precompute a stat per SectionChange (memoize); tiny but trivial.
  14. No “accept then edit before save” affordance

    • Problem: ProposalDiffView accept replaces editor content immediately; user wanting to tweak the agent’s prose has to find the just-applied text in the editor.
    • Fix: secondary “Accept into draft” action that inserts but leaves a marker / opens an editor diff overlay.
  15. No empty-document edge case in ProposalDiffView

    • Problem: when original === proposed (rare but possible), the view renders an empty body with +0/-0; no message.
    • Fix: render the same “No changes” sentinel as PageRevisionView uses.

Prioritized backlog

ItemScopeFilesSizePriority
Visible stale bannerheader alert in both viewsProposalDiffView.vue, SectionPatchView.vueXSP1
Collapse unchanged contextruns of context lines -> disclosurePageRevisionView.vue, ProposalDiffView.vue, SectionPatchView.vueSP1
Word-level diff inside linesdiffWordsWithSpace overlaysectionDiffUtils.ts, three viewsMP1
Theme-token colors / dark modeswap hex for varsthree view stylesheetsXSP1
Heading-match normalizationtrim + whitespace foldsectionDiffUtils.ts:94-131 + testXSP2
+/- stats in revision listprecompute or lazy computePageRevisionList.vue, storeSP2
Empty-diff sentinel in ProposalDiffViewreuse patternProposalDiffView.vueXSP2
Tri-state per-section accept/rejectUI + stateSectionPatchView.vueMP2
Compare arbitrary two revisionsdropdown + store wiringPageRevisionView.vue, pageEditorStore.tsMP2
Section-aware revision diff togglereuse sectionDiff in revisionsPageRevisionView.vueMP2
Memoize section diff statscomputed mapSectionPatchView.vueXSP3
overflow-wrap fixcss tweakthree view stylesheetsXSP3
Virtualize large diffsRecycleScroller above N linesthree viewsLP3
A11y / keyboard polish on checkboxesaria-live, accesskeySectionPatchView.vueSP3
”Accept into draft” affordancenew actionProposalDiffView.vue, storeLP3

References