Skip to content

PoC: client-side USFM document model + live checks via @sillsdev/lynx#327

Draft
henrique221 wants to merge 8 commits into
mainfrom
poc/lynx-client-usfm
Draft

PoC: client-side USFM document model + live checks via @sillsdev/lynx#327
henrique221 wants to merge 8 commits into
mainfrom
poc/lynx-client-usfm

Conversation

@henrique221

@henrique221 henrique221 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Proof of concept for "USFM, but on the client": an isolated authenticated route (/lynx-usfm) where fluent-web parses USFM into a typed ScriptureDocument in the browser using sillsdev/lynx (@sillsdev/lynx 0.3.5, -usfm 0.3.1, -punctuation-checker 0.2.0), runs five checkers live on every edit, and offers quick fixes, on-type smart quotes, and ignore/undo — no server round-trips.

Today Fluent only touches USFM server-side (usfm-grammar import, string-assembled export). This PoC gives the front end a real scripture document model and demonstrates the standard checking interface discussed in the 2026-02-10 Lynx | Fluent discovery session — the general form of the Checks tab/panel proposed for the Repeated Word Check (#305): DiagnosticProvider ↔ per-check hook, Diagnostic ranges ↔ verse-grouped findings, fixes/dismissal ↔ Ignore Here/Everywhere.

Docs on this branch:

  • docs/proposals/lynx-client-usfm-poc/design.md — architecture + the three browser packaging hazards (all predicted, all confirmed live, all worked around; each is an upstream contribution candidate for sillsdev/lynx + sillsdev/machine)
  • docs/proposals/lynx-client-usfm-poc/lynx-fluent-assessment.md — what Lynx can do for Fluent, ranked integration opportunities, risks, next steps

Not production code: everything is additive and isolated under src/features/lynx/ + one route file; no existing flows touched (only vite.config.ts gains browser aliases for @sillsdev/machine's Node built-ins, test mode unaffected).

Screenshots

1) Overview — sample with seeded issues

The PoC page after auto-loading the sample: USFM editor with inline diagnostic highlights on the left, Checks panel with per-check accordions on the right, stats chips showing counts and the client-side parse+check time (~3 ms).

01-overview-sample

2) Inline diagnostics in the editor

Severity-coded highlights rendered from Lynx Diagnostic ranges: red wavy = error (out-of-order \v 3, unmatched quote), amber = warning (missing-verse anchors, disallowed ©).

02-editor-highlights

3) Checks panel (echoes the #305 proposal)

One accordion per provider — Quotation marks, Allowed characters, Paired punctuation, Punctuation context, Verse order — with verse references resolved from the typed node tree, localized messages, fix buttons, Ignore, and a zero state.

03-checks-panel

4) Quick fix round-trip

After clicking Insert missing verse: the \v 4 marker is spliced into the USFM via a typed ScriptureVerse edit from UsfmEditFactory, and the missing-verse diagnostic resolves (verse-order count drops, warnings 5 → 4).

04-quickfix-inserted-v4

5) Ignore / Show ignored / Undo

App-side dismissal mirroring the Repeated-Word suppression UX: ignored findings are hidden from counts, revealed dimmed with an "Ignored" label and Undo ignore under the "Show ignored" toggle. (Published Lynx 0.3.5 has no dismissal store yet — it's in progress upstream; this maps Fluent's cascade onto it.)

05-ignore-undo

6) On-type smart quotes

A straight " typed after "named Boaz." was autocorrected on type to the context-correct closing curly quote by Lynx's QuotationCorrector (caret preserved).

06-ontype-smart-quote

7) The typed document model

Structure tab, both views rendered purely from the parsed node tree (no regex): a formatted scripture preview (chapter numerals, superscript verse numbers — note the out-of-order 1, 3, 2, 5 straight from the data) and the 25-node Book/Chapter/Paragraph/Verse/Text tree with source ranges.

07-structure-preview

8) Real data: Genesis 1 assembled and checked client-side

"Assemble chapter → USFM" fetches the chapter from the existing bible-texts endpoint and assembles it with a client-side mirror of the server export's generateUSFMText — Gujarati IRV Genesis 1 parsed + checked in ~28 ms. The ~2,600 warnings are the English rule set flagging Gujarati characters: concrete evidence that rule sets must be configured per target language (they're builder-based configs).

08-real-chapter-gujarati

Test plan

  • 14 vitest specs (stylesheet without fs, USFM assembly golden output, workspace wiring: parse/diagnostics/fix round-trip/on-type, verse mapping) — whole-repo suite 52/52 green
  • tsc --noEmit, eslint, and vite build clean (PoC ships as a lazy route chunk, ~75 KB gzip; main bundle untouched)
  • Live smoke against the local stack (screenshots above): sample + real chapter, quick fix, ignore/undo, on-type, structure/preview
  • Confirmed no server calls during checking (only the initial chapter fetch)

Notes for reviewers

  • The three @sillsdev/* browser-packaging workarounds (lib/stylesheet.ts cast, vite.config.ts aliases → lib/node-shims.ts, vendored locale pre-registration in lib/workspace.ts) are each commented with the matching upstream ask.
  • lib/verse-order-provider.ts is vendored from Lynx's unpublished examples package (MIT, provenance header) and adapted to the published 0.3.5 API — it doubles as the template for a future Greek Room provider.

…v/lynx

- /lynx-usfm authenticated route: parse USFM in the browser into a typed
  ScriptureDocument (no server round-trip), live diagnostics from the four
  StandardRuleSets.English checkers + a vendored verse-order provider,
  quick fixes, on-type smart quotes, app-side ignore/undo
- browser-safe UsfmStylesheet built from vendored usfm.sty (?raw import)
- usfm-assembly mirrors fluent-api generateUSFMText so a chapter fetched
  from the existing bible-texts endpoint round-trips through the same
  canonical USFM the export produces
- checks panel deliberately echoes docs/proposals/repeated-word-check
- 14 vitest specs cover stylesheet, assembly, workspace wiring, verse map
Three packaging hazards confirmed in the browser (all worked around locally,
all upstream candidates for sillsdev/machine + sillsdev/lynx):

1. @sillsdev/machine corpora computes dirname(fileURLToPath(import.meta.url))
   at module scope; its browser-field stubs leave fileURLToPath undefined and
   the module throws on import. Fix: alias url/path/fs/fs-promises to inert
   shims (exact-match regex; test mode keeps real Node modules for vitest).
2. Vite 8 (rolldown) treats string alias keys as prefixes: 'fs' captured
   'fs/promises'. Regex aliases avoid it.
3. lynx-punctuation-checker loads locale JSON via template-literal dynamic
   import, unresolvable after dep optimization -> raw i18next keys in the UI.
   Fix: pre-register the five namespaces with vendored en.json resources
   (Localizer.addNamespace is first-write-wins).

Verified live: sample + real Genesis 1 (Gujarati IRV) via the bible-texts
endpoint, quick fix, ignore/undo, on-type smart quote, structure/preview.
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4293b9c8-8990-4dc3-b4e5-d662ffb2d301

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch poc/lynx-client-usfm

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@henrique221 henrique221 self-assigned this Jul 1, 2026
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.

1 participant