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.
This commit is contained in:
John O'Keefe committed 2026-10-21 14:17:02 -04:00
1 parent 77cd268f4e
commit d7d1495753
14 files changed
+70 -60

No files matched your search

+7 -8
View File
@@ -9,7 +9,6 @@ import {
triggerDownload,
bannerBookTitle,
type AnnotationRow,
type AnnotationSource,
} from "./annotations-banner";
function getMediaId(): string {
@@ -69,8 +68,8 @@ function collectFormData(editorTags: string[]): Record<string, unknown> {
if (el.type === "number") {
const val = parseFloat(el.value);
data[el.name] = isNaN(val) ? 0 : val;
} else if (el.type === "checkbox") {
data[el.name] = (el as HTMLInputElement).checked;
} else if (el instanceof HTMLInputElement && el.type === "checkbox") {
data[el.name] = el.checked;
} else {
data[el.name] = el.value;
}
@@ -369,7 +368,7 @@ Alpine.data("bookDetail", () => {
}
await this.fetchBannerAnnotations();
showToast("Note saved", "success");
} catch (e) {
} catch {
showToast("Could not save the note", "error");
}
},
@@ -386,7 +385,7 @@ Alpine.data("bookDetail", () => {
// Tombstoned server-side: restorable from the Deleted modal.
showToast("Note deleted — restorable from Deleted", "success");
await this.fetchBannerAnnotations();
} catch (_e) {
} catch {
showToast("Could not delete the note", "error");
}
},
@@ -403,7 +402,7 @@ Alpine.data("bookDetail", () => {
// Tombstoned server-side: restorable from the Deleted modal.
showToast("Bookmark deleted — restorable from Deleted", "success");
await this.fetchBannerAnnotations();
} catch (_e) {
} catch {
showToast("Could not delete the bookmark", "error");
}
},
@@ -420,7 +419,7 @@ Alpine.data("bookDetail", () => {
// Tombstoned server-side: restorable from the Deleted modal.
showToast("Highlight deleted — restorable from Deleted", "success");
await this.fetchBannerAnnotations();
} catch (_e) {
} catch {
showToast("Could not delete the highlight", "error");
}
},
@@ -455,7 +454,7 @@ Alpine.data("bookDetail", () => {
);
}
showToast("Annotations exported", "success");
} catch (_e) {
} catch {
showToast("Export failed", "error");
}
},