kitfly 0.2.0 → 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 +68 -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/content/reference/slides-authoring-guidelines.md +129 -0
- package/dist/_raw/content/reference.md +1 -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 +422 -0
- package/dist/content/reference/structure.html +10 -6
- package/dist/content/reference.html +11 -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 +38 -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/plugins-dist/slides-visuals.css +166 -0
- package/plugins-dist/slides-visuals.js +124 -33
- package/registry/plugins.yaml +30 -5
- package/schemas/v0/site.schema.json +56 -0
- package/scripts/build.ts +195 -70
- package/scripts/bundle.ts +122 -11
- package/scripts/dev.ts +345 -178
- package/src/__tests__/brief.test.ts +151 -0
- package/src/__tests__/build.test.ts +234 -4
- package/src/__tests__/bundle.test.ts +134 -0
- package/src/__tests__/dev-plugin-errors.test.ts +20 -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/staircase-empty-steps.md +3 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/timeline-horizontal-no-events.md +2 -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/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/timeline-horizontal.md +9 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-vertical.md +10 -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 +621 -1
- package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
- package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +33 -0
- package/src/cli.ts +11 -4
- package/src/commands/init.ts +1 -1
- package/src/shared.ts +761 -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/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,17 +37,24 @@ import {
|
|
|
35
37
|
escapeHtml,
|
|
36
38
|
// File utilities
|
|
37
39
|
exists,
|
|
40
|
+
filterByProfile,
|
|
41
|
+
filterUnknownSlidesVisualsTypeDiagnostics,
|
|
38
42
|
// Provenance
|
|
39
43
|
generateProvenance,
|
|
44
|
+
loadDataBindings,
|
|
40
45
|
// YAML/Config parsing
|
|
41
46
|
loadSiteConfig,
|
|
47
|
+
mergeFrontmatterWithBody,
|
|
42
48
|
type Provenance,
|
|
49
|
+
pagePathForData,
|
|
43
50
|
// Markdown utilities
|
|
44
51
|
parseFrontmatter,
|
|
45
52
|
parseYaml,
|
|
53
|
+
resolveBindings,
|
|
46
54
|
resolveStylesPath,
|
|
47
55
|
resolveTemplatePath,
|
|
48
56
|
rewriteRelativeAssetUrls,
|
|
57
|
+
runPrebuildHooks,
|
|
49
58
|
// Types
|
|
50
59
|
type SiteConfig,
|
|
51
60
|
slugify,
|
|
@@ -59,6 +68,41 @@ const DEFAULT_OUT = "dist";
|
|
|
59
68
|
|
|
60
69
|
let ROOT = process.cwd();
|
|
61
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
|
+
}
|
|
62
106
|
|
|
63
107
|
// ---------------------------------------------------------------------------
|
|
64
108
|
// CLI argument parsing
|
|
@@ -68,6 +112,7 @@ interface ParsedArgs {
|
|
|
68
112
|
folder?: string;
|
|
69
113
|
out?: string;
|
|
70
114
|
raw?: boolean;
|
|
115
|
+
profile?: string;
|
|
71
116
|
}
|
|
72
117
|
|
|
73
118
|
function parseArgs(argv: string[]): ParsedArgs {
|
|
@@ -79,6 +124,9 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
79
124
|
if ((arg === "--out" || arg === "-o") && next && !next.startsWith("-")) {
|
|
80
125
|
result.out = next;
|
|
81
126
|
i++;
|
|
127
|
+
} else if (arg === "--profile" && next && !next.startsWith("-")) {
|
|
128
|
+
result.profile = next;
|
|
129
|
+
i++;
|
|
82
130
|
} else if (arg === "--raw") {
|
|
83
131
|
result.raw = true;
|
|
84
132
|
} else if (arg === "--no-raw") {
|
|
@@ -90,12 +138,13 @@ function parseArgs(argv: string[]): ParsedArgs {
|
|
|
90
138
|
return result;
|
|
91
139
|
}
|
|
92
140
|
|
|
93
|
-
function getConfig(): { folder?: string; out: string; raw: boolean } {
|
|
141
|
+
function getConfig(): { folder?: string; out: string; raw: boolean; profile?: string } {
|
|
94
142
|
const args = parseArgs(process.argv.slice(2));
|
|
95
143
|
return {
|
|
96
144
|
folder: args.folder,
|
|
97
145
|
out: args.out ?? envString("KITFLY_BUILD_OUT", DEFAULT_OUT),
|
|
98
146
|
raw: args.raw ?? envBool("KITFLY_BUILD_RAW", true),
|
|
147
|
+
profile: args.profile ?? process.env.KITFLY_PROFILE,
|
|
99
148
|
};
|
|
100
149
|
}
|
|
101
150
|
|
|
@@ -188,7 +237,7 @@ async function renderFile(
|
|
|
188
237
|
}
|
|
189
238
|
htmlContent = `<h1>${title}</h1>\n<pre><code class="language-json">${escapeHtml(prettyJson)}</code></pre>`;
|
|
190
239
|
} else {
|
|
191
|
-
const { frontmatter, body } =
|
|
240
|
+
const { frontmatter, body } = await applyDataBindingsToMarkdown(content, filePath, config);
|
|
192
241
|
if (frontmatter.title) {
|
|
193
242
|
title = frontmatter.title as string;
|
|
194
243
|
}
|
|
@@ -198,7 +247,7 @@ async function renderFile(
|
|
|
198
247
|
|
|
199
248
|
const pathPrefix = computePathPrefix(urlKey);
|
|
200
249
|
const nav = buildNavStatic(files, urlKey, config, pathPrefix);
|
|
201
|
-
const footer = buildFooter(provenance, config);
|
|
250
|
+
const footer = buildFooter(provenance, config, pathPrefix);
|
|
202
251
|
const breadcrumbs = buildBreadcrumbsStatic(urlKey, pathPrefix, files, config);
|
|
203
252
|
const toc = buildToc(htmlContent);
|
|
204
253
|
const brandTarget = config.brand.external ? ' target="_blank" rel="noopener"' : "";
|
|
@@ -206,32 +255,50 @@ async function renderFile(
|
|
|
206
255
|
const prismUrls = getPrismUrls(theme);
|
|
207
256
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
208
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
|
+
});
|
|
209
274
|
|
|
210
275
|
return template
|
|
211
276
|
.replace("{{BODY_CLASS}}", "mode-docs")
|
|
212
|
-
.replace(/\{\{PATH_PREFIX\}\}/g, pathPrefix)
|
|
213
|
-
.replace(/\{\{BRAND_URL\}\}/g, config.brand.url)
|
|
214
|
-
.replace(/\{\{BRAND_TARGET\}\}/g, brandTarget)
|
|
215
|
-
.replace(/\{\{BRAND_NAME\}\}/g, config.brand.name)
|
|
216
|
-
.replace(/\{\{BRAND_INITIAL\}\}/g, brandInitial)
|
|
217
|
-
.replace(
|
|
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("{{
|
|
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)
|
|
235
302
|
.replace("{{HOT_RELOAD_SCRIPT}}", "");
|
|
236
303
|
}
|
|
237
304
|
|
|
@@ -273,32 +340,50 @@ sections:
|
|
|
273
340
|
const pathPrefix = "./";
|
|
274
341
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
275
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
|
+
});
|
|
276
359
|
|
|
277
360
|
return template
|
|
278
361
|
.replace("{{BODY_CLASS}}", "mode-docs")
|
|
279
|
-
.replace(/\{\{PATH_PREFIX\}\}/g, pathPrefix)
|
|
280
|
-
.replace(/\{\{BRAND_URL\}\}/g, config.brand.url)
|
|
281
|
-
.replace(/\{\{BRAND_TARGET\}\}/g, brandTarget)
|
|
282
|
-
.replace(/\{\{BRAND_NAME\}\}/g, config.brand.name)
|
|
283
|
-
.replace(/\{\{BRAND_INITIAL\}\}/g, brandInitial)
|
|
284
|
-
.replace(
|
|
285
|
-
.replace(
|
|
286
|
-
.replace(/\{\{
|
|
287
|
-
.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)
|
|
288
373
|
.replace("{{TITLE}}", "Getting Started")
|
|
289
|
-
.replace("{{VERSION}}", uiVersion)
|
|
290
|
-
.replace("{{BRANCH}}", provenance.gitBranch)
|
|
374
|
+
.replace("{{VERSION}}", () => uiVersion)
|
|
375
|
+
.replace("{{BRANCH}}", () => provenance.gitBranch)
|
|
291
376
|
.replace("{{BREADCRUMBS}}", "")
|
|
292
377
|
.replace("{{PAGE_META}}", "")
|
|
293
378
|
.replace("{{NAV}}", "<ul></ul>")
|
|
294
|
-
.replace("{{CONTENT}}", htmlContent)
|
|
379
|
+
.replace("{{CONTENT}}", () => htmlContent)
|
|
295
380
|
.replace("{{TOC}}", "")
|
|
296
|
-
.replace("{{FOOTER}}", buildFooter(provenance, config))
|
|
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)
|
|
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)
|
|
302
387
|
.replace("{{HOT_RELOAD_SCRIPT}}", "");
|
|
303
388
|
}
|
|
304
389
|
|
|
@@ -312,7 +397,9 @@ async function renderSlidesIndex(
|
|
|
312
397
|
): Promise<string> {
|
|
313
398
|
const uiVersion = provenance.version ? `v${provenance.version}` : "unversioned";
|
|
314
399
|
const pathPrefix = "./";
|
|
315
|
-
const slides = await collectSlides(files
|
|
400
|
+
const slides = await collectSlides(files, {
|
|
401
|
+
markdownTransform: (raw, file) => applyDataBindingsForSlides(raw, file.path, config),
|
|
402
|
+
});
|
|
316
403
|
let validateFences = false;
|
|
317
404
|
try {
|
|
318
405
|
const raw = await readFile(join(ROOT, "kitfly.plugins.yaml"), "utf-8");
|
|
@@ -327,7 +414,9 @@ async function renderSlidesIndex(
|
|
|
327
414
|
let inner = "";
|
|
328
415
|
if (slide.kind === "markdown") {
|
|
329
416
|
if (validateFences) {
|
|
330
|
-
const diagnostics =
|
|
417
|
+
const diagnostics = filterUnknownSlidesVisualsTypeDiagnostics(
|
|
418
|
+
validateSlidesVisualsFences(slide.body),
|
|
419
|
+
);
|
|
331
420
|
if (diagnostics.length) {
|
|
332
421
|
const msg = diagnostics
|
|
333
422
|
.slice(0, 12)
|
|
@@ -373,38 +462,56 @@ async function renderSlidesIndex(
|
|
|
373
462
|
</div>
|
|
374
463
|
</div>`;
|
|
375
464
|
|
|
376
|
-
const nav =
|
|
465
|
+
const nav = buildSlideNavHierarchical(slides, config, "slide-1");
|
|
377
466
|
const brandTarget = config.brand.external ? ' target="_blank" rel="noopener"' : "";
|
|
378
467
|
const logoClass = config.brand.logoType === "wordmark" ? "logo-wordmark" : "logo-icon";
|
|
379
468
|
const themeCSS = generateThemeCSS(theme);
|
|
380
469
|
const prismUrls = getPrismUrls(theme);
|
|
381
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
|
+
});
|
|
382
487
|
|
|
383
488
|
return template
|
|
384
489
|
.replace("{{BODY_CLASS}}", "mode-slides")
|
|
385
|
-
.replace(/\{\{PATH_PREFIX\}\}/g, pathPrefix)
|
|
386
|
-
.replace(/\{\{BRAND_URL\}\}/g, config.brand.url)
|
|
387
|
-
.replace(/\{\{BRAND_TARGET\}\}/g, brandTarget)
|
|
388
|
-
.replace(/\{\{BRAND_NAME\}\}/g, config.brand.name)
|
|
389
|
-
.replace(/\{\{BRAND_INITIAL\}\}/g, brandInitial)
|
|
390
|
-
.replace(
|
|
391
|
-
.replace(
|
|
392
|
-
.replace(/\{\{
|
|
393
|
-
.replace(/\{\{
|
|
394
|
-
.replace(
|
|
395
|
-
.replace(
|
|
396
|
-
.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)
|
|
397
504
|
.replace("{{BREADCRUMBS}}", "")
|
|
398
505
|
.replace("{{PAGE_META}}", "")
|
|
399
|
-
.replace("{{NAV}}", nav)
|
|
400
|
-
.replace("{{CONTENT}}", htmlContent)
|
|
506
|
+
.replace("{{NAV}}", () => nav)
|
|
507
|
+
.replace("{{CONTENT}}", () => htmlContent)
|
|
401
508
|
.replace("{{TOC}}", "")
|
|
402
|
-
.replace("{{FOOTER}}", buildFooter(provenance, config))
|
|
403
|
-
.replace("{{THEME_CSS}}", themeCSS)
|
|
404
|
-
.replace("{{PLUGIN_HEAD}}", plugins.head)
|
|
405
|
-
.replace("{{PLUGIN_BODY_END}}", plugins.bodyEnd)
|
|
406
|
-
.replace("{{PRISM_LIGHT_URL}}", prismUrls.light)
|
|
407
|
-
.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)
|
|
408
515
|
.replace("{{HOT_RELOAD_SCRIPT}}", "");
|
|
409
516
|
}
|
|
410
517
|
|
|
@@ -413,6 +520,7 @@ export interface BuildOptions {
|
|
|
413
520
|
folder?: string;
|
|
414
521
|
out?: string;
|
|
415
522
|
raw?: boolean; // Include raw markdown files (default: true)
|
|
523
|
+
profile?: string;
|
|
416
524
|
}
|
|
417
525
|
|
|
418
526
|
let INCLUDE_RAW = true;
|
|
@@ -427,18 +535,29 @@ export async function build(options: BuildOptions = {}) {
|
|
|
427
535
|
if (options.raw === false) {
|
|
428
536
|
INCLUDE_RAW = false;
|
|
429
537
|
}
|
|
538
|
+
ACTIVE_PROFILE = options.profile;
|
|
430
539
|
await buildSite();
|
|
431
540
|
}
|
|
432
541
|
|
|
433
542
|
// Rename internal function
|
|
434
543
|
async function buildSite() {
|
|
435
|
-
const DIST =
|
|
544
|
+
const DIST = resolve(ROOT, normalizeMsysPath(OUT_DIR));
|
|
436
545
|
|
|
437
546
|
console.log("Building site...\n");
|
|
438
547
|
|
|
439
548
|
// Load configuration
|
|
440
549
|
const config = await loadSiteConfig(ROOT);
|
|
441
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
|
+
}
|
|
442
561
|
|
|
443
562
|
// Load theme
|
|
444
563
|
const theme = await loadTheme(ROOT);
|
|
@@ -496,7 +615,11 @@ async function buildSite() {
|
|
|
496
615
|
}
|
|
497
616
|
|
|
498
617
|
// Collect and render all files
|
|
499
|
-
const files = await
|
|
618
|
+
const files = await filterByProfile(
|
|
619
|
+
await collectFiles(ROOT, config),
|
|
620
|
+
ACTIVE_PROFILE,
|
|
621
|
+
config.profiles,
|
|
622
|
+
);
|
|
500
623
|
|
|
501
624
|
if (files.length === 0) {
|
|
502
625
|
// No content - render Getting Started page
|
|
@@ -730,6 +853,7 @@ Usage: bun run build [folder] [options]
|
|
|
730
853
|
|
|
731
854
|
Options:
|
|
732
855
|
-o, --out <dir> Output directory [env: KITFLY_BUILD_OUT] [default: ${DEFAULT_OUT}]
|
|
856
|
+
--profile <name> Active content profile [env: KITFLY_PROFILE]
|
|
733
857
|
--raw Include raw markdown files [env: KITFLY_BUILD_RAW] [default: true]
|
|
734
858
|
--no-raw Don't include raw markdown files
|
|
735
859
|
--help Show this help message
|
|
@@ -749,5 +873,6 @@ Examples:
|
|
|
749
873
|
folder: cfg.folder,
|
|
750
874
|
out: cfg.out,
|
|
751
875
|
raw: cfg.raw,
|
|
876
|
+
profile: cfg.profile,
|
|
752
877
|
}).catch(console.error);
|
|
753
878
|
}
|