@brandon_m_behring/book-scaffold-astro 3.0.1 → 3.2.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.
@@ -6,14 +6,23 @@
6
6
  * tools), so this component renders only the fields that are present on
7
7
  * the chapter data. Tools-profile metadata (volatility, last_verified,
8
8
  * tools_compared) appears when defined; academic-profile metadata (week,
9
- * status) appears in its place.
9
+ * part, status, companion artifacts) appears in its place.
10
10
  *
11
- * Use a chapter card's metadata to calibrate how much trust to place in
12
- * its specific claims stable principles age slowly; feature surfaces
13
- * age fast.
11
+ * v3.1.0 academic flavor: Roman-numeral part labels, StatusBadge
12
+ * component, and an optional companion-artifacts block.
13
+ *
14
+ * v3.2.0 — companions refactored from sibling <aside> to inline <span>
15
+ * elements inside the existing .chapter-meta flex row. v3.1.0 shipped
16
+ * <aside class="chapter-companions"> with no CSS; UA-default <ul> block
17
+ * layout added ~100px height at <=1280px, producing a uniform vertical
18
+ * pixel shift on all academic chapters. Inline rendering eliminates the
19
+ * extra block by construction. The data-companion attribute on each
20
+ * inline span preserves introspection.
14
21
  */
15
22
  import type { CollectionEntry } from 'astro:content';
16
23
  import { getFreshness, freshnessLabel } from '../src/lib/freshness';
24
+ import StatusBadge from './StatusBadge.astro';
25
+ import CodeRef from './CodeRef.astro';
17
26
 
18
27
  interface Props {
19
28
  data: CollectionEntry<'chapters'>['data'];
@@ -51,9 +60,24 @@ const freshnessText = freshness
51
60
  : 'Stale'
52
61
  : null;
53
62
 
63
+ // Academic-profile part labels (Roman-numeral · descriptive name).
64
+ // v2.0 post_transformers used this exact mapping; verbatim restore here so
65
+ // the header content density matches at narrow viewports. Keys mirror
66
+ // `academicParts` enum from src/schemas.ts.
67
+ const ACADEMIC_PART_LABELS: Record<string, string> = {
68
+ foundations: 'Part I · Foundations',
69
+ 'ssm-core': 'Part II · SSM Core',
70
+ 'beyond-ssm': 'Part III · Beyond SSMs',
71
+ integration: 'Part IV · Integration',
72
+ synthesis: 'Part V · Synthesis',
73
+ };
74
+
54
75
  // Display strings, profile-tagged for clarity in markup.
55
76
  const partLabel = (() => {
56
77
  const p = d.part;
78
+ if (hasAcademicMeta && typeof p === 'string' && p in ACADEMIC_PART_LABELS) {
79
+ return ACADEMIC_PART_LABELS[p];
80
+ }
57
81
  if (typeof p === 'number') return `Part ${p}`;
58
82
  if (typeof p === 'string' && p.length > 0) return `Part: ${p}`;
59
83
  return null;
@@ -61,8 +85,6 @@ const partLabel = (() => {
61
85
  const chapterNum =
62
86
  typeof d.chapter === 'number' ? `Chapter ${d.chapter}` : null;
63
87
  const weekNum = typeof d.week === 'number' ? `Week ${d.week}` : null;
64
- const statusBadge =
65
- typeof d.status === 'string' ? d.status.replace(/_/g, ' ') : null;
66
88
  const title = typeof d.title === 'string' ? d.title : '(untitled)';
67
89
  const description = typeof d.description === 'string' ? d.description : null;
68
90
  const toolsCompared = Array.isArray(d.tools_compared)
@@ -71,16 +93,31 @@ const toolsCompared = Array.isArray(d.tools_compared)
71
93
  const lastVerified = d.last_verified instanceof Date ? d.last_verified : null;
72
94
  const updated = d.updated instanceof Date ? d.updated : null;
73
95
  const volatility = typeof d.volatility === 'string' ? d.volatility : null;
96
+
97
+ // Academic companion artifacts. Notebook source path is transformed via
98
+ // generic basename strip so any book whose render-notebooks output lands
99
+ // under public/notebooks/ gets a correct deep link (v2.0 hardcoded a
100
+ // post_transformers-specific prefix; v3.1.0 generalizes).
101
+ //
102
+ // v3.2.0: each companion renders as an inline <span class="chapter-companion">
103
+ // inside .chapter-meta — no sibling <aside>, no <ul>, no "Companion artifacts:"
104
+ // label. Zero added vertical height vs v2.0.
105
+ const codePath = hasAcademicMeta && typeof d.code_path === 'string' ? d.code_path : null;
106
+ const testsPath = hasAcademicMeta && typeof d.tests_path === 'string' ? d.tests_path : null;
107
+ const notebookHtmlPath =
108
+ hasAcademicMeta && typeof d.notebook_path === 'string'
109
+ ? `/notebooks/${(d.notebook_path as string)
110
+ .replace(/^.*\//, '')
111
+ .replace(/\.ipynb$/, '')}.html`
112
+ : null;
74
113
  ---
75
114
  <header class="chapter-header">
76
115
  <div class="chapter-meta">
77
- {partLabel && <span>{partLabel}</span>}
116
+ {partLabel && <span class="chapter-part">{partLabel}</span>}
78
117
  {chapterNum && <span>{chapterNum}</span>}
79
- {weekNum && <span>{weekNum}</span>}
80
- {statusBadge && (
81
- <span class="status-badge" data-status={d.status as string}>
82
- {statusBadge}
83
- </span>
118
+ {weekNum && <span class="chapter-week">{weekNum}</span>}
119
+ {hasAcademicMeta && typeof d.status === 'string' && (
120
+ <StatusBadge status={d.status as never} />
84
121
  )}
85
122
  {lastVerified && (
86
123
  <span>
@@ -96,9 +133,25 @@ const volatility = typeof d.volatility === 'string' ? d.volatility : null;
96
133
  </span>
97
134
  )}
98
135
  {updated && <span>Updated {formatDate(updated)}</span>}
136
+ {codePath && (
137
+ <span class="chapter-companion" data-companion="code">
138
+ <CodeRef path={codePath} />
139
+ </span>
140
+ )}
141
+ {testsPath && (
142
+ <span class="chapter-companion" data-companion="tests">
143
+ <CodeRef path={testsPath} />
144
+ </span>
145
+ )}
146
+ {notebookHtmlPath && (
147
+ <span class="chapter-companion" data-companion="notebook">
148
+ <a href={notebookHtmlPath}>Notebook</a>
149
+ </span>
150
+ )}
99
151
  </div>
100
152
  <h1>{title}</h1>
101
153
  {description && <p class="chapter-description">{description}</p>}
154
+
102
155
  {hasToolsMeta && volatility && (
103
156
  <div class="chapter-badge-row">
104
157
  <span class="chapter-badge-row-label">Volatility:</span>
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.1",
4
+ "version": "3.2.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Brandon Behring",
@@ -38,6 +38,29 @@
38
38
  font-size: 0.75em;
39
39
  }
40
40
 
41
+ /* Inline companion-artifact chips inside .chapter-meta.
42
+ * v3.2.0: structural inline rendering. Previous v3.1.0 emitted a sibling
43
+ * <aside class="chapter-companions"><ul>...</ul></aside> with no CSS
44
+ * coverage; UA-default block layout added ~100px height at <=1280px,
45
+ * producing a uniform vertical pixel shift on academic chapter pages
46
+ * vs the v2.0 baseline. The inline span here adds zero block height by
47
+ * construction; styling matches the surrounding .chapter-meta spans. */
48
+ .chapter-companion {
49
+ font-family: var(--font-code);
50
+ font-size: var(--text-sm);
51
+ color: var(--color-text-muted);
52
+ }
53
+ .chapter-companion a,
54
+ .chapter-companion code {
55
+ color: inherit;
56
+ text-decoration: none;
57
+ border-bottom: 1px dotted var(--color-border);
58
+ }
59
+ .chapter-companion a:hover {
60
+ color: var(--color-link);
61
+ border-bottom-style: solid;
62
+ }
63
+
41
64
  /* Volatility + tool badges: reuse .tool-badge from callouts.css */
42
65
  .volatility-badge {
43
66
  display: inline-block;