Commit Graph
4 Commits
Author SHA1 Message Date
John O'Keefe d7d1495753 fix(types): clear web/src type and lint debt with real types
tsc --noEmit and oxlint web/src are now both clean; vp check passes.

- htmx: one canonical Htmx interface in shims.d.ts (ajax/process/trigger
  per the htmx 2.x API). alpine.ts had a second, conflicting Window.htmx
  declaration (TS2717) — removed; bookPicker/collections lose their
  (window as any) escapes now that Window.htmx is properly typed.
- tsconfig lib ES2020 -> ES2021 + DOM.Iterable: replaceAll exists
  (escapeHtml) and for..of over querySelectorAll results is legal.
  Type-checking only — vite emits at esnext regardless.
- custom-section-builder: selectedBooks holds identity fields only;
  typed as Pick<BookInfo, ...> instead of forcing partial API shapes.
  The API-critical BookInfo contract is untouched.
- toast: real bug found by the type system — setup.ts passes 'warning'
  but getToastConfig had no entry, so the warning toast threw TypeError
  at runtime instead of displaying. Added the type + config. Uses
  bg-yellow-500 (verified present in scanned templates => generated by
  Tailwind); the one intentional behavior change in this commit.
- book-detail: instanceof narrowing replaces an as-cast; unused
  AnnotationSource import removed.
- password_validation: explicit HTMLElement | null for the checklist
  list (querySelector('ul') infers HTMLUListElement, conflicting with
  the container-as-list branch).
- stats: tooltip parsed value typed via annotation (bar=scalar,
  line={x,y}); dead getActiveRange() removed (was already tree-shaken
  out of every bundle).
- library-switcher: onSwitch honestly typed void | Promise<void>
  (series.ts passes a sync navigator).
- 20 unused catch bindings converted to optional catch binding.

Verification: fresh build is byte-reproducible; every chunk is
identical to the pre-fix build except main.js (instanceof narrowing,
regex fix, stats refactor), reader.js (destructure), and the shared
storage chunk (renamed — carries toast.ts). reader/main diffs audited
hunk-by-hunk against the intended edits.
2026-10-21 14:17:02 -04:00
John O'Keefe 77cd268f4e style: format web/src with oxfmt defaults (via vp fmt)
One-time mechanical sweep, isolated from any functional change.
- oxfmt defaults: double quotes, semicolons, 2-space indent, LF,
  printWidth 100, trailing commas, bracket spacing, arrow parens.
- vite.config.ts gains an fmt.ignorePatterns block for web/static:
  vendored/minified assets (htmx.min.js, compiled css) must never
  be reformatted.
- Verified: rebuild after the sweep produces byte-identical JS/CSS
  bundles; only sourcemaps changed (they embed sourcesContent).
2026-10-21 13:18:24 -04:00
John O'Keefe f1844591b4 feat(web): templ partials replace the last UI twins (collections grid, conflicts list)
Both pages had a templ render (initial load) and a TypeScript twin
(innerHTML rebuild on refresh) of the same fragment. The twins are
deleted; the server-rendered markup is now the only markup.

- collections: the library switcher swaps in the new books-grid partial
  (GET /collections/:id/books-grid) instead of re-rendering cards from
  the /api/collections JSON. Book loading is extracted into
  Config.loadCollectionBooks, shared verbatim by the page and the
  partial (smart / dashboard-query / manual branches, family-lens
  scoping via resolveLibrary + UserVisibleLibraryIDs) so the two cannot
  drift.
- conflicts: resolve/dismiss refreshes swap the new list partial
  (GET /conflicts/list) with the stats bar as an out-of-band swap,
  instead of the TS twin — which rendered a degraded card (no
  checkboxes, no timestamps), pointed at a window.showResolveModal that
  does not exist, and updated stats via element ids the SSR never had.
- shims.d.ts: window.htmx typing (ajax/process) for TS callers.

Visibility note: the books-grid partial inherits its family-lens
scoping from the shared loader (identical to the detail page); a
dedicated hidden-library pin test for the partial route is still worth
adding in the next test pass.
2026-10-18 12:28:25 -04:00
John O'Keefe 4b5e193d9f feat(web): notes render as markdown — markdown-it+DOMPurify pipeline, rich paste
Objective 6 (web half). Storage/sync stay raw markdown (KOReader shows
literal source, accepted). Notes-grade syntax: headings, emphasis/
strikethrough, code, links, lists, blockquotes, GFM tables, reference
links, footnotes (markdown-it-footnote); images/raw HTML/math excluded
(html:false posture + FORBID img/style/form + default safe-scheme URI
check). Display sites: annotations-drawer note rows and highlight note
lines render pre-sanitized HTML (x-html); render-time sanitize only.

Rich paste: both note textareas intercept paste; a text/html clipboard
flavor converts via turndown (gfm tables incl. a headingless-table rule
emitting pipe syntax; images blanked) and inserts at the caret; plain
pastics fall through to the default paste unchanged.

Verified live in Brave: bold/italic/strike/code/lists/quote/table/
footnote-ref/reference-link render; <script> renders inert literal
text with no alert; javascript: hrefs absent from the DOM; paste of
rich HTML lands markdown at the caret; plain pastes untouched.
2026-10-04 13:25:17 -04:00