@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,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* CodeRef — inline reference to a specific file (and optional line range)
|
|
4
|
+
* in the post_transformers repo on GitHub.
|
|
5
|
+
*
|
|
6
|
+
* Maps the LaTeX `\code{path:N}` and `\path{path}` idioms (which only
|
|
7
|
+
* styled monospace text) to a real hyperlink that lands the reader on
|
|
8
|
+
* the right line.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* The discretization is implemented in
|
|
12
|
+
* <CodeRef path="experiments/jax/week04/s4_hippo.py" line={42}>
|
|
13
|
+
* <code>s4_hippo.py:42</code>
|
|
14
|
+
* </CodeRef>.
|
|
15
|
+
*
|
|
16
|
+
* Line range:
|
|
17
|
+
* See <CodeRef path="experiments/jax/week04/s4_hippo.py" line={42} lineEnd={58}>
|
|
18
|
+
* the ZOH discretization block
|
|
19
|
+
* </CodeRef>.
|
|
20
|
+
*
|
|
21
|
+
* Bare path (no line):
|
|
22
|
+
* The full module lives at <CodeRef path="experiments/jax/week04/s4_hippo.py" />.
|
|
23
|
+
*
|
|
24
|
+
* The slot's content is rendered as the link text; if no slot content
|
|
25
|
+
* is given, a sensible default (the file basename plus line range) is
|
|
26
|
+
* synthesized.
|
|
27
|
+
*/
|
|
28
|
+
import { buildGithubUrl } from '../src/lib/repo-url';
|
|
29
|
+
|
|
30
|
+
interface Props {
|
|
31
|
+
path: string;
|
|
32
|
+
line?: number;
|
|
33
|
+
lineEnd?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { path, line, lineEnd } = Astro.props;
|
|
37
|
+
const url = buildGithubUrl(path, line, lineEnd);
|
|
38
|
+
const hasSlot = await Astro.slots.has('default');
|
|
39
|
+
const basename = path.split('/').pop() ?? path;
|
|
40
|
+
const defaultText =
|
|
41
|
+
line === undefined
|
|
42
|
+
? basename
|
|
43
|
+
: lineEnd === undefined
|
|
44
|
+
? `${basename}:${line}`
|
|
45
|
+
: `${basename}:${line}-${lineEnd}`;
|
|
46
|
+
---
|
|
47
|
+
<a href={url} rel="external noopener" class="code-ref">
|
|
48
|
+
{hasSlot ? <slot /> : <code>{defaultText}</code>}
|
|
49
|
+
</a>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ConceptBox — durable vocabulary / definition. The textbook definition
|
|
4
|
+
* slot. Used when a term first appears and needs a precise fix.
|
|
5
|
+
*
|
|
6
|
+
* The `term` prop is the defined word/phrase and renders in the title;
|
|
7
|
+
* the body is the definition. By convention, definitions stay in
|
|
8
|
+
* Representation sections of a chapter (per the Koller-Friedman
|
|
9
|
+
* decomposition).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* <ConceptBox term="Context rot">
|
|
13
|
+
* The degradation of agent output quality as the KV-cache fills,
|
|
14
|
+
* observable as drift, loss of early-context references, or
|
|
15
|
+
* incoherent code.
|
|
16
|
+
* </ConceptBox>
|
|
17
|
+
*/
|
|
18
|
+
interface Props {
|
|
19
|
+
term: string;
|
|
20
|
+
}
|
|
21
|
+
const { term } = Astro.props;
|
|
22
|
+
---
|
|
23
|
+
<aside class="callout callout-concept" role="note" aria-label={`Concept: ${term}`}>
|
|
24
|
+
<strong class="callout-title">Concept · {term}</strong>
|
|
25
|
+
<div class="callout-body"><slot /></div>
|
|
26
|
+
</aside>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Convergence — where two or more tools have landed on the same
|
|
4
|
+
* pattern. Signals: stable principle for the next ~3 years (unless a
|
|
5
|
+
* new tool paradigm appears).
|
|
6
|
+
*
|
|
7
|
+
* Pedagogically central: the KF Evolution cornerstone of every chapter
|
|
8
|
+
* uses Convergence + Divergence to surface how agentic practices are
|
|
9
|
+
* solidifying. A convergence box reads as "it is safe to bet on this."
|
|
10
|
+
*
|
|
11
|
+
* Props:
|
|
12
|
+
* tools — optional array of tool slugs to display as inline badges.
|
|
13
|
+
* Omit for a narrative-only convergence (e.g. "On session
|
|
14
|
+
* ergonomics, all mainstream CLIs converged by late 2025.")
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* <Convergence tools={['claude-code', 'gemini-cli', 'codex-cli']}>
|
|
18
|
+
* All three now treat context as a budgeted, decaying resource
|
|
19
|
+
* and ship an explicit compaction command.
|
|
20
|
+
* </Convergence>
|
|
21
|
+
*/
|
|
22
|
+
import { toolSlugs } from '../src/schemas.js';
|
|
23
|
+
|
|
24
|
+
type ToolSlug = typeof toolSlugs[number];
|
|
25
|
+
interface Props {
|
|
26
|
+
tools?: ToolSlug[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { tools = [] } = Astro.props;
|
|
30
|
+
---
|
|
31
|
+
<aside class="callout callout-convergence" role="note" aria-label="Convergence">
|
|
32
|
+
<strong class="callout-title">
|
|
33
|
+
Convergence
|
|
34
|
+
{tools.length > 0 && (
|
|
35
|
+
<span class="tool-badges">
|
|
36
|
+
{tools.map((t) => <span class="tool-badge">{t}</span>)}
|
|
37
|
+
</span>
|
|
38
|
+
)}
|
|
39
|
+
</strong>
|
|
40
|
+
<div class="callout-body"><slot /></div>
|
|
41
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* CounterBox — maps to LaTeX `counterbox` env.
|
|
4
|
+
* Default use: counter-evidence to the dominant view, limitations of cited claims.
|
|
5
|
+
* Family: rose (warn).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Counter-evidence' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-counter" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Divergence — where tools have NOT converged on a pattern. Signals:
|
|
4
|
+
* open design space; the choice is still live; practices may shift.
|
|
5
|
+
*
|
|
6
|
+
* Same color family as Convergence (gold = insight) but with a dashed
|
|
7
|
+
* left-bar as the visual signal of instability. Readers should
|
|
8
|
+
* calibrate confidence accordingly: a Divergence box says "the tradeoff
|
|
9
|
+
* is still being worked out."
|
|
10
|
+
*
|
|
11
|
+
* Props:
|
|
12
|
+
* axis — the dimension on which tools differ (e.g. "window size",
|
|
13
|
+
* "hook timing", "delegation granularity"). Required — a
|
|
14
|
+
* divergence without a named axis is just hand-waving.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* <Divergence axis="window size">
|
|
18
|
+
* Claude: 200K · Gemini: 1M · Codex: 128K. The tradeoff between
|
|
19
|
+
* retrieval accuracy and ceiling is not settled.
|
|
20
|
+
* </Divergence>
|
|
21
|
+
*/
|
|
22
|
+
interface Props {
|
|
23
|
+
axis: string;
|
|
24
|
+
}
|
|
25
|
+
const { axis } = Astro.props;
|
|
26
|
+
---
|
|
27
|
+
<aside class="callout callout-divergence" role="note" aria-label={`Divergence: ${axis}`}>
|
|
28
|
+
<strong class="callout-title">
|
|
29
|
+
Divergence <span class="divergence-axis">· {axis}</span>
|
|
30
|
+
</strong>
|
|
31
|
+
<div class="callout-body"><slot /></div>
|
|
32
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* DynConnect — maps to LaTeX `dynconnect` env.
|
|
4
|
+
* Default use: dynamical-systems / PhD-bridge connections to the SSM material.
|
|
5
|
+
* Family: blue accent (info).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Dynamical Systems Connection' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-dynconnect" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ExampleBox — maps to LaTeX `examplebox` env.
|
|
4
|
+
* Default use: extended multi-paragraph walkthroughs.
|
|
5
|
+
* Family: blue accent (info).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Example' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-example" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Figure — replaces LaTeX `\includegraphics{...}` + `\caption{...}` +
|
|
4
|
+
* `\label{...}` pattern with a single component.
|
|
5
|
+
*
|
|
6
|
+
* Source assets are emitted under public/figures/weekNN/ by
|
|
7
|
+
* scripts/build-figures.sh (Phase 2.4); src paths are absolute from
|
|
8
|
+
* site root.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* <Figure
|
|
12
|
+
* src="/figures/week04/ex2_hippo_eigenvalues.svg"
|
|
13
|
+
* caption="HiPPO-LegS eigenvalue structure for N = 16 and N = 64."
|
|
14
|
+
* width="100%"
|
|
15
|
+
* id="w4-fig-hippo-eigenvalues"
|
|
16
|
+
* />
|
|
17
|
+
*
|
|
18
|
+
* The id prop registers the figure with the cross-reference label map
|
|
19
|
+
* (consumed by `<XRef>`).
|
|
20
|
+
*/
|
|
21
|
+
interface Props {
|
|
22
|
+
src: string;
|
|
23
|
+
caption?: string;
|
|
24
|
+
width?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
alt?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { src, caption, width = '100%', id, alt } = Astro.props;
|
|
30
|
+
const altText = alt ?? caption ?? '';
|
|
31
|
+
---
|
|
32
|
+
<figure class="figure" id={id}>
|
|
33
|
+
<img src={src} alt={altText} style={`width: ${width}; max-width: 100%;`} />
|
|
34
|
+
{caption && <figcaption>{caption}</figcaption>}
|
|
35
|
+
</figure>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* InsightBox — maps to LaTeX `insightbox` env.
|
|
4
|
+
* Default use: non-obvious observations, surprising properties.
|
|
5
|
+
* Family: gold (insight).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Insight' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-insight" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* KeyIdea — crystallized takeaway. "Memorize this." Carries over from
|
|
4
|
+
* the LaTeX book's \keybox: thicker left-bar as a visual signal that
|
|
5
|
+
* the content is high-density.
|
|
6
|
+
*
|
|
7
|
+
* Used sparingly — perhaps 1–2 per chapter. If every third paragraph
|
|
8
|
+
* has a KeyIdea, nothing is a key idea. Place at the end of a section
|
|
9
|
+
* where an argument resolves to something retention-worthy.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* <KeyIdea>
|
|
13
|
+
* Context is a budget, not an infinity. Treat it like a finite
|
|
14
|
+
* resource: account for it, spend it deliberately, reclaim it.
|
|
15
|
+
* </KeyIdea>
|
|
16
|
+
*/
|
|
17
|
+
---
|
|
18
|
+
<aside class="callout callout-key" role="note" aria-label="Key idea">
|
|
19
|
+
<strong class="callout-title">Key idea</strong>
|
|
20
|
+
<div class="callout-body"><slot /></div>
|
|
21
|
+
</aside>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* MarginNote — short variant of Sidenote with semantic coloring.
|
|
4
|
+
*
|
|
5
|
+
* Maps the three LaTeX margin-callout commands:
|
|
6
|
+
* \marginnotebox[title]{body} → variant="note" (blue)
|
|
7
|
+
* \marginwarning[title]{body} → variant="warning" (rose)
|
|
8
|
+
* \margintip[title]{body} → variant="tip" (green)
|
|
9
|
+
*
|
|
10
|
+
* Per STANDARDS.md margin notes are capped at 25 words — enforce in
|
|
11
|
+
* the validator (Phase 2.6); not enforced here at runtime.
|
|
12
|
+
*
|
|
13
|
+
* Unlike Sidenote (which floats into the page margin on desktop),
|
|
14
|
+
* MarginNote always renders inline with the surrounding prose as a
|
|
15
|
+
* colored aside. Use Sidenote when the note is footnote-like and
|
|
16
|
+
* should not interrupt the line; use MarginNote when the note is
|
|
17
|
+
* load-bearing and the reader must see it.
|
|
18
|
+
*/
|
|
19
|
+
type Variant = 'note' | 'warning' | 'tip';
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
variant?: Variant;
|
|
23
|
+
label?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { variant = 'note', label } = Astro.props;
|
|
27
|
+
const VARIANT_DEFAULT_LABEL: Record<Variant, string> = {
|
|
28
|
+
note: 'Note',
|
|
29
|
+
warning: 'Caveat',
|
|
30
|
+
tip: 'Tip',
|
|
31
|
+
};
|
|
32
|
+
const displayLabel = label ?? VARIANT_DEFAULT_LABEL[variant];
|
|
33
|
+
---
|
|
34
|
+
<aside class="margin-note" data-variant={variant} role="note">
|
|
35
|
+
<span class="margin-note-label">[{displayLabel}]</span>
|
|
36
|
+
<slot />
|
|
37
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* NoteBox — maps to LaTeX `notebox` env.
|
|
4
|
+
* Default use: chapter overviews, neutral info, learning objectives.
|
|
5
|
+
* Family: blue accent (info).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Note' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-note" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* OpenQuestion — maps to LaTeX `openquestion` env.
|
|
4
|
+
* Default use: research questions still open at time of writing.
|
|
5
|
+
* Family: plum (authority).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Open Question' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-openquestion" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* PaperBox — maps to LaTeX `paperbox` env.
|
|
4
|
+
* Default use: contextual restatement of a cited paper's claim.
|
|
5
|
+
* Family: gray (neutral, dashed border to signal external source).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'From the literature' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-paper" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* PatternTimeline — one card per pattern on the convergence dashboard.
|
|
4
|
+
*
|
|
5
|
+
* Renders:
|
|
6
|
+
* - Header: pattern name, category, convergence badge (date or "not
|
|
7
|
+
* yet converged"), adoption count "N of 3 tools".
|
|
8
|
+
* - Description paragraph.
|
|
9
|
+
* - 3 horizontal tool tracks (claude-code / gemini-cli / codex-cli).
|
|
10
|
+
* Adoption events render as dots positioned along a shared time
|
|
11
|
+
* axis (earliest → latest event in this pattern's timeline).
|
|
12
|
+
* - Empty state when no tool has adopted the pattern yet.
|
|
13
|
+
*
|
|
14
|
+
* Dots carry their metadata in title= attributes so hover in any
|
|
15
|
+
* browser shows the full note without extra tooltip infrastructure.
|
|
16
|
+
*
|
|
17
|
+
* Props:
|
|
18
|
+
* pattern — the PatternEntry from getAllPatterns().
|
|
19
|
+
*/
|
|
20
|
+
import type { PatternEntry } from '../src/lib/patterns';
|
|
21
|
+
import {
|
|
22
|
+
getPatternTimeline,
|
|
23
|
+
toolsInTimeline,
|
|
24
|
+
TIMELINE_TOOL_ORDER,
|
|
25
|
+
CATEGORY_LABELS,
|
|
26
|
+
} from '../src/lib/patterns';
|
|
27
|
+
|
|
28
|
+
interface Props {
|
|
29
|
+
pattern: PatternEntry;
|
|
30
|
+
}
|
|
31
|
+
const { pattern } = Astro.props;
|
|
32
|
+
const data = pattern.data;
|
|
33
|
+
|
|
34
|
+
const timeline = await getPatternTimeline(pattern.id);
|
|
35
|
+
const adopters = toolsInTimeline(timeline);
|
|
36
|
+
|
|
37
|
+
function formatDate(d: Date): string {
|
|
38
|
+
return d.toISOString().slice(0, 10);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Compute position along the shared time axis. A single-event pattern
|
|
42
|
+
// centers its dot at 50%. No events → no axis computed.
|
|
43
|
+
const times = timeline.map((t) => t.date.getTime());
|
|
44
|
+
const minTime = times.length > 0 ? Math.min(...times) : 0;
|
|
45
|
+
const maxTime = times.length > 0 ? Math.max(...times) : 0;
|
|
46
|
+
const span = maxTime - minTime;
|
|
47
|
+
|
|
48
|
+
function positionPct(date: Date): number {
|
|
49
|
+
if (span <= 0) return 50;
|
|
50
|
+
return ((date.getTime() - minTime) / span) * 100;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const converged = data.convergence_date != null;
|
|
54
|
+
const category = data.category ?? 'other';
|
|
55
|
+
---
|
|
56
|
+
<article class="pattern-card" data-pattern={pattern.id}>
|
|
57
|
+
<header class="pattern-card-header">
|
|
58
|
+
<div class="pattern-card-title-row">
|
|
59
|
+
<h3 class="pattern-card-title">{data.name}</h3>
|
|
60
|
+
<span
|
|
61
|
+
class={`pattern-category-badge pattern-category-${category}`}
|
|
62
|
+
title={`Category: ${CATEGORY_LABELS[category]}`}
|
|
63
|
+
>{CATEGORY_LABELS[category]}</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="pattern-card-meta">
|
|
66
|
+
{converged ? (
|
|
67
|
+
<span class="pattern-convergence-badge converged" title="All tracked tools have adopted">
|
|
68
|
+
Converged {formatDate(data.convergence_date as Date)}
|
|
69
|
+
</span>
|
|
70
|
+
) : (
|
|
71
|
+
<span class="pattern-convergence-badge not-converged">
|
|
72
|
+
Not yet converged
|
|
73
|
+
</span>
|
|
74
|
+
)}
|
|
75
|
+
<span class="pattern-adoption-count">
|
|
76
|
+
{adopters.length} of {TIMELINE_TOOL_ORDER.length} tools
|
|
77
|
+
</span>
|
|
78
|
+
</div>
|
|
79
|
+
</header>
|
|
80
|
+
|
|
81
|
+
{data.description && (
|
|
82
|
+
<p class="pattern-description">{data.description}</p>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
{timeline.length === 0 ? (
|
|
86
|
+
<p class="pattern-empty-timeline">
|
|
87
|
+
No tool has adopted this pattern yet (or no data captured).
|
|
88
|
+
</p>
|
|
89
|
+
) : (
|
|
90
|
+
<div
|
|
91
|
+
class="pattern-timeline"
|
|
92
|
+
role="img"
|
|
93
|
+
aria-label={`Adoption timeline: ${timeline.length} event${timeline.length === 1 ? '' : 's'} across ${adopters.length} of ${TIMELINE_TOOL_ORDER.length} tools.`}
|
|
94
|
+
>
|
|
95
|
+
{TIMELINE_TOOL_ORDER.map((tool) => {
|
|
96
|
+
const events = timeline.filter((e) => e.tool === tool);
|
|
97
|
+
const adopted = events.length > 0;
|
|
98
|
+
return (
|
|
99
|
+
<div
|
|
100
|
+
class="pattern-timeline-track"
|
|
101
|
+
data-tool={tool}
|
|
102
|
+
data-adopted={adopted ? 'true' : 'false'}
|
|
103
|
+
>
|
|
104
|
+
<span class="pattern-timeline-tool-label">{tool}</span>
|
|
105
|
+
<div class="pattern-timeline-rail">
|
|
106
|
+
{!adopted && (
|
|
107
|
+
<span class="pattern-timeline-not-yet">not yet</span>
|
|
108
|
+
)}
|
|
109
|
+
{events.map((e) => (
|
|
110
|
+
<span
|
|
111
|
+
class="pattern-timeline-dot"
|
|
112
|
+
data-kind={e.kind}
|
|
113
|
+
style={`left: ${positionPct(e.date)}%`}
|
|
114
|
+
title={`${tool} ${e.version} (${formatDate(e.date)}): ${e.note}`}
|
|
115
|
+
>
|
|
116
|
+
<span class="pattern-timeline-dot-label" aria-hidden="true">
|
|
117
|
+
v{e.version}
|
|
118
|
+
</span>
|
|
119
|
+
</span>
|
|
120
|
+
))}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
})}
|
|
125
|
+
<div class="pattern-timeline-axis">
|
|
126
|
+
<span class="pattern-timeline-axis-start">{formatDate(new Date(minTime))}</span>
|
|
127
|
+
{span > 0 && (
|
|
128
|
+
<span class="pattern-timeline-axis-end">{formatDate(new Date(maxTime))}</span>
|
|
129
|
+
)}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
</article>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Recovery — escape route from a named anti-pattern. Structured as
|
|
4
|
+
* (pattern name, symptom, escape). Pairs with the anti-pattern
|
|
5
|
+
* chapters; provides actionable recovery when readers recognize they
|
|
6
|
+
* are in the failure mode.
|
|
7
|
+
*
|
|
8
|
+
* Matches the LaTeX book's \recovery{pattern}{symptom}{escape} command
|
|
9
|
+
* (see claude-best-practices.sty). Symptom is optional; escape can be
|
|
10
|
+
* the body slot for longer explanations.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* <Recovery
|
|
14
|
+
* pattern="CLAUDE.md bloat"
|
|
15
|
+
* symptom="Compaction triggers every 3 turns; new features land
|
|
16
|
+
* with context rot."
|
|
17
|
+
* >
|
|
18
|
+
* Split CLAUDE.md into hub-and-spoke. Move project specifics to
|
|
19
|
+
* `.claude/rules/*.md` with path-scoped loading. Keep hub under 200 lines.
|
|
20
|
+
* </Recovery>
|
|
21
|
+
*/
|
|
22
|
+
interface Props {
|
|
23
|
+
pattern: string;
|
|
24
|
+
symptom?: string;
|
|
25
|
+
}
|
|
26
|
+
const { pattern, symptom } = Astro.props;
|
|
27
|
+
---
|
|
28
|
+
<aside class="callout callout-recovery" role="note" aria-label={`Recovery: ${pattern}`}>
|
|
29
|
+
<strong class="callout-title">Recovery · {pattern}</strong>
|
|
30
|
+
{symptom && (
|
|
31
|
+
<p class="recovery-symptom"><strong>Symptom:</strong> {symptom}</p>
|
|
32
|
+
)}
|
|
33
|
+
<div class="callout-body"><slot /></div>
|
|
34
|
+
</aside>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ResultBox — maps to LaTeX `resultbox` env.
|
|
4
|
+
* Default use: author's own experimental findings, key takeaways from a chapter's code.
|
|
5
|
+
* Family: green (tip / positive).
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
const { title = 'Result' } = Astro.props;
|
|
11
|
+
---
|
|
12
|
+
<aside class="callout callout-result" role="note">
|
|
13
|
+
<strong class="callout-title">{title}</strong>
|
|
14
|
+
<div class="callout-body"><slot /></div>
|
|
15
|
+
</aside>
|