@brandon_m_behring/book-scaffold-astro 3.0.0-alpha.8 → 3.0.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -50,7 +50,9 @@ export default function VersionSelector() {
50
50
  };
51
51
  }, [open]);
52
52
 
53
- const current = STUB_VERSIONS.find((v) => v.current) || STUB_VERSIONS[0];
53
+ // STUB_VERSIONS is a const literal with >=1 entry so the fallback is
54
+ // always defined; the bang silences TS's noUncheckedIndexedAccess.
55
+ const current = STUB_VERSIONS.find((v) => v.current) ?? STUB_VERSIONS[0]!;
54
56
 
55
57
  return (
56
58
  <div class="version-selector" ref={ref}>
@@ -0,0 +1,5 @@
1
+ import * as preact from 'preact';
2
+
3
+ declare function ToolFilter(): preact.JSX.Element;
4
+
5
+ export { ToolFilter as default };
@@ -0,0 +1,118 @@
1
+ // components/ToolFilter.tsx
2
+ import { useState, useRef, useEffect } from "preact/hooks";
3
+ import { jsx, jsxs } from "preact/jsx-runtime";
4
+ var STORAGE_KEY = "book:tool-filter";
5
+ var EVENT_NAME = "book:tool-filter:change";
6
+ var FILTERABLE_TOOLS = ["claude-code", "gemini-cli", "codex-cli"];
7
+ function readSaved() {
8
+ try {
9
+ const raw = localStorage.getItem(STORAGE_KEY);
10
+ if (!raw) return [];
11
+ const parsed = JSON.parse(raw);
12
+ if (!Array.isArray(parsed)) return [];
13
+ return parsed.filter((s) => typeof s === "string");
14
+ } catch {
15
+ return [];
16
+ }
17
+ }
18
+ function writeSaved(selected) {
19
+ try {
20
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(selected));
21
+ } catch {
22
+ }
23
+ }
24
+ function announce(selected) {
25
+ window.dispatchEvent(
26
+ new CustomEvent(EVENT_NAME, { detail: { selected } })
27
+ );
28
+ }
29
+ function ToolFilter() {
30
+ const [open, setOpen] = useState(false);
31
+ const [selected, setSelected] = useState([]);
32
+ const ref = useRef(null);
33
+ useEffect(() => {
34
+ const saved = readSaved();
35
+ setSelected(saved);
36
+ announce(saved);
37
+ }, []);
38
+ useEffect(() => {
39
+ if (!open) return;
40
+ const onClick = (e) => {
41
+ if (ref.current && !ref.current.contains(e.target)) {
42
+ setOpen(false);
43
+ }
44
+ };
45
+ const onKey = (e) => {
46
+ if (e.key === "Escape") setOpen(false);
47
+ };
48
+ window.addEventListener("click", onClick);
49
+ window.addEventListener("keydown", onKey);
50
+ return () => {
51
+ window.removeEventListener("click", onClick);
52
+ window.removeEventListener("keydown", onKey);
53
+ };
54
+ }, [open]);
55
+ function toggle(tool) {
56
+ const next = selected.includes(tool) ? selected.filter((s) => s !== tool) : [...selected, tool];
57
+ setSelected(next);
58
+ writeSaved(next);
59
+ announce(next);
60
+ }
61
+ function clearAll() {
62
+ setSelected([]);
63
+ writeSaved([]);
64
+ announce([]);
65
+ }
66
+ const activeCount = selected.length;
67
+ const buttonLabel = activeCount === 0 ? "Filter chapters by tool (showing all)" : `Filter chapters by tool (${activeCount} active)`;
68
+ return /* @__PURE__ */ jsxs("div", { class: "tool-filter", ref, children: [
69
+ /* @__PURE__ */ jsxs(
70
+ "button",
71
+ {
72
+ type: "button",
73
+ class: "chrome-button tool-filter-trigger",
74
+ "aria-label": buttonLabel,
75
+ title: buttonLabel,
76
+ "aria-expanded": open,
77
+ "aria-haspopup": "true",
78
+ onClick: () => setOpen((v) => !v),
79
+ children: [
80
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2690" }),
81
+ activeCount > 0 && /* @__PURE__ */ jsx("span", { class: "tool-filter-count", "aria-hidden": "true", children: activeCount })
82
+ ]
83
+ }
84
+ ),
85
+ open && /* @__PURE__ */ jsxs("div", { class: "tool-filter-panel", role: "group", "aria-label": "Tool filters", children: [
86
+ /* @__PURE__ */ jsx("p", { class: "tool-filter-heading", children: "Show chapters for" }),
87
+ /* @__PURE__ */ jsx("ul", { class: "tool-filter-chips", children: FILTERABLE_TOOLS.map((tool) => {
88
+ const on = selected.includes(tool);
89
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
90
+ "button",
91
+ {
92
+ type: "button",
93
+ class: `tool-filter-chip${on ? " tool-filter-chip-on" : ""}`,
94
+ "aria-pressed": on,
95
+ onClick: () => toggle(tool),
96
+ children: tool
97
+ }
98
+ ) });
99
+ }) }),
100
+ /* @__PURE__ */ jsxs("div", { class: "tool-filter-footer", children: [
101
+ /* @__PURE__ */ jsx(
102
+ "button",
103
+ {
104
+ type: "button",
105
+ class: "tool-filter-clear",
106
+ onClick: clearAll,
107
+ disabled: activeCount === 0,
108
+ children: "Clear"
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsx("span", { class: "tool-filter-note", children: "Cross-tool chapters stay visible under any filter." })
112
+ ] })
113
+ ] })
114
+ ] });
115
+ }
116
+ export {
117
+ ToolFilter as default
118
+ };
@@ -0,0 +1,5 @@
1
+ import * as preact from 'preact';
2
+
3
+ declare function VersionSelector(): preact.JSX.Element;
4
+
5
+ export { VersionSelector as default };
@@ -0,0 +1,58 @@
1
+ // components/VersionSelector.tsx
2
+ import { useState, useRef, useEffect } from "preact/hooks";
3
+ import { jsx, jsxs } from "preact/jsx-runtime";
4
+ var STUB_VERSIONS = [
5
+ { id: "", label: "Latest (main)", date: "2026-04-17", current: true },
6
+ { id: "v1.0", label: "v1.0", date: "2026-05-01", current: false }
7
+ ];
8
+ function VersionSelector() {
9
+ const [open, setOpen] = useState(false);
10
+ const ref = useRef(null);
11
+ useEffect(() => {
12
+ if (!open) return;
13
+ const onClick = (e) => {
14
+ if (ref.current && !ref.current.contains(e.target)) {
15
+ setOpen(false);
16
+ }
17
+ };
18
+ const onKey = (e) => {
19
+ if (e.key === "Escape") setOpen(false);
20
+ };
21
+ window.addEventListener("click", onClick);
22
+ window.addEventListener("keydown", onKey);
23
+ return () => {
24
+ window.removeEventListener("click", onClick);
25
+ window.removeEventListener("keydown", onKey);
26
+ };
27
+ }, [open]);
28
+ const current = STUB_VERSIONS.find((v) => v.current) ?? STUB_VERSIONS[0];
29
+ return /* @__PURE__ */ jsxs("div", { class: "version-selector", ref, children: [
30
+ /* @__PURE__ */ jsx(
31
+ "button",
32
+ {
33
+ type: "button",
34
+ class: "chrome-button version-selector-trigger",
35
+ "aria-label": "Select book version",
36
+ title: `Current: ${current.label} (${current.date})`,
37
+ "aria-expanded": open,
38
+ "aria-haspopup": "listbox",
39
+ onClick: () => setOpen((v) => !v),
40
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "v" })
41
+ }
42
+ ),
43
+ open && /* @__PURE__ */ jsx("ul", { class: "version-selector-menu", role: "listbox", children: STUB_VERSIONS.map((v) => /* @__PURE__ */ jsx("li", { role: "option", "aria-selected": v.current, children: /* @__PURE__ */ jsxs(
44
+ "a",
45
+ {
46
+ href: v.id ? `/${v.id}/` : "/",
47
+ class: v.current ? "version-current" : "",
48
+ children: [
49
+ /* @__PURE__ */ jsx("span", { class: "version-label", children: v.label }),
50
+ /* @__PURE__ */ jsx("span", { class: "version-date", children: v.date })
51
+ ]
52
+ }
53
+ ) })) })
54
+ ] });
55
+ }
56
+ export {
57
+ VersionSelector as default
58
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@brandon_m_behring/book-scaffold-astro",
3
3
  "description": "Astro 6 + MDX toolkit for long-form technical books. Profile-aware (academic / tools / minimal); ships Tufte typography, KaTeX, BibTeX citations, Pagefind, Cloudflare Workers deploy. See PACKAGE_DESIGN.md for the API contract.",
4
- "version": "3.0.0-alpha.8",
4
+ "version": "3.0.0-alpha.9",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Brandon Behring",
@@ -73,9 +73,15 @@
73
73
  "./components/Tag.astro": "./components/Tag.astro",
74
74
  "./components/Theorem.astro": "./components/Theorem.astro",
75
75
  "./components/TipBox.astro": "./components/TipBox.astro",
76
- "./components/ToolFilter": "./components/ToolFilter.tsx",
76
+ "./components/ToolFilter": {
77
+ "types": "./dist/components/ToolFilter.d.ts",
78
+ "import": "./dist/components/ToolFilter.mjs"
79
+ },
77
80
  "./components/TryThis.astro": "./components/TryThis.astro",
78
- "./components/VersionSelector": "./components/VersionSelector.tsx",
81
+ "./components/VersionSelector": {
82
+ "types": "./dist/components/VersionSelector.d.ts",
83
+ "import": "./dist/components/VersionSelector.mjs"
84
+ },
79
85
  "./components/WarnBox.astro": "./components/WarnBox.astro",
80
86
  "./components/WeekRef.astro": "./components/WeekRef.astro",
81
87
  "./components/XRef.astro": "./components/XRef.astro",