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.
Files changed (108) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +25 -10
  3. package/VERSION +1 -1
  4. package/dist/_raw/content/guide/branding.md +146 -0
  5. package/dist/_raw/content/guide/data-driven-content.md +204 -0
  6. package/dist/_raw/content/reference/configuration.md +145 -7
  7. package/dist/_raw/content/reference/environment-variables.md +26 -1
  8. package/dist/_raw/content/reference/glossary.md +25 -1
  9. package/dist/_raw/content/reference/key-concepts.md +30 -2
  10. package/dist/_raw/content/reference/plugins.md +14 -0
  11. package/dist/_raw/docs/decisions/ADR-0006-data-driven-content.md +350 -0
  12. package/dist/content/deployment/preflight.html +10 -6
  13. package/dist/content/deployment/recipes/aws-s3.html +10 -6
  14. package/dist/content/deployment/recipes/cloudflare-pages.html +10 -6
  15. package/dist/content/deployment/recipes/cloudflare-r2.html +10 -6
  16. package/dist/content/deployment/recipes/fly-io.html +10 -6
  17. package/dist/content/deployment/recipes/github-pages.html +10 -6
  18. package/dist/content/deployment/recipes/netlify.html +10 -6
  19. package/dist/content/deployment/recipes/vercel.html +10 -6
  20. package/dist/content/deployment/secrets-and-env-vars.html +10 -6
  21. package/dist/content/deployment.html +10 -6
  22. package/dist/content/guide/approaches.html +10 -6
  23. package/dist/content/guide/branding.html +510 -0
  24. package/dist/content/guide/data-driven-content.html +543 -0
  25. package/dist/content/guide/features.html +10 -6
  26. package/dist/content/guide/getting-started.html +10 -6
  27. package/dist/content/guide/kitfly-overview.html +10 -6
  28. package/dist/content/reference/configuration.html +135 -9
  29. package/dist/content/reference/design-catalog.html +10 -6
  30. package/dist/content/reference/environment-variables.html +50 -8
  31. package/dist/content/reference/glossary.html +24 -8
  32. package/dist/content/reference/key-concepts.html +33 -9
  33. package/dist/content/reference/plugins.html +22 -7
  34. package/dist/content/reference/slides-authoring-guidelines.html +10 -6
  35. package/dist/content/reference/structure.html +10 -6
  36. package/dist/content/reference.html +10 -6
  37. package/dist/content/templates/crucible.html +10 -6
  38. package/dist/content/templates/handbook.html +10 -6
  39. package/dist/content/templates/minimal.html +10 -6
  40. package/dist/content/templates/overview.html +10 -6
  41. package/dist/content/templates/pipeline.html +10 -6
  42. package/dist/content/templates/productbook.html +10 -6
  43. package/dist/content/templates/runbook.html +10 -6
  44. package/dist/content/templates/servicebook.html +10 -6
  45. package/dist/content-index.json +29 -2
  46. package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +10 -6
  47. package/dist/docs/decisions/ADR-0002-ai-accessibility.html +10 -6
  48. package/dist/docs/decisions/ADR-0003-single-file-bundle.html +10 -6
  49. package/dist/docs/decisions/ADR-0004-bun-runtime.html +10 -6
  50. package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +10 -6
  51. package/dist/docs/decisions/ADR-0006-data-driven-content.html +752 -0
  52. package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +10 -6
  53. package/dist/docs/decisions/DDR-0002-theme-system.html +10 -6
  54. package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +10 -6
  55. package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +10 -6
  56. package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +10 -6
  57. package/dist/docs/userguide/cli/build.html +10 -6
  58. package/dist/docs/userguide/cli/bundle.html +10 -6
  59. package/dist/docs/userguide/cli/dev.html +10 -6
  60. package/dist/docs/userguide/cli/init.html +10 -6
  61. package/dist/docs/userguide/cli/servers.html +10 -6
  62. package/dist/docs/userguide/cli/stop.html +10 -6
  63. package/dist/docs/userguide/cli/update.html +10 -6
  64. package/dist/docs/userguide/cli/version.html +10 -6
  65. package/dist/docs/userguide/cli.html +10 -6
  66. package/dist/docs/userguide/sharing.html +10 -6
  67. package/dist/index.html +10 -6
  68. package/dist/llms.txt +3 -3
  69. package/dist/provenance.json +4 -4
  70. package/dist/schemas/plugin-registry.schema.html +10 -6
  71. package/dist/schemas/plugin-schemas-notes.html +10 -6
  72. package/dist/schemas/plugin.schema.html +10 -6
  73. package/dist/schemas/plugins.schema.html +10 -6
  74. package/dist/schemas/v0/common.schema.html +14 -10
  75. package/dist/schemas/v0/plugin-registry.schema.html +13 -9
  76. package/dist/schemas/v0/plugin.schema.html +13 -9
  77. package/dist/schemas/v0/plugins.schema.html +13 -9
  78. package/dist/schemas/v0/site.schema.html +67 -7
  79. package/dist/schemas/v0/theme.schema.html +21 -17
  80. package/dist/schemas.html +10 -6
  81. package/dist/styles.css +39 -4
  82. package/package.json +1 -1
  83. package/plugins-dist/latex-runtime.js +140 -0
  84. package/plugins-dist/latex.js +178 -0
  85. package/plugins-dist/slides-charts-lite-runtime.js +179 -0
  86. package/plugins-dist/slides-charts-lite.js +198 -0
  87. package/registry/plugins.yaml +25 -0
  88. package/schemas/v0/site.schema.json +56 -0
  89. package/scripts/build.ts +191 -69
  90. package/scripts/bundle.ts +118 -10
  91. package/scripts/dev.ts +245 -166
  92. package/src/__tests__/brief.test.ts +151 -0
  93. package/src/__tests__/build.test.ts +169 -1
  94. package/src/__tests__/bundle.test.ts +134 -0
  95. package/src/__tests__/init.test.ts +51 -2
  96. package/src/__tests__/latex-runtime.bun.test.ts +35 -0
  97. package/src/__tests__/shared.test.ts +598 -1
  98. package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
  99. package/src/cli.ts +11 -4
  100. package/src/commands/init.ts +1 -1
  101. package/src/shared.ts +725 -18
  102. package/src/site/styles.css +39 -4
  103. package/src/site/template.html +5 -2
  104. package/src/templates/brief.ts +486 -0
  105. package/src/templates/deck.ts +59 -0
  106. package/src/templates/driver.ts +46 -13
  107. package/src/templates/handbook.ts +32 -0
  108. package/src/templates/runbook.ts +32 -0
@@ -0,0 +1,140 @@
1
+ (() => {
2
+ function isCurrency(expr) {
3
+ return /^\d[\d,]*(?:\.\d+)?(?:\s*(?:to|-)\s*\d[\d,]*(?:\.\d+)?)?$/.test(expr.trim());
4
+ }
5
+
6
+ function shouldTreatAsLiteralInline(expr, text, closingIndex) {
7
+ if (/^\s|\s$/.test(expr) || /\n/.test(expr) || isCurrency(expr)) return true;
8
+ // Guard common currency range form "$5-$10" where expr becomes "5-".
9
+ if (/-\s*$/.test(expr)) {
10
+ const tail = text.slice(closingIndex + 1);
11
+ if (/^\d/.test(tail)) return true;
12
+ }
13
+ return false;
14
+ }
15
+
16
+ function findClosing(text, start, display) {
17
+ for (let i = start; i < text.length; i++) {
18
+ if (text[i] === "\\" && text[i + 1] === "$") {
19
+ i++;
20
+ continue;
21
+ }
22
+ if (display) {
23
+ if (text[i] === "$" && text[i + 1] === "$") return i;
24
+ } else if (text[i] === "$" && text[i - 1] !== "$" && text[i + 1] !== "$") {
25
+ return i;
26
+ }
27
+ }
28
+ return -1;
29
+ }
30
+
31
+ function splitMath(text) {
32
+ const out = [];
33
+ let buf = "";
34
+ let i = 0;
35
+
36
+ while (i < text.length) {
37
+ if (text[i] === "\\" && text[i + 1] === "$") {
38
+ buf += "$";
39
+ i += 2;
40
+ continue;
41
+ }
42
+ if (text[i] !== "$") {
43
+ buf += text[i++];
44
+ continue;
45
+ }
46
+
47
+ const display = text[i + 1] === "$";
48
+ const openLen = display ? 2 : 1;
49
+ const end = findClosing(text, i + openLen, display);
50
+ if (end < 0) {
51
+ buf += display ? "$$" : "$";
52
+ i += openLen;
53
+ continue;
54
+ }
55
+
56
+ const expr = text.slice(i + openLen, end);
57
+ if (!display && shouldTreatAsLiteralInline(expr, text, end)) {
58
+ buf += `$${expr}$`;
59
+ i = end + 1;
60
+ continue;
61
+ }
62
+
63
+ if (buf) out.push({ text: buf });
64
+ out.push({ math: expr, display });
65
+ buf = "";
66
+ i = end + openLen;
67
+ }
68
+
69
+ if (buf) out.push({ text: buf });
70
+ return out;
71
+ }
72
+
73
+ function renderFencedMath() {
74
+ document.querySelectorAll("pre > code.language-math").forEach((code) => {
75
+ const pre = code.parentElement;
76
+ if (!pre || pre.tagName !== "PRE") return;
77
+ const host = document.createElement("div");
78
+ host.className = "kitfly-katex-display";
79
+ window.katex.render(code.textContent || "", host, { displayMode: true, throwOnError: false });
80
+ pre.replaceWith(host);
81
+ });
82
+ }
83
+
84
+ function renderDelimitedMath() {
85
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
86
+ const nodes = [];
87
+ while (walker.nextNode()) {
88
+ const node = walker.currentNode;
89
+ const parent = node.parentElement;
90
+ if (!parent || parent.closest("pre, code, script, style, textarea, .katex")) continue;
91
+ if (!(node.textContent || "").includes("$")) continue;
92
+ nodes.push(node);
93
+ }
94
+
95
+ for (const node of nodes) {
96
+ const parts = splitMath(node.textContent || "");
97
+ if (!parts.some((p) => p.math)) continue;
98
+ const frag = document.createDocumentFragment();
99
+ for (const p of parts) {
100
+ if (p.text != null) {
101
+ frag.appendChild(document.createTextNode(p.text));
102
+ } else if (p.math != null) {
103
+ const host = document.createElement("span");
104
+ host.className = p.display ? "kitfly-katex-display" : "kitfly-katex-inline";
105
+ window.katex.render(p.math, host, { displayMode: Boolean(p.display), throwOnError: false });
106
+ frag.appendChild(host);
107
+ }
108
+ }
109
+ node.replaceWith(frag);
110
+ }
111
+ }
112
+
113
+ function apply() {
114
+ const styleId = "kitfly-latex-style";
115
+ if (!document.getElementById(styleId)) {
116
+ const style = document.createElement("style");
117
+ style.id = styleId;
118
+ style.textContent = ".kitfly-katex-display{display:block;text-align:center;margin:1em 0;overflow-x:auto}";
119
+ document.head.appendChild(style);
120
+ }
121
+
122
+ if (!window.katex?.render) {
123
+ console.warn("[kitfly:latex] KaTeX unavailable");
124
+ return;
125
+ }
126
+
127
+ renderFencedMath();
128
+ renderDelimitedMath();
129
+ }
130
+
131
+ if (typeof document === "undefined") {
132
+ globalThis.__kitflyLatexTest = { splitMath, isCurrency, shouldTreatAsLiteralInline };
133
+ return;
134
+ }
135
+ if (document.readyState === "loading") {
136
+ document.addEventListener("DOMContentLoaded", apply);
137
+ } else {
138
+ apply();
139
+ }
140
+ })();
@@ -0,0 +1,178 @@
1
+ (() => {
2
+ const KATEX_CSS_URL = "https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css";
3
+ const KATEX_CSS_SRI = "sha384-zh0CIslj+VczCZtlzBcjt5ppRcsAmDnRem7ESsYwWwg3m/OaJ2l4x7YBZl9Kxxib";
4
+ const KATEX_JS_URL = "https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js";
5
+ const KATEX_JS_SRI = "sha384-Rma6DA2IPUwhNxmrB/7S3Tno0YY7sFu9WSYMCuulLhIqYSGZ2gKCJWIqhBWqMQfh";
6
+
7
+ function isCurrency(expr) {
8
+ return /^\d[\d,]*(?:\.\d+)?(?:\s*(?:to|-)\s*\d[\d,]*(?:\.\d+)?)?$/.test(expr.trim());
9
+ }
10
+
11
+ function shouldTreatAsLiteralInline(expr, text, closingIndex) {
12
+ if (/^\s|\s$/.test(expr) || /\n/.test(expr) || isCurrency(expr)) return true;
13
+ // Guard common currency range form "$5-$10" where expr becomes "5-".
14
+ if (/-\s*$/.test(expr)) {
15
+ const tail = text.slice(closingIndex + 1);
16
+ if (/^\d/.test(tail)) return true;
17
+ }
18
+ return false;
19
+ }
20
+
21
+ function findClosing(text, start, display) {
22
+ for (let i = start; i < text.length; i++) {
23
+ if (text[i] === "\\" && text[i + 1] === "$") {
24
+ i++;
25
+ continue;
26
+ }
27
+ if (display) {
28
+ if (text[i] === "$" && text[i + 1] === "$") return i;
29
+ } else if (text[i] === "$" && text[i - 1] !== "$" && text[i + 1] !== "$") {
30
+ return i;
31
+ }
32
+ }
33
+ return -1;
34
+ }
35
+
36
+ function splitMath(text) {
37
+ const out = [];
38
+ let buf = "";
39
+ let i = 0;
40
+
41
+ while (i < text.length) {
42
+ if (text[i] === "\\" && text[i + 1] === "$") {
43
+ buf += "$";
44
+ i += 2;
45
+ continue;
46
+ }
47
+ if (text[i] !== "$") {
48
+ buf += text[i++];
49
+ continue;
50
+ }
51
+
52
+ const display = text[i + 1] === "$";
53
+ const openLen = display ? 2 : 1;
54
+ const end = findClosing(text, i + openLen, display);
55
+ if (end < 0) {
56
+ buf += display ? "$$" : "$";
57
+ i += openLen;
58
+ continue;
59
+ }
60
+
61
+ const expr = text.slice(i + openLen, end);
62
+ if (!display && shouldTreatAsLiteralInline(expr, text, end)) {
63
+ buf += `$${expr}$`;
64
+ i = end + 1;
65
+ continue;
66
+ }
67
+
68
+ if (buf) out.push({ text: buf });
69
+ out.push({ math: expr, display });
70
+ buf = "";
71
+ i = end + openLen;
72
+ }
73
+
74
+ if (buf) out.push({ text: buf });
75
+ return out;
76
+ }
77
+
78
+ function ensureTag(tag, attrs) {
79
+ const id = attrs["data-kitfly-latex"];
80
+ const existing = document.querySelector(`${tag}[data-kitfly-latex="${id}"]`);
81
+ if (existing) return Promise.resolve(existing);
82
+ return new Promise((resolve, reject) => {
83
+ const el = document.createElement(tag);
84
+ Object.entries(attrs).forEach(([k, v]) => el.setAttribute(k, v));
85
+ el.addEventListener("load", () => resolve(el), { once: true });
86
+ el.addEventListener("error", () => reject(new Error(`Failed to load ${attrs.href || attrs.src}`)), {
87
+ once: true,
88
+ });
89
+ document.head.appendChild(el);
90
+ });
91
+ }
92
+
93
+ function renderFencedMath() {
94
+ document.querySelectorAll("pre > code.language-math").forEach((code) => {
95
+ const pre = code.parentElement;
96
+ if (!pre || pre.tagName !== "PRE") return;
97
+ const host = document.createElement("div");
98
+ host.className = "kitfly-katex-display";
99
+ window.katex.render(code.textContent || "", host, { displayMode: true, throwOnError: false });
100
+ pre.replaceWith(host);
101
+ });
102
+ }
103
+
104
+ function renderDelimitedMath() {
105
+ const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
106
+ const nodes = [];
107
+ while (walker.nextNode()) {
108
+ const node = walker.currentNode;
109
+ const parent = node.parentElement;
110
+ if (!parent || parent.closest("pre, code, script, style, textarea, .katex")) continue;
111
+ if (!(node.textContent || "").includes("$")) continue;
112
+ nodes.push(node);
113
+ }
114
+
115
+ for (const node of nodes) {
116
+ const parts = splitMath(node.textContent || "");
117
+ if (!parts.some((p) => p.math)) continue;
118
+ const frag = document.createDocumentFragment();
119
+ for (const p of parts) {
120
+ if (p.text != null) {
121
+ frag.appendChild(document.createTextNode(p.text));
122
+ } else if (p.math != null) {
123
+ const host = document.createElement("span");
124
+ host.className = p.display ? "kitfly-katex-display" : "kitfly-katex-inline";
125
+ window.katex.render(p.math, host, { displayMode: Boolean(p.display), throwOnError: false });
126
+ frag.appendChild(host);
127
+ }
128
+ }
129
+ node.replaceWith(frag);
130
+ }
131
+ }
132
+
133
+ function apply() {
134
+ const styleId = "kitfly-latex-style";
135
+ if (!document.getElementById(styleId)) {
136
+ const style = document.createElement("style");
137
+ style.id = styleId;
138
+ style.textContent = ".kitfly-katex-display{display:block;text-align:center;margin:1em 0;overflow-x:auto}";
139
+ document.head.appendChild(style);
140
+ }
141
+
142
+ Promise.all([
143
+ ensureTag("link", {
144
+ rel: "stylesheet",
145
+ href: KATEX_CSS_URL,
146
+ integrity: KATEX_CSS_SRI,
147
+ crossorigin: "anonymous",
148
+ "data-kitfly-latex": "css",
149
+ }),
150
+ ensureTag("script", {
151
+ src: KATEX_JS_URL,
152
+ integrity: KATEX_JS_SRI,
153
+ crossorigin: "anonymous",
154
+ defer: "",
155
+ "data-kitfly-latex": "js",
156
+ }),
157
+ ])
158
+ .then(() => {
159
+ if (!window.katex?.render) {
160
+ console.warn("[kitfly:latex] KaTeX unavailable");
161
+ return;
162
+ }
163
+ renderFencedMath();
164
+ renderDelimitedMath();
165
+ })
166
+ .catch((e) => console.warn("[kitfly:latex]", e instanceof Error ? e.message : String(e)));
167
+ }
168
+
169
+ if (typeof document === "undefined") {
170
+ globalThis.__kitflyLatexTest = { splitMath, isCurrency, shouldTreatAsLiteralInline };
171
+ return;
172
+ }
173
+ if (document.readyState === "loading") {
174
+ document.addEventListener("DOMContentLoaded", apply);
175
+ } else {
176
+ apply();
177
+ }
178
+ })();
@@ -0,0 +1,179 @@
1
+ (() => {
2
+ const KIND_SET = new Set(["bar", "line", "pie"]);
3
+
4
+ function parseValue(raw) {
5
+ const v = String(raw ?? "").trim();
6
+ if (!v) return "";
7
+ if ((v.startsWith("[") && v.endsWith("]")) || (v.startsWith("{") && v.endsWith("}"))) {
8
+ try {
9
+ return JSON.parse(v);
10
+ } catch {
11
+ return v;
12
+ }
13
+ }
14
+ if (/^-?\d+(?:\.\d+)?$/.test(v)) return Number(v);
15
+ const m = v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/);
16
+ return m ? m[1] : v;
17
+ }
18
+
19
+ function parseSpec(text) {
20
+ const out = {};
21
+ const lines = String(text ?? "").split(/\r?\n/);
22
+ for (const raw of lines) {
23
+ const line = raw.trim();
24
+ if (!line || line.startsWith("#")) continue;
25
+ const kv = line.match(/^([a-z0-9_-]+)\s*:\s*(.*)$/i);
26
+ if (!kv) continue;
27
+ out[kv[1]] = parseValue(kv[2]);
28
+ }
29
+
30
+ const kind = String(out.kind || "").toLowerCase();
31
+ const labels = Array.isArray(out.labels) ? out.labels.map((x) => String(x ?? "")) : [];
32
+ const data = Array.isArray(out.data)
33
+ ? out.data
34
+ .map((x) => Number(x))
35
+ .filter((n) => Number.isFinite(n))
36
+ : [];
37
+
38
+ if (!KIND_SET.has(kind)) return null;
39
+ if (!labels.length || !data.length || labels.length !== data.length) return null;
40
+
41
+ const height = typeof out.height === "number" && Number.isFinite(out.height) ? out.height : 300;
42
+ const color = typeof out.color === "string" ? out.color.trim().toLowerCase() : "primary";
43
+ const title = typeof out.title === "string" ? out.title : "";
44
+
45
+ return {
46
+ kind,
47
+ labels,
48
+ data,
49
+ title,
50
+ color,
51
+ height: Math.max(160, Math.min(640, Math.round(height))),
52
+ };
53
+ }
54
+
55
+ function pickThemeColor(hint) {
56
+ const style = getComputedStyle(document.documentElement);
57
+ const map = {
58
+ primary: style.getPropertyValue("--color-primary").trim() || "#2563eb",
59
+ accent: style.getPropertyValue("--color-accent").trim() || "#f59e0b",
60
+ muted: style.getPropertyValue("--color-text-muted")?.trim() || "#6b7280",
61
+ text: style.getPropertyValue("--color-text")?.trim() || "#111827",
62
+ border: style.getPropertyValue("--color-border")?.trim() || "#d1d5db",
63
+ surface: style.getPropertyValue("--color-surface")?.trim() || "#f9fafb",
64
+ };
65
+ return map[hint] || map.primary;
66
+ }
67
+
68
+ function configFromSpec(spec) {
69
+ const base = pickThemeColor(spec.color);
70
+ const isPie = spec.kind === "pie";
71
+ const bg = isPie
72
+ ? spec.labels.map((_, i) => `color-mix(in srgb, ${base} ${Math.max(20, 90 - i * 12)}%, white)`)
73
+ : base;
74
+
75
+ return {
76
+ type: spec.kind,
77
+ data: {
78
+ labels: spec.labels,
79
+ datasets: [
80
+ {
81
+ label: spec.title || "Series",
82
+ data: spec.data,
83
+ backgroundColor: bg,
84
+ borderColor: base,
85
+ borderWidth: 2,
86
+ tension: spec.kind === "line" ? 0.25 : 0,
87
+ fill: spec.kind === "line" ? false : true,
88
+ },
89
+ ],
90
+ },
91
+ options: {
92
+ responsive: true,
93
+ maintainAspectRatio: false,
94
+ plugins: {
95
+ legend: { display: spec.kind === "pie", labels: { color: pickThemeColor("text") } },
96
+ title: { display: Boolean(spec.title), text: spec.title, color: pickThemeColor("text") },
97
+ },
98
+ scales:
99
+ spec.kind === "pie"
100
+ ? undefined
101
+ : {
102
+ x: { ticks: { color: pickThemeColor("text") }, grid: { color: pickThemeColor("border") } },
103
+ y: { ticks: { color: pickThemeColor("text") }, grid: { color: pickThemeColor("border") } },
104
+ },
105
+ },
106
+ };
107
+ }
108
+
109
+ function renderWrapper(wrapper, spec) {
110
+ let canvas = wrapper.querySelector("canvas");
111
+ if (!canvas) {
112
+ canvas = document.createElement("canvas");
113
+ wrapper.appendChild(canvas);
114
+ }
115
+ wrapper.style.height = `${spec.height}px`;
116
+ if (wrapper.__kitflyChart && typeof wrapper.__kitflyChart.destroy === "function") {
117
+ wrapper.__kitflyChart.destroy();
118
+ }
119
+ wrapper.__kitflyChart = new window.Chart(canvas, configFromSpec(spec));
120
+ }
121
+
122
+ function transformCodeBlock(code) {
123
+ const pre = code.parentElement;
124
+ if (!pre || pre.tagName !== "PRE") return;
125
+ const spec = parseSpec(code.textContent || "");
126
+ if (!spec) {
127
+ console.warn("[kitfly:slides-charts-lite] Invalid chart block, leaving as code");
128
+ return;
129
+ }
130
+
131
+ const wrapper = document.createElement("div");
132
+ wrapper.className = "kitfly-chart-wrapper";
133
+ wrapper.setAttribute("data-kitfly-chart-spec", JSON.stringify(spec));
134
+ pre.replaceWith(wrapper);
135
+ renderWrapper(wrapper, spec);
136
+ }
137
+
138
+ function renderAll() {
139
+ if (!window.Chart) {
140
+ console.warn("[kitfly:slides-charts-lite] Chart.js unavailable");
141
+ return;
142
+ }
143
+
144
+ const styleId = "kitfly-charts-lite-style";
145
+ if (!document.getElementById(styleId)) {
146
+ const style = document.createElement("style");
147
+ style.id = styleId;
148
+ style.textContent = ".kitfly-chart-wrapper{position:relative;width:100%;max-width:100%;margin:1rem 0}";
149
+ document.head.appendChild(style);
150
+ }
151
+
152
+ document.querySelectorAll("pre > code.language-chart").forEach(transformCodeBlock);
153
+ }
154
+
155
+ function reinitCharts() {
156
+ document.querySelectorAll(".kitfly-chart-wrapper[data-kitfly-chart-spec]").forEach((wrapper) => {
157
+ const raw = wrapper.getAttribute("data-kitfly-chart-spec") || "";
158
+ try {
159
+ const spec = JSON.parse(raw);
160
+ renderWrapper(wrapper, spec);
161
+ } catch {
162
+ // ignore broken metadata
163
+ }
164
+ });
165
+ }
166
+
167
+ if (typeof document === "undefined") {
168
+ globalThis.__kitflyChartsLiteTest = { parseSpec };
169
+ return;
170
+ }
171
+
172
+ window.reinitCharts = reinitCharts;
173
+
174
+ if (document.readyState === "loading") {
175
+ document.addEventListener("DOMContentLoaded", renderAll);
176
+ } else {
177
+ renderAll();
178
+ }
179
+ })();