kitfly 0.2.1 → 0.2.4
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/CHANGELOG.md +79 -0
- package/README.md +38 -21
- package/VERSION +1 -1
- package/dist/_raw/content/guide/branding.md +146 -0
- package/dist/_raw/content/guide/data-driven-content.md +204 -0
- package/dist/_raw/content/reference/configuration.md +145 -7
- package/dist/_raw/content/reference/environment-variables.md +26 -1
- package/dist/_raw/content/reference/gantt-widget.md +468 -0
- package/dist/_raw/content/reference/glossary.md +25 -1
- package/dist/_raw/content/reference/key-concepts.md +30 -2
- package/dist/_raw/content/reference/plugins.md +170 -1
- package/dist/_raw/docs/decisions/ADR-0006-data-driven-content.md +350 -0
- package/dist/content/deployment/preflight.html +11 -8
- package/dist/content/deployment/recipes/aws-s3.html +11 -8
- package/dist/content/deployment/recipes/cloudflare-pages.html +11 -8
- package/dist/content/deployment/recipes/cloudflare-r2.html +11 -8
- package/dist/content/deployment/recipes/fly-io.html +11 -8
- package/dist/content/deployment/recipes/github-pages.html +11 -8
- package/dist/content/deployment/recipes/netlify.html +11 -8
- package/dist/content/deployment/recipes/vercel.html +11 -8
- package/dist/content/deployment/secrets-and-env-vars.html +11 -8
- package/dist/content/deployment.html +11 -8
- package/dist/content/guide/approaches.html +11 -8
- package/dist/content/guide/branding.html +509 -0
- package/dist/content/guide/data-driven-content.html +542 -0
- package/dist/content/guide/features.html +11 -8
- package/dist/content/guide/getting-started.html +11 -8
- package/dist/content/guide/kitfly-overview.html +11 -8
- package/dist/content/reference/configuration.html +136 -11
- package/dist/content/reference/design-catalog.html +11 -8
- package/dist/content/reference/environment-variables.html +51 -10
- package/dist/content/reference/gantt-widget.html +899 -0
- package/dist/content/reference/glossary.html +25 -10
- package/dist/content/reference/key-concepts.html +34 -11
- package/dist/content/reference/plugins.html +261 -10
- package/dist/content/reference/slides-authoring-guidelines.html +11 -8
- package/dist/content/reference/structure.html +11 -8
- package/dist/content/reference.html +11 -8
- package/dist/content/templates/crucible.html +11 -8
- package/dist/content/templates/handbook.html +11 -8
- package/dist/content/templates/minimal.html +11 -8
- package/dist/content/templates/overview.html +11 -8
- package/dist/content/templates/pipeline.html +11 -8
- package/dist/content/templates/productbook.html +11 -8
- package/dist/content/templates/runbook.html +11 -8
- package/dist/content/templates/servicebook.html +11 -8
- package/dist/content-index.json +37 -2
- package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +11 -8
- package/dist/docs/decisions/ADR-0002-ai-accessibility.html +11 -8
- package/dist/docs/decisions/ADR-0003-single-file-bundle.html +11 -8
- package/dist/docs/decisions/ADR-0004-bun-runtime.html +11 -8
- package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +11 -8
- package/dist/docs/decisions/ADR-0006-data-driven-content.html +751 -0
- package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +11 -8
- package/dist/docs/decisions/DDR-0002-theme-system.html +11 -8
- package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +11 -8
- package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +11 -8
- package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +11 -8
- package/dist/docs/userguide/cli/build.html +11 -8
- package/dist/docs/userguide/cli/bundle.html +11 -8
- package/dist/docs/userguide/cli/dev.html +11 -8
- package/dist/docs/userguide/cli/init.html +11 -8
- package/dist/docs/userguide/cli/servers.html +11 -8
- package/dist/docs/userguide/cli/stop.html +11 -8
- package/dist/docs/userguide/cli/update.html +11 -8
- package/dist/docs/userguide/cli/version.html +11 -8
- package/dist/docs/userguide/cli.html +11 -8
- package/dist/docs/userguide/sharing.html +11 -8
- package/dist/index.html +11 -8
- package/dist/llms.txt +3 -3
- package/dist/provenance.json +4 -5
- package/dist/reports/license-inventory.csv +199 -0
- package/dist/schemas/plugin-registry.schema.html +11 -8
- package/dist/schemas/plugin-schemas-notes.html +11 -8
- package/dist/schemas/plugin.schema.html +11 -8
- package/dist/schemas/plugins.schema.html +11 -8
- package/dist/schemas/v0/common.schema.html +15 -12
- package/dist/schemas/v0/plugin-registry.schema.html +14 -11
- package/dist/schemas/v0/plugin.schema.html +14 -11
- package/dist/schemas/v0/plugins.schema.html +14 -11
- package/dist/schemas/v0/site.schema.html +68 -9
- package/dist/schemas/v0/theme.schema.html +22 -19
- package/dist/schemas.html +11 -8
- package/dist/styles.css +39 -4
- package/package.json +1 -1
- package/plugins-dist/latex-runtime.js +140 -0
- package/plugins-dist/latex.js +178 -0
- package/plugins-dist/planning-visuals.css +261 -0
- package/plugins-dist/planning-visuals.js +669 -0
- package/plugins-dist/slides-charts-lite-runtime.js +179 -0
- package/plugins-dist/slides-charts-lite.js +198 -0
- package/registry/plugins.yaml +40 -1
- package/schemas/v0/site.schema.json +56 -0
- package/scripts/build-all.ts +5 -0
- package/scripts/build.ts +264 -80
- package/scripts/bundle.ts +188 -17
- package/scripts/dev.ts +294 -171
- package/scripts/embed-docs.ts +119 -0
- package/src/__tests__/brief.test.ts +151 -0
- package/src/__tests__/build.test.ts +293 -1
- package/src/__tests__/bundle.test.ts +195 -0
- package/src/__tests__/docs.test.ts +117 -0
- package/src/__tests__/fixtures/fences/planning-visuals/invalid/bad-month-format.md +10 -0
- package/src/__tests__/fixtures/fences/planning-visuals/invalid/marker-format-mismatch.md +13 -0
- package/src/__tests__/fixtures/fences/planning-visuals/invalid/milestone-format-mismatch.md +13 -0
- package/src/__tests__/fixtures/fences/planning-visuals/invalid/missing-tracks.md +5 -0
- package/src/__tests__/fixtures/fences/planning-visuals/invalid/track-reversed.md +10 -0
- package/src/__tests__/fixtures/fences/planning-visuals/valid/markers-basic.md +15 -0
- package/src/__tests__/fixtures/fences/planning-visuals/valid/markers-no-milestones.md +13 -0
- package/src/__tests__/fixtures/fences/planning-visuals/valid/month-basic.md +16 -0
- package/src/__tests__/fixtures/fences/planning-visuals/valid/no-milestones.md +10 -0
- package/src/__tests__/fixtures/fences/planning-visuals/valid/week-basic.md +20 -0
- package/src/__tests__/init.test.ts +51 -2
- package/src/__tests__/latex-runtime.bun.test.ts +35 -0
- package/src/__tests__/planning-visuals-fence-contract.test.ts +28 -0
- package/src/__tests__/planning-visuals-runtime-regressions.bun.test.ts +68 -0
- package/src/__tests__/planning-visuals-runtime.bun.test.ts +192 -0
- package/src/__tests__/shared.test.ts +719 -1
- package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
- package/src/cli.ts +124 -22
- package/src/commands/docs.ts +71 -0
- package/src/commands/init.ts +1 -1
- package/src/generated/embedded-docs.ts +2384 -0
- package/src/server-registry.ts +50 -10
- package/src/shared.ts +1174 -43
- package/src/site/styles.css +39 -4
- package/src/site/template.html +5 -2
- package/src/templates/brief.ts +486 -0
- package/src/templates/deck.ts +59 -0
- package/src/templates/driver.ts +46 -13
- package/src/templates/handbook.ts +32 -0
- package/src/templates/runbook.ts +32 -0
package/scripts/bundle.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Options:
|
|
7
7
|
* -o, --out <dir> Output directory [env: KITFLY_BUNDLE_OUT] [default: bundles]
|
|
8
8
|
* -n, --name <file> Bundle filename [env: KITFLY_BUNDLE_NAME] [default: bundle.html]
|
|
9
|
+
* --profile <name> Active content profile [env: KITFLY_PROFILE]
|
|
9
10
|
* --raw Include raw markdown in bundle [env: KITFLY_BUNDLE_RAW] [default: true]
|
|
10
11
|
* --no-raw Don't include raw markdown
|
|
11
12
|
* --help Show help message
|
|
@@ -21,29 +22,39 @@ import { ENGINE_ASSETS_DIR } from "../src/engine.ts";
|
|
|
21
22
|
import { loadPluginInjections } from "../src/plugin-loader.ts";
|
|
22
23
|
import {
|
|
23
24
|
buildBundleFooter,
|
|
25
|
+
buildLogoImgHtml,
|
|
24
26
|
buildSectionNav,
|
|
25
27
|
// Navigation/template building
|
|
26
|
-
|
|
27
|
-
// Types
|
|
28
|
+
buildSlideNavHierarchical,
|
|
28
29
|
type ContentFile,
|
|
29
30
|
collectFiles,
|
|
31
|
+
// Types
|
|
32
|
+
collectPlanningVisualsContainmentWarnings,
|
|
30
33
|
collectSlides,
|
|
31
34
|
envBool,
|
|
32
35
|
// Config helpers
|
|
33
36
|
envString,
|
|
34
37
|
// Formatting
|
|
35
38
|
escapeHtml,
|
|
39
|
+
filterByProfile,
|
|
40
|
+
filterUnknownPlanningVisualsTypeDiagnostics,
|
|
36
41
|
filterUnknownSlidesVisualsTypeDiagnostics,
|
|
37
42
|
// YAML/Config parsing
|
|
43
|
+
loadDataBindings,
|
|
38
44
|
loadSiteConfig,
|
|
45
|
+
mergeFrontmatterWithBody,
|
|
39
46
|
// Markdown utilities
|
|
47
|
+
pagePathForData,
|
|
40
48
|
parseFrontmatter,
|
|
41
49
|
parseYaml,
|
|
50
|
+
resolveBindings,
|
|
42
51
|
resolveSiteVersion,
|
|
43
52
|
resolveStylesPath,
|
|
53
|
+
runPrebuildHooks,
|
|
44
54
|
type SiteConfig,
|
|
45
55
|
slugify,
|
|
46
56
|
validatePath,
|
|
57
|
+
validatePlanningVisualsFences,
|
|
47
58
|
validateSlidesVisualsFences,
|
|
48
59
|
} from "../src/shared.ts";
|
|
49
60
|
import { generateThemeCSS, getPrismUrls, loadTheme } from "../src/theme.ts";
|
|
@@ -55,6 +66,41 @@ const DEFAULT_NAME = "bundle.html";
|
|
|
55
66
|
let ROOT = process.cwd();
|
|
56
67
|
let OUT_DIR = DEFAULT_OUT;
|
|
57
68
|
let BUNDLE_NAME = DEFAULT_NAME;
|
|
69
|
+
let ACTIVE_PROFILE: string | undefined;
|
|
70
|
+
|
|
71
|
+
async function applyDataBindingsToMarkdown(
|
|
72
|
+
rawMarkdown: string,
|
|
73
|
+
filePath: string,
|
|
74
|
+
config: SiteConfig,
|
|
75
|
+
): Promise<{ frontmatter: Record<string, unknown>; body: string }> {
|
|
76
|
+
const parsed = parseFrontmatter(rawMarkdown);
|
|
77
|
+
const dataRef = typeof parsed.frontmatter.data === "string" ? parsed.frontmatter.data.trim() : "";
|
|
78
|
+
if (!dataRef) return parsed;
|
|
79
|
+
|
|
80
|
+
const pagePath = pagePathForData(ROOT, config.docroot, filePath);
|
|
81
|
+
const bindings = await loadDataBindings(dataRef, pagePath, ROOT, config.docroot, config.dataroot);
|
|
82
|
+
return {
|
|
83
|
+
frontmatter: parsed.frontmatter,
|
|
84
|
+
body: resolveBindings(parsed.body, bindings, pagePath),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function applyDataBindingsForSlides(
|
|
89
|
+
rawMarkdown: string,
|
|
90
|
+
filePath: string,
|
|
91
|
+
config: SiteConfig,
|
|
92
|
+
): Promise<string> {
|
|
93
|
+
const resolved = await applyDataBindingsToMarkdown(rawMarkdown, filePath, config);
|
|
94
|
+
return mergeFrontmatterWithBody(rawMarkdown, resolved.body);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function normalizeMsysPath(p: string): string {
|
|
98
|
+
// Git Bash / MSYS-style paths: /c/Users/... -> C:\Users\...
|
|
99
|
+
if (process.platform !== "win32") return p;
|
|
100
|
+
const m = p.match(/^\/([a-zA-Z])\/(.*)$/);
|
|
101
|
+
if (!m) return p;
|
|
102
|
+
return `${m[1].toUpperCase()}:\\${m[2].replaceAll("/", "\\")}`;
|
|
103
|
+
}
|
|
58
104
|
|
|
59
105
|
// ---------------------------------------------------------------------------
|
|
60
106
|
// CLI argument parsing
|
|
@@ -65,6 +111,7 @@ interface ParsedArgs {
|
|
|
65
111
|
out?: string;
|
|
66
112
|
name?: string;
|
|
67
113
|
raw?: boolean;
|
|
114
|
+
profile?: string;
|
|
68
115
|
}
|
|
69
116
|
|
|
70
117
|
function parseArgs(argv: string[]): ParsedArgs {
|
|
@@ -79,6 +126,9 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
79
126
|
} else if ((arg === "--name" || arg === "-n") && next && !next.startsWith("-")) {
|
|
80
127
|
result.name = next;
|
|
81
128
|
i++;
|
|
129
|
+
} else if (arg === "--profile" && next && !next.startsWith("-")) {
|
|
130
|
+
result.profile = next;
|
|
131
|
+
i++;
|
|
82
132
|
} else if (arg === "--raw") {
|
|
83
133
|
result.raw = true;
|
|
84
134
|
} else if (arg === "--no-raw") {
|
|
@@ -95,6 +145,7 @@ function getConfig(): {
|
|
|
95
145
|
out: string;
|
|
96
146
|
name: string;
|
|
97
147
|
raw: boolean;
|
|
148
|
+
profile?: string;
|
|
98
149
|
} {
|
|
99
150
|
const args = parseArgs(process.argv.slice(2));
|
|
100
151
|
const legacyOut = envString("KITFLY_BUILD_OUT", DEFAULT_OUT);
|
|
@@ -106,6 +157,7 @@ function getConfig(): {
|
|
|
106
157
|
out,
|
|
107
158
|
name: args.name ?? envString("KITFLY_BUNDLE_NAME", DEFAULT_NAME),
|
|
108
159
|
raw,
|
|
160
|
+
profile: args.profile ?? process.env.KITFLY_PROFILE,
|
|
109
161
|
};
|
|
110
162
|
}
|
|
111
163
|
|
|
@@ -305,22 +357,35 @@ function buildBundleNav(files: ContentFile[], config: SiteConfig): string {
|
|
|
305
357
|
return html;
|
|
306
358
|
}
|
|
307
359
|
|
|
308
|
-
async function
|
|
309
|
-
|
|
310
|
-
|
|
360
|
+
async function getFenceValidationFlags(root: string): Promise<{
|
|
361
|
+
slidesVisuals: boolean;
|
|
362
|
+
planningVisuals: boolean;
|
|
363
|
+
}> {
|
|
311
364
|
try {
|
|
312
|
-
const raw = await readFile(join(
|
|
365
|
+
const raw = await readFile(join(root, "kitfly.plugins.yaml"), "utf-8");
|
|
313
366
|
const parsed = parseYaml(raw) as unknown as Record<string, unknown>;
|
|
314
367
|
const enabled = Array.isArray(parsed?.plugins) ? (parsed.plugins as unknown[]) : [];
|
|
315
|
-
|
|
368
|
+
return {
|
|
369
|
+
slidesVisuals: enabled.some((p) => typeof p === "string" && p.startsWith("slides-visuals@")),
|
|
370
|
+
planningVisuals: enabled.some(
|
|
371
|
+
(p) => typeof p === "string" && p.startsWith("planning-visuals@"),
|
|
372
|
+
),
|
|
373
|
+
};
|
|
316
374
|
} catch {
|
|
317
|
-
|
|
375
|
+
return { slidesVisuals: false, planningVisuals: false };
|
|
318
376
|
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function buildSlidesBundleContent(files: ContentFile[], config: SiteConfig): Promise<string> {
|
|
380
|
+
const slides = await collectSlides(files, {
|
|
381
|
+
markdownTransform: (raw, file) => applyDataBindingsForSlides(raw, file.path, config),
|
|
382
|
+
});
|
|
383
|
+
const fenceValidation = await getFenceValidationFlags(ROOT);
|
|
319
384
|
const renderedSlides = await Promise.all(
|
|
320
385
|
slides.map(async (slide, i) => {
|
|
321
386
|
let inner = "";
|
|
322
387
|
if (slide.kind === "markdown") {
|
|
323
|
-
if (
|
|
388
|
+
if (fenceValidation.slidesVisuals) {
|
|
324
389
|
const diagnostics = filterUnknownSlidesVisualsTypeDiagnostics(
|
|
325
390
|
validateSlidesVisualsFences(slide.body),
|
|
326
391
|
);
|
|
@@ -332,6 +397,22 @@ async function buildSlidesBundleContent(files: ContentFile[], config: SiteConfig
|
|
|
332
397
|
throw new Error(`slides-visuals fence contract violations:\n${msg}`);
|
|
333
398
|
}
|
|
334
399
|
}
|
|
400
|
+
if (fenceValidation.planningVisuals) {
|
|
401
|
+
const diagnostics = filterUnknownPlanningVisualsTypeDiagnostics(
|
|
402
|
+
validatePlanningVisualsFences(slide.body),
|
|
403
|
+
);
|
|
404
|
+
if (diagnostics.length) {
|
|
405
|
+
const msg = diagnostics
|
|
406
|
+
.slice(0, 12)
|
|
407
|
+
.map((d) => ` - ${slide.sourcePath}:${d.line} ${d.message}`)
|
|
408
|
+
.join("\n");
|
|
409
|
+
throw new Error(`planning-visuals fence contract violations:\n${msg}`);
|
|
410
|
+
}
|
|
411
|
+
const warnings = collectPlanningVisualsContainmentWarnings(slide.body);
|
|
412
|
+
for (const warning of warnings.slice(0, 12)) {
|
|
413
|
+
console.warn(` ⚠ ${slide.sourcePath}:${warning.line} ${warning.message}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
335
416
|
inner = marked.parse(slide.body) as string;
|
|
336
417
|
} else if (slide.kind === "yaml") {
|
|
337
418
|
inner = `<pre><code class="language-yaml">${escapeHtml(slide.body)}</code></pre>`;
|
|
@@ -376,18 +457,26 @@ function buildBundleSidebarHeader(
|
|
|
376
457
|
config: SiteConfig,
|
|
377
458
|
version: string | undefined,
|
|
378
459
|
brandLogo: string,
|
|
460
|
+
brandLogoDark?: string,
|
|
379
461
|
): string {
|
|
380
462
|
const brandTarget = config.brand.external ? ' target="_blank" rel="noopener"' : "";
|
|
381
463
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
382
464
|
const productHref = config.home ? "#home" : "#";
|
|
383
465
|
const versionLabel = version ? `v${version}` : "unversioned";
|
|
384
466
|
const brandInitial = escapeHtml(config.brand.name.trim().charAt(0).toUpperCase() || "K");
|
|
467
|
+
const brandLogoHtml = buildLogoImgHtml({
|
|
468
|
+
logo: brandLogo,
|
|
469
|
+
logoDark: brandLogoDark,
|
|
470
|
+
alt: config.brand.name,
|
|
471
|
+
className: "logo-img",
|
|
472
|
+
onerrorFallback: true,
|
|
473
|
+
});
|
|
385
474
|
|
|
386
475
|
return `
|
|
387
476
|
<div class="sidebar-header">
|
|
388
477
|
<div class="logo ${logoClass}">
|
|
389
478
|
<a href="${config.brand.url}" class="logo-icon" data-initial="${brandInitial}"${brandTarget}>
|
|
390
|
-
|
|
479
|
+
${brandLogoHtml}
|
|
391
480
|
</a>
|
|
392
481
|
<span class="logo-text">
|
|
393
482
|
<a href="${config.brand.url}" class="brand"${brandTarget}>${config.brand.name}</a>
|
|
@@ -427,6 +516,18 @@ async function inlineBrandAsset(assetPath: string): Promise<string> {
|
|
|
427
516
|
/* continue */
|
|
428
517
|
}
|
|
429
518
|
}
|
|
519
|
+
|
|
520
|
+
// Support any safe site-root-relative path (e.g., logos/footer.png).
|
|
521
|
+
const siteRootPath = validatePath(ROOT, ".", clean, false);
|
|
522
|
+
if (siteRootPath) {
|
|
523
|
+
try {
|
|
524
|
+
await stat(siteRootPath);
|
|
525
|
+
const uri = await fileToDataUri(siteRootPath);
|
|
526
|
+
if (uri) return uri;
|
|
527
|
+
} catch {
|
|
528
|
+
/* continue */
|
|
529
|
+
}
|
|
530
|
+
}
|
|
430
531
|
return assetPath;
|
|
431
532
|
}
|
|
432
533
|
|
|
@@ -466,12 +567,26 @@ async function bundle() {
|
|
|
466
567
|
|
|
467
568
|
const config = await loadSiteConfig(ROOT, "Documentation");
|
|
468
569
|
console.log(` ✓ Loaded config: "${config.title}" (${config.sections.length} sections)`);
|
|
570
|
+
if (config.prebuild?.length) {
|
|
571
|
+
await runPrebuildHooks(
|
|
572
|
+
config.prebuild,
|
|
573
|
+
ROOT,
|
|
574
|
+
"bundle",
|
|
575
|
+
ACTIVE_PROFILE,
|
|
576
|
+
config.dataroot || "data",
|
|
577
|
+
);
|
|
578
|
+
console.log(` ✓ prebuild hooks (${config.prebuild.length})`);
|
|
579
|
+
}
|
|
469
580
|
|
|
470
581
|
const theme = await loadTheme(ROOT);
|
|
471
582
|
console.log(` ✓ Loaded theme: "${theme.name || "default"}"`);
|
|
472
583
|
const prismUrls = getPrismUrls(theme);
|
|
473
584
|
|
|
474
|
-
const files = await
|
|
585
|
+
const files = await filterByProfile(
|
|
586
|
+
await collectFiles(ROOT, config),
|
|
587
|
+
ACTIVE_PROFILE,
|
|
588
|
+
config.profiles,
|
|
589
|
+
);
|
|
475
590
|
if (files.length === 0) {
|
|
476
591
|
console.error("No content files found. Cannot create bundle.");
|
|
477
592
|
process.exit(1);
|
|
@@ -514,9 +629,10 @@ async function bundle() {
|
|
|
514
629
|
section: slide.section,
|
|
515
630
|
});
|
|
516
631
|
}
|
|
517
|
-
navHtml =
|
|
632
|
+
navHtml = buildSlideNavHierarchical(slides, config, "slide-1");
|
|
518
633
|
contentHtml = await buildSlidesBundleContent(files, config);
|
|
519
634
|
} else {
|
|
635
|
+
const fenceValidation = await getFenceValidationFlags(ROOT);
|
|
520
636
|
// Build navigation and content sections
|
|
521
637
|
const sections: Map<string, { id: string; title: string; html: string }[]> = new Map();
|
|
522
638
|
|
|
@@ -527,7 +643,27 @@ async function bundle() {
|
|
|
527
643
|
try {
|
|
528
644
|
await stat(homePath);
|
|
529
645
|
const content = await readFile(homePath, "utf-8");
|
|
530
|
-
const { frontmatter, body } =
|
|
646
|
+
const { frontmatter, body } = await applyDataBindingsToMarkdown(
|
|
647
|
+
content,
|
|
648
|
+
homePath,
|
|
649
|
+
config,
|
|
650
|
+
);
|
|
651
|
+
if (fenceValidation.planningVisuals) {
|
|
652
|
+
const diagnostics = filterUnknownPlanningVisualsTypeDiagnostics(
|
|
653
|
+
validatePlanningVisualsFences(body),
|
|
654
|
+
);
|
|
655
|
+
if (diagnostics.length) {
|
|
656
|
+
const msg = diagnostics
|
|
657
|
+
.slice(0, 12)
|
|
658
|
+
.map((d) => ` - ${homePath}:${d.line} ${d.message}`)
|
|
659
|
+
.join("\n");
|
|
660
|
+
throw new Error(`planning-visuals fence contract violations:\n${msg}`);
|
|
661
|
+
}
|
|
662
|
+
const warnings = collectPlanningVisualsContainmentWarnings(body);
|
|
663
|
+
for (const warning of warnings.slice(0, 12)) {
|
|
664
|
+
console.warn(` ⚠ ${homePath}:${warning.line} ${warning.message}`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
531
667
|
const title = (frontmatter.title as string) || "Home";
|
|
532
668
|
let htmlContent = marked.parse(body) as string;
|
|
533
669
|
htmlContent = await inlineLocalImages(htmlContent, config);
|
|
@@ -558,13 +694,29 @@ async function bundle() {
|
|
|
558
694
|
}
|
|
559
695
|
htmlContent = `<pre><code class="language-json">${escapeHtml(prettyJson)}</code></pre>`;
|
|
560
696
|
} else {
|
|
561
|
-
const { frontmatter, body } =
|
|
697
|
+
const { frontmatter, body } = await applyDataBindingsToMarkdown(content, file.path, config);
|
|
562
698
|
if (frontmatter.title) {
|
|
563
699
|
title = frontmatter.title as string;
|
|
564
700
|
}
|
|
565
701
|
if (frontmatter.description) {
|
|
566
702
|
description = frontmatter.description as string;
|
|
567
703
|
}
|
|
704
|
+
if (fenceValidation.planningVisuals) {
|
|
705
|
+
const diagnostics = filterUnknownPlanningVisualsTypeDiagnostics(
|
|
706
|
+
validatePlanningVisualsFences(body),
|
|
707
|
+
);
|
|
708
|
+
if (diagnostics.length) {
|
|
709
|
+
const msg = diagnostics
|
|
710
|
+
.slice(0, 12)
|
|
711
|
+
.map((d) => ` - ${file.path}:${d.line} ${d.message}`)
|
|
712
|
+
.join("\n");
|
|
713
|
+
throw new Error(`planning-visuals fence contract violations:\n${msg}`);
|
|
714
|
+
}
|
|
715
|
+
const warnings = collectPlanningVisualsContainmentWarnings(body);
|
|
716
|
+
for (const warning of warnings.slice(0, 12)) {
|
|
717
|
+
console.warn(` ⚠ ${file.path}:${warning.line} ${warning.message}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
568
720
|
htmlContent = marked.parse(body) as string;
|
|
569
721
|
|
|
570
722
|
// Collect raw markdown for AI accessibility
|
|
@@ -617,7 +769,19 @@ async function bundle() {
|
|
|
617
769
|
|
|
618
770
|
// Inline brand assets for self-contained bundle
|
|
619
771
|
const brandLogo = await inlineBrandAsset(config.brand.logo || "assets/brand/logo.png");
|
|
772
|
+
const brandLogoDark =
|
|
773
|
+
typeof config.brand.logoDark === "string"
|
|
774
|
+
? await inlineBrandAsset(config.brand.logoDark)
|
|
775
|
+
: undefined;
|
|
620
776
|
const brandFavicon = await inlineBrandAsset(config.brand.favicon || "assets/brand/favicon.png");
|
|
777
|
+
const footerLogo =
|
|
778
|
+
typeof config.footer?.logo === "string"
|
|
779
|
+
? await inlineBrandAsset(config.footer.logo)
|
|
780
|
+
: undefined;
|
|
781
|
+
const footerLogoDark =
|
|
782
|
+
typeof config.footer?.logoDark === "string"
|
|
783
|
+
? await inlineBrandAsset(config.footer.logoDark)
|
|
784
|
+
: undefined;
|
|
621
785
|
|
|
622
786
|
// Build the complete HTML document
|
|
623
787
|
const html = `<!DOCTYPE html>
|
|
@@ -672,7 +836,7 @@ ${assets.prismCssDark}
|
|
|
672
836
|
<body class="${config.mode === "slides" ? "mode-slides" : "mode-docs"}">
|
|
673
837
|
<div class="layout">
|
|
674
838
|
<nav class="sidebar">
|
|
675
|
-
${buildBundleSidebarHeader(config, version, brandLogo)}
|
|
839
|
+
${buildBundleSidebarHeader(config, version, brandLogo, brandLogoDark)}
|
|
676
840
|
<div class="sidebar-nav">
|
|
677
841
|
${navHtml}
|
|
678
842
|
</div>
|
|
@@ -683,7 +847,7 @@ ${buildBundleSidebarHeader(config, version, brandLogo)}
|
|
|
683
847
|
</article>
|
|
684
848
|
</main>
|
|
685
849
|
</div>
|
|
686
|
-
${buildBundleFooter(version, config)}
|
|
850
|
+
${buildBundleFooter(version, config, footerLogo, footerLogoDark)}
|
|
687
851
|
<script>
|
|
688
852
|
${assets.prismCore}
|
|
689
853
|
</script>
|
|
@@ -749,6 +913,9 @@ ${assets.mermaid}
|
|
|
749
913
|
if (window.reinitMermaid) {
|
|
750
914
|
window.reinitMermaid();
|
|
751
915
|
}
|
|
916
|
+
if (window.reinitCharts) {
|
|
917
|
+
window.reinitCharts();
|
|
918
|
+
}
|
|
752
919
|
}
|
|
753
920
|
|
|
754
921
|
// Slides mode hash routing
|
|
@@ -864,7 +1031,7 @@ ${JSON.stringify(
|
|
|
864
1031
|
</html>`;
|
|
865
1032
|
|
|
866
1033
|
// Write the bundle
|
|
867
|
-
const outDir =
|
|
1034
|
+
const outDir = resolve(ROOT, normalizeMsysPath(OUT_DIR));
|
|
868
1035
|
await mkdir(outDir, { recursive: true });
|
|
869
1036
|
const bundlePath = join(outDir, BUNDLE_NAME);
|
|
870
1037
|
await writeFile(bundlePath, html);
|
|
@@ -881,6 +1048,7 @@ export interface BundleOptions {
|
|
|
881
1048
|
out?: string;
|
|
882
1049
|
name?: string;
|
|
883
1050
|
raw?: boolean; // Include raw markdown in bundle (default: true)
|
|
1051
|
+
profile?: string;
|
|
884
1052
|
}
|
|
885
1053
|
|
|
886
1054
|
let INCLUDE_RAW = true;
|
|
@@ -910,6 +1078,7 @@ export async function bundleSite(options: BundleOptions = {}) {
|
|
|
910
1078
|
if (options.raw === false) {
|
|
911
1079
|
INCLUDE_RAW = false;
|
|
912
1080
|
}
|
|
1081
|
+
ACTIVE_PROFILE = options.profile;
|
|
913
1082
|
await bundle();
|
|
914
1083
|
}
|
|
915
1084
|
|
|
@@ -922,6 +1091,7 @@ Usage: bun run bundle [folder] [options]
|
|
|
922
1091
|
Options:
|
|
923
1092
|
-o, --out <dir> Output directory [env: KITFLY_BUNDLE_OUT] [default: ${DEFAULT_OUT}]
|
|
924
1093
|
-n, --name <file> Bundle filename [env: KITFLY_BUNDLE_NAME] [default: ${DEFAULT_NAME}]
|
|
1094
|
+
--profile <name> Active content profile [env: KITFLY_PROFILE]
|
|
925
1095
|
--raw Include raw markdown in bundle [env: KITFLY_BUNDLE_RAW] [default: true]
|
|
926
1096
|
--no-raw Don't include raw markdown
|
|
927
1097
|
--help Show this help message
|
|
@@ -943,5 +1113,6 @@ Examples:
|
|
|
943
1113
|
out: cfg.out,
|
|
944
1114
|
name: cfg.name,
|
|
945
1115
|
raw: cfg.raw,
|
|
1116
|
+
profile: cfg.profile,
|
|
946
1117
|
}).catch(console.error);
|
|
947
1118
|
}
|