@brandon_m_behring/book-scaffold-astro 4.26.2 → 4.27.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/AGENTS.md +6 -0
- package/CLAUDE.md +35 -10
- package/LATEX_TO_MDX_MAPPING.md +1 -1
- package/LICENSE +27 -0
- package/LICENSE-CONTENT +19 -0
- package/README.md +33 -0
- package/components/AssessmentTest.astro +2 -1
- package/components/ChapterNav.astro +2 -2
- package/components/Cite.astro +2 -1
- package/components/NavContent.astro +2 -2
- package/components/PartReview.astro +2 -1
- package/components/Rationale.astro +3 -2
- package/components/Sidebar.astro +2 -1
- package/components/Term.astro +2 -1
- package/components/TipsCard.astro +2 -1
- package/components/VersionSelector.tsx +39 -36
- package/components/WeekRef.astro +2 -1
- package/components/XRef.astro +2 -1
- package/dist/components/VersionSelector.d.ts +16 -2
- package/dist/components/VersionSelector.mjs +18 -17
- package/dist/index.d.ts +24 -6
- package/dist/index.mjs +96 -8
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.mjs +8 -1
- package/dist/{types-Hue-uSeQ.d.ts → types-CZrkqzpC.d.ts} +84 -48
- package/layouts/Base.astro +23 -21
- package/package.json +7 -4
- package/pages/answers.astro +2 -1
- package/pages/chapters.astro +2 -1
- package/pages/exercises.astro +2 -1
- package/pages/index.astro +4 -3
- package/pages/search.astro +2 -1
- package/pages/tips.astro +2 -1
- package/recipes/01-add-math.md +40 -28
- package/recipes/04-component-library.md +14 -4
- package/recipes/05-deploy-cloudflare.md +44 -0
- package/recipes/06-mobile-first-layout.md +25 -2
- package/recipes/09-validation.md +18 -9
- package/recipes/15-defining-styles.md +7 -3
- package/recipes/19-prevalidate-hook.md +29 -83
- package/scripts/build-bib.mjs +7 -3
- package/scripts/build-labels.mjs +33 -16
- package/scripts/read-env.mjs +25 -0
- package/scripts/resolve-book-config.mjs +96 -0
- package/scripts/validate.mjs +125 -84
- package/src/lib/define-style.ts +25 -8
- package/src/lib/nav-href.ts +24 -2
- package/styles/layout.css +14 -8
- package/styles/tokens.css +8 -10
package/dist/index.mjs
CHANGED
|
@@ -912,6 +912,7 @@ var BOOK_PROFILES = Object.keys(PROFILES);
|
|
|
912
912
|
// src/types.ts
|
|
913
913
|
import { existsSync, readFileSync } from "fs";
|
|
914
914
|
var BOOK_PRESETS = BOOK_PROFILES;
|
|
915
|
+
var NUMBER_STYLES = ["shared", "per-kind"];
|
|
915
916
|
var BookConfigError = class extends Error {
|
|
916
917
|
constructor(message) {
|
|
917
918
|
super(message);
|
|
@@ -936,6 +937,7 @@ function readEnvFile(path = ".env") {
|
|
|
936
937
|
return {};
|
|
937
938
|
}
|
|
938
939
|
}
|
|
940
|
+
var PRESET_FALLBACK_WARNING = /* @__PURE__ */ Symbol.for("book-scaffold-astro:preset-fallback-warning");
|
|
939
941
|
function resolvePreset(explicitPreset, explicitProfile) {
|
|
940
942
|
let candidate = explicitPreset ?? explicitProfile ?? process.env.BOOK_PRESET ?? process.env.BOOK_PROFILE;
|
|
941
943
|
let source = "default";
|
|
@@ -956,7 +958,13 @@ function resolvePreset(explicitPreset, explicitProfile) {
|
|
|
956
958
|
);
|
|
957
959
|
}
|
|
958
960
|
if (source === "default") {
|
|
959
|
-
|
|
961
|
+
const warningState = globalThis;
|
|
962
|
+
if (!warningState[PRESET_FALLBACK_WARNING]) {
|
|
963
|
+
warningState[PRESET_FALLBACK_WARNING] = true;
|
|
964
|
+
console.warn(
|
|
965
|
+
"book-scaffold-astro: no preset resolved; falling back to 'minimal' for v4 compatibility. This fallback will be removed in v5. Add a built-in style to defineBookConfig and pass the same preset to defineBookSchemas, or set BOOK_PRESET."
|
|
966
|
+
);
|
|
967
|
+
}
|
|
960
968
|
}
|
|
961
969
|
return candidate;
|
|
962
970
|
}
|
|
@@ -966,7 +974,7 @@ function resolveProfile(explicit) {
|
|
|
966
974
|
|
|
967
975
|
// src/integration.ts
|
|
968
976
|
import { fileURLToPath } from "url";
|
|
969
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
977
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
970
978
|
import { join } from "path";
|
|
971
979
|
|
|
972
980
|
// src/lib/define-style.ts
|
|
@@ -981,8 +989,12 @@ function composeStyles(styles) {
|
|
|
981
989
|
for (const style of styles) {
|
|
982
990
|
if (style.name !== void 0) merged.name = style.name;
|
|
983
991
|
if (style.preset !== void 0) merged.preset = style.preset;
|
|
992
|
+
if (style.numberStyle !== void 0) merged.numberStyle = style.numberStyle;
|
|
984
993
|
if (style.site !== void 0) merged.site = style.site;
|
|
985
994
|
if (style.deploy !== void 0) merged.deploy = style.deploy;
|
|
995
|
+
if (style.releaseStatus !== void 0) {
|
|
996
|
+
merged.releaseStatus = style.releaseStatus;
|
|
997
|
+
}
|
|
986
998
|
if (style.mdxComponentsModule !== void 0) {
|
|
987
999
|
merged.mdxComponentsModule = style.mdxComponentsModule;
|
|
988
1000
|
}
|
|
@@ -1194,6 +1206,17 @@ var ROUTE_REGISTRY = {
|
|
|
1194
1206
|
// empty string → '/[slug]'; arbitrary string → '/<prefix>/[slug]').
|
|
1195
1207
|
frontmatter: { pattern: "/frontmatter/[slug]", file: "frontmatter/[...slug].astro" }
|
|
1196
1208
|
};
|
|
1209
|
+
var DEFAULT_CONTENT_SECURITY_POLICY = "default-src 'self'; script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval' https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://cloudflareinsights.com; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'";
|
|
1210
|
+
function renderSecurityHeaders(contentSecurityPolicy) {
|
|
1211
|
+
const csp = contentSecurityPolicy ?? DEFAULT_CONTENT_SECURITY_POLICY;
|
|
1212
|
+
return `/*
|
|
1213
|
+
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
|
1214
|
+
X-Content-Type-Options: nosniff
|
|
1215
|
+
Referrer-Policy: strict-origin-when-cross-origin
|
|
1216
|
+
Permissions-Policy: camera=(), microphone=(), geolocation=()
|
|
1217
|
+
Content-Security-Policy: ${csp}
|
|
1218
|
+
`;
|
|
1219
|
+
}
|
|
1197
1220
|
function frontmatterPatternFromPrefix(prefix) {
|
|
1198
1221
|
if (prefix === void 0) return ROUTE_REGISTRY.frontmatter.pattern;
|
|
1199
1222
|
if (prefix === "") return "/[slug]";
|
|
@@ -1205,6 +1228,7 @@ function resolvePage(file) {
|
|
|
1205
1228
|
function bookScaffoldIntegration(opts) {
|
|
1206
1229
|
const {
|
|
1207
1230
|
profile,
|
|
1231
|
+
numberStyle = "shared",
|
|
1208
1232
|
routes: userOverrides = {},
|
|
1209
1233
|
extraStyles = [],
|
|
1210
1234
|
mdxComponentsModule,
|
|
@@ -1212,6 +1236,7 @@ function bookScaffoldIntegration(opts) {
|
|
|
1212
1236
|
title,
|
|
1213
1237
|
subtitle,
|
|
1214
1238
|
releaseStatus,
|
|
1239
|
+
securityHeaders,
|
|
1215
1240
|
description,
|
|
1216
1241
|
portfolio,
|
|
1217
1242
|
// v4.6.0: book-level author + SEO config, propagated through the
|
|
@@ -1242,7 +1267,7 @@ function bookScaffoldIntegration(opts) {
|
|
|
1242
1267
|
),
|
|
1243
1268
|
frontmatter: fmEnabled
|
|
1244
1269
|
};
|
|
1245
|
-
|
|
1270
|
+
const integration = {
|
|
1246
1271
|
name: "book-scaffold-astro",
|
|
1247
1272
|
hooks: {
|
|
1248
1273
|
"astro:config:setup": ({ injectScript, injectRoute, updateConfig, config }) => {
|
|
@@ -1312,9 +1337,38 @@ function bookScaffoldIntegration(opts) {
|
|
|
1312
1337
|
}
|
|
1313
1338
|
}
|
|
1314
1339
|
});
|
|
1340
|
+
},
|
|
1341
|
+
// v4.27.0 (#188): emit defaults for every build, not only newly
|
|
1342
|
+
// scaffolded projects, so existing consumers become protected on their
|
|
1343
|
+
// next package upgrade. Astro copies public/_headers into the output
|
|
1344
|
+
// before build:done; an existing target therefore means the consumer
|
|
1345
|
+
// owns the complete file and must win byte-for-byte.
|
|
1346
|
+
"astro:build:done": ({ dir, logger }) => {
|
|
1347
|
+
if (securityHeaders === false) {
|
|
1348
|
+
logger.info("security-header emission disabled by defineBookConfig (#188)");
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const target = join(fileURLToPath(dir), "_headers");
|
|
1352
|
+
if (existsSync3(target)) {
|
|
1353
|
+
logger.info("consumer public/_headers present; scaffold defaults skipped (#188)");
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
writeFileSync(
|
|
1357
|
+
target,
|
|
1358
|
+
renderSecurityHeaders(securityHeaders?.contentSecurityPolicy),
|
|
1359
|
+
"utf8"
|
|
1360
|
+
);
|
|
1361
|
+
logger.info("emitted default security headers; public/_headers overrides them (#188)");
|
|
1315
1362
|
}
|
|
1316
1363
|
}
|
|
1317
1364
|
};
|
|
1365
|
+
Object.defineProperty(integration, "__bookScaffoldResolvedConfig", {
|
|
1366
|
+
value: Object.freeze({ preset: profile, numberStyle }),
|
|
1367
|
+
enumerable: false,
|
|
1368
|
+
configurable: false,
|
|
1369
|
+
writable: false
|
|
1370
|
+
});
|
|
1371
|
+
return integration;
|
|
1318
1372
|
}
|
|
1319
1373
|
|
|
1320
1374
|
// src/config.ts
|
|
@@ -1351,8 +1405,19 @@ async function defineBookConfig(opts) {
|
|
|
1351
1405
|
if ("preset" in opts || "profile" in opts) {
|
|
1352
1406
|
throw v3MigrationError(opts);
|
|
1353
1407
|
}
|
|
1408
|
+
if (Object.prototype.hasOwnProperty.call(opts, "deploy")) {
|
|
1409
|
+
console.warn(
|
|
1410
|
+
"book-scaffold-astro: defineBookConfig({ deploy }) is inert and deprecated; create-book chooses wrangler.toml from its CLI preset. Remove this field before upgrading to v5 (#180)."
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1354
1413
|
const composed = composeStyles(opts.styles ?? []);
|
|
1355
|
-
const profile =
|
|
1414
|
+
const profile = resolvePreset(composed.preset);
|
|
1415
|
+
const numberStyle = opts.numberStyle ?? composed.numberStyle ?? "shared";
|
|
1416
|
+
if (!NUMBER_STYLES.includes(numberStyle)) {
|
|
1417
|
+
throw new BookConfigError(
|
|
1418
|
+
`numberStyle must be one of ${NUMBER_STYLES.join(" | ")} (got ${JSON.stringify(numberStyle)})`
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1356
1421
|
const site = opts.site ?? composed.site;
|
|
1357
1422
|
if (!site) {
|
|
1358
1423
|
throw new BookConfigError(
|
|
@@ -1378,6 +1443,7 @@ async function defineBookConfig(opts) {
|
|
|
1378
1443
|
const mdxComponentsModule = opts.mdxComponentsModule ?? composed.mdxComponentsModule;
|
|
1379
1444
|
const composedMarkdown = composed.markdown ?? {};
|
|
1380
1445
|
const userMarkdown = opts.markdown ?? {};
|
|
1446
|
+
const resolvedReleaseStatus = opts.releaseStatus !== void 0 ? opts.releaseStatus : composed.releaseStatus;
|
|
1381
1447
|
const wantsKatex = PROFILES[profile]?.katex === true;
|
|
1382
1448
|
const remarkPlugins = [];
|
|
1383
1449
|
const rehypePlugins = [];
|
|
@@ -1419,6 +1485,7 @@ async function defineBookConfig(opts) {
|
|
|
1419
1485
|
sitemap(sitemapOptions),
|
|
1420
1486
|
bookScaffoldIntegration({
|
|
1421
1487
|
profile,
|
|
1488
|
+
numberStyle,
|
|
1422
1489
|
routes: mergedRoutes,
|
|
1423
1490
|
mdxComponentsModule,
|
|
1424
1491
|
extraStyles: mergedExtraStyles,
|
|
@@ -1427,8 +1494,13 @@ async function defineBookConfig(opts) {
|
|
|
1427
1494
|
title: opts.title,
|
|
1428
1495
|
// v4.23.0 (#135): sidebar brand subtitle.
|
|
1429
1496
|
subtitle: opts.subtitle,
|
|
1430
|
-
// v4.
|
|
1431
|
-
releaseStatus:
|
|
1497
|
+
// v4.26.2 (#149; style inheritance + opt-out fixed in v4.26.3).
|
|
1498
|
+
releaseStatus: resolvedReleaseStatus === false ? void 0 : resolvedReleaseStatus,
|
|
1499
|
+
// v4.27.0 (#188): undefined enables the audited defaults, false opts
|
|
1500
|
+
// out, and an object can replace only the CSP. The integration owns
|
|
1501
|
+
// the build:done emission because that is where Astro's output dir is
|
|
1502
|
+
// known and a copied consumer public/_headers can be detected.
|
|
1503
|
+
securityHeaders: opts.securityHeaders,
|
|
1432
1504
|
description: opts.description,
|
|
1433
1505
|
portfolio: resolvedPortfolio,
|
|
1434
1506
|
// v4.6.0: book-level author + SEO config (ogImage, twitterHandle),
|
|
@@ -1475,6 +1547,7 @@ async function defineBookConfig(opts) {
|
|
|
1475
1547
|
};
|
|
1476
1548
|
const {
|
|
1477
1549
|
styles: _styles,
|
|
1550
|
+
numberStyle: _numberStyle,
|
|
1478
1551
|
site: _site,
|
|
1479
1552
|
routes: _routes,
|
|
1480
1553
|
deploy: _deploy,
|
|
@@ -1486,8 +1559,10 @@ async function defineBookConfig(opts) {
|
|
|
1486
1559
|
// v4.5.0: strip new landing-related opts so they don't leak into AstroUserConfig.
|
|
1487
1560
|
title: _title,
|
|
1488
1561
|
subtitle: _subtitle,
|
|
1489
|
-
// v4.
|
|
1562
|
+
// v4.26.2 (#149): strip the release-state banner config.
|
|
1490
1563
|
releaseStatus: _releaseStatus,
|
|
1564
|
+
// v4.27.0 (#188): consumed by the scaffold integration at build:done.
|
|
1565
|
+
securityHeaders: _securityHeaders,
|
|
1491
1566
|
description: _description,
|
|
1492
1567
|
portfolio: _portfolio,
|
|
1493
1568
|
// v4.6.0: strip new book-level SEO opts (author + seo block).
|
|
@@ -1508,6 +1583,7 @@ async function defineBookConfig(opts) {
|
|
|
1508
1583
|
...rest
|
|
1509
1584
|
} = opts;
|
|
1510
1585
|
void _styles;
|
|
1586
|
+
void _numberStyle;
|
|
1511
1587
|
void _site;
|
|
1512
1588
|
void _routes;
|
|
1513
1589
|
void _deploy;
|
|
@@ -1519,6 +1595,7 @@ async function defineBookConfig(opts) {
|
|
|
1519
1595
|
void _title;
|
|
1520
1596
|
void _subtitle;
|
|
1521
1597
|
void _releaseStatus;
|
|
1598
|
+
void _securityHeaders;
|
|
1522
1599
|
void _description;
|
|
1523
1600
|
void _portfolio;
|
|
1524
1601
|
void _author;
|
|
@@ -1624,6 +1701,9 @@ function assertEnumProp(value, allowed, ctx) {
|
|
|
1624
1701
|
);
|
|
1625
1702
|
}
|
|
1626
1703
|
|
|
1704
|
+
// src/index.ts
|
|
1705
|
+
init_katex_macros();
|
|
1706
|
+
|
|
1627
1707
|
// src/lib/book-link.ts
|
|
1628
1708
|
function resolveBookHref(siblingBooks, book, to) {
|
|
1629
1709
|
const base = siblingBooks?.[book];
|
|
@@ -1637,9 +1717,13 @@ function resolveBookHref(siblingBooks, book, to) {
|
|
|
1637
1717
|
}
|
|
1638
1718
|
|
|
1639
1719
|
// src/lib/nav-href.ts
|
|
1640
|
-
function
|
|
1720
|
+
function normalizeBase(baseUrl) {
|
|
1641
1721
|
return (baseUrl || "/").replace(/\/*$/, "/");
|
|
1642
1722
|
}
|
|
1723
|
+
function baseNoSlash(baseUrl) {
|
|
1724
|
+
return (baseUrl || "/").replace(/\/+$/, "");
|
|
1725
|
+
}
|
|
1726
|
+
var normBase = normalizeBase;
|
|
1643
1727
|
function fillTokens(pattern, tokens) {
|
|
1644
1728
|
return pattern.replace(/:(book|slug|route|id)\b/g, (_m, k) => tokens[k] ?? "");
|
|
1645
1729
|
}
|
|
@@ -1912,6 +1996,7 @@ export {
|
|
|
1912
1996
|
BookConfigError,
|
|
1913
1997
|
DEFAULT_GITHUB_BRANCH,
|
|
1914
1998
|
KIND_LABEL,
|
|
1999
|
+
NUMBER_STYLES,
|
|
1915
2000
|
THEOREM_KINDS,
|
|
1916
2001
|
UNKNOWN_PART_ORDINAL,
|
|
1917
2002
|
academicChapterSchema,
|
|
@@ -1924,6 +2009,7 @@ export {
|
|
|
1924
2009
|
apparatusHref,
|
|
1925
2010
|
assertEnumProp,
|
|
1926
2011
|
assertKnownDomain,
|
|
2012
|
+
baseNoSlash,
|
|
1927
2013
|
bloomLevels,
|
|
1928
2014
|
bookOf,
|
|
1929
2015
|
bookScaffoldIntegration,
|
|
@@ -1958,6 +2044,7 @@ export {
|
|
|
1958
2044
|
layoutModes,
|
|
1959
2045
|
minimalChapterSchema,
|
|
1960
2046
|
minimalStyle,
|
|
2047
|
+
normalizeBase,
|
|
1961
2048
|
normalizeFrontmatterConfig,
|
|
1962
2049
|
originUrlFromGitConfig,
|
|
1963
2050
|
parseRepoSlug,
|
|
@@ -1989,6 +2076,7 @@ export {
|
|
|
1989
2076
|
sourceTiersResearch,
|
|
1990
2077
|
sourcesSchema,
|
|
1991
2078
|
spreadBlueprint,
|
|
2079
|
+
ssmMacros,
|
|
1992
2080
|
theoremLabel,
|
|
1993
2081
|
tocHeadings,
|
|
1994
2082
|
toolSlugs,
|
package/dist/schemas.d.ts
CHANGED
package/dist/schemas.mjs
CHANGED
|
@@ -814,6 +814,7 @@ function readEnvFile(path = ".env") {
|
|
|
814
814
|
return {};
|
|
815
815
|
}
|
|
816
816
|
}
|
|
817
|
+
var PRESET_FALLBACK_WARNING = /* @__PURE__ */ Symbol.for("book-scaffold-astro:preset-fallback-warning");
|
|
817
818
|
function resolvePreset(explicitPreset, explicitProfile) {
|
|
818
819
|
let candidate = explicitPreset ?? explicitProfile ?? process.env.BOOK_PRESET ?? process.env.BOOK_PROFILE;
|
|
819
820
|
let source = "default";
|
|
@@ -834,7 +835,13 @@ function resolvePreset(explicitPreset, explicitProfile) {
|
|
|
834
835
|
);
|
|
835
836
|
}
|
|
836
837
|
if (source === "default") {
|
|
837
|
-
|
|
838
|
+
const warningState = globalThis;
|
|
839
|
+
if (!warningState[PRESET_FALLBACK_WARNING]) {
|
|
840
|
+
warningState[PRESET_FALLBACK_WARNING] = true;
|
|
841
|
+
console.warn(
|
|
842
|
+
"book-scaffold-astro: no preset resolved; falling back to 'minimal' for v4 compatibility. This fallback will be removed in v5. Add a built-in style to defineBookConfig and pass the same preset to defineBookSchemas, or set BOOK_PRESET."
|
|
843
|
+
);
|
|
844
|
+
}
|
|
838
845
|
}
|
|
839
846
|
return candidate;
|
|
840
847
|
}
|
|
@@ -353,6 +353,8 @@ interface Style {
|
|
|
353
353
|
readonly name?: string;
|
|
354
354
|
/** Profile that backs this style — determines schema + default routes + styles + KaTeX wiring. */
|
|
355
355
|
readonly preset?: BookPreset;
|
|
356
|
+
/** Theorem-family numbering strategy; shallow override (last wins). */
|
|
357
|
+
readonly numberStyle?: NumberStyle;
|
|
356
358
|
/** Book's deployed origin (sitemap, canonical, Pagefind). Required at composition end;
|
|
357
359
|
* optional inside a Style (so styles can omit it and consumers can provide per-book). */
|
|
358
360
|
readonly site?: string;
|
|
@@ -374,17 +376,17 @@ interface Style {
|
|
|
374
376
|
* `remarkPlugins` and `rehypePlugins` arrays concat across the chain;
|
|
375
377
|
* scalar fields override. */
|
|
376
378
|
readonly markdown?: AstroUserConfig['markdown'];
|
|
377
|
-
/**
|
|
378
|
-
* -
|
|
379
|
-
*
|
|
380
|
-
*
|
|
379
|
+
/** Reserved legacy metadata. It is composed but has no runtime or
|
|
380
|
+
* create-book effect: create-book chooses wrangler.toml from its CLI
|
|
381
|
+
* preset before a consumer Style exists (#180).
|
|
382
|
+
* @deprecated Inert in v4; scheduled for removal in v5. */
|
|
381
383
|
readonly deploy?: 'pages' | 'workers';
|
|
382
|
-
/**
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
384
|
+
/**
|
|
385
|
+
* v4.26.2 (#149; style inheritance fixed in v4.26.3): release-state
|
|
386
|
+
* banner. Shallow override (last defined wins); `false` suppresses a
|
|
387
|
+
* banner inherited from an earlier style.
|
|
388
|
+
*/
|
|
389
|
+
readonly releaseStatus?: ReleaseStatusConfig | false;
|
|
388
390
|
/**
|
|
389
391
|
* Scoped consumer-side metadata. Ignored by the toolkit; survives composition
|
|
390
392
|
* as per-key spread (last wins per key). Use this for workflow data that
|
|
@@ -445,7 +447,8 @@ declare function defineStyle(opts: StyleInput): Style;
|
|
|
445
447
|
* - Top-level `defineBookConfig` fields beat any style (handled in config.ts)
|
|
446
448
|
*
|
|
447
449
|
* Per-key merge strategy:
|
|
448
|
-
* - `preset`, `site`, `deploy`, `mdxComponentsModule`, `name`
|
|
450
|
+
* - `preset`, `numberStyle`, `site`, `deploy`, `mdxComponentsModule`, `name`, `releaseStatus`
|
|
451
|
+
* → shallow override (last defined wins; `releaseStatus: false` suppresses)
|
|
449
452
|
* - `routes` → per-route spread (each route key independently overridable)
|
|
450
453
|
* - `katexMacros` → per-macro spread (each macro key independently overridable)
|
|
451
454
|
* - `extra` → per-key spread (consumer metadata accumulates across the chain)
|
|
@@ -481,6 +484,41 @@ declare function normalizeFrontmatterConfig(v: FrontmatterRouteConfig | undefine
|
|
|
481
484
|
|
|
482
485
|
type BookPreset = BookProfile;
|
|
483
486
|
declare const BOOK_PRESETS: readonly ("academic" | "tools" | "minimal" | "course-notes" | "research-portfolio")[];
|
|
487
|
+
/** Theorem-family numbering strategy used by build-labels and validate. */
|
|
488
|
+
type NumberStyle = 'shared' | 'per-kind';
|
|
489
|
+
declare const NUMBER_STYLES: readonly ["shared", "per-kind"];
|
|
490
|
+
/**
|
|
491
|
+
* v4.27.0 (#188): build-time security-header customization.
|
|
492
|
+
*
|
|
493
|
+
* The integration emits a Cloudflare-compatible `dist/_headers` file by
|
|
494
|
+
* default. Consumers that need a different CSP can replace that one header
|
|
495
|
+
* while retaining the scaffold's other audited defaults.
|
|
496
|
+
*/
|
|
497
|
+
interface SecurityHeadersConfig {
|
|
498
|
+
contentSecurityPolicy?: string;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* v4.26.2 (#149): book-level release state rendered by
|
|
502
|
+
* `<PreReleaseBanner>` across every page.
|
|
503
|
+
*
|
|
504
|
+
* Style inheritance and explicit suppression were fixed in v4.26.3.
|
|
505
|
+
*/
|
|
506
|
+
interface ReleaseStatusConfig {
|
|
507
|
+
state: 'alpha' | 'beta' | 'rc' | 'locked';
|
|
508
|
+
dismissAt?: string;
|
|
509
|
+
message?: string;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* v4.26.2 (#149): book-level release state rendered by
|
|
513
|
+
* `<PreReleaseBanner>` across every page.
|
|
514
|
+
*
|
|
515
|
+
* Style inheritance and explicit suppression were fixed in v4.26.3.
|
|
516
|
+
*/
|
|
517
|
+
interface ReleaseStatusConfig {
|
|
518
|
+
state: 'alpha' | 'beta' | 'rc' | 'locked';
|
|
519
|
+
dismissAt?: string;
|
|
520
|
+
message?: string;
|
|
521
|
+
}
|
|
484
522
|
/**
|
|
485
523
|
* Options for `defineBookConfig`. See PACKAGE_DESIGN.md §4.
|
|
486
524
|
*
|
|
@@ -519,6 +557,13 @@ interface BookConfigOptions {
|
|
|
519
557
|
* });
|
|
520
558
|
*/
|
|
521
559
|
styles?: readonly Style[];
|
|
560
|
+
/**
|
|
561
|
+
* v4.27.0 (#175): theorem-family counter strategy. `shared` preserves the
|
|
562
|
+
* amsthm-style sequence used through v4.26; `per-kind` gives theorem,
|
|
563
|
+
* proposition, lemma, and each other theorem kind an independent sequence.
|
|
564
|
+
* May also be supplied by a Style; top-level config wins.
|
|
565
|
+
*/
|
|
566
|
+
numberStyle?: NumberStyle;
|
|
522
567
|
/**
|
|
523
568
|
* Optional per-route override of the composed profile's defaults. Use to
|
|
524
569
|
* disable an auto-injected route (e.g. multi-book consumer that ships
|
|
@@ -533,10 +578,13 @@ interface BookConfigOptions {
|
|
|
533
578
|
*/
|
|
534
579
|
routes?: PartialRouteToggles;
|
|
535
580
|
/**
|
|
536
|
-
* v4.0.0 (
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
581
|
+
* v4.0.0 (#50): RESERVED — accepted and style-chain-merged, but currently
|
|
582
|
+
* has NO runtime effect (#180). The wrangler.toml shape is decided once, at
|
|
583
|
+
* scaffold time, by `create-book` from the profile name (academic/tools/
|
|
584
|
+
* minimal → Workers; course-notes/research-portfolio → Pages); setting this
|
|
585
|
+
* field changes nothing afterward. Wire-or-remove is a v5 decision.
|
|
586
|
+
* @deprecated Inert in v4; remove this option before v5. Deployment shape
|
|
587
|
+
* is chosen by create-book or by the consumer's own deployment files.
|
|
540
588
|
*/
|
|
541
589
|
deploy?: 'pages' | 'workers';
|
|
542
590
|
/**
|
|
@@ -607,16 +655,20 @@ interface BookConfigOptions {
|
|
|
607
655
|
*/
|
|
608
656
|
subtitle?: string;
|
|
609
657
|
/**
|
|
610
|
-
* v4.
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
658
|
+
* v4.26.2 (#149; style inheritance fixed in v4.26.3): book-level release
|
|
659
|
+
* state. When set, Base.astro renders the existing <PreReleaseBanner>
|
|
660
|
+
* site-wide (top of <body>) with these props. Omit to inherit from the
|
|
661
|
+
* composed style chain; set `false` to suppress an inherited banner.
|
|
614
662
|
*/
|
|
615
|
-
releaseStatus?:
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
663
|
+
releaseStatus?: ReleaseStatusConfig | false;
|
|
664
|
+
/**
|
|
665
|
+
* v4.27.0 (#188): security headers emitted to `dist/_headers` after an
|
|
666
|
+
* Astro build. Omit for the scaffold defaults; set `false` to emit no
|
|
667
|
+
* scaffold-owned file; provide `contentSecurityPolicy` to replace only the
|
|
668
|
+
* default CSP while retaining HSTS, XCTO, Referrer-Policy, and
|
|
669
|
+
* Permissions-Policy. A consumer-owned `public/_headers` always wins.
|
|
670
|
+
*/
|
|
671
|
+
securityHeaders?: SecurityHeadersConfig | false;
|
|
620
672
|
/**
|
|
621
673
|
* v4.5.0: Book description. Read by the auto-injected `/` landing page (lead paragraph + <meta description>).
|
|
622
674
|
* Optional; landing renders no description paragraph if unset.
|
|
@@ -749,6 +801,8 @@ interface BookSchemasOptions {
|
|
|
749
801
|
/** Options for the internal `bookScaffoldIntegration`. See PACKAGE_DESIGN.md §6. */
|
|
750
802
|
interface BookScaffoldIntegrationOptions {
|
|
751
803
|
profile: BookProfile;
|
|
804
|
+
/** Resolved theorem-family counter strategy exposed to package CLI tooling. */
|
|
805
|
+
numberStyle?: NumberStyle;
|
|
752
806
|
/**
|
|
753
807
|
* Per-route override; merged into the profile's defaults.
|
|
754
808
|
* v4.0.0: `routes.frontmatter` widened to `boolean | { enabled, prefix? }`
|
|
@@ -769,13 +823,11 @@ interface BookScaffoldIntegrationOptions {
|
|
|
769
823
|
/** v4.23.0 (#135): sidebar brand subtitle, propagated via the book-config
|
|
770
824
|
* virtual module to Sidebar.astro. */
|
|
771
825
|
subtitle?: string;
|
|
772
|
-
/** v4.
|
|
773
|
-
*
|
|
774
|
-
releaseStatus?:
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
message?: string;
|
|
778
|
-
};
|
|
826
|
+
/** v4.26.2 (#149; style inheritance fixed in v4.26.3): resolved
|
|
827
|
+
* release-state banner propagated via the book-config virtual module. */
|
|
828
|
+
releaseStatus?: ReleaseStatusConfig;
|
|
829
|
+
/** v4.27.0 (#188): resolved security-header emission policy. */
|
|
830
|
+
securityHeaders?: SecurityHeadersConfig | false;
|
|
779
831
|
/** v4.5.0: book description, propagated to `/` landing via vite.define. */
|
|
780
832
|
description?: string;
|
|
781
833
|
/**
|
|
@@ -827,22 +879,6 @@ interface BookScaffoldIntegrationOptions {
|
|
|
827
879
|
declare class BookConfigError extends Error {
|
|
828
880
|
constructor(message: string);
|
|
829
881
|
}
|
|
830
|
-
/**
|
|
831
|
-
* Resolve preset from explicit args → env → .env → default. Throws on invalid.
|
|
832
|
-
*
|
|
833
|
-
* v3.4.0 (closes #9): canonical resolver. Accepts both `preset` and `profile`
|
|
834
|
-
* (back-compat) explicit args; reads both `BOOK_PRESET` (preferred) and
|
|
835
|
-
* `BOOK_PROFILE` (alias) env vars; same for .env file lookups.
|
|
836
|
-
*
|
|
837
|
-
* Resolution order:
|
|
838
|
-
* 1. explicitPreset (from defineBookConfig({ preset: ... }))
|
|
839
|
-
* 2. explicitProfile (from defineBookConfig({ profile: ... }))
|
|
840
|
-
* 3. process.env.BOOK_PRESET
|
|
841
|
-
* 4. process.env.BOOK_PROFILE
|
|
842
|
-
* 5. .env BOOK_PRESET
|
|
843
|
-
* 6. .env BOOK_PROFILE
|
|
844
|
-
* 7. 'minimal' (with console.warn)
|
|
845
|
-
*/
|
|
846
882
|
declare function resolvePreset(explicitPreset?: BookPreset, explicitProfile?: BookProfile): BookPreset;
|
|
847
883
|
/**
|
|
848
884
|
* Backward-compat alias. New code should use `resolvePreset()`.
|
|
@@ -851,4 +887,4 @@ declare function resolvePreset(explicitPreset?: BookPreset, explicitProfile?: Bo
|
|
|
851
887
|
*/
|
|
852
888
|
declare function resolveProfile(explicit?: BookProfile): BookProfile;
|
|
853
889
|
|
|
854
|
-
export { BOOK_PRESETS as B, type ChapterFor as C, type FreshnessAffordance as F, type PartKey as P, type
|
|
890
|
+
export { BOOK_PRESETS as B, type ChapterFor as C, type FreshnessAffordance as F, NUMBER_STYLES as N, type PartKey as P, type ReleaseStatusConfig as R, type SecurityHeadersConfig as S, type VolatilityBadge as V, BOOK_PROFILES as a, BookConfigError as b, type BookConfigOptions as c, type BookPreset as d, type BookProfile as e, type BookScaffoldIntegrationOptions as f, type BookSchemasOptions as g, type ChaptersRenderer as h, type FrontmatterRouteConfig as i, type NumberStyle as j, type PartialRouteToggles as k, type ProfileDefinition as l, type RouteToggles as m, type StatusBadge as n, type Style as o, type StyleInput as p, composeStyles as q, defineProfile as r, defineStyle as s, normalizeFrontmatterConfig as t, resolvePreset as u, resolveProfile as v };
|
package/layouts/Base.astro
CHANGED
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
* lang — html lang attribute (default: en)
|
|
16
16
|
* showSidebar — render the left chapter-navigation sidebar (default: true).
|
|
17
17
|
* Set false for the landing page or any full-bleed surface.
|
|
18
|
-
* Below
|
|
19
|
-
*
|
|
18
|
+
* Below 80rem (1280px) the sidebar is auto-hidden via CSS and
|
|
19
|
+
* the mobile nav drawer is the chapter nav (v4.26.0, #80).
|
|
20
|
+
* showChrome — render the automatic tools chrome (ToolFilter)
|
|
20
21
|
* (default: true). Set false on a landing/hub page with no
|
|
21
|
-
*
|
|
22
|
+
* chapter filtering; search + theme toggle always render.
|
|
22
23
|
*
|
|
23
|
-
* Tools chrome (ToolFilter
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* (search + theme toggle only).
|
|
24
|
+
* Tools chrome (ToolFilter): only mounted when BOOK_PROFILE !== 'academic' AND
|
|
25
|
+
* showChrome !== false. VersionSelector is a prop-driven public component that
|
|
26
|
+
* consumers mount explicitly once they have real deployed-version data.
|
|
27
27
|
*
|
|
28
28
|
* Usage:
|
|
29
29
|
* ---
|
|
@@ -51,15 +51,15 @@ import '../styles/chapter.css';
|
|
|
51
51
|
import '../styles/tool-filter.css';
|
|
52
52
|
import '../styles/convergence.css';
|
|
53
53
|
import '../styles/print.css';
|
|
54
|
-
// Use package-path
|
|
54
|
+
// Use the package-path import for the .tsx island so Vite resolves it
|
|
55
55
|
// via the exports map (→ pre-compiled dist/components/*.mjs with proper
|
|
56
56
|
// preact JSX). The raw .tsx files are also shipped so consumers can
|
|
57
57
|
// import them directly, but relative imports from inside the package
|
|
58
58
|
// must route through the exports map, not the raw source.
|
|
59
|
-
import VersionSelector from '@brandon_m_behring/book-scaffold-astro/components/VersionSelector';
|
|
60
59
|
import ToolFilter from '@brandon_m_behring/book-scaffold-astro/components/ToolFilter';
|
|
61
60
|
import Sidebar from '../components/Sidebar.astro';
|
|
62
|
-
// v4.
|
|
61
|
+
// v4.26.2 (#149; style inheritance fixed in v4.26.3): site-wide
|
|
62
|
+
// release-state banner, driven by defineBookConfig.
|
|
63
63
|
import PreReleaseBanner from '../components/PreReleaseBanner.astro';
|
|
64
64
|
// v4.26.0 (#80): the mobile/tablet drawer reuses the same book-scoped nav source.
|
|
65
65
|
import NavContent from '../components/NavContent.astro';
|
|
@@ -67,6 +67,7 @@ import NavContent from '../components/NavContent.astro';
|
|
|
67
67
|
// description fallback, ogImage default, twitterHandle) from the
|
|
68
68
|
// book-config virtual module (was landing-config in v4.5.1).
|
|
69
69
|
import bookConfig from 'virtual:book-scaffold/book-config';
|
|
70
|
+
import { normalizeBase } from '../src/lib/nav-href';
|
|
70
71
|
|
|
71
72
|
const profile = import.meta.env.BOOK_PROFILE ?? 'minimal';
|
|
72
73
|
|
|
@@ -75,10 +76,10 @@ interface Props {
|
|
|
75
76
|
description?: string;
|
|
76
77
|
lang?: string;
|
|
77
78
|
showSidebar?: boolean;
|
|
78
|
-
/** #163: render the optional
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
79
|
+
/** #163: render the optional automatic tools chrome — the ToolFilter island
|
|
80
|
+
* (default true). Set false on landing/hub pages with no chapter filtering;
|
|
81
|
+
* the search + theme-toggle cluster always renders. (No-op for the academic
|
|
82
|
+
* profile, which never shows the ToolFilter.) */
|
|
82
83
|
showChrome?: boolean;
|
|
83
84
|
/** v4.6.0: Open Graph image URL (relative to site root, or absolute). */
|
|
84
85
|
ogImage?: string;
|
|
@@ -96,9 +97,9 @@ const {
|
|
|
96
97
|
ogType = 'website',
|
|
97
98
|
} = Astro.props;
|
|
98
99
|
|
|
99
|
-
//
|
|
100
|
+
// Automatic tools chrome (ToolFilter) renders only for non-academic
|
|
100
101
|
// profiles AND when the page opts in via showChrome (default true). A
|
|
101
|
-
// landing/hub page (no
|
|
102
|
+
// landing/hub page (no chapter filtering) passes showChrome={false} to
|
|
102
103
|
// get the search + theme-toggle cluster only — the chrome-free path that
|
|
103
104
|
// previously forced borrowing the academic profile (and its katex deps). #163.
|
|
104
105
|
const showToolsChrome = (profile !== 'academic') && showChrome;
|
|
@@ -119,7 +120,7 @@ const absoluteOgImage = resolvedOgImage
|
|
|
119
120
|
const twitterHandle = bookConfig.seo?.twitterHandle ?? null;
|
|
120
121
|
const ogSiteName = bookConfig.title ?? title;
|
|
121
122
|
const ogDescription = description ?? bookConfig.description ?? '';
|
|
122
|
-
const baseUrl = (import.meta.env.BASE_URL
|
|
123
|
+
const baseUrl = normalizeBase(import.meta.env.BASE_URL);
|
|
123
124
|
---
|
|
124
125
|
|
|
125
126
|
<!doctype html>
|
|
@@ -167,7 +168,7 @@ const baseUrl = (import.meta.env.BASE_URL ?? '/').replace(/\/*$/, '/');
|
|
|
167
168
|
<slot name="head" />
|
|
168
169
|
</head>
|
|
169
170
|
<body>
|
|
170
|
-
{/* v4.
|
|
171
|
+
{/* v4.26.2 (#149; style inheritance fixed in v4.26.3): book-level release banner — first element in <body> so
|
|
171
172
|
every page (chapters, apparatus, landing) declares its state. Renders
|
|
172
173
|
only when defineBookConfig({ releaseStatus }) is set. */}
|
|
173
174
|
{bookConfig.releaseStatus && (
|
|
@@ -195,7 +196,6 @@ const baseUrl = (import.meta.env.BASE_URL ?? '/').replace(/\/*$/, '/');
|
|
|
195
196
|
</a>
|
|
196
197
|
)}
|
|
197
198
|
{showToolsChrome && <ToolFilter client:idle />}
|
|
198
|
-
{showToolsChrome && <VersionSelector client:idle />}
|
|
199
199
|
<a
|
|
200
200
|
href={`${baseUrl}search/`}
|
|
201
201
|
class="chrome-button"
|
|
@@ -216,7 +216,8 @@ const baseUrl = (import.meta.env.BASE_URL ?? '/').replace(/\/*$/, '/');
|
|
|
216
216
|
</button>
|
|
217
217
|
</div>
|
|
218
218
|
{/* v4.26.0 (#80): mobile/tablet nav drawer (sibling of <main>, NOT a <main>).
|
|
219
|
-
CSS-hidden ≥
|
|
219
|
+
CSS-hidden ≥80rem where the Sidebar is the nav (layout.css and the
|
|
220
|
+
controller's desktop auto-close agree on 80rem). role=dialog + the inline
|
|
220
221
|
controller below provide focus-trap/ESC; `:target` is the no-JS fallback. */}
|
|
221
222
|
{showSidebar && (
|
|
222
223
|
<div id="nav-drawer" class="nav-drawer" data-nav-drawer>
|
|
@@ -389,7 +390,8 @@ const baseUrl = (import.meta.env.BASE_URL ?? '/').replace(/\/*$/, '/');
|
|
|
389
390
|
text-decoration: none;
|
|
390
391
|
}
|
|
391
392
|
|
|
392
|
-
/*
|
|
393
|
+
/* Opt-in VersionSelector dropdown. Base provides its visual language but
|
|
394
|
+
* deliberately does not mount it without a consumer-owned version manifest. */
|
|
393
395
|
.version-selector {
|
|
394
396
|
position: relative;
|
|
395
397
|
display: inline-block;
|