kitfly 0.1.2 → 0.2.1
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 +46 -0
- package/README.md +63 -16
- package/VERSION +1 -1
- package/dist/_raw/content/deployment/preflight.md +134 -0
- package/dist/_raw/content/deployment/recipes/aws-s3.md +128 -0
- package/dist/_raw/content/deployment/recipes/cloudflare-pages.md +73 -0
- package/dist/_raw/content/deployment/recipes/cloudflare-r2.md +156 -0
- package/dist/_raw/content/deployment/recipes/fly-io.md +57 -0
- package/dist/_raw/content/deployment/recipes/github-pages.md +112 -0
- package/dist/_raw/content/deployment/recipes/netlify.md +99 -0
- package/dist/_raw/content/deployment/recipes/vercel.md +88 -0
- package/dist/_raw/content/deployment/secrets-and-env-vars.md +75 -0
- package/dist/_raw/content/deployment.md +128 -0
- package/dist/_raw/content/guide/approaches.md +182 -0
- package/dist/_raw/content/guide/features.md +121 -0
- package/dist/_raw/content/guide/getting-started.md +112 -0
- package/dist/_raw/content/guide/kitfly-overview.md +209 -0
- package/dist/_raw/content/reference/configuration.md +259 -0
- package/dist/_raw/content/reference/design-catalog.md +167 -0
- package/dist/_raw/content/reference/environment-variables.md +66 -0
- package/dist/_raw/content/reference/glossary.md +92 -0
- package/dist/_raw/content/reference/key-concepts.md +118 -0
- package/dist/_raw/content/reference/plugins.md +220 -0
- package/dist/_raw/content/reference/slides-authoring-guidelines.md +129 -0
- package/dist/_raw/content/reference/structure.md +166 -0
- package/dist/_raw/content/reference.md +20 -0
- package/dist/_raw/content/templates/crucible.md +192 -0
- package/dist/_raw/content/templates/handbook.md +83 -0
- package/dist/_raw/content/templates/minimal.md +138 -0
- package/dist/_raw/content/templates/overview.md +187 -0
- package/dist/_raw/content/templates/pipeline.md +151 -0
- package/dist/_raw/content/templates/productbook.md +187 -0
- package/dist/_raw/content/templates/runbook.md +193 -0
- package/dist/_raw/content/templates/servicebook.md +163 -0
- package/dist/_raw/docs/decisions/ADR-0001-minimalist-site-code.md +118 -0
- package/dist/_raw/docs/decisions/ADR-0002-ai-accessibility.md +153 -0
- package/dist/_raw/docs/decisions/ADR-0003-single-file-bundle.md +93 -0
- package/dist/_raw/docs/decisions/ADR-0004-bun-runtime.md +98 -0
- package/dist/_raw/docs/decisions/ADR-0005-plugin-contract-and-distribution.md +110 -0
- package/dist/_raw/docs/decisions/DDR-0001-viewport-locked-layout.md +111 -0
- package/dist/_raw/docs/decisions/DDR-0002-theme-system.md +131 -0
- package/dist/_raw/docs/decisions/DDR-0003-bounded-logo-slot.md +106 -0
- package/dist/_raw/docs/decisions/DDR-0004-slides-rendering-model.md +113 -0
- package/dist/_raw/docs/decisions/DDR-0005-deterministic-layout-boundary.md +107 -0
- package/dist/_raw/docs/userguide/cli/build.md +85 -0
- package/dist/_raw/docs/userguide/cli/bundle.md +81 -0
- package/dist/_raw/docs/userguide/cli/dev.md +92 -0
- package/dist/_raw/docs/userguide/cli/init.md +116 -0
- package/dist/_raw/docs/userguide/cli/servers.md +69 -0
- package/dist/_raw/docs/userguide/cli/stop.md +76 -0
- package/dist/_raw/docs/userguide/cli/update.md +78 -0
- package/dist/_raw/docs/userguide/cli/version.md +65 -0
- package/dist/_raw/docs/userguide/cli.md +34 -0
- package/dist/_raw/docs/userguide/sharing.md +94 -0
- package/dist/_raw/schemas/plugin-schemas-notes.md +71 -0
- package/dist/_raw/schemas.md +42 -0
- package/dist/assets/brand/kitfly-favicon-32.png +0 -0
- package/dist/assets/brand/kitfly-icon-64.png +0 -0
- package/dist/assets/brand/kitfly-logo-128.png +0 -0
- package/dist/assets/brand/kitfly-logo-512.png +0 -0
- package/dist/assets/brand/kitfly-logo.svg +12132 -0
- package/dist/assets/brand/kitfly-neon-128.png +0 -0
- package/dist/assets/brand/kitfly-neon-192.png +0 -0
- package/dist/assets/brand/kitfly-neon-256.png +0 -0
- package/dist/assets/brand/kitfly-neon.png +0 -0
- package/dist/assets/brand/palette.md +75 -0
- package/dist/content/deployment/index.html +11 -0
- package/dist/content/deployment/preflight.html +418 -0
- package/dist/content/deployment/recipes/aws-s3.html +421 -0
- package/dist/content/deployment/recipes/cloudflare-pages.html +372 -0
- package/dist/content/deployment/recipes/cloudflare-r2.html +443 -0
- package/dist/content/deployment/recipes/fly-io.html +356 -0
- package/dist/content/deployment/recipes/github-pages.html +414 -0
- package/dist/content/deployment/recipes/index.html +11 -0
- package/dist/content/deployment/recipes/netlify.html +394 -0
- package/dist/content/deployment/recipes/vercel.html +382 -0
- package/dist/content/deployment/secrets-and-env-vars.html +380 -0
- package/dist/content/deployment.html +426 -0
- package/dist/content/guide/approaches.html +501 -0
- package/dist/content/guide/features.html +436 -0
- package/dist/content/guide/getting-started.html +403 -0
- package/dist/content/guide/index.html +11 -0
- package/dist/content/guide/kitfly-overview.html +544 -0
- package/dist/content/index.html +11 -0
- package/dist/content/reference/configuration.html +580 -0
- package/dist/content/reference/design-catalog.html +449 -0
- package/dist/content/reference/environment-variables.html +367 -0
- package/dist/content/reference/glossary.html +368 -0
- package/dist/content/reference/index.html +11 -0
- package/dist/content/reference/key-concepts.html +399 -0
- package/dist/content/reference/plugins.html +491 -0
- package/dist/content/reference/slides-authoring-guidelines.html +418 -0
- package/dist/content/reference/structure.html +463 -0
- package/dist/content/reference.html +335 -0
- package/dist/content/templates/crucible.html +546 -0
- package/dist/content/templates/handbook.html +405 -0
- package/dist/content/templates/index.html +11 -0
- package/dist/content/templates/minimal.html +447 -0
- package/dist/content/templates/overview.html +558 -0
- package/dist/content/templates/pipeline.html +494 -0
- package/dist/content/templates/productbook.html +540 -0
- package/dist/content/templates/runbook.html +543 -0
- package/dist/content/templates/servicebook.html +523 -0
- package/dist/content-index.json +549 -0
- package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +491 -0
- package/dist/docs/decisions/ADR-0002-ai-accessibility.html +434 -0
- package/dist/docs/decisions/ADR-0003-single-file-bundle.html +412 -0
- package/dist/docs/decisions/ADR-0004-bun-runtime.html +409 -0
- package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +402 -0
- package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +459 -0
- package/dist/docs/decisions/DDR-0002-theme-system.html +452 -0
- package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +423 -0
- package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +399 -0
- package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +422 -0
- package/dist/docs/decisions/index.html +11 -0
- package/dist/docs/userguide/cli/build.html +408 -0
- package/dist/docs/userguide/cli/bundle.html +419 -0
- package/dist/docs/userguide/cli/dev.html +428 -0
- package/dist/docs/userguide/cli/index.html +11 -0
- package/dist/docs/userguide/cli/init.html +436 -0
- package/dist/docs/userguide/cli/servers.html +393 -0
- package/dist/docs/userguide/cli/stop.html +408 -0
- package/dist/docs/userguide/cli/update.html +406 -0
- package/dist/docs/userguide/cli/version.html +406 -0
- package/dist/docs/userguide/cli.html +386 -0
- package/dist/docs/userguide/index.html +11 -0
- package/dist/docs/userguide/sharing.html +465 -0
- package/dist/index.html +387 -0
- package/dist/llms.txt +18 -0
- package/dist/provenance.json +7 -0
- package/dist/schemas/index.html +11 -0
- package/dist/schemas/plugin-registry.schema.html +327 -0
- package/dist/schemas/plugin-schemas-notes.html +364 -0
- package/dist/schemas/plugin.schema.html +327 -0
- package/dist/schemas/plugins.schema.html +327 -0
- package/dist/schemas/v0/common.schema.html +386 -0
- package/dist/schemas/v0/index.html +11 -0
- package/dist/schemas/v0/plugin-registry.schema.html +547 -0
- package/dist/schemas/v0/plugin.schema.html +497 -0
- package/dist/schemas/v0/plugins.schema.html +406 -0
- package/dist/schemas/v0/site.schema.html +541 -0
- package/dist/schemas/v0/theme.schema.html +615 -0
- package/dist/schemas.html +351 -0
- package/dist/styles.css +1262 -0
- package/package.json +4 -2
- package/plugins-dist/callouts.css +32 -0
- package/plugins-dist/callouts.js +46 -0
- package/plugins-dist/slides-visuals.css +390 -0
- package/plugins-dist/slides-visuals.js +689 -0
- package/registry/plugins.yaml +35 -0
- package/schemas/README.md +10 -0
- package/schemas/plugin-registry.schema.json +5 -0
- package/schemas/plugin-schemas-notes.md +71 -0
- package/schemas/plugin.schema.json +5 -0
- package/schemas/plugins.schema.json +5 -0
- package/schemas/v0/common.schema.json +64 -0
- package/schemas/v0/plugin-registry.schema.json +225 -0
- package/schemas/v0/plugin.schema.json +175 -0
- package/schemas/v0/plugins.schema.json +84 -0
- package/schemas/v0/site.schema.json +56 -9
- package/schemas/v0/theme.schema.json +105 -22
- package/scripts/build.ts +158 -3
- package/scripts/bundle.ts +261 -95
- package/scripts/dev.ts +301 -11
- package/src/__tests__/build.test.ts +220 -1
- package/src/__tests__/bundle.test.ts +31 -0
- package/src/__tests__/cli.test.ts +14 -3
- package/src/__tests__/dev-plugin-errors.test.ts +20 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/bad-list-indent.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/blank-line.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/compare-object-items.md +9 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-branching-no-source.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-converging-no-target.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/indented-fence.md +4 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/staircase-empty-steps.md +3 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/stat-grid-missing-fields.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/timeline-horizontal-no-events.md +2 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/unknown-type.md +3 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/compare.md +10 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/comparison-table.md +14 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching-no-split.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging-no-merge.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/funnel.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/kpi.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/layer-cake.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/pyramid.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/quadrant-grid.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/scorecard.md +13 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase-down.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/stat-grid.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-horizontal.md +9 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-vertical.md +10 -0
- package/src/__tests__/init.test.ts +35 -0
- package/src/__tests__/plugin-loader.test.ts +221 -0
- package/src/__tests__/shared.test.ts +451 -0
- package/src/__tests__/slides-visuals-fence-contract.test.ts +28 -0
- package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +147 -0
- package/src/__tests__/styles.test.ts +35 -0
- package/src/cli.ts +9 -4
- package/src/plugin-loader.ts +245 -0
- package/src/shared.ts +650 -7
- package/src/site/styles.css +331 -0
- package/src/site/template.html +66 -5
- package/src/templates/deck.ts +186 -0
- package/src/templates/driver.ts +11 -1
- package/src/templates/minimal.ts +1 -0
package/scripts/bundle.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Usage: bun run bundle [folder] [options]
|
|
5
5
|
*
|
|
6
6
|
* Options:
|
|
7
|
-
* -o, --out <dir> Output directory [env:
|
|
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
|
-
* --raw Include raw markdown in bundle [env:
|
|
9
|
+
* --raw Include raw markdown in bundle [env: KITFLY_BUNDLE_RAW] [default: true]
|
|
10
10
|
* --no-raw Don't include raw markdown
|
|
11
11
|
* --help Show help message
|
|
12
12
|
*
|
|
13
|
-
* Creates
|
|
13
|
+
* Creates bundles/bundle.html - a single file containing all content,
|
|
14
14
|
* styles, and scripts for offline viewing.
|
|
15
15
|
*/
|
|
16
16
|
|
|
@@ -18,32 +18,38 @@ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
|
18
18
|
import { basename, extname, join, resolve } from "node:path";
|
|
19
19
|
import { marked, Renderer } from "marked";
|
|
20
20
|
import { ENGINE_ASSETS_DIR } from "../src/engine.ts";
|
|
21
|
+
import { loadPluginInjections } from "../src/plugin-loader.ts";
|
|
21
22
|
import {
|
|
22
23
|
buildBundleFooter,
|
|
23
|
-
// Navigation/template building
|
|
24
24
|
buildSectionNav,
|
|
25
|
+
// Navigation/template building
|
|
26
|
+
buildSlideNav,
|
|
25
27
|
// Types
|
|
26
28
|
type ContentFile,
|
|
27
29
|
collectFiles,
|
|
30
|
+
collectSlides,
|
|
28
31
|
envBool,
|
|
29
32
|
// Config helpers
|
|
30
33
|
envString,
|
|
31
34
|
// Formatting
|
|
32
35
|
escapeHtml,
|
|
36
|
+
filterUnknownSlidesVisualsTypeDiagnostics,
|
|
33
37
|
// YAML/Config parsing
|
|
34
38
|
loadSiteConfig,
|
|
35
39
|
// Markdown utilities
|
|
36
40
|
parseFrontmatter,
|
|
41
|
+
parseYaml,
|
|
37
42
|
resolveSiteVersion,
|
|
38
43
|
resolveStylesPath,
|
|
39
44
|
type SiteConfig,
|
|
40
45
|
slugify,
|
|
41
46
|
validatePath,
|
|
47
|
+
validateSlidesVisualsFences,
|
|
42
48
|
} from "../src/shared.ts";
|
|
43
49
|
import { generateThemeCSS, getPrismUrls, loadTheme } from "../src/theme.ts";
|
|
44
50
|
|
|
45
51
|
// Defaults
|
|
46
|
-
const DEFAULT_OUT = "
|
|
52
|
+
const DEFAULT_OUT = "bundles";
|
|
47
53
|
const DEFAULT_NAME = "bundle.html";
|
|
48
54
|
|
|
49
55
|
let ROOT = process.cwd();
|
|
@@ -91,11 +97,15 @@ function getConfig(): {
|
|
|
91
97
|
raw: boolean;
|
|
92
98
|
} {
|
|
93
99
|
const args = parseArgs(process.argv.slice(2));
|
|
100
|
+
const legacyOut = envString("KITFLY_BUILD_OUT", DEFAULT_OUT);
|
|
101
|
+
const out = args.out ?? envString("KITFLY_BUNDLE_OUT", legacyOut);
|
|
102
|
+
const legacyRaw = envBool("KITFLY_BUILD_RAW", true);
|
|
103
|
+
const raw = args.raw ?? envBool("KITFLY_BUNDLE_RAW", legacyRaw);
|
|
94
104
|
return {
|
|
95
105
|
folder: args.folder,
|
|
96
|
-
out
|
|
106
|
+
out,
|
|
97
107
|
name: args.name ?? envString("KITFLY_BUNDLE_NAME", DEFAULT_NAME),
|
|
98
|
-
raw
|
|
108
|
+
raw,
|
|
99
109
|
};
|
|
100
110
|
}
|
|
101
111
|
|
|
@@ -295,6 +305,73 @@ function buildBundleNav(files: ContentFile[], config: SiteConfig): string {
|
|
|
295
305
|
return html;
|
|
296
306
|
}
|
|
297
307
|
|
|
308
|
+
async function buildSlidesBundleContent(files: ContentFile[], config: SiteConfig): Promise<string> {
|
|
309
|
+
const slides = await collectSlides(files);
|
|
310
|
+
let validateFences = false;
|
|
311
|
+
try {
|
|
312
|
+
const raw = await readFile(join(ROOT, "kitfly.plugins.yaml"), "utf-8");
|
|
313
|
+
const parsed = parseYaml(raw) as unknown as Record<string, unknown>;
|
|
314
|
+
const enabled = Array.isArray(parsed?.plugins) ? (parsed.plugins as unknown[]) : [];
|
|
315
|
+
validateFences = enabled.some((p) => typeof p === "string" && p.startsWith("slides-visuals@"));
|
|
316
|
+
} catch {
|
|
317
|
+
// no config, skip
|
|
318
|
+
}
|
|
319
|
+
const renderedSlides = await Promise.all(
|
|
320
|
+
slides.map(async (slide, i) => {
|
|
321
|
+
let inner = "";
|
|
322
|
+
if (slide.kind === "markdown") {
|
|
323
|
+
if (validateFences) {
|
|
324
|
+
const diagnostics = filterUnknownSlidesVisualsTypeDiagnostics(
|
|
325
|
+
validateSlidesVisualsFences(slide.body),
|
|
326
|
+
);
|
|
327
|
+
if (diagnostics.length) {
|
|
328
|
+
const msg = diagnostics
|
|
329
|
+
.slice(0, 12)
|
|
330
|
+
.map((d) => ` - ${slide.sourcePath}:${d.line} ${d.message}`)
|
|
331
|
+
.join("\n");
|
|
332
|
+
throw new Error(`slides-visuals fence contract violations:\n${msg}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
inner = marked.parse(slide.body) as string;
|
|
336
|
+
} else if (slide.kind === "yaml") {
|
|
337
|
+
inner = `<pre><code class="language-yaml">${escapeHtml(slide.body)}</code></pre>`;
|
|
338
|
+
} else {
|
|
339
|
+
let prettyJson = slide.body;
|
|
340
|
+
try {
|
|
341
|
+
prettyJson = JSON.stringify(JSON.parse(slide.body), null, 2);
|
|
342
|
+
} catch {
|
|
343
|
+
// Keep original text
|
|
344
|
+
}
|
|
345
|
+
inner = `<pre><code class="language-json">${escapeHtml(prettyJson)}</code></pre>`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
inner = await inlineLocalImages(inner, config);
|
|
349
|
+
inner = rewriteContentLinks(inner, files, slide.sourceUrlPath, config.docroot);
|
|
350
|
+
|
|
351
|
+
const activeClass = i === 0 ? " active" : "";
|
|
352
|
+
const classToken = slide.className ? ` ${slide.className}` : "";
|
|
353
|
+
return `<section id="${slide.id}" class="slide${classToken}${activeClass}" data-slide-index="${i}">${inner}</section>`;
|
|
354
|
+
}),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return `
|
|
358
|
+
<div class="slides-shell" style="--slide-aspect: ${config.aspect || "16/9"}">
|
|
359
|
+
<div class="slide-viewport">
|
|
360
|
+
<div class="slide-frame">
|
|
361
|
+
${renderedSlides.join("\n")}
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="slide-nav" aria-label="Slide navigation">
|
|
365
|
+
<button class="slide-prev" type="button" aria-label="Previous slide">Prev</button>
|
|
366
|
+
<span class="slide-counter">1 / ${slides.length}</span>
|
|
367
|
+
<button class="slide-next" type="button" aria-label="Next slide">Next</button>
|
|
368
|
+
<div class="slide-progress" role="presentation">
|
|
369
|
+
<span class="slide-progress-bar" style="width: ${(1 / slides.length) * 100}%"></span>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
</div>`;
|
|
373
|
+
}
|
|
374
|
+
|
|
298
375
|
function buildBundleSidebarHeader(
|
|
299
376
|
config: SiteConfig,
|
|
300
377
|
version: string | undefined,
|
|
@@ -304,12 +381,13 @@ function buildBundleSidebarHeader(
|
|
|
304
381
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
305
382
|
const productHref = config.home ? "#home" : "#";
|
|
306
383
|
const versionLabel = version ? `v${version}` : "unversioned";
|
|
384
|
+
const brandInitial = escapeHtml(config.brand.name.trim().charAt(0).toUpperCase() || "K");
|
|
307
385
|
|
|
308
386
|
return `
|
|
309
387
|
<div class="sidebar-header">
|
|
310
388
|
<div class="logo ${logoClass}">
|
|
311
|
-
<a href="${config.brand.url}" class="logo-icon"${brandTarget}>
|
|
312
|
-
<img src="${brandLogo}" alt="${config.brand.name}" class="logo-img">
|
|
389
|
+
<a href="${config.brand.url}" class="logo-icon" data-initial="${brandInitial}"${brandTarget}>
|
|
390
|
+
<img src="${brandLogo}" alt="${config.brand.name}" class="logo-img" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')">
|
|
313
391
|
</a>
|
|
314
392
|
<span class="logo-text">
|
|
315
393
|
<a href="${config.brand.url}" class="brand"${brandTarget}>${config.brand.name}</a>
|
|
@@ -410,29 +488,6 @@ async function bundle() {
|
|
|
410
488
|
// Resolve site version (site.yaml version, then git tag)
|
|
411
489
|
const version = await resolveSiteVersion(ROOT, config.version);
|
|
412
490
|
|
|
413
|
-
// Build navigation and content sections
|
|
414
|
-
const sections: Map<string, { id: string; title: string; html: string }[]> = new Map();
|
|
415
|
-
|
|
416
|
-
// Add home page as first item if specified
|
|
417
|
-
if (config.home) {
|
|
418
|
-
const homePath = validatePath(ROOT, config.docroot, config.home);
|
|
419
|
-
if (homePath) {
|
|
420
|
-
try {
|
|
421
|
-
await stat(homePath);
|
|
422
|
-
const content = await readFile(homePath, "utf-8");
|
|
423
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
424
|
-
const title = (frontmatter.title as string) || "Home";
|
|
425
|
-
let htmlContent = marked.parse(body) as string;
|
|
426
|
-
htmlContent = await inlineLocalImages(htmlContent, config);
|
|
427
|
-
htmlContent = rewriteContentLinks(htmlContent, files, undefined, config.docroot);
|
|
428
|
-
sections.set("Home", [{ id: "home", title, html: htmlContent }]);
|
|
429
|
-
console.log(` ✓ Added home page: ${config.home}`);
|
|
430
|
-
} catch {
|
|
431
|
-
console.warn(` ⚠ Home page ${config.home} not found`);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
491
|
// Collect page metadata and raw content for AI accessibility
|
|
437
492
|
const pageIndex: {
|
|
438
493
|
path: string;
|
|
@@ -441,77 +496,124 @@ async function bundle() {
|
|
|
441
496
|
description?: string;
|
|
442
497
|
}[] = [];
|
|
443
498
|
const rawMarkdown: { path: string; content: string }[] = [];
|
|
499
|
+
let navHtml = "";
|
|
500
|
+
let contentHtml = "";
|
|
444
501
|
|
|
445
|
-
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (file.path.endsWith(".yaml")) {
|
|
452
|
-
htmlContent = `<pre><code class="language-yaml">${escapeHtml(content)}</code></pre>`;
|
|
453
|
-
} else if (file.path.endsWith(".json")) {
|
|
454
|
-
// Render JSON as code block (pretty-printed)
|
|
455
|
-
let prettyJson = content;
|
|
456
|
-
try {
|
|
457
|
-
prettyJson = JSON.stringify(JSON.parse(content), null, 2);
|
|
458
|
-
} catch {
|
|
459
|
-
// Use original if not valid JSON
|
|
460
|
-
}
|
|
461
|
-
htmlContent = `<pre><code class="language-json">${escapeHtml(prettyJson)}</code></pre>`;
|
|
462
|
-
} else {
|
|
463
|
-
const { frontmatter, body } = parseFrontmatter(content);
|
|
464
|
-
if (frontmatter.title) {
|
|
465
|
-
title = frontmatter.title as string;
|
|
502
|
+
if (config.mode === "slides") {
|
|
503
|
+
const slides = await collectSlides(files);
|
|
504
|
+
for (const file of files) {
|
|
505
|
+
const content = await readFile(file.path, "utf-8");
|
|
506
|
+
if (INCLUDE_RAW && file.path.endsWith(".md")) {
|
|
507
|
+
rawMarkdown.push({ path: file.urlPath, content });
|
|
466
508
|
}
|
|
467
|
-
|
|
468
|
-
|
|
509
|
+
}
|
|
510
|
+
for (const slide of slides) {
|
|
511
|
+
pageIndex.push({
|
|
512
|
+
path: slide.id,
|
|
513
|
+
title: slide.title,
|
|
514
|
+
section: slide.section,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
navHtml = buildSlideNav(slides, config, "slide-1");
|
|
518
|
+
contentHtml = await buildSlidesBundleContent(files, config);
|
|
519
|
+
} else {
|
|
520
|
+
// Build navigation and content sections
|
|
521
|
+
const sections: Map<string, { id: string; title: string; html: string }[]> = new Map();
|
|
522
|
+
|
|
523
|
+
// Add home page as first item if specified
|
|
524
|
+
if (config.home) {
|
|
525
|
+
const homePath = validatePath(ROOT, config.docroot, config.home);
|
|
526
|
+
if (homePath) {
|
|
527
|
+
try {
|
|
528
|
+
await stat(homePath);
|
|
529
|
+
const content = await readFile(homePath, "utf-8");
|
|
530
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
531
|
+
const title = (frontmatter.title as string) || "Home";
|
|
532
|
+
let htmlContent = marked.parse(body) as string;
|
|
533
|
+
htmlContent = await inlineLocalImages(htmlContent, config);
|
|
534
|
+
htmlContent = rewriteContentLinks(htmlContent, files, undefined, config.docroot);
|
|
535
|
+
sections.set("Home", [{ id: "home", title, html: htmlContent }]);
|
|
536
|
+
console.log(` ✓ Added home page: ${config.home}`);
|
|
537
|
+
} catch {
|
|
538
|
+
console.warn(` ⚠ Home page ${config.home} not found`);
|
|
539
|
+
}
|
|
469
540
|
}
|
|
470
|
-
|
|
541
|
+
}
|
|
471
542
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
543
|
+
for (const file of files) {
|
|
544
|
+
const content = await readFile(file.path, "utf-8");
|
|
545
|
+
let title = basename(file.path).replace(/\.(md|yaml|json)$/, "");
|
|
546
|
+
let description: string | undefined;
|
|
547
|
+
let htmlContent: string;
|
|
548
|
+
|
|
549
|
+
if (file.path.endsWith(".yaml")) {
|
|
550
|
+
htmlContent = `<pre><code class="language-yaml">${escapeHtml(content)}</code></pre>`;
|
|
551
|
+
} else if (file.path.endsWith(".json")) {
|
|
552
|
+
// Render JSON as code block (pretty-printed)
|
|
553
|
+
let prettyJson = content;
|
|
554
|
+
try {
|
|
555
|
+
prettyJson = JSON.stringify(JSON.parse(content), null, 2);
|
|
556
|
+
} catch {
|
|
557
|
+
// Use original if not valid JSON
|
|
558
|
+
}
|
|
559
|
+
htmlContent = `<pre><code class="language-json">${escapeHtml(prettyJson)}</code></pre>`;
|
|
560
|
+
} else {
|
|
561
|
+
const { frontmatter, body } = parseFrontmatter(content);
|
|
562
|
+
if (frontmatter.title) {
|
|
563
|
+
title = frontmatter.title as string;
|
|
564
|
+
}
|
|
565
|
+
if (frontmatter.description) {
|
|
566
|
+
description = frontmatter.description as string;
|
|
567
|
+
}
|
|
568
|
+
htmlContent = marked.parse(body) as string;
|
|
569
|
+
|
|
570
|
+
// Collect raw markdown for AI accessibility
|
|
571
|
+
if (INCLUDE_RAW) {
|
|
572
|
+
rawMarkdown.push({ path: file.urlPath, content });
|
|
573
|
+
}
|
|
475
574
|
}
|
|
476
|
-
}
|
|
477
575
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
576
|
+
// Collect page metadata for content index
|
|
577
|
+
pageIndex.push({
|
|
578
|
+
path: file.urlPath,
|
|
579
|
+
title,
|
|
580
|
+
section: file.section,
|
|
581
|
+
description,
|
|
582
|
+
});
|
|
485
583
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
584
|
+
// Inline any SVG references
|
|
585
|
+
htmlContent = await inlineLocalImages(htmlContent, config);
|
|
586
|
+
htmlContent = rewriteContentLinks(htmlContent, files, file.urlPath, config.docroot);
|
|
489
587
|
|
|
490
|
-
|
|
588
|
+
const sectionId = slugify(file.urlPath);
|
|
491
589
|
|
|
492
|
-
|
|
493
|
-
|
|
590
|
+
if (!sections.has(file.section)) {
|
|
591
|
+
sections.set(file.section, []);
|
|
592
|
+
}
|
|
593
|
+
sections.get(file.section)?.push({ id: sectionId, title, html: htmlContent });
|
|
494
594
|
}
|
|
495
|
-
sections.get(file.section)?.push({ id: sectionId, title, html: htmlContent });
|
|
496
|
-
}
|
|
497
595
|
|
|
498
|
-
|
|
499
|
-
|
|
596
|
+
// Build navigation HTML from shared hierarchical nav tree
|
|
597
|
+
navHtml = buildBundleNav(files, config);
|
|
500
598
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
contentHtml += `
|
|
599
|
+
// Build content HTML
|
|
600
|
+
for (const [, items] of sections) {
|
|
601
|
+
for (const item of items) {
|
|
602
|
+
contentHtml += `
|
|
506
603
|
<section id="${item.id}" class="bundle-section">
|
|
507
604
|
<h1 class="section-title">${item.title}</h1>
|
|
508
605
|
${item.html}
|
|
509
606
|
</section>
|
|
510
607
|
`;
|
|
608
|
+
}
|
|
511
609
|
}
|
|
512
610
|
}
|
|
513
611
|
|
|
514
612
|
const themeCSS = generateThemeCSS(theme);
|
|
613
|
+
const plugins = await loadPluginInjections({
|
|
614
|
+
root: ROOT,
|
|
615
|
+
mode: config.mode === "slides" ? "slides" : "docs",
|
|
616
|
+
});
|
|
515
617
|
|
|
516
618
|
// Inline brand assets for self-contained bundle
|
|
517
619
|
const brandLogo = await inlineBrandAsset(config.brand.logo || "assets/brand/logo.png");
|
|
@@ -551,6 +653,7 @@ ${assets.prismCss}
|
|
|
551
653
|
<style id="prism-dark" disabled>
|
|
552
654
|
${assets.prismCssDark}
|
|
553
655
|
</style>
|
|
656
|
+
${plugins.head}
|
|
554
657
|
<script>
|
|
555
658
|
(function() {
|
|
556
659
|
const saved = localStorage.getItem('theme');
|
|
@@ -566,7 +669,7 @@ ${assets.prismCssDark}
|
|
|
566
669
|
})();
|
|
567
670
|
</script>
|
|
568
671
|
</head>
|
|
569
|
-
<body>
|
|
672
|
+
<body class="${config.mode === "slides" ? "mode-slides" : "mode-docs"}">
|
|
570
673
|
<div class="layout">
|
|
571
674
|
<nav class="sidebar">
|
|
572
675
|
${buildBundleSidebarHeader(config, version, brandLogo)}
|
|
@@ -590,6 +693,7 @@ ${assets.prismAutoloader}
|
|
|
590
693
|
<script>
|
|
591
694
|
${assets.mermaid}
|
|
592
695
|
</script>
|
|
696
|
+
${plugins.bodyEnd}
|
|
593
697
|
<script>
|
|
594
698
|
// Initialize Mermaid
|
|
595
699
|
function getMermaidTheme() {
|
|
@@ -647,17 +751,78 @@ ${assets.mermaid}
|
|
|
647
751
|
}
|
|
648
752
|
}
|
|
649
753
|
|
|
650
|
-
//
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
754
|
+
// Slides mode hash routing
|
|
755
|
+
(function initSlidesMode() {
|
|
756
|
+
const shell = document.querySelector('.slides-shell');
|
|
757
|
+
if (!shell) {
|
|
758
|
+
// Docs mode: retain smooth in-page anchor scrolling.
|
|
759
|
+
document.querySelectorAll('a[href^="#"]').forEach((link) => {
|
|
760
|
+
link.addEventListener('click', (e) => {
|
|
761
|
+
const href = link.getAttribute('href') || '';
|
|
762
|
+
if (href.length <= 1) return;
|
|
763
|
+
const target = document.querySelector(href);
|
|
764
|
+
if (!target) return;
|
|
765
|
+
e.preventDefault();
|
|
766
|
+
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
767
|
+
history.replaceState(null, '', href);
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const slides = Array.from(document.querySelectorAll('.slide'));
|
|
774
|
+
if (!slides.length) return;
|
|
775
|
+
|
|
776
|
+
const prevBtn = document.querySelector('.slide-prev');
|
|
777
|
+
const nextBtn = document.querySelector('.slide-next');
|
|
778
|
+
const counter = document.querySelector('.slide-counter');
|
|
779
|
+
const progressBar = document.querySelector('.slide-progress-bar');
|
|
780
|
+
const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
|
|
781
|
+
let current = 0;
|
|
782
|
+
|
|
783
|
+
function setActive(n) {
|
|
784
|
+
current = Math.max(0, Math.min(n, slides.length - 1));
|
|
785
|
+
slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
|
|
786
|
+
navLinks.forEach((link) => {
|
|
787
|
+
const active = link.getAttribute('href') === '#' + slides[current].id;
|
|
788
|
+
link.classList.toggle('active', active);
|
|
789
|
+
});
|
|
790
|
+
if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
|
|
791
|
+
if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
|
|
792
|
+
if (prevBtn) prevBtn.disabled = current === 0;
|
|
793
|
+
if (nextBtn) nextBtn.disabled = current === slides.length - 1;
|
|
794
|
+
history.replaceState(null, '', '#'+slides[current].id);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function setFromHash() {
|
|
798
|
+
const hash = window.location.hash || '';
|
|
799
|
+
const idx = slides.findIndex((s) => '#'+s.id === hash);
|
|
800
|
+
if (idx >= 0) setActive(idx);
|
|
801
|
+
else setActive(0);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
prevBtn?.addEventListener('click', () => setActive(current - 1));
|
|
805
|
+
nextBtn?.addEventListener('click', () => setActive(current + 1));
|
|
806
|
+
|
|
807
|
+
document.addEventListener('keydown', (e) => {
|
|
808
|
+
if (e.key === 'ArrowRight' || e.key === ' ') {
|
|
809
|
+
e.preventDefault();
|
|
810
|
+
setActive(current + 1);
|
|
811
|
+
} else if (e.key === 'ArrowLeft') {
|
|
812
|
+
e.preventDefault();
|
|
813
|
+
setActive(current - 1);
|
|
814
|
+
} else if (e.key === 'Home') {
|
|
815
|
+
e.preventDefault();
|
|
816
|
+
setActive(0);
|
|
817
|
+
} else if (e.key === 'End') {
|
|
818
|
+
e.preventDefault();
|
|
819
|
+
setActive(slides.length - 1);
|
|
658
820
|
}
|
|
659
821
|
});
|
|
660
|
-
|
|
822
|
+
|
|
823
|
+
window.addEventListener('hashchange', setFromHash);
|
|
824
|
+
setFromHash();
|
|
825
|
+
})();
|
|
661
826
|
</script>
|
|
662
827
|
<!-- AI Accessibility: Content Index -->
|
|
663
828
|
<script type="application/json" id="kitfly-content-index">
|
|
@@ -755,9 +920,9 @@ if (import.meta.main) {
|
|
|
755
920
|
Usage: bun run bundle [folder] [options]
|
|
756
921
|
|
|
757
922
|
Options:
|
|
758
|
-
-o, --out <dir> Output directory [env:
|
|
923
|
+
-o, --out <dir> Output directory [env: KITFLY_BUNDLE_OUT] [default: ${DEFAULT_OUT}]
|
|
759
924
|
-n, --name <file> Bundle filename [env: KITFLY_BUNDLE_NAME] [default: ${DEFAULT_NAME}]
|
|
760
|
-
--raw Include raw markdown in bundle [env:
|
|
925
|
+
--raw Include raw markdown in bundle [env: KITFLY_BUNDLE_RAW] [default: true]
|
|
761
926
|
--no-raw Don't include raw markdown
|
|
762
927
|
--help Show this help message
|
|
763
928
|
|
|
@@ -765,8 +930,9 @@ Examples:
|
|
|
765
930
|
bun run bundle
|
|
766
931
|
bun run bundle ./docs
|
|
767
932
|
bun run bundle --name docs.html
|
|
768
|
-
bun run bundle ./docs --out ./
|
|
933
|
+
bun run bundle ./docs --out ./bundles --name handbook.html
|
|
769
934
|
KITFLY_BUNDLE_NAME=docs.html bun run bundle
|
|
935
|
+
KITFLY_BUNDLE_OUT=release bun run bundle
|
|
770
936
|
`);
|
|
771
937
|
process.exit(0);
|
|
772
938
|
}
|