Skip to content

v1.5.0: review workflow, correctness fixes, Word tracked changes, calmer UI - #5

Merged
tschnorpfeil merged 58 commits into
mainfrom
claude/focused-mayer-3stkgw
Oct 23, 2026
Merged

tschnorpfeil merged 58 commits into
mainfrom
claude/focused-mayer-3stkgw

Conversation

@tschnorpfeil

Copy link
Copy Markdown
Owner

Everything for v1.5.0. The full user-facing list is in docs/releases/v1.5.0.md, which the release workflow uses as the release notes.

Summary

  • Review workflow: accept or reject each change (Word semantics), undo, auto-advance to the next open change, review progress, .docdiff saved work.
  • Correctness: fixes for hidden or misstated changes (header/footer filter, word diff reconstruction, page-number shapes, DOCX soft breaks, Unicode normalisation, renumbering, moves), PDF/DOCX intake, race conditions and edit handling, all covered by tests.
  • Export: Word .docx with real tracked changes, a self-contained HTML report, Markdown, and a dedicated print layout.
  • Speed: comparisons run in a Web Worker; navigation and search only update what changed.

UI polish (last round)

  • A small design system in src/styles.css: spacing on a 4 px grid, three radii, one hairline colour, two weights, one title size.
  • One plain surface per pane; page breaks are a labelled hairline instead of framed cards.
  • The current change is a soft tint plus an ink edge instead of a black ring; changed paragraphs get a thinner, lighter amber edge.
  • Overview ruler between the two versions in the side-by-side view (next to the text when stacked). Pane widths are identical in both views, so switching never re-wraps text.
  • Change list: title, progress (as text and a hairline) and one native select filter (all / open / reviewed / changed / added / removed). Inbox-style cards: reference, the first change in at most two lines, "+N" for further changes.
  • Review status is a ✓ / ✗ mark in the right margin with an undo arrow below it, so reviewing never shifts text and the old version shows no mark.
  • View menu: segmented layout and colour scheme choices with icons.
  • File names on narrow screens keep only the distinguishing part; help moves into the ⋯ menu below 1200 px; the files popover, phone bottom bar, pane focus indicator and help text are reworked; menus fade in (off with reduced motion).
  • README screenshots refreshed.

Verification

  • python3 scripts/build.py && node scripts/check.mjs passes (privacy and standalone invariants, i18n parity, core/export/worker/app-state tests).
  • axe-core: no WCAG 2.2 AA violations on landing, compare (both views), all menus, files popover, search, help dialog, tablet drawer and phone sheets, in light and dark at 1440, 820 and 390 px. color-contrast-enhanced (AAA) is clean in the same states.
  • Browser flow in headless Chromium: load sample → accept / reject / undo → Word export (opens in LibreOffice) → save work → new comparison → reopen work with review state restored, no console errors. Reviewing shifts text by 0 px.

After merge, tag v1.5.0 on the merge commit to trigger .github/workflows/release.yml.


Generated by Claude Code

claude added 30 commits October 22, 2026 14:53
The single 7,000-line template is now a page shell plus src/styles.css and
ten src/js/*.js files that build.py concatenates in file-name order into the
same single inline <script>. The bundle stays one self-contained HTML file.

check.mjs audits the JS sources directly and verifies de/en i18n parity by
evaluating the dictionary instead of matching indentation with a regex; it
also rejects empty translation strings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
- Pass isEvalSupported: false to PDF.js getDocument() (CVE-2024-4367
  defense in depth on top of the CSP that already omits unsafe-eval).
- Add manifest-src 'self' so default-src 'none' no longer blocks the
  web app manifest on docdiff.app.
- README.md and SECURITY.md now quote the shipped CSP verbatim, and
  check.mjs fails the build if they drift again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
pages.yml deployed the committed site/ without rebuilding, so a stale bundle
could ship. It now builds, runs check.mjs, and fails if the committed bundle
differs. check.mjs now matches bracket and bare access to storage APIs
(e.g. window['localStorage']), not only the dotted form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…kdown, print view

- Word: replace the HTML-as-.doc export with a genuine .docx generated in the
  browser (minimal STORE ZIP writer with CRC-32 plus hand-written
  WordprocessingML). Every difference is a real tracked change (w:ins/w:del,
  author "Doc Diff", w:date), inserted/deleted paragraphs also track the
  paragraph mark, so Accept all / Reject all yield exactly the new / old text.
  Heading 1-3, clause and bullet styles with hanging indents, title and
  metadata block, A4 page setup, footer with page X of Y, no compatibility mode.
- HTML report: self-contained read-only report with strict CSP, lang, summary
  counts, linked change list with clause references, legend, full redline,
  del/ins with visually hidden "removed:/added:" text, dark-mode and print CSS.
  States scope ("changes only" or full) and the header/footer setting.
- Markdown: escape document text, keep whitespace outside ~~ / ** delimiters,
  fall back to <del>/<ins> where CommonMark flanking rules would fail, add a
  summary table with clause references.
- Print/PDF: dedicated row-aligned print view for pages, split and inline
  modes with file names, date, counts, repeated column headers, page numbers
  in the page margin, break-inside: avoid, greyscale-safe marks.
- Copy/.txt: shared final-text builder, header/footer lines always excluded,
  internal intake markers filtered defensively.
- File names: both document names (e.g. Vertrag_v2_vs_v1_Redline.docx),
  sanitized for Windows/macOS/Linux, localized suffixes.
- Export menu: real disabled/aria-disabled state until a comparison exists,
  no more silent sample substitution, menu keyboard pattern (arrows,
  Home/End, Escape returns focus), .docx label, all strings in de and en.
- Add scripts/test-export.mjs (node:vm, no dependencies) and run it from
  scripts/check.mjs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Word diff
- Number expansion in computeCleanWordDiff only applies to groups that both
  remove and add text; "Frist 14 Tage" -> "Frist 7 14 Tage" no longer drops
  "14" from the old side. Every diff is checked against the invariant
  (unchanged+removed == old text, unchanged+added == new text) and falls back
  to the raw diff if it would ever be violated.

Headers / footers
- PDF margin lines are headers/footers only if their digit-normalised text
  repeats on >= 50% (and >= 2) of the pages at a similar vertical position;
  body lines near the margins are kept. Text-only candidates ("... Page 4 of
  9") must repeat as well; pure page labels stay hidden.
- A header/footer whose text differs between the versions (beyond page
  numbers) is never hidden: its first occurrence stays visible. The hidden
  line count reflects what is actually hidden in both documents.

PDF / file intake
- Spaces between PDF.js items come from glyph geometry and hasEOL, so a font
  change inside a word no longer splits it; paragraph breaks use the page's
  line step; hyphenated line breaks are joined, also across pages.
- Files are sniffed by content: scanned/image-only PDFs, images, legacy .doc,
  RTF, other ZIPs and non-UTF-8 binaries are rejected with a bilingual
  role="alert" notice (also for drag & drop); HTML is reduced to text via an
  inert DOMParser document.
- DOCX list numbers are computed from numbering.xml/styles.xml the way Word
  counts them (continuation across interruptions, level restarts,
  startOverride) and injected into Mammoth's output; nesting depth is kept
  as block indent. When the paragraphs cannot be matched reliably, no
  numbers are shown instead of invented ones.

Document model
- Page breaks, header/footer flags and list depth are encoded with control
  characters that are stripped from all external input, so typed text such
  as "=== [SEITE 2] ===" or "[[HF]]" is plain text. The paste dialog shows
  plain text and keeps a loaded document if its text was not edited.
- Undo/redo history is cleared whenever a document is replaced (upload,
  paste, swap, sample, language switch of the sample, clear).

Alignment noise
- Block similarity uses a multiset word intersection.
- Blocks are aligned on their body without list marker / heading number;
  renumber-only rows are equal rows flagged markerOnly, rendered with a muted
  marker change and not counted as changes.
- Identical deleted/inserted blocks are paired as moved (flag on the
  deleted/inserted rows, labelled "Verschoben" / "Moved").

Also removes dead code (form-feed check, state.syncScroll, syncBtn) and adds
scripts/test-core.mjs (run by check.mjs) covering the invariant, samples,
header/footer detection, PDF line handling, renumbering and moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Resolved conflicts with the export rework: keep the export guard without
the sample fallback, and run both the export and the core test suites
from check.mjs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
… accessibility

Landing page (empty state):
- Static, indexable landing markup in src/template.html (works without JS):
  headline, trust facts, two large keyboard-accessible drop slots
  (old / new version), "View example" and "Paste text", then
  "How it works", honest "can / can't" limits and a short FAQ.
- Comparison-only controls (view switch, search, counts, reset, sidebar
  toggle, document bars, dock, phone bottom bar) are hidden until two
  versions are loaded. Drag wording is hidden on touch / narrow screens.
- Dismissible in-memory legend after the first comparison.

Comparison view:
- Header counts match the sidebar filters (modified / added / removed).
- Plain-language labels: Pages / Side by side / Inline, Old / New version,
  "New comparison"; help dialog rewritten without jargon.
- Sidebar cards drop the redundant type badge and stray bullet refs.
- Minimum UI text 12px, less monospace for prose labels; 24px targets,
  44px on coarse pointers; phone Edit / Revert reachable by tapping.

Accessibility (WCAG 2.2 AA):
- setTooltip also sets accessible names; all aria-labels translated.
- Filter bar uses aria-pressed; change cards are real buttons with
  sibling actions (no nested interactive controls).
- Focusable, labelled document panes; polite live region announces the
  comparison result and "Change 3 of 19 ..." on J / K.
- Single-key shortcuts ignore modifier combos, text fields and open
  dialogs and can be switched off in the help dialog.
- h1 in both states, landmarks for document bars and legend, dialog
  initial focus, focus handoff when the landing appears / disappears.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…iew switching incremental

- Run parsing, alignment and word diffs in a Web Worker built from a Blob.
  scripts/build.py bundles jsdiff + src/js/04-core.js + src/js/04a-align.js +
  src/worker/compare-worker.js into the page, so the worker shares the exact
  same code. Small inputs and environments without blob workers use the
  synchronous path.
- Only the latest comparison is applied (sequence ids, busy worker is
  terminated); show an accessible role=status progress pill.
- Align long middles by anchoring on unique identical blocks and running the
  DP only inside the gaps (banded DP for huge gaps). Middles up to 250k cells
  keep the exact global DP.
- Edits, reverts, undo and redo re-diff only the changed row when possible
  and patch it in place.
- Render panes in chunks; keep left/right row heights aligned with a
  ResizeObserver instead of re-measuring every row; J/K and sidebar clicks
  only touch the two affected rows; search is debounced and re-highlights
  matching rows only.
- Run PDF.js in a real Blob worker instead of its main-thread fake worker.
- Lighter per-clause action buttons (CSS mask icons).
- Respect prefers-reduced-motion.
- Add scripts/check-diff-worker.mjs (worker vs. main-thread equivalence).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Resolve conflicts with claude/focused-mayer-3stkgw and integrate:
- Header counts and sidebar filters share one helper over changeRows,
  so renumbered (marker-only) rows are not counted anywhere.
- Moved rows are labelled "Verschoben" / "Moved" in cards and
  announcements; the landing page now lists moved-paragraph detection
  under "can" instead of "can't".
- Sample loading resets the edit history; new file-intake messages kept.
- Header/footer clauses no longer use opacity (insertions inside them
  failed contrast); the pressed header/footer count pill uses a solid
  background.
- Leave a marked slot for further secondary start actions (session).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Run prepareComparisonBlocks + alignDocumentBlocks (incl. markMovedRows) in the
worker and the sync fallback; anchor on alignKey; keep moved-row notes in the
chunked renderer; incremental updates re-check header/footer reconciliation
and moved-row flags; load 04a-align.js in the core and export tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
The headline is now 'Dokumente einfach vergleichen.' / 'Compare documents
easily.', with a struck-out word and its green replacement in the app's own
diff colours. The struck-out word is aria-hidden, so screen readers read the
plain sentence.

README: describe what the tool does and what it does not do, state the
privacy guarantees precisely (including what the CSP does not cover), and
drop the comparison table and feature marketing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
docs/og-image.html is the source of site/og-image.png now, so the preview
can be re-rendered when the UI changes. The image shows the new headline
and a mockup that uses the current labels. The og:image URL gets a version
query so link previews pick up the new image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…o it

- Open session button sits in the landing's secondary actions slot.
- Save session item uses data-i18n and the rebuilt export menu (keyboard,
  closeExportMenu); exports including print mark a clean point.
- Confirm dialog follows the dialog focus pattern (title autofocus, focus
  returns to the trigger); stale close events no longer cancel a reopened
  dialog.
- Guards cover the landing "remove new version" button and compare the paste
  dialog against the plain text it showed.
- Help dialog explains that a session file contains the full text of both
  versions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
- Resolve conflicts with the live-region announcements; the progress pill
  keeps its percentage aria-hidden and reuses the announcement strings.
- Keep panes of the inactive view laid out but skipped
  (content-visibility: hidden) instead of display: none on desktop, so
  switching views does not recompute ~25k elements.
- Re-highlight large search result sets in short slices, nearest rows
  first; preserve the scroll position when panes are re-rendered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
- Merge the session save/restore work (11-session.js wraps runComparison;
  the async runComparison keeps returning its Promise through the wrapper).
- The single-row fast path for edits/undo/redo is now only used for long
  documents and only for rows that stay a matched pair; a full comparison
  then runs in the worker and replaces the result if the alignment differs,
  so results always end up identical to a full comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
On long documents an edit that changes the number of paragraphs (or a revert of an
insertion, undo/redo of such an edit) is re-compared in the worker. Until the result
arrives the old rows stay on screen, and a second edit or revert built the new text
from those old rows, silently dropping the first edit. Exports in that window wrote
the old rows but still marked the session clean, so the edit could be lost on close.

Edits and reverts now wait until the shown rows match the current text, an editor
left open while the document is replaced is discarded instead of being written into
the new document, and exports/printing bring the comparison up to date synchronously.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…r noise

- Word diff: bound the Myers work per paragraph pair (maxEditLength) and fall
  back to an exact prefix/suffix + replacement diff. Two rewritten 10,000-word
  paragraphs took ~45 s, a 1 MB paragraph effectively never finished.
- Bare page-number shapes ("2/3", "3 von 9", "- 4 -") in documents without
  pages (DOCX, pasted text) are only hidden as page numbers when they form an
  increasing sequence. A changed DOCX table cell "2/3" -> "1/3" was hidden.
- Normalise text before comparing: NFC, Latin ligatures, fixed-width spaces,
  soft hyphen / zero-width space / word joiner / BOM, CR and U+2028/2029
  line ends. Visually identical text no longer shows as changed.
- PDF: wrapped lines starting with an amount or a date ("145.000,00 EUR",
  "12.03.2026") no longer start a new paragraph; symbol-font bullets
  (U+F0B7, U+F095, ...) become "•".
- Running headers of a PDF are not reported as inserted/deleted against a
  version without any page structure or headers (DOCX, text).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Performance
- Move the inline vendor libraries and the app script from <head> to the
  end of <body>, so the static landing page is parsed and painted before
  ~2.5 MB of script. Throttled mobile profile (150 ms RTT, 1.6 Mbit/s,
  4x CPU, gzip): FCP/LCP 3.7 s -> 1.2 s; uncompressed 11.6 s -> 1.8 s.

Pages and metadata
- build.py renders the page per language from one template:
  site/index.html and DocDiff.html (German, browser auto-detect as before)
  and site/en/index.html (English, starts in English via data-page-lang).
- Static landing text (data-i18n, data-i18n-html, data-i18n-label) is
  rendered at build time from src/js/01-i18n.js, so crawlers and no-JS
  visitors see the page language; the app still translates at runtime.
- Head: precise title and description per language, canonical, hreflang
  de/en/x-default, og:locale, theme-color, application-name; assets use
  ../ from /en/.
- JSON-LD (WebApplication, SoftwareSourceCode, FAQPage) generated from the
  same i18n strings; the FAQPage matches the visible FAQ.

Content
- Two FAQ entries (scanned PDFs / OCR, continuing in Word via the tracked
  changes export); mixed formats (PDF against Word) in the feature list.
- The struck-out hero word is drawn via CSS, so the heading text reads
  "Dokumente einfach vergleichen." / "Compare documents easily."
- Footer links get a 24px tap target; legal links in the privacy dialog
  are absolute so they work from /en/ and the offline file.

Crawl files
- site/llms.txt and site/llms-full.txt with plain facts and limits.
- sitemap.xml lists / and /en/ with hreflang alternates and lastmod;
  robots.txt allows all crawlers, including AI crawlers.

Checks
- check.mjs verifies both pages: lang, canonical, hreflang, title and
  description length, asset paths, scripts after the landing content,
  rendered static text, single landing h1, JSON-LD parse and FAQ match,
  robots, sitemap, llms.txt links, manifest icons, same CSP.
- Workflows fail when any file under site/ differs from a fresh build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Saving or cancelling an inline edit with the keyboard, reverting a change from the
document or the change list, pressing Undo/Redo until the stack is empty, and choosing
an export menu item all removed or disabled the focused control, so focus fell back to
<body> and keyboard users had to start again from the top of the page.

Focus now returns to the row's edit button (or the next item in the change list, or the
document pane when row actions are hidden on narrow screens), moves to the other history
button when one gets disabled, and goes back to the export button when the menu closes.
Undo/Redo button states are also updated immediately, not only after a long comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
- DOCX: a line break inside a paragraph (Shift+Enter) now separates words.
  "Hauptstraße 1" + break + "12345 Berlin" was read as "Hauptstraße 112345".
- DOCX: warn when the document contains tracked changes; Mammoth compares
  the all-accepted version and silently drops deleted text.
- DOCX: drop Mammoth's footnote/endnote back-link arrows from the text.
- File intake: a slow read (large PDF) that finishes after another file was
  chosen for the same side, or after the side was replaced by paste, sample
  or clear, is discarded instead of overwriting the newer document.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…to their page

Search engines render with an English browser language, so the root page
switched itself to English and duplicated /en/. The hosted pages now keep
their language (data-page-lang); a visitor whose browser language differs
gets a one-line link to the other page. The offline DocDiff.html still
follows the browser language and no longer links website-only icon and
manifest files that do not exist next to it (console error under file://).

document.title now follows the in-app language toggle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…codings

- Password-protected PDF, damaged/truncated PDF and damaged DOCX now show a
  translated message instead of raw library errors ("No password given",
  "Invalid PDF structure", "Corrupted zip: ...").
- A DOCX is recognised by its word/document.xml entry even when the file is
  named .pdf; a text file that mentions "%PDF-" stays text.
- Text files that are not UTF-8 (Windows-1252 "ANSI") are read with a
  warning instead of being rejected; UTF-16 without BOM is detected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…line

- PDF: a paragraph broken at a page end continues when the next page starts
  with a capitalised word (German nouns), provided the previous text is a
  long paragraph of the same font size; short unpunctuated lines (headings)
  are not joined.
- The table-cell separator " · " produced by DOCX/HTML intake no longer makes
  a row differ from the same row extracted from a PDF.
- Bare page-number shapes in a PDF body (not marked as footer by position and
  repetition) follow the same sequence rule as DOCX/text: a single-page PDF
  table cell "2/3" -> "1/3" was hidden.

Same-content DOCX vs PDF (LibreOffice export, 61 rows): 7 -> 2 change rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
claude added 28 commits October 22, 2026 16:33
…nding reads

Hidden header/footer candidates of both versions are now matched as token
sequences (top/bottom band + text with page numbers normalised) in document
order, which pairs them page by page even when pages are inserted or removed.
Only matched occurrences stay hidden; every unmatched occurrence is shown and
diffed like body text. A running line changed on one page, a footer changed on
every page, or a repeated body line misdetected as a header and edited or
deleted on one page can no longer disappear. The hidden-line badge counts only
the identical lines that remain hidden.

Unchanged exceptions: pure page-number labels stay hidden, and a PDF's running
lines stay hidden against a version without pages and without any
header/footer lines (DOCX, pasted text).

Samples: the changed running header ("Fassung v1.0" -> "Gegenentwurf v2.0") is
now shown on each of its 3 pages instead of once (modified 17 -> 19, hidden
lines 10 -> 6). No change in the samples had been hidden before.

"Clear all" / clearing a side now also cancels a file that is still being read
for that side, even when the side was already empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…ment

The selected row id survived clearing a side, swapping, pasting or loading new
documents, so an unrelated row of the next document was highlighted as active and
J/K continued from there. It is now reset when documents are replaced and whenever the
row no longer exists. An edit that empties the revised document now moves focus to the
landing heading instead of leaving it on <body>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
When neither the Clipboard API nor execCommand could copy, the export button still
flashed "Copied" and the copy counted as saving the edits, so closing the tab no longer
warned about unsaved changes. A failed copy now shows a translated notice, and only
downloads (txt, docx, html, md) mark the session clean: a cancelled print dialog or a
volatile clipboard must not silence the unsaved-changes warning.

Closing the import/copy notice from the keyboard now returns focus to the control that
was focused before (or the start of the view) instead of <body>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Dependency-free regression tests for the guards that keep edits, reverts and exports
from using rows that are still being re-compared, for the list of exports that count as
saving, the failed-copy feedback, immediate undo/redo button updates and the reset of a
stale selected row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Rebuilding the revised text after an inline edit wrote hidden header/footer lines back
per page as "first line at the top, all others at the bottom". That moved running
headers and footers to different positions, so the header/footer reconciliation no
longer matched them: on the sample, appending one sentence to § 1 (1) turned the six
hidden header/footer lines into changes (22 -> 26 changes, badge gone, old side 31 -> 34
paragraphs). The text is now rebuilt from all blocks of the revised document in their
original order with only the edited paragraph replaced (or inserted after the nearest
preceding block for a deleted row), so an edit changes exactly that paragraph.

Single-letter shortcuts (J/K, C, H, /) now only act while focus is on the page, in a
document pane or in the change list; letters typed with focus on a toolbar control, in a
dialog or in any editable element never toggle filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…e headings

- Pairing no longer requires the same heading/paragraph kind. Appending a
  sentence (or a period) to "1. Scope of Services" re-derives it as a
  paragraph; it is now shown as modified instead of deleted + inserted.
- The section reference ("1.", "§ 5") follows the numbering in the text, not
  the derived kind, so such an edit no longer changes the clause references
  below it, which had re-paired a neighbouring "(3)" clause.
- Similarity words ignore leading/trailing punctuation ("Services." =
  "Services").
- Ordinary sentences are never headings: a line ending in sentence
  punctuation with 6+ words, or any line with 14+ words, is body text
  ("§ 2 Der Auftragnehmer erbringt die Leistungen fristgerecht." was bold).

Stability check: 232 single-paragraph edits of the de/en samples (append word,
sentence, period, clause), every other row keeps its pairing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…ruler

Replace the five bands of comparison chrome (header stats, document bars,
legend, floating dock, centre gutter) with a single top bar: the file pair
as one button with a popover, a change stepper, find in document, a View
menu, Export and a More menu. Counts appear only in the change list.

- Accept / reject each change (Word terminology). Rejecting restores the
  old wording; both mark the change as reviewed, survive undo/redo and are
  kept in the list. Actions are docked under the current paragraph and on
  the active list card; A / R / J / K work while the document or list has
  focus. A toast offers undo.
- Overview ruler next to the scrollbar replaces the centre minimap.
- Find in document highlights matches in both versions with a counter.
- Two views (side by side / inline) plus a page-breaks option.
- Word export: accepted changes as plain text, open ones as tracked
  changes. Reports show the review status.
- Saved work format version 2 stores the review status; version 1 opens.
- New tokens: root font size 100 %, document text 15.5-16 px, colour roles
  with blue only for actions; removed/added text at >= 7:1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
…ixes

- Export tests: Word semantics of accepted / rejected / open changes
  (Accept all = current new version, Reject all = old version with the
  accepted changes applied), unchanged output before any review, review
  status in the HTML and Markdown reports.
- App state tests: review status across re-comparisons and undo/redo,
  saved work format version 2 and version 1 compatibility.
- One tab stop per action group (document action row, active list card).
- Focus returns to the menu button after a dialog opened from a menu; a
  rejected header/footer change stays in the list when its line is hidden.
- README and llms-full.txt describe the new views, review and shortcuts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
- Phones offer only the stacked view; a split request falls back to it.
- Tapping the sheet backdrop closes an open menu and returns focus to its button.
- The toast action keeps focus on the page when it switches views.
- Review status serializes with a stable field order.
- Header/footer label capitalization is consistent in English.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
- Accept, reject and edit are three quiet icon buttons (32 px, 44 px on touch) floating at the
  top right of the current paragraph; colour only on hover and keyboard focus. Tooltips name
  the shortcut (A, R, E); E is a new shortcut for editing.
- List cards are navigation only: no buttons, a status glyph and muted text once reviewed.
- Accepting or rejecting moves on to the next open change. The reviewed paragraph keeps a
  status pill with Undo; the toast is no longer shown for review actions.
- First-visit hint explains the glyphs and J/K; Help describes the new placement.
- No hover fill on unchanged paragraphs; the header/footer group no longer grows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
… stable layout

- Accept / reject move on to the next open change, focus its accept button and announce it
  ("Angenommen – weiter mit Änderung 5 von 20"); pressing A repeatedly accepts in a row.
- The same header/footer change on several pages is one list item ("auf 3 Seiten gleich
  geändert"), listed after the body changes; accepting or rejecting it covers every page.
  Exports still list every occurrence in document order.
- The icon group sits in a reserved right margin of the new version, so selecting a paragraph
  never changes any height. Reviewed paragraphs carry a status line with Undo.
- List cards show each change in its sentence with grey context words; whole paragraphs read
  "entfernt: …" / "hinzugefügt: …". Unnumbered paragraphs are "Absatz".
- Editing a reviewed paragraph says that the change is open again.
- Screen readers hear "entfernt: … (Ende entfernt)" / "hinzugefügt: …"; panes are named
  "Alte Fassung: <file>"; replacing a file announces the new comparison and keeps focus.
- Separate filters for review status (Offen / Geprüft) and kind of change; the stepper and the
  list title say what they count.
- Find bar inside the top bar; no toast over open menus or sheets; import errors appear under
  the file slots with an error border; one file name shortening rule everywhere.
- Search hits are blue; print hides the toggles and the current-change ring and always states
  the review progress; forced-colors styles for the ruler and the current change.
- Help sits in the top bar on both screens; the language button names its target language.
- Export menu: Word, print, copy, then "Weitere Formate" (HTML, Markdown, .txt); the ⋯ menu is
  trimmed to new comparison, saved work, language, legal notice and GitHub.
- Copy fixes (spelled-out counts, .txt label, J/K help, scan and empty-file messages, English
  quotation marks), 44 px targets and ≥ 3:1 control edges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
… and the extension

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tvts5ekgounJpaCtwNFnng
Spacing on a 4 px grid, three radii, one hairline colour and two weights.
Panes become one white surface; page breaks are a labelled hairline instead
of cards. The current change is a soft tint with an ink edge instead of a
black ring, changed paragraphs get a thinner, lighter amber edge, the file
pair and stepper no longer look like inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KgQ1PFoGfZMrXQmhBk2wgG
- List header: title, review progress as text and a hairline, one filter
  (all, a review status or a kind of change) instead of hint box,
  segmented control and tabs.
- List cards scan like an inbox: reference, the first change in two lines,
  further changes counted as "+N".
- A reviewed paragraph shows a check or cross in the right margin with an
  undo button below it; the status line above the text is gone, so
  reviewing no longer shifts text, and the old version shows no mark.
- Header/footer groups lose their dividers and get a smaller toggle.
- Narrow screens show only the distinguishing parts of the file names
  ("v1_Vorher -> v2_Nachher").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KgQ1PFoGfZMrXQmhBk2wgG
In the stacked view it stays at the right edge of the document. Both
panes keep the same width in either view, so switching never re-wraps
their text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KgQ1PFoGfZMrXQmhBk2wgG
- Files popover: short "Replace…" / "Remove" buttons in one row (full
  names stay in the accessible labels); consistent spacing.
- More menu: remove a duplicated separator.
- Help text describes the ruler between the versions and the undo arrow.
- Below 1200 px help moves into the more menu so the file names fit.
- File names on wide screens are shortened later; ellipsis as a fallback.
- Pane keyboard focus is a line under the caption instead of a ring
  around the whole pane.
- Phone bottom bar: the step counter reads as a list button, not a block.
- No focus tooltips on touch screens.
- Menus, popover and find bar fade in (off with reduced motion).
- Dark mode: primary hover keeps 7:1 text contrast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KgQ1PFoGfZMrXQmhBk2wgG
@tschnorpfeil
tschnorpfeil merged commit 8a3fdd3 into main Oct 23, 2026
1 check 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