kitfly 0.2.1 → 0.2.3
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 +56 -0
- package/README.md +25 -10
- 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/glossary.md +25 -1
- package/dist/_raw/content/reference/key-concepts.md +30 -2
- package/dist/_raw/content/reference/plugins.md +14 -0
- package/dist/_raw/docs/decisions/ADR-0006-data-driven-content.md +350 -0
- package/dist/content/deployment/preflight.html +10 -6
- package/dist/content/deployment/recipes/aws-s3.html +10 -6
- package/dist/content/deployment/recipes/cloudflare-pages.html +10 -6
- package/dist/content/deployment/recipes/cloudflare-r2.html +10 -6
- package/dist/content/deployment/recipes/fly-io.html +10 -6
- package/dist/content/deployment/recipes/github-pages.html +10 -6
- package/dist/content/deployment/recipes/netlify.html +10 -6
- package/dist/content/deployment/recipes/vercel.html +10 -6
- package/dist/content/deployment/secrets-and-env-vars.html +10 -6
- package/dist/content/deployment.html +10 -6
- package/dist/content/guide/approaches.html +10 -6
- package/dist/content/guide/branding.html +510 -0
- package/dist/content/guide/data-driven-content.html +543 -0
- package/dist/content/guide/features.html +10 -6
- package/dist/content/guide/getting-started.html +10 -6
- package/dist/content/guide/kitfly-overview.html +10 -6
- package/dist/content/reference/configuration.html +135 -9
- package/dist/content/reference/design-catalog.html +10 -6
- package/dist/content/reference/environment-variables.html +50 -8
- package/dist/content/reference/glossary.html +24 -8
- package/dist/content/reference/key-concepts.html +33 -9
- package/dist/content/reference/plugins.html +22 -7
- package/dist/content/reference/slides-authoring-guidelines.html +10 -6
- package/dist/content/reference/structure.html +10 -6
- package/dist/content/reference.html +10 -6
- package/dist/content/templates/crucible.html +10 -6
- package/dist/content/templates/handbook.html +10 -6
- package/dist/content/templates/minimal.html +10 -6
- package/dist/content/templates/overview.html +10 -6
- package/dist/content/templates/pipeline.html +10 -6
- package/dist/content/templates/productbook.html +10 -6
- package/dist/content/templates/runbook.html +10 -6
- package/dist/content/templates/servicebook.html +10 -6
- package/dist/content-index.json +29 -2
- package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +10 -6
- package/dist/docs/decisions/ADR-0002-ai-accessibility.html +10 -6
- package/dist/docs/decisions/ADR-0003-single-file-bundle.html +10 -6
- package/dist/docs/decisions/ADR-0004-bun-runtime.html +10 -6
- package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +10 -6
- package/dist/docs/decisions/ADR-0006-data-driven-content.html +752 -0
- package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +10 -6
- package/dist/docs/decisions/DDR-0002-theme-system.html +10 -6
- package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +10 -6
- package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +10 -6
- package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +10 -6
- package/dist/docs/userguide/cli/build.html +10 -6
- package/dist/docs/userguide/cli/bundle.html +10 -6
- package/dist/docs/userguide/cli/dev.html +10 -6
- package/dist/docs/userguide/cli/init.html +10 -6
- package/dist/docs/userguide/cli/servers.html +10 -6
- package/dist/docs/userguide/cli/stop.html +10 -6
- package/dist/docs/userguide/cli/update.html +10 -6
- package/dist/docs/userguide/cli/version.html +10 -6
- package/dist/docs/userguide/cli.html +10 -6
- package/dist/docs/userguide/sharing.html +10 -6
- package/dist/index.html +10 -6
- package/dist/llms.txt +3 -3
- package/dist/provenance.json +4 -4
- package/dist/schemas/plugin-registry.schema.html +10 -6
- package/dist/schemas/plugin-schemas-notes.html +10 -6
- package/dist/schemas/plugin.schema.html +10 -6
- package/dist/schemas/plugins.schema.html +10 -6
- package/dist/schemas/v0/common.schema.html +14 -10
- package/dist/schemas/v0/plugin-registry.schema.html +13 -9
- package/dist/schemas/v0/plugin.schema.html +13 -9
- package/dist/schemas/v0/plugins.schema.html +13 -9
- package/dist/schemas/v0/site.schema.html +67 -7
- package/dist/schemas/v0/theme.schema.html +21 -17
- package/dist/schemas.html +10 -6
- 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/slides-charts-lite-runtime.js +179 -0
- package/plugins-dist/slides-charts-lite.js +198 -0
- package/registry/plugins.yaml +25 -0
- package/schemas/v0/site.schema.json +56 -0
- package/scripts/build.ts +191 -69
- package/scripts/bundle.ts +118 -10
- package/scripts/dev.ts +245 -166
- package/src/__tests__/brief.test.ts +151 -0
- package/src/__tests__/build.test.ts +169 -1
- package/src/__tests__/bundle.test.ts +134 -0
- package/src/__tests__/init.test.ts +51 -2
- package/src/__tests__/latex-runtime.bun.test.ts +35 -0
- package/src/__tests__/shared.test.ts +598 -1
- package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
- package/src/cli.ts +11 -4
- package/src/commands/init.ts +1 -1
- package/src/shared.ts +725 -18
- 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/registry/plugins.yaml
CHANGED
|
@@ -33,3 +33,28 @@ plugins:
|
|
|
33
33
|
assetSha256:
|
|
34
34
|
js: "sha256:716059fbb0045af5305e552557368fe812569121986e0ce533f96753197c6ee1"
|
|
35
35
|
css: "sha256:dae86214e5f3f628deb6fec3f7bd871fa419f0310993cde9e845392ac16d9ec1"
|
|
36
|
+
latex:
|
|
37
|
+
name: "LaTeX/KaTeX Math Rendering"
|
|
38
|
+
description: "LaTeX math rendering via KaTeX ($...$ inline, $$...$$ display, fenced math)"
|
|
39
|
+
version: "0.2.2"
|
|
40
|
+
contract: "1"
|
|
41
|
+
kitfly: ">=0.2.0 <1.0.0"
|
|
42
|
+
license: MIT
|
|
43
|
+
verified: true
|
|
44
|
+
assets:
|
|
45
|
+
js: "plugins-dist/latex.js"
|
|
46
|
+
assetSha256:
|
|
47
|
+
js: "sha256:5475e344dc424e3015c3a8b2fd8152101354fe2622d322a0fa4f73d1335b4232"
|
|
48
|
+
slides-charts-lite:
|
|
49
|
+
name: "Slides Charts Lite"
|
|
50
|
+
description: "Simple charts for Kitfly slides (bar, line, pie)"
|
|
51
|
+
version: "0.2.2"
|
|
52
|
+
contract: "1"
|
|
53
|
+
kitfly: ">=0.2.0 <1.0.0"
|
|
54
|
+
license: MIT
|
|
55
|
+
verified: true
|
|
56
|
+
modes: ["slides"]
|
|
57
|
+
assets:
|
|
58
|
+
js: "plugins-dist/slides-charts-lite.js"
|
|
59
|
+
assetSha256:
|
|
60
|
+
js: "sha256:ea1afbe48bc94c617af1ee2d53febcaa8d48e83253094a1cbef598f77ec15013"
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
"description": "Root directory for content (relative to repo root)",
|
|
23
23
|
"default": "."
|
|
24
24
|
},
|
|
25
|
+
"dataroot": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Root directory for data binding files (relative to repo root)",
|
|
28
|
+
"default": "data"
|
|
29
|
+
},
|
|
25
30
|
"title": {
|
|
26
31
|
"type": "string",
|
|
27
32
|
"description": "Site title shown in browser tab and header",
|
|
@@ -90,6 +95,10 @@
|
|
|
90
95
|
"description": "Path to logo image (relative to site root)",
|
|
91
96
|
"default": "assets/brand/logo.png"
|
|
92
97
|
},
|
|
98
|
+
"logoDark": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"description": "Path to dark mode logo image (relative to site root). Shown when dark theme is active."
|
|
101
|
+
},
|
|
93
102
|
"favicon": {
|
|
94
103
|
"type": "string",
|
|
95
104
|
"description": "Path to favicon image (relative to site root)",
|
|
@@ -191,6 +200,29 @@
|
|
|
191
200
|
"type": "boolean",
|
|
192
201
|
"description": "Show Built with Kitfly attribution",
|
|
193
202
|
"default": true
|
|
203
|
+
},
|
|
204
|
+
"logo": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"description": "Path to footer logo image (relative to site root)"
|
|
207
|
+
},
|
|
208
|
+
"logoDark": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"description": "Path to dark mode footer logo image (relative to site root)"
|
|
211
|
+
},
|
|
212
|
+
"logoUrl": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "Make footer logo a clickable link to this URL"
|
|
215
|
+
},
|
|
216
|
+
"logoAlt": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"description": "Alt text for footer logo (defaults to copyright text if set, brand name otherwise)"
|
|
219
|
+
},
|
|
220
|
+
"logoHeight": {
|
|
221
|
+
"type": "integer",
|
|
222
|
+
"description": "Max height of footer logo in pixels",
|
|
223
|
+
"minimum": 10,
|
|
224
|
+
"maximum": 40,
|
|
225
|
+
"default": 20
|
|
194
226
|
}
|
|
195
227
|
},
|
|
196
228
|
"additionalProperties": false
|
|
@@ -213,6 +245,30 @@
|
|
|
213
245
|
}
|
|
214
246
|
},
|
|
215
247
|
"additionalProperties": false
|
|
248
|
+
},
|
|
249
|
+
"prebuild": {
|
|
250
|
+
"type": "array",
|
|
251
|
+
"description": "Commands to run before dev/build/bundle",
|
|
252
|
+
"items": {
|
|
253
|
+
"type": "object",
|
|
254
|
+
"required": [
|
|
255
|
+
"command"
|
|
256
|
+
],
|
|
257
|
+
"properties": {
|
|
258
|
+
"command": {
|
|
259
|
+
"type": "string",
|
|
260
|
+
"description": "Shell command to execute"
|
|
261
|
+
},
|
|
262
|
+
"watch": {
|
|
263
|
+
"type": "array",
|
|
264
|
+
"description": "File patterns that re-run this hook in dev mode",
|
|
265
|
+
"items": {
|
|
266
|
+
"type": "string"
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
"additionalProperties": false
|
|
271
|
+
}
|
|
216
272
|
}
|
|
217
273
|
},
|
|
218
274
|
"additionalProperties": false
|
package/scripts/build.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Options:
|
|
7
7
|
* -o, --out <dir> Output directory [env: KITFLY_BUILD_OUT] [default: dist]
|
|
8
|
+
* --profile <name> Active content profile [env: KITFLY_PROFILE]
|
|
8
9
|
* --raw Include raw markdown files [env: KITFLY_BUILD_RAW] [default: true]
|
|
9
10
|
* --no-raw Don't include raw markdown files
|
|
10
11
|
* --help Show help message
|
|
@@ -20,9 +21,10 @@ import { loadPluginInjections, type PluginInjections } from "../src/plugin-loade
|
|
|
20
21
|
import {
|
|
21
22
|
buildBreadcrumbsStatic,
|
|
22
23
|
buildFooter,
|
|
24
|
+
buildLogoImgHtml,
|
|
23
25
|
buildNavStatic,
|
|
24
26
|
buildPageMeta,
|
|
25
|
-
|
|
27
|
+
buildSlideNavHierarchical,
|
|
26
28
|
buildToc,
|
|
27
29
|
type ContentFile,
|
|
28
30
|
collectFiles,
|
|
@@ -35,18 +37,24 @@ import {
|
|
|
35
37
|
escapeHtml,
|
|
36
38
|
// File utilities
|
|
37
39
|
exists,
|
|
40
|
+
filterByProfile,
|
|
38
41
|
filterUnknownSlidesVisualsTypeDiagnostics,
|
|
39
42
|
// Provenance
|
|
40
43
|
generateProvenance,
|
|
44
|
+
loadDataBindings,
|
|
41
45
|
// YAML/Config parsing
|
|
42
46
|
loadSiteConfig,
|
|
47
|
+
mergeFrontmatterWithBody,
|
|
43
48
|
type Provenance,
|
|
49
|
+
pagePathForData,
|
|
44
50
|
// Markdown utilities
|
|
45
51
|
parseFrontmatter,
|
|
46
52
|
parseYaml,
|
|
53
|
+
resolveBindings,
|
|
47
54
|
resolveStylesPath,
|
|
48
55
|
resolveTemplatePath,
|
|
49
56
|
rewriteRelativeAssetUrls,
|
|
57
|
+
runPrebuildHooks,
|
|
50
58
|
// Types
|
|
51
59
|
type SiteConfig,
|
|
52
60
|
slugify,
|
|
@@ -60,6 +68,41 @@ const DEFAULT_OUT = "dist";
|
|
|
60
68
|
|
|
61
69
|
let ROOT = process.cwd();
|
|
62
70
|
let OUT_DIR = DEFAULT_OUT;
|
|
71
|
+
let ACTIVE_PROFILE: string | undefined;
|
|
72
|
+
|
|
73
|
+
async function applyDataBindingsToMarkdown(
|
|
74
|
+
rawMarkdown: string,
|
|
75
|
+
filePath: string,
|
|
76
|
+
config: SiteConfig,
|
|
77
|
+
): Promise<{ frontmatter: Record<string, unknown>; body: string }> {
|
|
78
|
+
const parsed = parseFrontmatter(rawMarkdown);
|
|
79
|
+
const dataRef = typeof parsed.frontmatter.data === "string" ? parsed.frontmatter.data.trim() : "";
|
|
80
|
+
if (!dataRef) return parsed;
|
|
81
|
+
|
|
82
|
+
const pagePath = pagePathForData(ROOT, config.docroot, filePath);
|
|
83
|
+
const bindings = await loadDataBindings(dataRef, pagePath, ROOT, config.docroot, config.dataroot);
|
|
84
|
+
return {
|
|
85
|
+
frontmatter: parsed.frontmatter,
|
|
86
|
+
body: resolveBindings(parsed.body, bindings, pagePath),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function applyDataBindingsForSlides(
|
|
91
|
+
rawMarkdown: string,
|
|
92
|
+
filePath: string,
|
|
93
|
+
config: SiteConfig,
|
|
94
|
+
): Promise<string> {
|
|
95
|
+
const resolved = await applyDataBindingsToMarkdown(rawMarkdown, filePath, config);
|
|
96
|
+
return mergeFrontmatterWithBody(rawMarkdown, resolved.body);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function normalizeMsysPath(p: string): string {
|
|
100
|
+
// Git Bash / MSYS-style paths: /c/Users/... -> C:\Users\...
|
|
101
|
+
if (process.platform !== "win32") return p;
|
|
102
|
+
const m = p.match(/^\/([a-zA-Z])\/(.*)$/);
|
|
103
|
+
if (!m) return p;
|
|
104
|
+
return `${m[1].toUpperCase()}:\\${m[2].replaceAll("/", "\\")}`;
|
|
105
|
+
}
|
|
63
106
|
|
|
64
107
|
// ---------------------------------------------------------------------------
|
|
65
108
|
// CLI argument parsing
|
|
@@ -69,6 +112,7 @@ interface ParsedArgs {
|
|
|
69
112
|
folder?: string;
|
|
70
113
|
out?: string;
|
|
71
114
|
raw?: boolean;
|
|
115
|
+
profile?: string;
|
|
72
116
|
}
|
|
73
117
|
|
|
74
118
|
function parseArgs(argv: string[]): ParsedArgs {
|
|
@@ -80,6 +124,9 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
80
124
|
if ((arg === "--out" || arg === "-o") && next && !next.startsWith("-")) {
|
|
81
125
|
result.out = next;
|
|
82
126
|
i++;
|
|
127
|
+
} else if (arg === "--profile" && next && !next.startsWith("-")) {
|
|
128
|
+
result.profile = next;
|
|
129
|
+
i++;
|
|
83
130
|
} else if (arg === "--raw") {
|
|
84
131
|
result.raw = true;
|
|
85
132
|
} else if (arg === "--no-raw") {
|
|
@@ -91,12 +138,13 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
91
138
|
return result;
|
|
92
139
|
}
|
|
93
140
|
|
|
94
|
-
function getConfig(): { folder?: string; out: string; raw: boolean } {
|
|
141
|
+
function getConfig(): { folder?: string; out: string; raw: boolean; profile?: string } {
|
|
95
142
|
const args = parseArgs(process.argv.slice(2));
|
|
96
143
|
return {
|
|
97
144
|
folder: args.folder,
|
|
98
145
|
out: args.out ?? envString("KITFLY_BUILD_OUT", DEFAULT_OUT),
|
|
99
146
|
raw: args.raw ?? envBool("KITFLY_BUILD_RAW", true),
|
|
147
|
+
profile: args.profile ?? process.env.KITFLY_PROFILE,
|
|
100
148
|
};
|
|
101
149
|
}
|
|
102
150
|
|
|
@@ -189,7 +237,7 @@ async function renderFile(
|
|
|
189
237
|
}
|
|
190
238
|
htmlContent = `<h1>${title}</h1>\n<pre><code class="language-json">${escapeHtml(prettyJson)}</code></pre>`;
|
|
191
239
|
} else {
|
|
192
|
-
const { frontmatter, body } =
|
|
240
|
+
const { frontmatter, body } = await applyDataBindingsToMarkdown(content, filePath, config);
|
|
193
241
|
if (frontmatter.title) {
|
|
194
242
|
title = frontmatter.title as string;
|
|
195
243
|
}
|
|
@@ -199,7 +247,7 @@ async function renderFile(
|
|
|
199
247
|
|
|
200
248
|
const pathPrefix = computePathPrefix(urlKey);
|
|
201
249
|
const nav = buildNavStatic(files, urlKey, config, pathPrefix);
|
|
202
|
-
const footer = buildFooter(provenance, config);
|
|
250
|
+
const footer = buildFooter(provenance, config, pathPrefix);
|
|
203
251
|
const breadcrumbs = buildBreadcrumbsStatic(urlKey, pathPrefix, files, config);
|
|
204
252
|
const toc = buildToc(htmlContent);
|
|
205
253
|
const brandTarget = config.brand.external ? ' target="_blank" rel="noopener"' : "";
|
|
@@ -207,32 +255,50 @@ async function renderFile(
|
|
|
207
255
|
const prismUrls = getPrismUrls(theme);
|
|
208
256
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
209
257
|
const brandInitial = escapeHtml(config.brand.name.trim().charAt(0).toUpperCase() || "K");
|
|
258
|
+
const mobileLogoHtml = buildLogoImgHtml({
|
|
259
|
+
logo: config.brand.logo || "assets/brand/logo.png",
|
|
260
|
+
logoDark: config.brand.logoDark,
|
|
261
|
+
alt: config.brand.name,
|
|
262
|
+
className: `logo-img ${logoClass}`,
|
|
263
|
+
pathPrefix,
|
|
264
|
+
onerrorFallback: true,
|
|
265
|
+
});
|
|
266
|
+
const sidebarLogoHtml = buildLogoImgHtml({
|
|
267
|
+
logo: config.brand.logo || "assets/brand/logo.png",
|
|
268
|
+
logoDark: config.brand.logoDark,
|
|
269
|
+
alt: config.brand.name,
|
|
270
|
+
className: "logo-img",
|
|
271
|
+
pathPrefix,
|
|
272
|
+
onerrorFallback: true,
|
|
273
|
+
});
|
|
210
274
|
|
|
211
275
|
return template
|
|
212
276
|
.replace("{{BODY_CLASS}}", "mode-docs")
|
|
213
|
-
.replace(/\{\{PATH_PREFIX\}\}/g, pathPrefix)
|
|
214
|
-
.replace(/\{\{BRAND_URL\}\}/g, config.brand.url)
|
|
215
|
-
.replace(/\{\{BRAND_TARGET\}\}/g, brandTarget)
|
|
216
|
-
.replace(/\{\{BRAND_NAME\}\}/g, config.brand.name)
|
|
217
|
-
.replace(/\{\{BRAND_INITIAL\}\}/g, brandInitial)
|
|
218
|
-
.replace(
|
|
219
|
-
.replace(
|
|
220
|
-
.replace(/\{\{
|
|
221
|
-
.replace(/\{\{
|
|
222
|
-
.replace(
|
|
223
|
-
.replace(
|
|
224
|
-
.replace("{{
|
|
225
|
-
.replace("{{
|
|
226
|
-
.replace("{{
|
|
227
|
-
.replace("{{
|
|
228
|
-
.replace("{{
|
|
229
|
-
.replace("{{
|
|
230
|
-
.replace("{{
|
|
231
|
-
.replace("{{
|
|
232
|
-
.replace("{{
|
|
233
|
-
.replace("{{
|
|
234
|
-
.replace("{{
|
|
235
|
-
.replace("{{
|
|
277
|
+
.replace(/\{\{PATH_PREFIX\}\}/g, () => pathPrefix)
|
|
278
|
+
.replace(/\{\{BRAND_URL\}\}/g, () => config.brand.url)
|
|
279
|
+
.replace(/\{\{BRAND_TARGET\}\}/g, () => brandTarget)
|
|
280
|
+
.replace(/\{\{BRAND_NAME\}\}/g, () => config.brand.name)
|
|
281
|
+
.replace(/\{\{BRAND_INITIAL\}\}/g, () => brandInitial)
|
|
282
|
+
.replace("{{MOBILE_BRAND_LOGO_IMG}}", () => mobileLogoHtml)
|
|
283
|
+
.replace("{{SIDEBAR_BRAND_LOGO_IMG}}", () => sidebarLogoHtml)
|
|
284
|
+
.replace(/\{\{BRAND_LOGO\}\}/g, () => config.brand.logo || "assets/brand/logo.png")
|
|
285
|
+
.replace(/\{\{BRAND_FAVICON\}\}/g, () => config.brand.favicon || "assets/brand/favicon.png")
|
|
286
|
+
.replace(/\{\{BRAND_LOGO_CLASS\}\}/g, () => logoClass)
|
|
287
|
+
.replace(/\{\{SITE_TITLE\}\}/g, () => config.title)
|
|
288
|
+
.replace("{{TITLE}}", () => title)
|
|
289
|
+
.replace("{{VERSION}}", () => uiVersion)
|
|
290
|
+
.replace("{{BRANCH}}", () => provenance.gitBranch)
|
|
291
|
+
.replace("{{BREADCRUMBS}}", () => breadcrumbs)
|
|
292
|
+
.replace("{{PAGE_META}}", () => pageMeta)
|
|
293
|
+
.replace("{{NAV}}", () => nav)
|
|
294
|
+
.replace("{{CONTENT}}", () => htmlContent)
|
|
295
|
+
.replace("{{TOC}}", () => toc)
|
|
296
|
+
.replace("{{FOOTER}}", () => footer)
|
|
297
|
+
.replace("{{THEME_CSS}}", () => themeCSS)
|
|
298
|
+
.replace("{{PLUGIN_HEAD}}", () => plugins.head)
|
|
299
|
+
.replace("{{PLUGIN_BODY_END}}", () => plugins.bodyEnd)
|
|
300
|
+
.replace("{{PRISM_LIGHT_URL}}", () => prismUrls.light)
|
|
301
|
+
.replace("{{PRISM_DARK_URL}}", () => prismUrls.dark)
|
|
236
302
|
.replace("{{HOT_RELOAD_SCRIPT}}", "");
|
|
237
303
|
}
|
|
238
304
|
|
|
@@ -274,32 +340,50 @@ sections:
|
|
|
274
340
|
const pathPrefix = "./";
|
|
275
341
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
276
342
|
const brandInitial = escapeHtml(config.brand.name.trim().charAt(0).toUpperCase() || "K");
|
|
343
|
+
const mobileLogoHtml = buildLogoImgHtml({
|
|
344
|
+
logo: config.brand.logo || "assets/brand/logo.png",
|
|
345
|
+
logoDark: config.brand.logoDark,
|
|
346
|
+
alt: config.brand.name,
|
|
347
|
+
className: `logo-img ${logoClass}`,
|
|
348
|
+
pathPrefix,
|
|
349
|
+
onerrorFallback: true,
|
|
350
|
+
});
|
|
351
|
+
const sidebarLogoHtml = buildLogoImgHtml({
|
|
352
|
+
logo: config.brand.logo || "assets/brand/logo.png",
|
|
353
|
+
logoDark: config.brand.logoDark,
|
|
354
|
+
alt: config.brand.name,
|
|
355
|
+
className: "logo-img",
|
|
356
|
+
pathPrefix,
|
|
357
|
+
onerrorFallback: true,
|
|
358
|
+
});
|
|
277
359
|
|
|
278
360
|
return template
|
|
279
361
|
.replace("{{BODY_CLASS}}", "mode-docs")
|
|
280
|
-
.replace(/\{\{PATH_PREFIX\}\}/g, pathPrefix)
|
|
281
|
-
.replace(/\{\{BRAND_URL\}\}/g, config.brand.url)
|
|
282
|
-
.replace(/\{\{BRAND_TARGET\}\}/g, brandTarget)
|
|
283
|
-
.replace(/\{\{BRAND_NAME\}\}/g, config.brand.name)
|
|
284
|
-
.replace(/\{\{BRAND_INITIAL\}\}/g, brandInitial)
|
|
285
|
-
.replace(
|
|
286
|
-
.replace(
|
|
287
|
-
.replace(/\{\{
|
|
288
|
-
.replace(/\{\{
|
|
362
|
+
.replace(/\{\{PATH_PREFIX\}\}/g, () => pathPrefix)
|
|
363
|
+
.replace(/\{\{BRAND_URL\}\}/g, () => config.brand.url)
|
|
364
|
+
.replace(/\{\{BRAND_TARGET\}\}/g, () => brandTarget)
|
|
365
|
+
.replace(/\{\{BRAND_NAME\}\}/g, () => config.brand.name)
|
|
366
|
+
.replace(/\{\{BRAND_INITIAL\}\}/g, () => brandInitial)
|
|
367
|
+
.replace("{{MOBILE_BRAND_LOGO_IMG}}", () => mobileLogoHtml)
|
|
368
|
+
.replace("{{SIDEBAR_BRAND_LOGO_IMG}}", () => sidebarLogoHtml)
|
|
369
|
+
.replace(/\{\{BRAND_LOGO\}\}/g, () => config.brand.logo || "assets/brand/logo.png")
|
|
370
|
+
.replace(/\{\{BRAND_FAVICON\}\}/g, () => config.brand.favicon || "assets/brand/favicon.png")
|
|
371
|
+
.replace(/\{\{BRAND_LOGO_CLASS\}\}/g, () => logoClass)
|
|
372
|
+
.replace(/\{\{SITE_TITLE\}\}/g, () => config.title)
|
|
289
373
|
.replace("{{TITLE}}", "Getting Started")
|
|
290
|
-
.replace("{{VERSION}}", uiVersion)
|
|
291
|
-
.replace("{{BRANCH}}", provenance.gitBranch)
|
|
374
|
+
.replace("{{VERSION}}", () => uiVersion)
|
|
375
|
+
.replace("{{BRANCH}}", () => provenance.gitBranch)
|
|
292
376
|
.replace("{{BREADCRUMBS}}", "")
|
|
293
377
|
.replace("{{PAGE_META}}", "")
|
|
294
378
|
.replace("{{NAV}}", "<ul></ul>")
|
|
295
|
-
.replace("{{CONTENT}}", htmlContent)
|
|
379
|
+
.replace("{{CONTENT}}", () => htmlContent)
|
|
296
380
|
.replace("{{TOC}}", "")
|
|
297
|
-
.replace("{{FOOTER}}", buildFooter(provenance, config))
|
|
298
|
-
.replace("{{THEME_CSS}}", themeCSS)
|
|
299
|
-
.replace("{{PLUGIN_HEAD}}", plugins.head)
|
|
300
|
-
.replace("{{PLUGIN_BODY_END}}", plugins.bodyEnd)
|
|
301
|
-
.replace("{{PRISM_LIGHT_URL}}", prismUrls.light)
|
|
302
|
-
.replace("{{PRISM_DARK_URL}}", prismUrls.dark)
|
|
381
|
+
.replace("{{FOOTER}}", () => buildFooter(provenance, config, pathPrefix))
|
|
382
|
+
.replace("{{THEME_CSS}}", () => themeCSS)
|
|
383
|
+
.replace("{{PLUGIN_HEAD}}", () => plugins.head)
|
|
384
|
+
.replace("{{PLUGIN_BODY_END}}", () => plugins.bodyEnd)
|
|
385
|
+
.replace("{{PRISM_LIGHT_URL}}", () => prismUrls.light)
|
|
386
|
+
.replace("{{PRISM_DARK_URL}}", () => prismUrls.dark)
|
|
303
387
|
.replace("{{HOT_RELOAD_SCRIPT}}", "");
|
|
304
388
|
}
|
|
305
389
|
|
|
@@ -313,7 +397,9 @@ async function renderSlidesIndex(
|
|
|
313
397
|
): Promise<string> {
|
|
314
398
|
const uiVersion = provenance.version ? `v${provenance.version}` : "unversioned";
|
|
315
399
|
const pathPrefix = "./";
|
|
316
|
-
const slides = await collectSlides(files
|
|
400
|
+
const slides = await collectSlides(files, {
|
|
401
|
+
markdownTransform: (raw, file) => applyDataBindingsForSlides(raw, file.path, config),
|
|
402
|
+
});
|
|
317
403
|
let validateFences = false;
|
|
318
404
|
try {
|
|
319
405
|
const raw = await readFile(join(ROOT, "kitfly.plugins.yaml"), "utf-8");
|
|
@@ -376,38 +462,56 @@ async function renderSlidesIndex(
|
|
|
376
462
|
</div>
|
|
377
463
|
</div>`;
|
|
378
464
|
|
|
379
|
-
const nav =
|
|
465
|
+
const nav = buildSlideNavHierarchical(slides, config, "slide-1");
|
|
380
466
|
const brandTarget = config.brand.external ? ' target="_blank" rel="noopener"' : "";
|
|
381
467
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
382
468
|
const themeCSS = generateThemeCSS(theme);
|
|
383
469
|
const prismUrls = getPrismUrls(theme);
|
|
384
470
|
const brandInitial = escapeHtml(config.brand.name.trim().charAt(0).toUpperCase() || "K");
|
|
471
|
+
const mobileLogoHtml = buildLogoImgHtml({
|
|
472
|
+
logo: config.brand.logo || "assets/brand/logo.png",
|
|
473
|
+
logoDark: config.brand.logoDark,
|
|
474
|
+
alt: config.brand.name,
|
|
475
|
+
className: `logo-img ${logoClass}`,
|
|
476
|
+
pathPrefix,
|
|
477
|
+
onerrorFallback: true,
|
|
478
|
+
});
|
|
479
|
+
const sidebarLogoHtml = buildLogoImgHtml({
|
|
480
|
+
logo: config.brand.logo || "assets/brand/logo.png",
|
|
481
|
+
logoDark: config.brand.logoDark,
|
|
482
|
+
alt: config.brand.name,
|
|
483
|
+
className: "logo-img",
|
|
484
|
+
pathPrefix,
|
|
485
|
+
onerrorFallback: true,
|
|
486
|
+
});
|
|
385
487
|
|
|
386
488
|
return template
|
|
387
489
|
.replace("{{BODY_CLASS}}", "mode-slides")
|
|
388
|
-
.replace(/\{\{PATH_PREFIX\}\}/g, pathPrefix)
|
|
389
|
-
.replace(/\{\{BRAND_URL\}\}/g, config.brand.url)
|
|
390
|
-
.replace(/\{\{BRAND_TARGET\}\}/g, brandTarget)
|
|
391
|
-
.replace(/\{\{BRAND_NAME\}\}/g, config.brand.name)
|
|
392
|
-
.replace(/\{\{BRAND_INITIAL\}\}/g, brandInitial)
|
|
393
|
-
.replace(
|
|
394
|
-
.replace(
|
|
395
|
-
.replace(/\{\{
|
|
396
|
-
.replace(/\{\{
|
|
397
|
-
.replace(
|
|
398
|
-
.replace(
|
|
399
|
-
.replace("{{
|
|
490
|
+
.replace(/\{\{PATH_PREFIX\}\}/g, () => pathPrefix)
|
|
491
|
+
.replace(/\{\{BRAND_URL\}\}/g, () => config.brand.url)
|
|
492
|
+
.replace(/\{\{BRAND_TARGET\}\}/g, () => brandTarget)
|
|
493
|
+
.replace(/\{\{BRAND_NAME\}\}/g, () => config.brand.name)
|
|
494
|
+
.replace(/\{\{BRAND_INITIAL\}\}/g, () => brandInitial)
|
|
495
|
+
.replace("{{MOBILE_BRAND_LOGO_IMG}}", () => mobileLogoHtml)
|
|
496
|
+
.replace("{{SIDEBAR_BRAND_LOGO_IMG}}", () => sidebarLogoHtml)
|
|
497
|
+
.replace(/\{\{BRAND_LOGO\}\}/g, () => config.brand.logo || "assets/brand/logo.png")
|
|
498
|
+
.replace(/\{\{BRAND_FAVICON\}\}/g, () => config.brand.favicon || "assets/brand/favicon.png")
|
|
499
|
+
.replace(/\{\{BRAND_LOGO_CLASS\}\}/g, () => logoClass)
|
|
500
|
+
.replace(/\{\{SITE_TITLE\}\}/g, () => config.title)
|
|
501
|
+
.replace("{{TITLE}}", () => config.title)
|
|
502
|
+
.replace("{{VERSION}}", () => uiVersion)
|
|
503
|
+
.replace("{{BRANCH}}", () => provenance.gitBranch)
|
|
400
504
|
.replace("{{BREADCRUMBS}}", "")
|
|
401
505
|
.replace("{{PAGE_META}}", "")
|
|
402
|
-
.replace("{{NAV}}", nav)
|
|
403
|
-
.replace("{{CONTENT}}", htmlContent)
|
|
506
|
+
.replace("{{NAV}}", () => nav)
|
|
507
|
+
.replace("{{CONTENT}}", () => htmlContent)
|
|
404
508
|
.replace("{{TOC}}", "")
|
|
405
|
-
.replace("{{FOOTER}}", buildFooter(provenance, config))
|
|
406
|
-
.replace("{{THEME_CSS}}", themeCSS)
|
|
407
|
-
.replace("{{PLUGIN_HEAD}}", plugins.head)
|
|
408
|
-
.replace("{{PLUGIN_BODY_END}}", plugins.bodyEnd)
|
|
409
|
-
.replace("{{PRISM_LIGHT_URL}}", prismUrls.light)
|
|
410
|
-
.replace("{{PRISM_DARK_URL}}", prismUrls.dark)
|
|
509
|
+
.replace("{{FOOTER}}", () => buildFooter(provenance, config, pathPrefix))
|
|
510
|
+
.replace("{{THEME_CSS}}", () => themeCSS)
|
|
511
|
+
.replace("{{PLUGIN_HEAD}}", () => plugins.head)
|
|
512
|
+
.replace("{{PLUGIN_BODY_END}}", () => plugins.bodyEnd)
|
|
513
|
+
.replace("{{PRISM_LIGHT_URL}}", () => prismUrls.light)
|
|
514
|
+
.replace("{{PRISM_DARK_URL}}", () => prismUrls.dark)
|
|
411
515
|
.replace("{{HOT_RELOAD_SCRIPT}}", "");
|
|
412
516
|
}
|
|
413
517
|
|
|
@@ -416,6 +520,7 @@ export interface BuildOptions {
|
|
|
416
520
|
folder?: string;
|
|
417
521
|
out?: string;
|
|
418
522
|
raw?: boolean; // Include raw markdown files (default: true)
|
|
523
|
+
profile?: string;
|
|
419
524
|
}
|
|
420
525
|
|
|
421
526
|
let INCLUDE_RAW = true;
|
|
@@ -430,18 +535,29 @@ export async function build(options: BuildOptions = {}) {
|
|
|
430
535
|
if (options.raw === false) {
|
|
431
536
|
INCLUDE_RAW = false;
|
|
432
537
|
}
|
|
538
|
+
ACTIVE_PROFILE = options.profile;
|
|
433
539
|
await buildSite();
|
|
434
540
|
}
|
|
435
541
|
|
|
436
542
|
// Rename internal function
|
|
437
543
|
async function buildSite() {
|
|
438
|
-
const DIST =
|
|
544
|
+
const DIST = resolve(ROOT, normalizeMsysPath(OUT_DIR));
|
|
439
545
|
|
|
440
546
|
console.log("Building site...\n");
|
|
441
547
|
|
|
442
548
|
// Load configuration
|
|
443
549
|
const config = await loadSiteConfig(ROOT);
|
|
444
550
|
console.log(` ✓ Loaded config: "${config.title}" (${config.sections.length} sections)`);
|
|
551
|
+
if (config.prebuild?.length) {
|
|
552
|
+
await runPrebuildHooks(
|
|
553
|
+
config.prebuild,
|
|
554
|
+
ROOT,
|
|
555
|
+
"build",
|
|
556
|
+
ACTIVE_PROFILE,
|
|
557
|
+
config.dataroot || "data",
|
|
558
|
+
);
|
|
559
|
+
console.log(` ✓ prebuild hooks (${config.prebuild.length})`);
|
|
560
|
+
}
|
|
445
561
|
|
|
446
562
|
// Load theme
|
|
447
563
|
const theme = await loadTheme(ROOT);
|
|
@@ -499,7 +615,11 @@ async function buildSite() {
|
|
|
499
615
|
}
|
|
500
616
|
|
|
501
617
|
// Collect and render all files
|
|
502
|
-
const files = await
|
|
618
|
+
const files = await filterByProfile(
|
|
619
|
+
await collectFiles(ROOT, config),
|
|
620
|
+
ACTIVE_PROFILE,
|
|
621
|
+
config.profiles,
|
|
622
|
+
);
|
|
503
623
|
|
|
504
624
|
if (files.length === 0) {
|
|
505
625
|
// No content - render Getting Started page
|
|
@@ -733,6 +853,7 @@ Usage: bun run build [folder] [options]
|
|
|
733
853
|
|
|
734
854
|
Options:
|
|
735
855
|
-o, --out <dir> Output directory [env: KITFLY_BUILD_OUT] [default: ${DEFAULT_OUT}]
|
|
856
|
+
--profile <name> Active content profile [env: KITFLY_PROFILE]
|
|
736
857
|
--raw Include raw markdown files [env: KITFLY_BUILD_RAW] [default: true]
|
|
737
858
|
--no-raw Don't include raw markdown files
|
|
738
859
|
--help Show this help message
|
|
@@ -752,5 +873,6 @@ Examples:
|
|
|
752
873
|
folder: cfg.folder,
|
|
753
874
|
out: cfg.out,
|
|
754
875
|
raw: cfg.raw,
|
|
876
|
+
profile: cfg.profile,
|
|
755
877
|
}).catch(console.error);
|
|
756
878
|
}
|