@brandon_m_behring/book-scaffold-astro 3.0.0-alpha.0
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.
- package/CLAUDE.md +179 -0
- package/bin/book-scaffold.mjs +61 -0
- package/components/CaseStudy.astro +36 -0
- package/components/ChapterHeader.astro +61 -0
- package/components/ChapterNav.astro +29 -0
- package/components/ChapterTOC.astro +33 -0
- package/components/Citation.astro +94 -0
- package/components/Cite.astro +71 -0
- package/components/CodeBlock.astro +115 -0
- package/components/CodeRef.astro +49 -0
- package/components/ConceptBox.astro +26 -0
- package/components/Convergence.astro +41 -0
- package/components/CounterBox.astro +15 -0
- package/components/Divergence.astro +32 -0
- package/components/DynConnect.astro +15 -0
- package/components/ExampleBox.astro +15 -0
- package/components/Figure.astro +35 -0
- package/components/InsightBox.astro +15 -0
- package/components/KeyIdea.astro +21 -0
- package/components/MarginNote.astro +37 -0
- package/components/NoteBox.astro +15 -0
- package/components/OpenQuestion.astro +15 -0
- package/components/PaperBox.astro +15 -0
- package/components/PatternTimeline.astro +133 -0
- package/components/Recovery.astro +34 -0
- package/components/ResultBox.astro +15 -0
- package/components/Sidebar.astro +268 -0
- package/components/Sidenote.astro +26 -0
- package/components/SkillBox.astro +24 -0
- package/components/SourceArchive.astro +285 -0
- package/components/StatusBadge.astro +51 -0
- package/components/Tag.astro +60 -0
- package/components/Theorem.astro +65 -0
- package/components/TipBox.astro +15 -0
- package/components/ToolFilter.tsx +160 -0
- package/components/TryThis.astro +23 -0
- package/components/VersionSelector.tsx +85 -0
- package/components/WarnBox.astro +15 -0
- package/components/WeekRef.astro +51 -0
- package/components/XRef.astro +40 -0
- package/dist/index.d.ts +135 -0
- package/dist/index.mjs +369 -0
- package/dist/lib/katex-macros.d.ts +26 -0
- package/dist/lib/katex-macros.mjs +98 -0
- package/dist/schemas.d.ts +17 -0
- package/dist/schemas.mjs +160 -0
- package/dist/types-Cz-pwE1N.d.ts +61 -0
- package/examples/chapter-template-academic.mdx +100 -0
- package/examples/chapter-template-tools.mdx +90 -0
- package/layouts/Base.astro +250 -0
- package/layouts/Chapter.astro +37 -0
- package/package.json +137 -0
- package/pages/chapters.astro +371 -0
- package/pages/convergence.astro +96 -0
- package/pages/print.astro +39 -0
- package/pages/references.astro +160 -0
- package/pages/search.astro +87 -0
- package/pedagogy/kf-chapter-shape.md +96 -0
- package/pedagogy/source-tiers.md +121 -0
- package/pedagogy/volatility-classes.md +110 -0
- package/recipes/00-getting-started.md +77 -0
- package/recipes/01-add-math.md +71 -0
- package/recipes/02-bibliography-pipeline.md +82 -0
- package/recipes/03-asset-pipelines.md +84 -0
- package/recipes/04-component-library.md +118 -0
- package/recipes/05-deploy-cloudflare.md +74 -0
- package/recipes/06-mobile-first-layout.md +73 -0
- package/recipes/07-chapter-shapes.md +84 -0
- package/recipes/08-decisions-ledger.md +110 -0
- package/recipes/09-validation.md +106 -0
- package/recipes/10-custom-domain.md +72 -0
- package/recipes/README.md +43 -0
- package/scripts/build-bib.mjs +99 -0
- package/scripts/build-figures.mjs +179 -0
- package/scripts/render-notebooks.mjs +223 -0
- package/scripts/validate.mjs +179 -0
- package/styles/callouts.css +303 -0
- package/styles/chapter.css +209 -0
- package/styles/convergence.css +349 -0
- package/styles/layout.css +156 -0
- package/styles/print.css +203 -0
- package/styles/tokens.css +194 -0
- package/styles/tool-filter.css +135 -0
- package/styles/typography.css +147 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Tag — inline practice tag that flags the provenance of a stated claim.
|
|
4
|
+
*
|
|
5
|
+
* Maps LaTeX `\tagofficial{}`, `\tagpractitioner{}`, `\tagconv{}` which are
|
|
6
|
+
* placed immediately after a claim to flag its trust level:
|
|
7
|
+
*
|
|
8
|
+
* official Vendor / paper / canonical reference. Pair with \cite{...}.
|
|
9
|
+
* practitioner Repo lore or one-shop convention. Useful but not load-bearing.
|
|
10
|
+
* convergence >=2 independent implementations agree. Stronger than practitioner,
|
|
11
|
+
* weaker than official.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* S4D-Lin is the default initialization <Tag kind="convergence" />
|
|
15
|
+
* in the S4D, S5, and mamba-minimal reference codebases.
|
|
16
|
+
*/
|
|
17
|
+
type Kind = 'official' | 'practitioner' | 'convergence';
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
kind: Kind;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { kind } = Astro.props;
|
|
24
|
+
|
|
25
|
+
const LABEL: Record<Kind, string> = {
|
|
26
|
+
official: 'Official',
|
|
27
|
+
practitioner: 'Practitioner',
|
|
28
|
+
convergence: 'Convergence',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const TITLE: Record<Kind, string> = {
|
|
32
|
+
official: 'Vendor or canonical reference — paired with a citation',
|
|
33
|
+
practitioner: 'Repo lore or single-shop convention — not load-bearing',
|
|
34
|
+
convergence: 'Two or more independent implementations agree',
|
|
35
|
+
};
|
|
36
|
+
---
|
|
37
|
+
<span class="practice-tag" data-kind={kind} title={TITLE[kind]}>
|
|
38
|
+
[{LABEL[kind]}]
|
|
39
|
+
</span>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
.practice-tag {
|
|
43
|
+
display: inline-block;
|
|
44
|
+
font-family: var(--font-code);
|
|
45
|
+
font-size: 0.75em;
|
|
46
|
+
font-weight: 500;
|
|
47
|
+
padding: 0 0.4em;
|
|
48
|
+
margin: 0 0.1em;
|
|
49
|
+
border-radius: var(--radius-sm);
|
|
50
|
+
background: var(--color-bg-subtle);
|
|
51
|
+
border: 1px solid var(--color-border);
|
|
52
|
+
color: var(--color-text-muted);
|
|
53
|
+
vertical-align: 0.1em;
|
|
54
|
+
letter-spacing: 0.04em;
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
}
|
|
57
|
+
.practice-tag[data-kind="official"] { border-color: var(--callout-official); color: var(--callout-official); }
|
|
58
|
+
.practice-tag[data-kind="convergence"] { border-color: var(--callout-insight); color: var(--callout-insight); }
|
|
59
|
+
.practice-tag[data-kind="practitioner"] { border-color: var(--callout-tip); color: var(--callout-tip); }
|
|
60
|
+
</style>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Theorem — unified component for amsthm-family environments.
|
|
4
|
+
*
|
|
5
|
+
* Maps LaTeX `\begin{theorem}`, `\begin{proposition}`, `\begin{lemma}`,
|
|
6
|
+
* `\begin{corollary}`, `\begin{definition}`, `\begin{example}`,
|
|
7
|
+
* `\begin{exercise}`, `\begin{remark}` to a single component with a
|
|
8
|
+
* `kind` prop.
|
|
9
|
+
*
|
|
10
|
+
* Auto-numbering note: the LaTeX preamble shares a counter between
|
|
11
|
+
* theorem/proposition/lemma/corollary (`\newtheorem{lemma}[theorem]`)
|
|
12
|
+
* and gives each of definition/example/exercise/remark its own counter.
|
|
13
|
+
* In MDX we accept that as a contract: per-chapter counters are
|
|
14
|
+
* implemented in Phase 2.6 (label scanner) — for now, the author
|
|
15
|
+
* passes `n` explicitly when numbering matters.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* <Theorem kind="theorem" n="4.2" name="Stable continuous-time eigenvalues" id="thm-w4-stability">
|
|
19
|
+
* For …
|
|
20
|
+
* </Theorem>
|
|
21
|
+
*
|
|
22
|
+
* <Theorem kind="definition" n="4.1" name="HiPPO-LegS">
|
|
23
|
+
* The HiPPO-LegS state matrix …
|
|
24
|
+
* </Theorem>
|
|
25
|
+
*/
|
|
26
|
+
type Kind =
|
|
27
|
+
| 'theorem'
|
|
28
|
+
| 'proposition'
|
|
29
|
+
| 'lemma'
|
|
30
|
+
| 'corollary'
|
|
31
|
+
| 'definition'
|
|
32
|
+
| 'example'
|
|
33
|
+
| 'exercise'
|
|
34
|
+
| 'remark'
|
|
35
|
+
| 'proof';
|
|
36
|
+
|
|
37
|
+
interface Props {
|
|
38
|
+
kind: Kind;
|
|
39
|
+
n?: string;
|
|
40
|
+
name?: string;
|
|
41
|
+
id?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { kind, n, name, id } = Astro.props;
|
|
45
|
+
|
|
46
|
+
const KIND_LABEL: Record<Kind, string> = {
|
|
47
|
+
theorem: 'Theorem',
|
|
48
|
+
proposition: 'Proposition',
|
|
49
|
+
lemma: 'Lemma',
|
|
50
|
+
corollary: 'Corollary',
|
|
51
|
+
definition: 'Definition',
|
|
52
|
+
example: 'Example',
|
|
53
|
+
exercise: 'Exercise',
|
|
54
|
+
remark: 'Remark',
|
|
55
|
+
proof: 'Proof',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const label = KIND_LABEL[kind];
|
|
59
|
+
const numbered = n ? `${label} ${n}` : label;
|
|
60
|
+
const fullLabel = name ? `${numbered} (${name})` : numbered;
|
|
61
|
+
---
|
|
62
|
+
<div class="theorem" data-kind={kind} id={id}>
|
|
63
|
+
<span class="theorem-label">{fullLabel}.</span>
|
|
64
|
+
<span class="theorem-body"><slot /></span>
|
|
65
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* TipBox — maps to LaTeX `tipbox` env.
|
|
4
|
+
* Default use: one-off practical hints, side-shows.
|
|
5
|
+
* Family: green (tip).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Tip' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-tip" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolFilter — Preact island for per-tool chapter filtering.
|
|
3
|
+
*
|
|
4
|
+
* Renders a filter button in the chrome row that opens a chip panel.
|
|
5
|
+
* Each chip toggles one tool slug in the active filter set. Empty set
|
|
6
|
+
* means "show all". The selection persists to localStorage and is
|
|
7
|
+
* announced via CustomEvent so non-hydrated pages (e.g., /chapters/)
|
|
8
|
+
* can react without a hydration cost of their own.
|
|
9
|
+
*
|
|
10
|
+
* Hydrated with `client:idle` from Base.astro. Keeps initial paint fast.
|
|
11
|
+
*
|
|
12
|
+
* Event contract (commit 2 emits; commit 3 adds a consumer on
|
|
13
|
+
* /chapters/):
|
|
14
|
+
* event name: "book:tool-filter:change"
|
|
15
|
+
* event detail: { selected: string[] } // tool slugs, may be empty
|
|
16
|
+
*
|
|
17
|
+
* Note: 'cross-tool' is intentionally not a chip. Chapters tagged as
|
|
18
|
+
* cross-tool should remain visible under any filter selection; the
|
|
19
|
+
* consumer script handles that rule.
|
|
20
|
+
*/
|
|
21
|
+
import { useState, useRef, useEffect } from 'preact/hooks';
|
|
22
|
+
|
|
23
|
+
const STORAGE_KEY = 'book:tool-filter';
|
|
24
|
+
const EVENT_NAME = 'book:tool-filter:change';
|
|
25
|
+
|
|
26
|
+
// Filter chips. 'cross-tool' is omitted on purpose (see module doc).
|
|
27
|
+
const FILTERABLE_TOOLS = ['claude-code', 'gemini-cli', 'codex-cli'] as const;
|
|
28
|
+
type FilterableTool = (typeof FILTERABLE_TOOLS)[number];
|
|
29
|
+
|
|
30
|
+
function readSaved(): string[] {
|
|
31
|
+
try {
|
|
32
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
33
|
+
if (!raw) return [];
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
if (!Array.isArray(parsed)) return [];
|
|
36
|
+
return parsed.filter((s): s is string => typeof s === 'string');
|
|
37
|
+
} catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function writeSaved(selected: string[]): void {
|
|
43
|
+
try {
|
|
44
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(selected));
|
|
45
|
+
} catch {
|
|
46
|
+
/* localStorage unavailable — keep in-memory state only */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function announce(selected: string[]): void {
|
|
51
|
+
window.dispatchEvent(
|
|
52
|
+
new CustomEvent(EVENT_NAME, { detail: { selected } }),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default function ToolFilter() {
|
|
57
|
+
const [open, setOpen] = useState(false);
|
|
58
|
+
const [selected, setSelected] = useState<string[]>([]);
|
|
59
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
60
|
+
|
|
61
|
+
// Hydrate from localStorage on mount, then announce so consumers
|
|
62
|
+
// apply the persisted filter immediately after hydration.
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const saved = readSaved();
|
|
65
|
+
setSelected(saved);
|
|
66
|
+
announce(saved);
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
// Close on outside click or Escape.
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (!open) return;
|
|
72
|
+
const onClick = (e: MouseEvent) => {
|
|
73
|
+
if (ref.current && !ref.current.contains(e.target as Node)) {
|
|
74
|
+
setOpen(false);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const onKey = (e: KeyboardEvent) => {
|
|
78
|
+
if (e.key === 'Escape') setOpen(false);
|
|
79
|
+
};
|
|
80
|
+
window.addEventListener('click', onClick);
|
|
81
|
+
window.addEventListener('keydown', onKey);
|
|
82
|
+
return () => {
|
|
83
|
+
window.removeEventListener('click', onClick);
|
|
84
|
+
window.removeEventListener('keydown', onKey);
|
|
85
|
+
};
|
|
86
|
+
}, [open]);
|
|
87
|
+
|
|
88
|
+
function toggle(tool: FilterableTool): void {
|
|
89
|
+
const next = selected.includes(tool)
|
|
90
|
+
? selected.filter((s) => s !== tool)
|
|
91
|
+
: [...selected, tool];
|
|
92
|
+
setSelected(next);
|
|
93
|
+
writeSaved(next);
|
|
94
|
+
announce(next);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function clearAll(): void {
|
|
98
|
+
setSelected([]);
|
|
99
|
+
writeSaved([]);
|
|
100
|
+
announce([]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const activeCount = selected.length;
|
|
104
|
+
const buttonLabel =
|
|
105
|
+
activeCount === 0
|
|
106
|
+
? 'Filter chapters by tool (showing all)'
|
|
107
|
+
: `Filter chapters by tool (${activeCount} active)`;
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<div class="tool-filter" ref={ref}>
|
|
111
|
+
<button
|
|
112
|
+
type="button"
|
|
113
|
+
class="chrome-button tool-filter-trigger"
|
|
114
|
+
aria-label={buttonLabel}
|
|
115
|
+
title={buttonLabel}
|
|
116
|
+
aria-expanded={open}
|
|
117
|
+
aria-haspopup="true"
|
|
118
|
+
onClick={() => setOpen((v) => !v)}
|
|
119
|
+
>
|
|
120
|
+
<span aria-hidden="true">⚐</span>
|
|
121
|
+
{activeCount > 0 && (
|
|
122
|
+
<span class="tool-filter-count" aria-hidden="true">{activeCount}</span>
|
|
123
|
+
)}
|
|
124
|
+
</button>
|
|
125
|
+
{open && (
|
|
126
|
+
<div class="tool-filter-panel" role="group" aria-label="Tool filters">
|
|
127
|
+
<p class="tool-filter-heading">Show chapters for</p>
|
|
128
|
+
<ul class="tool-filter-chips">
|
|
129
|
+
{FILTERABLE_TOOLS.map((tool) => {
|
|
130
|
+
const on = selected.includes(tool);
|
|
131
|
+
return (
|
|
132
|
+
<li>
|
|
133
|
+
<button
|
|
134
|
+
type="button"
|
|
135
|
+
class={`tool-filter-chip${on ? ' tool-filter-chip-on' : ''}`}
|
|
136
|
+
aria-pressed={on}
|
|
137
|
+
onClick={() => toggle(tool)}
|
|
138
|
+
>
|
|
139
|
+
{tool}
|
|
140
|
+
</button>
|
|
141
|
+
</li>
|
|
142
|
+
);
|
|
143
|
+
})}
|
|
144
|
+
</ul>
|
|
145
|
+
<div class="tool-filter-footer">
|
|
146
|
+
<button
|
|
147
|
+
type="button"
|
|
148
|
+
class="tool-filter-clear"
|
|
149
|
+
onClick={clearAll}
|
|
150
|
+
disabled={activeCount === 0}
|
|
151
|
+
>Clear</button>
|
|
152
|
+
<span class="tool-filter-note">
|
|
153
|
+
Cross-tool chapters stay visible under any filter.
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* TryThis — practice exercise, <10 minutes, applies to reader's own
|
|
4
|
+
* project. Converts reading to doing.
|
|
5
|
+
*
|
|
6
|
+
* Default title is "Try this" — override for specificity:
|
|
7
|
+
* <TryThis title="Measure your context budget">...</TryThis>
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* <TryThis>
|
|
11
|
+
* Open a chapter from your current work and run `/context`. What
|
|
12
|
+
* percentage is already filled? What would you evict first?
|
|
13
|
+
* </TryThis>
|
|
14
|
+
*/
|
|
15
|
+
interface Props {
|
|
16
|
+
title?: string;
|
|
17
|
+
}
|
|
18
|
+
const { title = 'Try this' } = Astro.props;
|
|
19
|
+
---
|
|
20
|
+
<aside class="callout callout-try" role="note" aria-label={title}>
|
|
21
|
+
<strong class="callout-title">{title}</strong>
|
|
22
|
+
<div class="callout-body"><slot /></div>
|
|
23
|
+
</aside>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VersionSelector — Preact island for the version dropdown.
|
|
3
|
+
*
|
|
4
|
+
* Stage 0: stub data. The real version list arrives in Stage 3 when
|
|
5
|
+
* multi-version CI deploys exist; the component is wired now to prove
|
|
6
|
+
* the islands architecture works with hydration, state, and event
|
|
7
|
+
* handlers.
|
|
8
|
+
*
|
|
9
|
+
* Hydrated with `client:idle` from Base.astro — won't run until the
|
|
10
|
+
* main thread is idle, keeping initial paint fast.
|
|
11
|
+
*
|
|
12
|
+
* Stage 3 will replace the STUB_VERSIONS constant with a runtime fetch
|
|
13
|
+
* or a build-time-injected prop that lists all deployed versions.
|
|
14
|
+
*/
|
|
15
|
+
import { useState, useRef, useEffect } from 'preact/hooks';
|
|
16
|
+
|
|
17
|
+
type VersionEntry = {
|
|
18
|
+
id: string; // URL subpath segment ('' for latest)
|
|
19
|
+
label: string; // display label
|
|
20
|
+
date: string; // release date
|
|
21
|
+
current: boolean; // mark the version being viewed
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Stub data for Stage 0. Stage 3 will inject the real list.
|
|
25
|
+
const STUB_VERSIONS: VersionEntry[] = [
|
|
26
|
+
{ id: '', label: 'Latest (main)', date: '2026-04-17', current: true },
|
|
27
|
+
{ id: 'v1.0', label: 'v1.0', date: '2026-05-01', current: false },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export default function VersionSelector() {
|
|
31
|
+
const [open, setOpen] = useState(false);
|
|
32
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
33
|
+
|
|
34
|
+
// Close on outside click or Escape.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!open) return;
|
|
37
|
+
const onClick = (e: MouseEvent) => {
|
|
38
|
+
if (ref.current && !ref.current.contains(e.target as Node)) {
|
|
39
|
+
setOpen(false);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const onKey = (e: KeyboardEvent) => {
|
|
43
|
+
if (e.key === 'Escape') setOpen(false);
|
|
44
|
+
};
|
|
45
|
+
window.addEventListener('click', onClick);
|
|
46
|
+
window.addEventListener('keydown', onKey);
|
|
47
|
+
return () => {
|
|
48
|
+
window.removeEventListener('click', onClick);
|
|
49
|
+
window.removeEventListener('keydown', onKey);
|
|
50
|
+
};
|
|
51
|
+
}, [open]);
|
|
52
|
+
|
|
53
|
+
const current = STUB_VERSIONS.find((v) => v.current) || STUB_VERSIONS[0];
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div class="version-selector" ref={ref}>
|
|
57
|
+
<button
|
|
58
|
+
type="button"
|
|
59
|
+
class="chrome-button version-selector-trigger"
|
|
60
|
+
aria-label="Select book version"
|
|
61
|
+
title={`Current: ${current.label} (${current.date})`}
|
|
62
|
+
aria-expanded={open}
|
|
63
|
+
aria-haspopup="listbox"
|
|
64
|
+
onClick={() => setOpen((v) => !v)}
|
|
65
|
+
>
|
|
66
|
+
<span aria-hidden="true">v</span>
|
|
67
|
+
</button>
|
|
68
|
+
{open && (
|
|
69
|
+
<ul class="version-selector-menu" role="listbox">
|
|
70
|
+
{STUB_VERSIONS.map((v) => (
|
|
71
|
+
<li role="option" aria-selected={v.current}>
|
|
72
|
+
<a
|
|
73
|
+
href={v.id ? `/${v.id}/` : '/'}
|
|
74
|
+
class={v.current ? 'version-current' : ''}
|
|
75
|
+
>
|
|
76
|
+
<span class="version-label">{v.label}</span>
|
|
77
|
+
<span class="version-date">{v.date}</span>
|
|
78
|
+
</a>
|
|
79
|
+
</li>
|
|
80
|
+
))}
|
|
81
|
+
</ul>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* WarnBox — maps to LaTeX `warnbox` env.
|
|
4
|
+
* Default use: caveats, failure modes, pitfalls.
|
|
5
|
+
* Family: rose (warn).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Warning' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-warn" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* WeekRef — inline pointer to another chapter, mapping LaTeX `\weekref{N}{body}`.
|
|
4
|
+
*
|
|
5
|
+
* Renders as: "-> Week N: body" with "Week N" hyperlinked to /chapters/weekNN/.
|
|
6
|
+
*
|
|
7
|
+
* Used for forward and backward references in prose. The target chapter
|
|
8
|
+
* may not exist yet (other weeks not yet converted to MDX); the link
|
|
9
|
+
* will 404 gracefully — once the target chapter lands, links resolve
|
|
10
|
+
* without further author work.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* <WeekRef week={4}>
|
|
14
|
+
* S4 layer uses SSMParam from this chapter for separate LR
|
|
15
|
+
* treatment of HiPPO parameters.
|
|
16
|
+
* </WeekRef>
|
|
17
|
+
*/
|
|
18
|
+
interface Props {
|
|
19
|
+
week: number;
|
|
20
|
+
}
|
|
21
|
+
const { week } = Astro.props;
|
|
22
|
+
const slug = `week${String(week).padStart(2, '0')}`;
|
|
23
|
+
const href = `/chapters/${slug}/`;
|
|
24
|
+
---
|
|
25
|
+
<aside class="week-ref">
|
|
26
|
+
<span class="week-ref-arrow" aria-hidden="true">→</span>
|
|
27
|
+
<a href={href} class="week-ref-link">Week {week}</a>:
|
|
28
|
+
<span class="week-ref-body"><slot /></span>
|
|
29
|
+
</aside>
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.week-ref {
|
|
33
|
+
margin: var(--space-3) 0;
|
|
34
|
+
padding: var(--space-2) var(--space-3);
|
|
35
|
+
background: var(--color-bg-subtle);
|
|
36
|
+
border-left: 2px solid var(--callout-info);
|
|
37
|
+
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
38
|
+
font-size: var(--text-sm);
|
|
39
|
+
}
|
|
40
|
+
.week-ref-arrow {
|
|
41
|
+
color: var(--callout-info);
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
margin-right: var(--space-1);
|
|
44
|
+
}
|
|
45
|
+
.week-ref-link {
|
|
46
|
+
font-weight: 500;
|
|
47
|
+
}
|
|
48
|
+
.week-ref-body {
|
|
49
|
+
color: var(--color-text-muted);
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* XRef — resolves a `\cref{label}` LaTeX reference to a hyperlink.
|
|
4
|
+
*
|
|
5
|
+
* Reads src/data/labels.json built by scripts/build-labels.mjs
|
|
6
|
+
* (Phase 2.6). For each known id, the map provides:
|
|
7
|
+
* { href: "/chapters/week04#thm-w4-stability", display: "Theorem 4.2" }
|
|
8
|
+
*
|
|
9
|
+
* Phase 2.2 (now): labels.json ships empty. An unknown id renders an
|
|
10
|
+
* inline "[?label]" placeholder so the build doesn't fail before we
|
|
11
|
+
* have chapters with labels. Once Phase 2.6 lands, the validator
|
|
12
|
+
* promotes "unknown label" from a warning to a build error.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* By <XRef id="thm:w4:stability" />, the discretized eigenvalues …
|
|
16
|
+
*/
|
|
17
|
+
type LabelEntry = { href: string; display: string };
|
|
18
|
+
|
|
19
|
+
// Resolve labels.json from the consumer's project root (Vite resolves `/`
|
|
20
|
+
// to project root, not the package). Missing file -> empty map -> [?id]
|
|
21
|
+
// placeholders (silent degrade for dev ergonomics; validator catches at CI).
|
|
22
|
+
const labelsModules = import.meta.glob<{ default: Record<string, LabelEntry> }>(
|
|
23
|
+
'/src/data/labels.json',
|
|
24
|
+
{ eager: true },
|
|
25
|
+
);
|
|
26
|
+
const labelsModule = labelsModules['/src/data/labels.json'];
|
|
27
|
+
const map = (labelsModule?.default ?? {}) as Record<string, LabelEntry>;
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
id: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { id } = Astro.props;
|
|
34
|
+
const entry = map[id];
|
|
35
|
+
---
|
|
36
|
+
{entry ? (
|
|
37
|
+
<a href={entry.href} class="xref">{entry.display}</a>
|
|
38
|
+
) : (
|
|
39
|
+
<span class="xref xref-unknown" title={`Unknown label: ${id}`}>[?{id}]</span>
|
|
40
|
+
)}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { AstroUserConfig, AstroIntegration } from 'astro';
|
|
2
|
+
import { b as BookConfigOptions, d as BookScaffoldIntegrationOptions } from './types-Cz-pwE1N.js';
|
|
3
|
+
export { B as BOOK_PROFILES, a as BookConfigError, c as BookProfile, e as BookSchemasOptions, r as resolveProfile } from './types-Cz-pwE1N.js';
|
|
4
|
+
import { z } from 'astro/zod';
|
|
5
|
+
|
|
6
|
+
declare function defineBookConfig(opts: BookConfigOptions): Promise<AstroUserConfig>;
|
|
7
|
+
|
|
8
|
+
declare function bookScaffoldIntegration(opts: BookScaffoldIntegrationOptions): AstroIntegration;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Zod schemas + enum constants for book content collections.
|
|
12
|
+
*
|
|
13
|
+
* Imports `z` from `astro/zod` (a real module re-export) so schemas can
|
|
14
|
+
* be constructed at package-load time outside an Astro runtime context.
|
|
15
|
+
* `defineBookSchemas` in index.ts wraps these into Astro `defineCollection`
|
|
16
|
+
* calls at the consumer's content-config load time.
|
|
17
|
+
*
|
|
18
|
+
* Schemas ported verbatim from v2.0 src/content.config.ts. See
|
|
19
|
+
* PACKAGE_DESIGN.md §5 for the public reproduction.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
declare const toolSlugs: readonly ["claude-code", "gemini-cli", "codex-cli", "cross-tool"];
|
|
23
|
+
declare const volatilityLevels: readonly ["stable-principle", "architectural-pattern", "feature-surface"];
|
|
24
|
+
declare const sourceTiers: readonly ["T1-official", "T2-release-notes", "T3-practitioner", "T4-conjecture"];
|
|
25
|
+
declare const changeKinds: readonly ["added", "removed", "changed", "deprecated"];
|
|
26
|
+
declare const patternCategories: readonly ["safety", "scale", "context", "interaction", "extension", "other"];
|
|
27
|
+
declare const academicParts: readonly ["foundations", "ssm-core", "beyond-ssm", "integration", "synthesis"];
|
|
28
|
+
declare const chapterStatus: readonly ["implemented", "chapter_only", "reading_only", "prose_only", "code_only", "scaffolded", "planned"];
|
|
29
|
+
declare const academicChapterSchema: z.ZodObject<{
|
|
30
|
+
week: z.ZodNumber;
|
|
31
|
+
part: z.ZodEnum<{
|
|
32
|
+
foundations: "foundations";
|
|
33
|
+
"ssm-core": "ssm-core";
|
|
34
|
+
"beyond-ssm": "beyond-ssm";
|
|
35
|
+
integration: "integration";
|
|
36
|
+
synthesis: "synthesis";
|
|
37
|
+
}>;
|
|
38
|
+
title: z.ZodString;
|
|
39
|
+
status: z.ZodEnum<{
|
|
40
|
+
implemented: "implemented";
|
|
41
|
+
chapter_only: "chapter_only";
|
|
42
|
+
reading_only: "reading_only";
|
|
43
|
+
prose_only: "prose_only";
|
|
44
|
+
code_only: "code_only";
|
|
45
|
+
scaffolded: "scaffolded";
|
|
46
|
+
planned: "planned";
|
|
47
|
+
}>;
|
|
48
|
+
roadmap_lines: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
49
|
+
code_path: z.ZodOptional<z.ZodString>;
|
|
50
|
+
tests_path: z.ZodOptional<z.ZodString>;
|
|
51
|
+
notebook_path: z.ZodOptional<z.ZodString>;
|
|
52
|
+
description: z.ZodOptional<z.ZodString>;
|
|
53
|
+
draft: z.ZodDefault<z.ZodBoolean>;
|
|
54
|
+
}, z.core.$strip>;
|
|
55
|
+
declare const toolsChapterSchema: z.ZodObject<{
|
|
56
|
+
title: z.ZodString;
|
|
57
|
+
part: z.ZodNumber;
|
|
58
|
+
chapter: z.ZodNumber;
|
|
59
|
+
volatility: z.ZodEnum<{
|
|
60
|
+
"stable-principle": "stable-principle";
|
|
61
|
+
"architectural-pattern": "architectural-pattern";
|
|
62
|
+
"feature-surface": "feature-surface";
|
|
63
|
+
}>;
|
|
64
|
+
tools_compared: z.ZodArray<z.ZodEnum<{
|
|
65
|
+
"claude-code": "claude-code";
|
|
66
|
+
"gemini-cli": "gemini-cli";
|
|
67
|
+
"codex-cli": "codex-cli";
|
|
68
|
+
"cross-tool": "cross-tool";
|
|
69
|
+
}>>;
|
|
70
|
+
last_verified: z.ZodDate;
|
|
71
|
+
sources: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
72
|
+
description: z.ZodOptional<z.ZodString>;
|
|
73
|
+
draft: z.ZodDefault<z.ZodBoolean>;
|
|
74
|
+
updated: z.ZodOptional<z.ZodDate>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
declare const sourcesSchema: z.ZodObject<{
|
|
77
|
+
url: z.ZodString;
|
|
78
|
+
title: z.ZodString;
|
|
79
|
+
author: z.ZodOptional<z.ZodString>;
|
|
80
|
+
publish_date: z.ZodOptional<z.ZodDate>;
|
|
81
|
+
captured_at: z.ZodDate;
|
|
82
|
+
content_hash: z.ZodOptional<z.ZodString>;
|
|
83
|
+
tier: z.ZodEnum<{
|
|
84
|
+
"T1-official": "T1-official";
|
|
85
|
+
"T2-release-notes": "T2-release-notes";
|
|
86
|
+
"T3-practitioner": "T3-practitioner";
|
|
87
|
+
"T4-conjecture": "T4-conjecture";
|
|
88
|
+
}>;
|
|
89
|
+
tool: z.ZodEnum<{
|
|
90
|
+
"claude-code": "claude-code";
|
|
91
|
+
"gemini-cli": "gemini-cli";
|
|
92
|
+
"codex-cli": "codex-cli";
|
|
93
|
+
"cross-tool": "cross-tool";
|
|
94
|
+
}>;
|
|
95
|
+
perma_cc: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
96
|
+
local_cache: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
declare const changelogSchema: z.ZodObject<{
|
|
99
|
+
tool: z.ZodEnum<{
|
|
100
|
+
"claude-code": "claude-code";
|
|
101
|
+
"gemini-cli": "gemini-cli";
|
|
102
|
+
"codex-cli": "codex-cli";
|
|
103
|
+
"cross-tool": "cross-tool";
|
|
104
|
+
}>;
|
|
105
|
+
versions: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
106
|
+
version: z.ZodString;
|
|
107
|
+
date: z.ZodDate;
|
|
108
|
+
changes: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
109
|
+
pattern: z.ZodString;
|
|
110
|
+
kind: z.ZodEnum<{
|
|
111
|
+
added: "added";
|
|
112
|
+
removed: "removed";
|
|
113
|
+
changed: "changed";
|
|
114
|
+
deprecated: "deprecated";
|
|
115
|
+
}>;
|
|
116
|
+
note: z.ZodString;
|
|
117
|
+
source_key: z.ZodOptional<z.ZodString>;
|
|
118
|
+
}, z.core.$strip>>>;
|
|
119
|
+
}, z.core.$strip>>>;
|
|
120
|
+
}, z.core.$strip>;
|
|
121
|
+
declare const patternsSchema: z.ZodObject<{
|
|
122
|
+
name: z.ZodString;
|
|
123
|
+
description: z.ZodOptional<z.ZodString>;
|
|
124
|
+
category: z.ZodOptional<z.ZodEnum<{
|
|
125
|
+
safety: "safety";
|
|
126
|
+
scale: "scale";
|
|
127
|
+
context: "context";
|
|
128
|
+
interaction: "interaction";
|
|
129
|
+
extension: "extension";
|
|
130
|
+
other: "other";
|
|
131
|
+
}>>;
|
|
132
|
+
convergence_date: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
|
|
133
|
+
}, z.core.$strip>;
|
|
134
|
+
|
|
135
|
+
export { BookConfigOptions, BookScaffoldIntegrationOptions, academicChapterSchema, academicParts, bookScaffoldIntegration, changeKinds, changelogSchema, chapterStatus, defineBookConfig, patternCategories, patternsSchema, sourceTiers, sourcesSchema, toolSlugs, toolsChapterSchema, volatilityLevels };
|