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.
Files changed (209) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +63 -16
  3. package/VERSION +1 -1
  4. package/dist/_raw/content/deployment/preflight.md +134 -0
  5. package/dist/_raw/content/deployment/recipes/aws-s3.md +128 -0
  6. package/dist/_raw/content/deployment/recipes/cloudflare-pages.md +73 -0
  7. package/dist/_raw/content/deployment/recipes/cloudflare-r2.md +156 -0
  8. package/dist/_raw/content/deployment/recipes/fly-io.md +57 -0
  9. package/dist/_raw/content/deployment/recipes/github-pages.md +112 -0
  10. package/dist/_raw/content/deployment/recipes/netlify.md +99 -0
  11. package/dist/_raw/content/deployment/recipes/vercel.md +88 -0
  12. package/dist/_raw/content/deployment/secrets-and-env-vars.md +75 -0
  13. package/dist/_raw/content/deployment.md +128 -0
  14. package/dist/_raw/content/guide/approaches.md +182 -0
  15. package/dist/_raw/content/guide/features.md +121 -0
  16. package/dist/_raw/content/guide/getting-started.md +112 -0
  17. package/dist/_raw/content/guide/kitfly-overview.md +209 -0
  18. package/dist/_raw/content/reference/configuration.md +259 -0
  19. package/dist/_raw/content/reference/design-catalog.md +167 -0
  20. package/dist/_raw/content/reference/environment-variables.md +66 -0
  21. package/dist/_raw/content/reference/glossary.md +92 -0
  22. package/dist/_raw/content/reference/key-concepts.md +118 -0
  23. package/dist/_raw/content/reference/plugins.md +220 -0
  24. package/dist/_raw/content/reference/slides-authoring-guidelines.md +129 -0
  25. package/dist/_raw/content/reference/structure.md +166 -0
  26. package/dist/_raw/content/reference.md +20 -0
  27. package/dist/_raw/content/templates/crucible.md +192 -0
  28. package/dist/_raw/content/templates/handbook.md +83 -0
  29. package/dist/_raw/content/templates/minimal.md +138 -0
  30. package/dist/_raw/content/templates/overview.md +187 -0
  31. package/dist/_raw/content/templates/pipeline.md +151 -0
  32. package/dist/_raw/content/templates/productbook.md +187 -0
  33. package/dist/_raw/content/templates/runbook.md +193 -0
  34. package/dist/_raw/content/templates/servicebook.md +163 -0
  35. package/dist/_raw/docs/decisions/ADR-0001-minimalist-site-code.md +118 -0
  36. package/dist/_raw/docs/decisions/ADR-0002-ai-accessibility.md +153 -0
  37. package/dist/_raw/docs/decisions/ADR-0003-single-file-bundle.md +93 -0
  38. package/dist/_raw/docs/decisions/ADR-0004-bun-runtime.md +98 -0
  39. package/dist/_raw/docs/decisions/ADR-0005-plugin-contract-and-distribution.md +110 -0
  40. package/dist/_raw/docs/decisions/DDR-0001-viewport-locked-layout.md +111 -0
  41. package/dist/_raw/docs/decisions/DDR-0002-theme-system.md +131 -0
  42. package/dist/_raw/docs/decisions/DDR-0003-bounded-logo-slot.md +106 -0
  43. package/dist/_raw/docs/decisions/DDR-0004-slides-rendering-model.md +113 -0
  44. package/dist/_raw/docs/decisions/DDR-0005-deterministic-layout-boundary.md +107 -0
  45. package/dist/_raw/docs/userguide/cli/build.md +85 -0
  46. package/dist/_raw/docs/userguide/cli/bundle.md +81 -0
  47. package/dist/_raw/docs/userguide/cli/dev.md +92 -0
  48. package/dist/_raw/docs/userguide/cli/init.md +116 -0
  49. package/dist/_raw/docs/userguide/cli/servers.md +69 -0
  50. package/dist/_raw/docs/userguide/cli/stop.md +76 -0
  51. package/dist/_raw/docs/userguide/cli/update.md +78 -0
  52. package/dist/_raw/docs/userguide/cli/version.md +65 -0
  53. package/dist/_raw/docs/userguide/cli.md +34 -0
  54. package/dist/_raw/docs/userguide/sharing.md +94 -0
  55. package/dist/_raw/schemas/plugin-schemas-notes.md +71 -0
  56. package/dist/_raw/schemas.md +42 -0
  57. package/dist/assets/brand/kitfly-favicon-32.png +0 -0
  58. package/dist/assets/brand/kitfly-icon-64.png +0 -0
  59. package/dist/assets/brand/kitfly-logo-128.png +0 -0
  60. package/dist/assets/brand/kitfly-logo-512.png +0 -0
  61. package/dist/assets/brand/kitfly-logo.svg +12132 -0
  62. package/dist/assets/brand/kitfly-neon-128.png +0 -0
  63. package/dist/assets/brand/kitfly-neon-192.png +0 -0
  64. package/dist/assets/brand/kitfly-neon-256.png +0 -0
  65. package/dist/assets/brand/kitfly-neon.png +0 -0
  66. package/dist/assets/brand/palette.md +75 -0
  67. package/dist/content/deployment/index.html +11 -0
  68. package/dist/content/deployment/preflight.html +418 -0
  69. package/dist/content/deployment/recipes/aws-s3.html +421 -0
  70. package/dist/content/deployment/recipes/cloudflare-pages.html +372 -0
  71. package/dist/content/deployment/recipes/cloudflare-r2.html +443 -0
  72. package/dist/content/deployment/recipes/fly-io.html +356 -0
  73. package/dist/content/deployment/recipes/github-pages.html +414 -0
  74. package/dist/content/deployment/recipes/index.html +11 -0
  75. package/dist/content/deployment/recipes/netlify.html +394 -0
  76. package/dist/content/deployment/recipes/vercel.html +382 -0
  77. package/dist/content/deployment/secrets-and-env-vars.html +380 -0
  78. package/dist/content/deployment.html +426 -0
  79. package/dist/content/guide/approaches.html +501 -0
  80. package/dist/content/guide/features.html +436 -0
  81. package/dist/content/guide/getting-started.html +403 -0
  82. package/dist/content/guide/index.html +11 -0
  83. package/dist/content/guide/kitfly-overview.html +544 -0
  84. package/dist/content/index.html +11 -0
  85. package/dist/content/reference/configuration.html +580 -0
  86. package/dist/content/reference/design-catalog.html +449 -0
  87. package/dist/content/reference/environment-variables.html +367 -0
  88. package/dist/content/reference/glossary.html +368 -0
  89. package/dist/content/reference/index.html +11 -0
  90. package/dist/content/reference/key-concepts.html +399 -0
  91. package/dist/content/reference/plugins.html +491 -0
  92. package/dist/content/reference/slides-authoring-guidelines.html +418 -0
  93. package/dist/content/reference/structure.html +463 -0
  94. package/dist/content/reference.html +335 -0
  95. package/dist/content/templates/crucible.html +546 -0
  96. package/dist/content/templates/handbook.html +405 -0
  97. package/dist/content/templates/index.html +11 -0
  98. package/dist/content/templates/minimal.html +447 -0
  99. package/dist/content/templates/overview.html +558 -0
  100. package/dist/content/templates/pipeline.html +494 -0
  101. package/dist/content/templates/productbook.html +540 -0
  102. package/dist/content/templates/runbook.html +543 -0
  103. package/dist/content/templates/servicebook.html +523 -0
  104. package/dist/content-index.json +549 -0
  105. package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +491 -0
  106. package/dist/docs/decisions/ADR-0002-ai-accessibility.html +434 -0
  107. package/dist/docs/decisions/ADR-0003-single-file-bundle.html +412 -0
  108. package/dist/docs/decisions/ADR-0004-bun-runtime.html +409 -0
  109. package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +402 -0
  110. package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +459 -0
  111. package/dist/docs/decisions/DDR-0002-theme-system.html +452 -0
  112. package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +423 -0
  113. package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +399 -0
  114. package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +422 -0
  115. package/dist/docs/decisions/index.html +11 -0
  116. package/dist/docs/userguide/cli/build.html +408 -0
  117. package/dist/docs/userguide/cli/bundle.html +419 -0
  118. package/dist/docs/userguide/cli/dev.html +428 -0
  119. package/dist/docs/userguide/cli/index.html +11 -0
  120. package/dist/docs/userguide/cli/init.html +436 -0
  121. package/dist/docs/userguide/cli/servers.html +393 -0
  122. package/dist/docs/userguide/cli/stop.html +408 -0
  123. package/dist/docs/userguide/cli/update.html +406 -0
  124. package/dist/docs/userguide/cli/version.html +406 -0
  125. package/dist/docs/userguide/cli.html +386 -0
  126. package/dist/docs/userguide/index.html +11 -0
  127. package/dist/docs/userguide/sharing.html +465 -0
  128. package/dist/index.html +387 -0
  129. package/dist/llms.txt +18 -0
  130. package/dist/provenance.json +7 -0
  131. package/dist/schemas/index.html +11 -0
  132. package/dist/schemas/plugin-registry.schema.html +327 -0
  133. package/dist/schemas/plugin-schemas-notes.html +364 -0
  134. package/dist/schemas/plugin.schema.html +327 -0
  135. package/dist/schemas/plugins.schema.html +327 -0
  136. package/dist/schemas/v0/common.schema.html +386 -0
  137. package/dist/schemas/v0/index.html +11 -0
  138. package/dist/schemas/v0/plugin-registry.schema.html +547 -0
  139. package/dist/schemas/v0/plugin.schema.html +497 -0
  140. package/dist/schemas/v0/plugins.schema.html +406 -0
  141. package/dist/schemas/v0/site.schema.html +541 -0
  142. package/dist/schemas/v0/theme.schema.html +615 -0
  143. package/dist/schemas.html +351 -0
  144. package/dist/styles.css +1262 -0
  145. package/package.json +4 -2
  146. package/plugins-dist/callouts.css +32 -0
  147. package/plugins-dist/callouts.js +46 -0
  148. package/plugins-dist/slides-visuals.css +390 -0
  149. package/plugins-dist/slides-visuals.js +689 -0
  150. package/registry/plugins.yaml +35 -0
  151. package/schemas/README.md +10 -0
  152. package/schemas/plugin-registry.schema.json +5 -0
  153. package/schemas/plugin-schemas-notes.md +71 -0
  154. package/schemas/plugin.schema.json +5 -0
  155. package/schemas/plugins.schema.json +5 -0
  156. package/schemas/v0/common.schema.json +64 -0
  157. package/schemas/v0/plugin-registry.schema.json +225 -0
  158. package/schemas/v0/plugin.schema.json +175 -0
  159. package/schemas/v0/plugins.schema.json +84 -0
  160. package/schemas/v0/site.schema.json +56 -9
  161. package/schemas/v0/theme.schema.json +105 -22
  162. package/scripts/build.ts +158 -3
  163. package/scripts/bundle.ts +261 -95
  164. package/scripts/dev.ts +301 -11
  165. package/src/__tests__/build.test.ts +220 -1
  166. package/src/__tests__/bundle.test.ts +31 -0
  167. package/src/__tests__/cli.test.ts +14 -3
  168. package/src/__tests__/dev-plugin-errors.test.ts +20 -0
  169. package/src/__tests__/fixtures/fences/slides-visuals/invalid/bad-list-indent.md +5 -0
  170. package/src/__tests__/fixtures/fences/slides-visuals/invalid/blank-line.md +5 -0
  171. package/src/__tests__/fixtures/fences/slides-visuals/invalid/compare-object-items.md +9 -0
  172. package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-branching-no-source.md +5 -0
  173. package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-converging-no-target.md +6 -0
  174. package/src/__tests__/fixtures/fences/slides-visuals/invalid/indented-fence.md +4 -0
  175. package/src/__tests__/fixtures/fences/slides-visuals/invalid/staircase-empty-steps.md +3 -0
  176. package/src/__tests__/fixtures/fences/slides-visuals/invalid/stat-grid-missing-fields.md +5 -0
  177. package/src/__tests__/fixtures/fences/slides-visuals/invalid/timeline-horizontal-no-events.md +2 -0
  178. package/src/__tests__/fixtures/fences/slides-visuals/invalid/unknown-type.md +3 -0
  179. package/src/__tests__/fixtures/fences/slides-visuals/valid/compare.md +10 -0
  180. package/src/__tests__/fixtures/fences/slides-visuals/valid/comparison-table.md +14 -0
  181. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching-no-split.md +7 -0
  182. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching.md +8 -0
  183. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging-no-merge.md +7 -0
  184. package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging.md +8 -0
  185. package/src/__tests__/fixtures/fences/slides-visuals/valid/funnel.md +7 -0
  186. package/src/__tests__/fixtures/fences/slides-visuals/valid/kpi.md +5 -0
  187. package/src/__tests__/fixtures/fences/slides-visuals/valid/layer-cake.md +6 -0
  188. package/src/__tests__/fixtures/fences/slides-visuals/valid/pyramid.md +6 -0
  189. package/src/__tests__/fixtures/fences/slides-visuals/valid/quadrant-grid.md +8 -0
  190. package/src/__tests__/fixtures/fences/slides-visuals/valid/scorecard.md +13 -0
  191. package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase-down.md +7 -0
  192. package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase.md +8 -0
  193. package/src/__tests__/fixtures/fences/slides-visuals/valid/stat-grid.md +8 -0
  194. package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-horizontal.md +9 -0
  195. package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-vertical.md +10 -0
  196. package/src/__tests__/init.test.ts +35 -0
  197. package/src/__tests__/plugin-loader.test.ts +221 -0
  198. package/src/__tests__/shared.test.ts +451 -0
  199. package/src/__tests__/slides-visuals-fence-contract.test.ts +28 -0
  200. package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +147 -0
  201. package/src/__tests__/styles.test.ts +35 -0
  202. package/src/cli.ts +9 -4
  203. package/src/plugin-loader.ts +245 -0
  204. package/src/shared.ts +650 -7
  205. package/src/site/styles.css +331 -0
  206. package/src/site/template.html +66 -5
  207. package/src/templates/deck.ts +186 -0
  208. package/src/templates/driver.ts +11 -1
  209. package/src/templates/minimal.ts +1 -0
@@ -0,0 +1,412 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>ADR-0003: Single-File Bundle Output - Kitfly Docs</title>
7
+ <link rel="icon" type="image/png" sizes="32x32" href="../../assets/brand/kitfly-favicon-32.png">
8
+ <link rel="icon" type="image/png" sizes="64x64" href="../../assets/brand/kitfly-neon-256.png">
9
+ <link rel="stylesheet" href="../../styles.css">
10
+ <style id="kitfly-theme">
11
+ :root { --color-bg: #ffffff;
12
+ --color-bg-sidebar: #f5f7f8;
13
+ --color-text: #374151;
14
+ --color-text-muted: #6b7280;
15
+ --color-border: #e5e7eb;
16
+ --color-link: #007182;
17
+ --color-link-hover: #0a6172;
18
+ --color-accent: #152F46;
19
+ --color-code-bg: #f5f7f8;
20
+ --color-logo: #152F46;
21
+ --sidebar-width: 280px;
22
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
23
+ --font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
24
+ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
25
+ html { font-size: 16px; }
26
+ @media (prefers-color-scheme: dark) {
27
+ :root:not([data-theme="light"]) { --color-bg: #0d1117;
28
+ --color-bg-sidebar: #152F46;
29
+ --color-text: #e5e7eb;
30
+ --color-text-muted: #9ca3af;
31
+ --color-border: #374151;
32
+ --color-link: #709EA6;
33
+ --color-link-hover: #8fb5bc;
34
+ --color-accent: #f9fafb;
35
+ --color-code-bg: #152F46;
36
+ --color-logo: #f9fafb; }
37
+ }
38
+ [data-theme="dark"] { --color-bg: #0d1117;
39
+ --color-bg-sidebar: #152F46;
40
+ --color-text: #e5e7eb;
41
+ --color-text-muted: #9ca3af;
42
+ --color-border: #374151;
43
+ --color-link: #709EA6;
44
+ --color-link-hover: #8fb5bc;
45
+ --color-accent: #f9fafb;
46
+ --color-code-bg: #152F46;
47
+ --color-logo: #f9fafb; }
48
+ [data-theme="light"] { --color-bg: #ffffff;
49
+ --color-bg-sidebar: #f5f7f8;
50
+ --color-text: #374151;
51
+ --color-text-muted: #6b7280;
52
+ --color-border: #e5e7eb;
53
+ --color-link: #007182;
54
+ --color-link-hover: #0a6172;
55
+ --color-accent: #152F46;
56
+ --color-code-bg: #f5f7f8;
57
+ --color-logo: #152F46;
58
+ --sidebar-width: 280px;
59
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
60
+ --font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
61
+ --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
62
+ </style>
63
+ <!-- Syntax highlighting - Prism.js -->
64
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.min.css" id="prism-light">
65
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-okaidia.min.css" id="prism-dark" disabled>
66
+
67
+ <script>
68
+ // Apply saved theme immediately to prevent flash
69
+ (function() {
70
+ const saved = localStorage.getItem('theme');
71
+ if (saved) {
72
+ document.documentElement.setAttribute('data-theme', saved);
73
+ }
74
+ // Set Prism theme based on saved or system preference
75
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
76
+ const isDark = saved === 'dark' || (!saved && prefersDark);
77
+ if (isDark) {
78
+ document.getElementById('prism-light')?.setAttribute('disabled', '');
79
+ document.getElementById('prism-dark')?.removeAttribute('disabled');
80
+ }
81
+ })();
82
+ </script>
83
+ </head>
84
+ <body class="mode-docs">
85
+ <div class="mobile-header">
86
+ <button class="nav-toggle" onclick="toggleNav()" aria-label="Toggle navigation">
87
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
88
+ <path d="M3 12h18M3 6h18M3 18h18"/>
89
+ </svg>
90
+ </button>
91
+ <a href="../../" class="mobile-logo" title="Home" data-initial="K">
92
+ <img src="../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img logo-icon" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')"/>
93
+ </a>
94
+ <button class="mobile-theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
95
+ <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
96
+ <circle cx="12" cy="12" r="5"/>
97
+ <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
98
+ </svg>
99
+ <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
100
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
101
+ </svg>
102
+ </button>
103
+ </div>
104
+ <div class="layout">
105
+ <nav class="sidebar">
106
+ <div class="sidebar-header">
107
+ <div class="logo logo-icon">
108
+ <a href="/" class="logo-icon" data-initial="K">
109
+ <img src="../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')"/>
110
+ </a>
111
+ <span class="logo-text">
112
+ <a href="/" class="brand">Kitfly</a>
113
+ <a href="../../" class="product">Kitfly Docs</a>
114
+ </span>
115
+ </div>
116
+ <div class="header-tools">
117
+ <button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
118
+ <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
119
+ <circle cx="12" cy="12" r="5"/>
120
+ <path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
121
+ </svg>
122
+ <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
123
+ <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
124
+ </svg>
125
+ </button>
126
+ <div class="sidebar-meta">
127
+ <span class="meta-version">v0.2.1</span>
128
+ <span class="meta-branch">HEAD</span>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ <div class="sidebar-nav">
133
+ <ul><li><a href="../../index.html" class="nav-home">Home</a></li><li><span class="nav-section">Guide</span><ul><li><a href="../../content/guide/approaches.html">approaches</a></li><li><a href="../../content/guide/features.html">features</a></li><li><a href="../../content/guide/getting-started.html">getting-started</a></li><li><a href="../../content/guide/kitfly-overview.html">kitfly-overview</a></li></ul></li><li><span class="nav-section">Templates</span><ul><li><a href="../../content/templates/crucible.html">crucible</a></li><li><a href="../../content/templates/handbook.html">handbook</a></li><li><a href="../../content/templates/minimal.html">minimal</a></li><li><a href="../../content/templates/overview.html">overview</a></li><li><a href="../../content/templates/pipeline.html">pipeline</a></li><li><a href="../../content/templates/productbook.html">productbook</a></li><li><a href="../../content/templates/runbook.html">runbook</a></li><li><a href="../../content/templates/servicebook.html">servicebook</a></li></ul></li><li><a href="../../content/reference.html" class="nav-section">Reference</a><ul><li><a href="../../content/reference/configuration.html">configuration</a></li><li><a href="../../content/reference/design-catalog.html">design-catalog</a></li><li><a href="../../content/reference/environment-variables.html">environment-variables</a></li><li><a href="../../content/reference/glossary.html">glossary</a></li><li><a href="../../content/reference/key-concepts.html">key-concepts</a></li><li><a href="../../content/reference/plugins.html">plugins</a></li><li><a href="../../content/reference/slides-authoring-guidelines.html">slides-authoring-guidelines</a></li><li><a href="../../content/reference/structure.html">structure</a></li></ul></li><li><a href="../../content/deployment.html" class="nav-section">Deployment</a><ul><li><a href="../../content/deployment/preflight.html">preflight</a></li><li><details><summary class="nav-group">recipes</summary><ul><li><a href="../../content/deployment/recipes/aws-s3.html">aws-s3</a></li><li><a href="../../content/deployment/recipes/cloudflare-pages.html">cloudflare-pages</a></li><li><a href="../../content/deployment/recipes/cloudflare-r2.html">cloudflare-r2</a></li><li><a href="../../content/deployment/recipes/fly-io.html">fly-io</a></li><li><a href="../../content/deployment/recipes/github-pages.html">github-pages</a></li><li><a href="../../content/deployment/recipes/netlify.html">netlify</a></li><li><a href="../../content/deployment/recipes/vercel.html">vercel</a></li></ul></details></li><li><a href="../../content/deployment/secrets-and-env-vars.html">secrets-and-env-vars</a></li></ul></li><li><span class="nav-section">User Guide</span><ul><li><details><summary class="nav-group"><a href="../../docs/userguide/cli.html">cli</a></summary><ul><li><a href="../../docs/userguide/cli/build.html">build</a></li><li><a href="../../docs/userguide/cli/bundle.html">bundle</a></li><li><a href="../../docs/userguide/cli/dev.html">dev</a></li><li><a href="../../docs/userguide/cli/init.html">init</a></li><li><a href="../../docs/userguide/cli/servers.html">servers</a></li><li><a href="../../docs/userguide/cli/stop.html">stop</a></li><li><a href="../../docs/userguide/cli/update.html">update</a></li><li><a href="../../docs/userguide/cli/version.html">version</a></li></ul></details></li><li><a href="../../docs/userguide/sharing.html">sharing</a></li></ul></li><li><span class="nav-section">Decisions</span><ul><li><a href="../../docs/decisions/ADR-0001-minimalist-site-code.html">ADR-0001-minimalist-site-code</a></li><li><a href="../../docs/decisions/ADR-0002-ai-accessibility.html">ADR-0002-ai-accessibility</a></li><li><a href="../../docs/decisions/ADR-0003-single-file-bundle.html" class="active">ADR-0003-single-file-bundle</a></li><li><a href="../../docs/decisions/ADR-0004-bun-runtime.html">ADR-0004-bun-runtime</a></li><li><a href="../../docs/decisions/ADR-0005-plugin-contract-and-distribution.html">ADR-0005-plugin-contract-and-distribution</a></li><li><a href="../../docs/decisions/DDR-0001-viewport-locked-layout.html">DDR-0001-viewport-locked-layout</a></li><li><a href="../../docs/decisions/DDR-0002-theme-system.html">DDR-0002-theme-system</a></li><li><a href="../../docs/decisions/DDR-0003-bounded-logo-slot.html">DDR-0003-bounded-logo-slot</a></li><li><a href="../../docs/decisions/DDR-0004-slides-rendering-model.html">DDR-0004-slides-rendering-model</a></li><li><a href="../../docs/decisions/DDR-0005-deterministic-layout-boundary.html">DDR-0005-deterministic-layout-boundary</a></li></ul></li><li><a href="../../schemas.html" class="nav-section">Schemas</a><ul><li><a href="../../schemas/plugin-registry.schema.html">plugin-registry.schema</a></li><li><a href="../../schemas/plugin-schemas-notes.html">plugin-schemas-notes</a></li><li><a href="../../schemas/plugin.schema.html">plugin.schema</a></li><li><a href="../../schemas/plugins.schema.html">plugins.schema</a></li><li><details><summary class="nav-group">v0</summary><ul><li><a href="../../schemas/v0/common.schema.html">common.schema</a></li><li><a href="../../schemas/v0/plugin-registry.schema.html">plugin-registry.schema</a></li><li><a href="../../schemas/v0/plugin.schema.html">plugin.schema</a></li><li><a href="../../schemas/v0/plugins.schema.html">plugins.schema</a></li><li><a href="../../schemas/v0/site.schema.html">site.schema</a></li><li><a href="../../schemas/v0/theme.schema.html">theme.schema</a></li></ul></details></li></ul></li></ul>
134
+ </div>
135
+ </nav>
136
+ <main class="content">
137
+ <article class="prose">
138
+ <nav class="breadcrumbs"><a href="../../docs/userguide/cli/build.html">Docs</a><span class="separator">›</span><a href="../../docs/decisions/ADR-0001-minimalist-site-code.html">Decisions</a><span class="separator">›</span><span>ADR-0003-single-file-bundle</span></nav>
139
+
140
+ <h1 id="adr-0003-single-file-bundle-output">ADR-0003: Single-File Bundle Output</h1>
141
+ <h2 id="status">Status</h2>
142
+ <p>Accepted</p>
143
+ <h2 id="context">Context</h2>
144
+ <p>Documentation is often shared outside of web hosting — via email, Slack uploads, shared drives, or USB handoff. Traditional static site generators produce a directory of HTML, CSS, JS, and images that require a web server or careful relative-path handling to open locally.</p>
145
+ <p>Kitfly needed a sharing model that works without any infrastructure: one file, any browser, no server.</p>
146
+ <h2 id="decision">Decision</h2>
147
+ <p>Implement <code>kitfly bundle</code> as a first-class output mode alongside <code>kitfly build</code>. The bundle produces a <strong>single self-contained HTML file</strong> with all dependencies inlined.</p>
148
+ <h3 id="what-gets-inlined">What Gets Inlined</h3>
149
+ <table>
150
+ <thead>
151
+ <tr>
152
+ <th>Asset</th>
153
+ <th>Inlining Strategy</th>
154
+ </tr>
155
+ </thead>
156
+ <tbody><tr>
157
+ <td>CSS (styles.css)</td>
158
+ <td><code>&lt;style&gt;</code> block in <code>&lt;head&gt;</code></td>
159
+ </tr>
160
+ <tr>
161
+ <td>Theme CSS</td>
162
+ <td>Generated <code>&lt;style id=&quot;kitfly-theme&quot;&gt;</code> block</td>
163
+ </tr>
164
+ <tr>
165
+ <td>Images (PNG, JPG, SVG, GIF)</td>
166
+ <td>Base64 data URIs in <code>&lt;img src&gt;</code></td>
167
+ </tr>
168
+ <tr>
169
+ <td>Brand logo + favicon</td>
170
+ <td>Base64 data URIs</td>
171
+ </tr>
172
+ <tr>
173
+ <td>Prism.js (syntax highlighting)</td>
174
+ <td>Full JS inlined in <code>&lt;script&gt;</code></td>
175
+ </tr>
176
+ <tr>
177
+ <td>Mermaid (diagrams)</td>
178
+ <td>Full JS inlined in <code>&lt;script&gt;</code></td>
179
+ </tr>
180
+ <tr>
181
+ <td>Dark mode toggle</td>
182
+ <td>Inline <code>&lt;script&gt;</code></td>
183
+ </tr>
184
+ <tr>
185
+ <td>All page content</td>
186
+ <td>Inline HTML sections with <code>id</code> anchors</td>
187
+ </tr>
188
+ </tbody></table>
189
+ <h3 id="navigation-model">Navigation Model</h3>
190
+ <p>The bundle uses <strong>hash-based navigation</strong> rather than page-per-file:</p>
191
+ <ul>
192
+ <li>Each content page becomes a <code>&lt;section id=&quot;slug&quot;&gt;</code> within the single document</li>
193
+ <li>Sidebar links use <code>href=&quot;#slug&quot;</code> anchors</li>
194
+ <li>JavaScript shows/hides sections on hash change</li>
195
+ <li>The sidebar hierarchy uses the same <code>buildSectionNav()</code> function as the static build, with a hash-link adapter: <code>makeHref = (urlPath) =&gt; &#39;#&#39; + slugify(urlPath)</code></li>
196
+ </ul>
197
+ <p>This ensures nav structure parity between <code>build</code> and <code>bundle</code> output.</p>
198
+ <h3 id="raw-markdown">Raw Markdown</h3>
199
+ <p>By default, raw <code>.md</code> source is embedded in the bundle as hidden <code>&lt;script type=&quot;text/markdown&quot;&gt;</code> blocks, accessible to AI agents. The <code>--no-raw</code> flag omits them to reduce file size.</p>
200
+ <h3 id="file-size">File Size</h3>
201
+ <p>Typical documentation produces 100KB–1MB bundles. Image-heavy sites are larger due to base64 overhead (~33% per image). The tradeoff is acceptable because the primary use case is sharing, not serving at scale.</p>
202
+ <h2 id="consequences">Consequences</h2>
203
+ <h3 id="positive">Positive</h3>
204
+ <ul>
205
+ <li><strong>Zero-infrastructure sharing</strong>: Email, Slack, Google Drive — recipients open in any browser</li>
206
+ <li><strong>Offline by default</strong>: No network requests, no CDN dependencies</li>
207
+ <li><strong>Point-in-time snapshot</strong>: Bundle captures exact state of docs at build time</li>
208
+ <li><strong>Same navigation as build</strong>: <code>buildSectionNav()</code> shared between both output modes</li>
209
+ <li><strong>AI-accessible</strong>: Raw markdown available inside the bundle</li>
210
+ </ul>
211
+ <h3 id="negative">Negative</h3>
212
+ <ul>
213
+ <li>Large bundles with many images (base64 adds ~33% overhead)</li>
214
+ <li>No incremental loading — entire document loads at once</li>
215
+ <li>Hash navigation doesn&#39;t support deep-linking from external URLs (fine for the sharing use case)</li>
216
+ <li>Prism + Mermaid JS inlining adds ~500KB to every bundle</li>
217
+ </ul>
218
+ <h3 id="neutral">Neutral</h3>
219
+ <ul>
220
+ <li>Bundle and build share <code>collectFiles()</code>, <code>loadConfig()</code>, and <code>buildSectionNav()</code> from <code>shared.ts</code> — shared code stays in one place</li>
221
+ <li>Bundle is a separate script (<code>scripts/bundle.ts</code>) rather than a mode flag on <code>scripts/build.ts</code>, keeping each focused</li>
222
+ </ul>
223
+ <h2 id="alternatives-considered">Alternatives Considered</h2>
224
+ <h3 id="pdf-export">PDF export</h3>
225
+ <p>Widely shareable but loses interactivity (dark mode, collapsible nav, syntax highlighting themes). Would require a headless browser dependency (Puppeteer/Playwright). May be added later as a third output mode.</p>
226
+ <h3 id="mhtml-web-archive">MHTML / Web Archive</h3>
227
+ <p>Browser-native single-file formats. Rejected because support is inconsistent (Safari doesn&#39;t support MHTML, Chrome&#39;s implementation has quirks) and generation requires browser automation.</p>
228
+ <h3 id="zip-of-static-files">ZIP of static files</h3>
229
+ <p>Solves the single-artifact problem but requires recipients to extract before viewing. Adds friction compared to double-clicking an HTML file.</p>
230
+
231
+ </article>
232
+ <aside class="toc"><span class="toc-title">On this page</span><ul><li><a href="#status">Status</a></li><li><a href="#context">Context</a></li><li><a href="#decision">Decision</a></li><li class="toc-h3"><a href="#what-gets-inlined">What Gets Inlined</a></li><li class="toc-h3"><a href="#navigation-model">Navigation Model</a></li><li class="toc-h3"><a href="#raw-markdown">Raw Markdown</a></li><li class="toc-h3"><a href="#file-size">File Size</a></li><li><a href="#consequences">Consequences</a></li><li class="toc-h3"><a href="#positive">Positive</a></li><li class="toc-h3"><a href="#negative">Negative</a></li><li class="toc-h3"><a href="#neutral">Neutral</a></li><li><a href="#alternatives-considered">Alternatives Considered</a></li><li class="toc-h3"><a href="#pdf-export">PDF export</a></li><li class="toc-h3"><a href="#mhtml-web-archive">MHTML / Web Archive</a></li><li class="toc-h3"><a href="#zip-of-static-files">ZIP of static files</a></li></ul></aside>
233
+ </main>
234
+ </div>
235
+
236
+ <footer class="site-footer">
237
+ <div class="footer-content">
238
+ <div class="footer-left">
239
+ <span class="footer-version">v0.2.1</span>
240
+ <span class="footer-separator">·</span>
241
+ <span class="footer-commit" title="Commit: 30dfc01">Published 2026-02-15</span>
242
+ </div>
243
+ <div class="footer-center">
244
+ <span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
245
+ <span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
246
+ </div>
247
+ <div class="footer-right">
248
+ <a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
249
+ </div>
250
+ </div>
251
+ </footer>
252
+ <!-- Syntax highlighting - Prism.js -->
253
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
254
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
255
+ <!-- Mermaid diagram support -->
256
+ <script type="module">
257
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
258
+
259
+ function getMermaidTheme() {
260
+ const theme = document.documentElement.getAttribute('data-theme');
261
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
262
+ const isDark = theme === 'dark' || (!theme && prefersDark);
263
+ return isDark ? 'dark' : 'neutral';
264
+ }
265
+
266
+ mermaid.initialize({
267
+ startOnLoad: true,
268
+ theme: getMermaidTheme()
269
+ });
270
+
271
+ // Re-render mermaid diagrams when theme changes
272
+ window.reinitMermaid = async function() {
273
+ mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
274
+ const diagrams = document.querySelectorAll('.mermaid');
275
+ for (const el of diagrams) {
276
+ const code = el.getAttribute('data-mermaid-source');
277
+ if (code) {
278
+ el.innerHTML = code;
279
+ el.removeAttribute('data-processed');
280
+ }
281
+ }
282
+ await mermaid.run({ nodes: diagrams });
283
+ };
284
+ </script>
285
+
286
+ <script>
287
+ function toggleTheme() {
288
+ const html = document.documentElement;
289
+ const current = html.getAttribute('data-theme');
290
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
291
+
292
+ let next;
293
+ if (current === 'dark') {
294
+ next = 'light';
295
+ } else if (current === 'light') {
296
+ next = 'dark';
297
+ } else {
298
+ // No explicit theme set, toggle from system preference
299
+ next = prefersDark ? 'light' : 'dark';
300
+ }
301
+
302
+ html.setAttribute('data-theme', next);
303
+ localStorage.setItem('theme', next);
304
+
305
+ // Switch Prism theme
306
+ const prismLight = document.getElementById('prism-light');
307
+ const prismDark = document.getElementById('prism-dark');
308
+ if (next === 'dark') {
309
+ prismLight?.setAttribute('disabled', '');
310
+ prismDark?.removeAttribute('disabled');
311
+ } else {
312
+ prismLight?.removeAttribute('disabled');
313
+ prismDark?.setAttribute('disabled', '');
314
+ }
315
+
316
+ // Re-render mermaid diagrams with new theme
317
+ if (window.reinitMermaid) {
318
+ window.reinitMermaid();
319
+ }
320
+ }
321
+
322
+ // Slides mode hash routing
323
+ (function initSlidesMode() {
324
+ const shell = document.querySelector('.slides-shell');
325
+ if (!shell) return;
326
+
327
+ const slides = Array.from(document.querySelectorAll('.slide'));
328
+ if (!slides.length) return;
329
+
330
+ const prevBtn = document.querySelector('.slide-prev');
331
+ const nextBtn = document.querySelector('.slide-next');
332
+ const counter = document.querySelector('.slide-counter');
333
+ const progressBar = document.querySelector('.slide-progress-bar');
334
+ const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
335
+ let current = 0;
336
+
337
+ function setActive(n) {
338
+ current = Math.max(0, Math.min(n, slides.length - 1));
339
+ slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
340
+ navLinks.forEach((link) => {
341
+ const active = link.getAttribute('href') === '#' + slides[current].id;
342
+ link.classList.toggle('active', active);
343
+ });
344
+ if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
345
+ if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
346
+ if (prevBtn) prevBtn.disabled = current === 0;
347
+ if (nextBtn) nextBtn.disabled = current === slides.length - 1;
348
+ history.replaceState(null, '', '#' + slides[current].id);
349
+ }
350
+
351
+ function setFromHash() {
352
+ const hash = window.location.hash || '';
353
+ const idx = slides.findIndex((s) => '#' + s.id === hash);
354
+ if (idx >= 0) setActive(idx);
355
+ else setActive(0);
356
+ }
357
+
358
+ prevBtn?.addEventListener('click', () => setActive(current - 1));
359
+ nextBtn?.addEventListener('click', () => setActive(current + 1));
360
+
361
+ document.addEventListener('keydown', (e) => {
362
+ if (e.key === 'ArrowRight' || e.key === ' ') {
363
+ e.preventDefault();
364
+ setActive(current + 1);
365
+ } else if (e.key === 'ArrowLeft') {
366
+ e.preventDefault();
367
+ setActive(current - 1);
368
+ } else if (e.key === 'Home') {
369
+ e.preventDefault();
370
+ setActive(0);
371
+ } else if (e.key === 'End') {
372
+ e.preventDefault();
373
+ setActive(slides.length - 1);
374
+ }
375
+ });
376
+
377
+ window.addEventListener('hashchange', setFromHash);
378
+ setFromHash();
379
+ })();
380
+
381
+ // Copy code button
382
+ document.querySelectorAll('.prose pre code').forEach(block => {
383
+ const button = document.createElement('button');
384
+ button.className = 'copy-button';
385
+ button.textContent = 'Copy';
386
+ button.onclick = async () => {
387
+ await navigator.clipboard.writeText(block.textContent);
388
+ button.textContent = 'Copied!';
389
+ setTimeout(() => button.textContent = 'Copy', 2000);
390
+ };
391
+ block.parentElement.appendChild(button);
392
+ });
393
+
394
+ // Mobile nav toggle
395
+ function toggleNav() {
396
+ document.querySelector('.sidebar').classList.toggle('open');
397
+ }
398
+
399
+ // Close nav when clicking outside on mobile
400
+ document.addEventListener('click', (e) => {
401
+ const sidebar = document.querySelector('.sidebar');
402
+ const toggle = document.querySelector('.nav-toggle');
403
+ if (sidebar.classList.contains('open') &&
404
+ !sidebar.contains(e.target) &&
405
+ !toggle.contains(e.target)) {
406
+ sidebar.classList.remove('open');
407
+ }
408
+ });
409
+ </script>
410
+
411
+ </body>
412
+ </html>