@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
|
|
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,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,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.
|
|
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":
|
|
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":
|
|
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",
|