@brandon_m_behring/book-scaffold-astro 3.0.0-alpha.4 → 3.0.0-alpha.5
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/components/ChapterHeader.astro +89 -34
- package/dist/index.mjs +2 -2
- package/package.json +3 -2
- package/dist/types-Cz-pwE1N.d.ts +0 -61
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
3
|
* ChapterHeader — renders the metadata block at the top of every chapter.
|
|
4
|
-
* Surfaces provenance signals the LaTeX book conveyed implicitly:
|
|
5
|
-
* - Part + chapter number (position in the book)
|
|
6
|
-
* - Volatility class (freshness calibration for the reader)
|
|
7
|
-
* - Tools compared (scope signal)
|
|
8
|
-
* - Last verified date (how stale are the claims?)
|
|
9
4
|
*
|
|
10
|
-
*
|
|
5
|
+
* Schema-agnostic: the package supports two profile schemas (academic and
|
|
6
|
+
* tools), so this component renders only the fields that are present on
|
|
7
|
+
* the chapter data. Tools-profile metadata (volatility, last_verified,
|
|
8
|
+
* tools_compared) appears when defined; academic-profile metadata (week,
|
|
9
|
+
* status) appears in its place.
|
|
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
14
|
*/
|
|
12
15
|
import type { CollectionEntry } from 'astro:content';
|
|
13
16
|
import { getFreshness, freshnessLabel } from '../src/lib/freshness';
|
|
@@ -17,45 +20,97 @@ interface Props {
|
|
|
17
20
|
}
|
|
18
21
|
const { data } = Astro.props;
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
// Widen for cross-schema field probing. The consumer's content.config.ts
|
|
24
|
+
// resolves `data` to exactly one of the two schemas at build time; this
|
|
25
|
+
// runtime probe handles either without crashing.
|
|
26
|
+
const d = data as Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
const hasToolsMeta =
|
|
29
|
+
typeof d.volatility === 'string' &&
|
|
30
|
+
d.last_verified instanceof Date &&
|
|
31
|
+
Array.isArray(d.tools_compared);
|
|
32
|
+
|
|
33
|
+
const hasAcademicMeta =
|
|
34
|
+
typeof d.week === 'number' && typeof d.status === 'string';
|
|
35
|
+
|
|
36
|
+
function formatDate(date: unknown): string {
|
|
37
|
+
return date instanceof Date ? date.toISOString().slice(0, 10) : '';
|
|
22
38
|
}
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
40
|
+
// Tools-profile freshness — only when all inputs are present + typed.
|
|
41
|
+
const freshness =
|
|
42
|
+
hasToolsMeta && d.last_verified instanceof Date && typeof d.volatility === 'string'
|
|
43
|
+
? getFreshness(d.last_verified, d.volatility as Parameters<typeof getFreshness>[1])
|
|
44
|
+
: null;
|
|
45
|
+
|
|
46
|
+
const freshnessText = freshness
|
|
47
|
+
? freshness.status === 'fresh'
|
|
27
48
|
? 'Fresh'
|
|
28
49
|
: freshness.status === 'verify-soon'
|
|
29
50
|
? 'Verify soon'
|
|
30
|
-
: 'Stale'
|
|
51
|
+
: 'Stale'
|
|
52
|
+
: null;
|
|
53
|
+
|
|
54
|
+
// Display strings, profile-tagged for clarity in markup.
|
|
55
|
+
const partLabel = (() => {
|
|
56
|
+
const p = d.part;
|
|
57
|
+
if (typeof p === 'number') return `Part ${p}`;
|
|
58
|
+
if (typeof p === 'string' && p.length > 0) return `Part: ${p}`;
|
|
59
|
+
return null;
|
|
60
|
+
})();
|
|
61
|
+
const chapterNum =
|
|
62
|
+
typeof d.chapter === 'number' ? `Chapter ${d.chapter}` : null;
|
|
63
|
+
const weekNum = typeof d.week === 'number' ? `Week ${d.week}` : null;
|
|
64
|
+
const statusBadge =
|
|
65
|
+
typeof d.status === 'string' ? d.status.replace(/_/g, ' ') : null;
|
|
66
|
+
const title = typeof d.title === 'string' ? d.title : '(untitled)';
|
|
67
|
+
const description = typeof d.description === 'string' ? d.description : null;
|
|
68
|
+
const toolsCompared = Array.isArray(d.tools_compared)
|
|
69
|
+
? (d.tools_compared as string[])
|
|
70
|
+
: [];
|
|
71
|
+
const lastVerified = d.last_verified instanceof Date ? d.last_verified : null;
|
|
72
|
+
const updated = d.updated instanceof Date ? d.updated : null;
|
|
73
|
+
const volatility = typeof d.volatility === 'string' ? d.volatility : null;
|
|
31
74
|
---
|
|
32
75
|
<header class="chapter-header">
|
|
33
76
|
<div class="chapter-meta">
|
|
34
|
-
<span>
|
|
35
|
-
<span>
|
|
36
|
-
<span>
|
|
37
|
-
|
|
38
|
-
<span
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
{partLabel && <span>{partLabel}</span>}
|
|
78
|
+
{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>
|
|
84
|
+
)}
|
|
85
|
+
{lastVerified && (
|
|
86
|
+
<span>
|
|
87
|
+
Last verified {formatDate(lastVerified)}
|
|
88
|
+
{freshness && freshnessText && (
|
|
89
|
+
<span
|
|
90
|
+
class="freshness-badge"
|
|
91
|
+
data-status={freshness.status}
|
|
92
|
+
aria-label={freshnessLabel(freshness)}
|
|
93
|
+
title={freshnessLabel(freshness)}
|
|
94
|
+
>{freshnessText}</span>
|
|
95
|
+
)}
|
|
96
|
+
</span>
|
|
97
|
+
)}
|
|
98
|
+
{updated && <span>Updated {formatDate(updated)}</span>}
|
|
54
99
|
</div>
|
|
55
|
-
{
|
|
100
|
+
<h1>{title}</h1>
|
|
101
|
+
{description && <p class="chapter-description">{description}</p>}
|
|
102
|
+
{hasToolsMeta && volatility && (
|
|
103
|
+
<div class="chapter-badge-row">
|
|
104
|
+
<span class="chapter-badge-row-label">Volatility:</span>
|
|
105
|
+
<span class={`volatility-badge volatility-${volatility}`}>
|
|
106
|
+
{volatility}
|
|
107
|
+
</span>
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
{hasToolsMeta && toolsCompared.length > 0 && (
|
|
56
111
|
<div class="chapter-badge-row">
|
|
57
112
|
<span class="chapter-badge-row-label">Tools compared:</span>
|
|
58
|
-
{
|
|
113
|
+
{toolsCompared.map((t) => <span class="tool-badge">{t}</span>)}
|
|
59
114
|
</div>
|
|
60
115
|
)}
|
|
61
116
|
</header>
|
package/dist/index.mjs
CHANGED
|
@@ -153,11 +153,11 @@ var ALWAYS_ON_STYLES = [
|
|
|
153
153
|
var TOOLS_ONLY_STYLES = ["convergence.css", "tool-filter.css"];
|
|
154
154
|
var DEFAULT_ROUTES_ALL = [
|
|
155
155
|
{ pattern: "/references", file: "references.astro" },
|
|
156
|
-
{ pattern: "/search", file: "search.astro" }
|
|
156
|
+
{ pattern: "/search", file: "search.astro" },
|
|
157
|
+
{ pattern: "/print", file: "print.astro" }
|
|
157
158
|
];
|
|
158
159
|
var DEFAULT_ROUTES_TOOLS = [
|
|
159
160
|
{ pattern: "/chapters", file: "chapters.astro" },
|
|
160
|
-
{ pattern: "/print", file: "print.astro" },
|
|
161
161
|
{ pattern: "/convergence", file: "convergence.astro" }
|
|
162
162
|
];
|
|
163
163
|
function resolvePage(file) {
|
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.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Brandon Behring",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"types": "./dist/schemas.d.ts",
|
|
41
41
|
"import": "./dist/schemas.mjs"
|
|
42
42
|
},
|
|
43
|
+
"./package.json": "./package.json",
|
|
43
44
|
"./components/CaseStudy.astro": "./components/CaseStudy.astro",
|
|
44
45
|
"./components/ChapterHeader.astro": "./components/ChapterHeader.astro",
|
|
45
46
|
"./components/ChapterNav.astro": "./components/ChapterNav.astro",
|
|
@@ -109,7 +110,7 @@
|
|
|
109
110
|
"README.md"
|
|
110
111
|
],
|
|
111
112
|
"scripts": {
|
|
112
|
-
"build": "tsup",
|
|
113
|
+
"build": "tsup && rm -f dist/types-*.d.ts",
|
|
113
114
|
"prepublishOnly": "npm run build"
|
|
114
115
|
},
|
|
115
116
|
"peerDependencies": {
|
package/dist/types-Cz-pwE1N.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { AstroIntegration, AstroUserConfig } from 'astro';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Shared types for @brandon_m_behring/book-scaffold-astro.
|
|
5
|
-
*
|
|
6
|
-
* Public types referenced from PACKAGE_DESIGN.md §4 / §5 / §6. Kept in
|
|
7
|
-
* one place so consumer IntelliSense surfaces a coherent API.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
type BookProfile = 'academic' | 'tools' | 'minimal';
|
|
11
|
-
declare const BOOK_PROFILES: readonly ["academic", "tools", "minimal"];
|
|
12
|
-
/**
|
|
13
|
-
* Options for `defineBookConfig`. See PACKAGE_DESIGN.md §4.
|
|
14
|
-
*
|
|
15
|
-
* Note on the index signature: `AstroUserConfig` carries generic
|
|
16
|
-
* parameters (`Locales`, `SessionDriverName`, fonts) that can't be
|
|
17
|
-
* threaded cleanly through a wrapper. Instead we type the package-
|
|
18
|
-
* specific fields strictly and allow arbitrary AstroUserConfig keys
|
|
19
|
-
* via the index signature — consumer types will lint clean but lose
|
|
20
|
-
* full IDE autocomplete on non-package fields. Acceptable trade.
|
|
21
|
-
*/
|
|
22
|
-
interface BookConfigOptions {
|
|
23
|
-
/** Required. Book's deployed origin (sitemap, canonical, Pagefind). */
|
|
24
|
-
site: string;
|
|
25
|
-
/**
|
|
26
|
-
* Optional. Falls back to `process.env.BOOK_PROFILE`, then `'minimal'`.
|
|
27
|
-
* Explicit param always wins over env.
|
|
28
|
-
*/
|
|
29
|
-
profile?: BookProfile;
|
|
30
|
-
/** Optional. Appended to the package-provided integration list. */
|
|
31
|
-
extraIntegrations?: AstroIntegration[];
|
|
32
|
-
/**
|
|
33
|
-
* Optional. CSS basenames to inject in addition to the profile-resolved
|
|
34
|
-
* set. Cross-profile escape hatch (e.g. an academic book using
|
|
35
|
-
* `<Convergence>`). Example: `['convergence.css']`.
|
|
36
|
-
*/
|
|
37
|
-
extraStyles?: string[];
|
|
38
|
-
/** Optional. Spread-merged into the package-provided markdown config. */
|
|
39
|
-
markdown?: AstroUserConfig['markdown'];
|
|
40
|
-
/** Escape hatch for any other AstroUserConfig field. */
|
|
41
|
-
[key: string]: unknown;
|
|
42
|
-
}
|
|
43
|
-
/** Options for `defineBookSchemas`. See PACKAGE_DESIGN.md §5. */
|
|
44
|
-
interface BookSchemasOptions {
|
|
45
|
-
profile?: BookProfile;
|
|
46
|
-
/** Defaults to `'./src/content/chapters'`. */
|
|
47
|
-
chaptersBase?: string;
|
|
48
|
-
}
|
|
49
|
-
/** Options for the internal `bookScaffoldIntegration`. See PACKAGE_DESIGN.md §6. */
|
|
50
|
-
interface BookScaffoldIntegrationOptions {
|
|
51
|
-
profile: BookProfile;
|
|
52
|
-
extraStyles?: string[];
|
|
53
|
-
}
|
|
54
|
-
/** Raised when the resolved profile is not one of `BOOK_PROFILES`. */
|
|
55
|
-
declare class BookConfigError extends Error {
|
|
56
|
-
constructor(message: string);
|
|
57
|
-
}
|
|
58
|
-
/** Resolve profile from explicit param → env → default. Throws on invalid. */
|
|
59
|
-
declare function resolveProfile(explicit?: BookProfile): BookProfile;
|
|
60
|
-
|
|
61
|
-
export { BOOK_PROFILES as B, BookConfigError as a, type BookConfigOptions as b, type BookProfile as c, type BookScaffoldIntegrationOptions as d, type BookSchemasOptions as e, resolveProfile as r };
|