Commit Graph
14 Commits
Author SHA1 Message Date
John O'Keefe c52b514036 chore: formatting sweep — vp fmt on drifted web files, templ regen
Pre-existing format drift in seven web/src files (blank-line and
indent normalization by vp fmt) plus templ generate normalization
across the generated _templ.go files; both swept up while committing
the filter-builder fix. No behavior changes.
2026-10-31 23:27:21 -04:00
John O'Keefe aaf57a66a2 fix(ui): cookie-authed users' writes silently stranded behind bearer gates
THE CLASS: the /api JWT middleware's TokenLookup is
"cookie:token,header:Authorization:Bearer" — the token cookie always
rides same-origin fetches, so a signed-in user never needs the
localStorage bearer. But a dozen call sites gated their fetch on
`localStorage.getItem("token")` and early-returned when absent. The
global 401 interceptor CLEARS that localStorage entry on any session
rotation/blip — after which every gated write silently did nothing:
theme changes reverted on reload (the reported "can no longer change
themes", on top of the interpolation bug d5dbaa48 fixed), chain-order
selects, collection deletion, Add-to-collection submits, filter
save/load/delete, conflict actions, linking actions, stats charts,
tag suggestions, device management, admin scanner actions — all dead
for perfectly authenticated cookie users.

Fix: storage.ts exports authHeaders() (bearer when present, cookie
carries otherwise) and every gate/header site now uses it — theme.ts,
collections.ts (htmx auth, doRemoveBook/doBulkRemove, setChainOrder),
bookPicker submit, filter_builder suggestions + match count,
collection-rules apply hook, bookshelf save/load/delete filter,
conflicts x4, linking x3, admin scanner actions + polls (requireToken
removed — its "Session expired" toast fired for cookie users too),
dashboard preferences/sections, custom-section-builder, header
scanner poll, stats x3, tag suggestions, profile delete-account
(which also wrongly redirected cookie users to /login), and
device-management x4. Reads keep their graceful-empty behavior; the
bearers still ride along when present.

Pinned: the knob-matrix theme spec now REMOVES localStorage.token and
asserts the theme PUT still fires (cookie-only) and the class flips —
the exact strand that started this.

Found by noticing the panel browser's Nord click switched the class
but produced no PUT: the persistence half was gated on a token the
401 interceptor had cleared.
2026-10-31 17:02:44 -04:00
John O'Keefe 3b66b981be fix(devices): copy buttons via component method (copyFromId) under CSP build 2026-10-25 18:14:44 -04:00
John O'Keefe 8631aa00bf refactor: remove dead code across server, templ pages, and client modules
Deadcode-verified removals (RTA reachability from main, cross-checked
with -test and textual xrefs):

Go server:
- whole files: middleware/transaction.go, middleware/error_handler.go,
  middleware/password_validator.go (live registration machinery kept),
  handlers/sync.go, sync/offline.go
- unrouted endpoints: auth UpdateUsername/UpdateEmail, timezone
  settings handler, OPDS RegisterOPDS, Kobo LibrarySync handler,
  ReaderHandler.ShowReader, JSON-data twins on collections/library
- duplicate book-matching handlers (book_matching.go Handler twins,
  matching.go MatchingHandler twins), superseded helpers
  (router.parseUUID, handlers.ValidateDeviceToken, normalizePath,
  userExists, generateLockID)

Templ/client:
- never-routed pages: api_explorer.templ, queue.templ and their
  client modules api-explorer.ts, api-explorer-docs.ts, queue.ts
- fully orphaned wrapper modules events.ts, dom.ts
- broken clearSyncQueue (called a nonexistent endpoint) and the
  hidden, never-populated sync-queue section on the devices page
- 166+ unused exports demoted to private (knip-verified); ~85 truly
  dead declarations removed via tsc noUnusedLocals fixpoint
- stale 'interactive API Explorer' claims dropped from docs

The docs drift-gate package (internal/docs), templ-bound modules
(dashboard, stats, header, index, login, profile*, bookshelf,
collections, toast-error), sqlc-generated code, and web/static are
intentionally untouched.
2026-10-25 11:06:08 -04:00
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-okeefe 855cbd1b74 fix(ts): resolve variable scoping and unused parameters in device management
Fix TypeScript issues in device-management.ts and unlinked_books.ts:

1. device-management.ts:
   - Move 'deviceType' variable declaration to function scope in showDeviceSettings()
   - Previously declared inside a Promise chain, creating potential scope issues
   - Now properly declared at function level before async operations

2. unlinked_books.ts:
   - Remove unused 'result' parameter from .then() handlers
   - Fixes autoLinkBook() and confirmManualLink() functions
   - Handlers don't use the API response result, only need success/failure

These changes improve code clarity and resolve potential runtime issues
with variable accessibility in async callback chains.

Technical details:
- deviceType: moved from Promise .then() block to function scope
- Unused parameters: removed to prevent linting warnings and improve clarity
2026-03-13 22:25:31 -04:00
john-okeefe 48eaa2d286 fix(alpine): wrap all Alpine.data() callbacks in arrow functions for proper component initialization
- Wrap all Alpine.data() object literals in arrow functions (() => ({}))
- This fixes "n.bind is not a function" errors when Alpine initializes components
- Alpine.data() requires a factory function, not a plain object
- Ensures each component instance gets its own closure and proper this binding

Fixed 24 TypeScript files:
- admin.ts, analytics.ts, api.ts, api-explorer-docs.ts
- bookshelf.ts, collection-rules.ts, collections.ts, conflicts.ts
- device-management.ts, docs.ts, header.ts, index.ts
- library.ts, linking.ts, login.ts, password_validation.ts
- profile-modal.ts, profile.ts, queue.ts, register.ts
- search.ts, theme.ts, toast-error.ts, toast.ts, unlinked_books.ts

Before: Alpine.data("name", { method1, method2 })
After:  Alpine.data("name", () => ({ method1, method2 }))

This is a critical fix for Alpine.js v3+ where components must be
registered as factory functions to ensure proper reactivity and
prevent binding errors during initialization.
2026-03-12 15:43:56 -04:00
john-okeefe d6b702e35a device: add copyToClipboard and fix regenerate token HTMX button
Phase 3: Complete device page functionality fixes

- Add copyToClipboard function to device-management.ts:
  - Uses navigator.clipboard.writeText() for copying
  - Shows success/error toast notifications
  - Already exported in Alpine.store, now properly defined
- Fix typo in devices.templ: change 'regerate-token' to 'regenerate-token'
- Add HTMX support to RegenerateDeviceToken handler:
  - Returns HTML with reload script for HTMX requests
  - Preserves JSON response for API calls

The regenerate token button now works via HTMX (hx-put) instead of
Alpine.js, matching the pattern used elsewhere in the app.
2026-03-11 16:42:34 -04:00
john-okeefe 083f152b35 Safety backup before Path A fixes - fixing function mismatches and missing functions 2026-03-11 11:01:43 -04:00
john-okeefe 6728ba83a1 refactor: Add Alpine.js registration to existing TypeScript modules
Added Alpine.global() registration to enable template access to functions:

- admin.ts: Added Alpine for scan, stats, and settings functions
- api-explorer.ts: Already had Alpine (kept as is)
- bookshelf.ts: Added Alpine for library/bookshelf interactions
- collections.ts: Added Alpine for collection management
- conflicts.ts: Added Alpine for conflict resolution
- device-management.ts: Added Alpine with event delegation for dynamic content
- header.ts: Added Alpine for theme dropdown and user menu
- library.ts: Added Alpine registrations
- linking.ts: Added Alpine registrations
- queue.ts: Added Alpine for queue operations
- search.ts: Added Alpine registrations
- themeDropdown.ts: Added Alpine for theme switching

Each module now exports functions both traditionally and via Alpine.global() for template access.
2026-03-08 21:36:24 -04:00
john-okeefe 9947a12f09 refactor(ts): Convert internal window dependencies to ES modules
Phase 1 of ESBuild migration: Convert 193+ internal window reads
to proper ES module imports across consumer modules.

Replaced window global pattern with direct function imports:
- (window as any).showToast → import { showToast } → showToast(msg, "type")
- (window as any).api.post → import { apiPost } → apiPost(url, data)
- (window as any).dom.getElementById → import { getElementById }

Modules migrated:
- admin.ts: Convert 14 showToast window reads
- analytics.ts: Add ES export (no window reads)
- conflicts.ts: Convert 6 showToast window reads
- custom-section-builder.ts: Convert api.post reads, add ES exports
- dashboard.ts: Convert 10 window reads (api, showToast)
- device-management.ts: Convert 4 showToast window reads, add Alpine registration
- linking.ts: Convert showToast window reads
- queue.ts: Convert 8 showToast window reads

Additionally added Alpine.js registration for templates:
- device-management.ts: Register copyToClipboard, regenerateDeviceToken

Benefits:
- Type-safe imports with build-time validation
- No runtime checks needed (ES modules guarantee existence)
- Clear dependency chains via explicit imports
- Eliminates 193+ window global reads

Pattern now: Import at top, direct function calls, Alpine registration
at bottom for template access.

Migration progress: Phase 1 complete
Next: Phase 2 (Alpine registration for remaining modules)
2026-03-08 01:14:35 -05:00
john-okeefe c9ebc5b11a feat: add authorization header to device token regeneration
- Add Bearer token from localStorage to regenerate-token API request
- Update code formatting for consistency (double quotes, indentation)

This ensures the device token regeneration endpoint receives proper
authentication via the Authorization header.
2026-02-16 09:18:02 -05:00
john-okeefe 81fbcfac11 feat: add RegenerateDeviceToken API endpoint
- Add handler to regenerate device auth tokens
- Add PUT /api/devices/:id/regenerate-token route
- Returns new token and sync URLs for device configuration
2026-02-13 12:12:28 -05:00