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,580 @@
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>Configuration - 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" class="active">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">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="../../content/guide/approaches.html">Content</a><span class="separator">›</span><a href="../../content/reference/configuration.html">Reference</a><span class="separator">›</span><span>configuration</span></nav>
139
+ <div class="page-meta">Last updated: 2026-02-03</div>
140
+ <h1 id="configuration">Configuration</h1>
141
+ <p>Kitfly uses <code>site.yaml</code> for configuration. Everything is optional - sensible defaults apply.</p>
142
+ <h2 id="quick-reference">Quick Reference</h2>
143
+ <pre><code class="language-yaml"># yaml-language-server: $schema=./schemas/v0/site.schema.json
144
+ schemaVersion: &quot;0.1.0&quot;
145
+ docroot: &quot;content&quot;
146
+ title: &quot;My Documentation&quot;
147
+ version: &quot;2.4.1&quot;
148
+ home: &quot;index.md&quot;
149
+
150
+ brand:
151
+ name: &quot;My Project&quot;
152
+ url: &quot;https://myproject.com&quot;
153
+ external: true
154
+
155
+ sections:
156
+ - name: &quot;Guide&quot;
157
+ path: &quot;guide&quot;
158
+ - name: &quot;API&quot;
159
+ path: &quot;api&quot;
160
+
161
+ footer:
162
+ copyright: &quot;© 2026 My Project. All rights reserved.&quot;
163
+ links:
164
+ - text: &quot;Privacy&quot;
165
+ url: &quot;/privacy&quot;
166
+ - text: &quot;Terms&quot;
167
+ url: &quot;/terms&quot;
168
+ attribution: true
169
+ </code></pre>
170
+ <h2 id="settings">Settings</h2>
171
+ <h3 id="docroot">docroot</h3>
172
+ <p><strong>The most important setting.</strong> Controls what folder contains your documentation.</p>
173
+ <pre><code class="language-yaml">docroot: &quot;content&quot;
174
+ </code></pre>
175
+ <table>
176
+ <thead>
177
+ <tr>
178
+ <th>Value</th>
179
+ <th>Meaning</th>
180
+ </tr>
181
+ </thead>
182
+ <tbody><tr>
183
+ <td><code>&quot;content&quot;</code></td>
184
+ <td>Markdown lives in <code>content/</code> (recommended)</td>
185
+ </tr>
186
+ <tr>
187
+ <td><code>&quot;docs&quot;</code></td>
188
+ <td>Markdown lives in <code>docs/</code></td>
189
+ </tr>
190
+ <tr>
191
+ <td><code>&quot;.&quot;</code></td>
192
+ <td>Markdown lives in repo root</td>
193
+ </tr>
194
+ </tbody></table>
195
+ <p><strong>Why it matters:</strong> Only files under <code>docroot</code> appear in your built site. Everything else - <code>src/</code>, <code>scripts/</code>, <code>README.md</code>, <code>package.json</code> - stays out of <code>dist/</code>.</p>
196
+ <p>See <a href="structure.html">Folder Structure</a> for details.</p>
197
+ <h3 id="title">title</h3>
198
+ <p>Site title. Appears in browser tab and header.</p>
199
+ <pre><code class="language-yaml">title: &quot;My Documentation&quot;
200
+ </code></pre>
201
+ <h3 id="version">version</h3>
202
+ <p>Site/content version shown in the footer provenance zone.</p>
203
+ <pre><code class="language-yaml">version: &quot;2.4.1&quot;
204
+ </code></pre>
205
+ <p>Version resolution order:</p>
206
+ <ol>
207
+ <li><code>site.yaml</code> <code>version</code></li>
208
+ <li>Git tag on <code>HEAD</code> (exact match, <code>v</code> prefix removed)</li>
209
+ <li>Omitted from footer provenance if neither is available</li>
210
+ </ol>
211
+ <h3 id="home">home</h3>
212
+ <p>Home page file (relative to docroot). Becomes <code>index.html</code>.</p>
213
+ <pre><code class="language-yaml">home: &quot;index.md&quot;
214
+ </code></pre>
215
+ <p>If omitted, the first file from the first section is used.</p>
216
+ <h3 id="brand">brand</h3>
217
+ <p>Header branding:</p>
218
+ <pre><code class="language-yaml">brand:
219
+ name: &quot;My Project&quot; # Text in header
220
+ url: &quot;/&quot; # Logo link
221
+ logo: &quot;assets/brand/my-logo.png&quot; # Sidebar logo image
222
+ favicon: &quot;assets/brand/favicon-32.png&quot; # Browser tab icon
223
+ external: true # Open in new tab (optional)
224
+ </code></pre>
225
+ <p>The logo renders inside a bounded slot that preserves aspect ratio:</p>
226
+ <table>
227
+ <thead>
228
+ <tr>
229
+ <th>Breakpoint</th>
230
+ <th>Max Height</th>
231
+ <th>Max Width</th>
232
+ </tr>
233
+ </thead>
234
+ <tbody><tr>
235
+ <td>Desktop</td>
236
+ <td>64px</td>
237
+ <td>180px</td>
238
+ </tr>
239
+ <tr>
240
+ <td>Tablet (≤1024px)</td>
241
+ <td>56px</td>
242
+ <td>150px</td>
243
+ </tr>
244
+ <tr>
245
+ <td>Mobile (≤768px)</td>
246
+ <td>48px</td>
247
+ <td>130px</td>
248
+ </tr>
249
+ </tbody></table>
250
+ <p>Both square marks and wide wordmarks fit cleanly. SVG and PNG formats are supported. For SVGs, ensure the <code>viewBox</code> is tightly cropped to the artwork — an oversized canvas will render the logo too small.</p>
251
+ <h3 id="sections">sections</h3>
252
+ <p>Navigation structure. Each section becomes a sidebar group.</p>
253
+ <pre><code class="language-yaml">sections:
254
+ - name: &quot;Guide&quot; # Display name
255
+ path: &quot;guide&quot; # Directory (relative to docroot)
256
+ - name: &quot;Reference&quot;
257
+ path: &quot;reference&quot;
258
+ </code></pre>
259
+ <p><strong>Auto-discovery:</strong> If <code>files</code> is omitted, all <code>.md</code> files in the directory are included, recursively up to <code>maxDepth</code> levels deep.</p>
260
+ <pre><code class="language-yaml">sections:
261
+ - name: &quot;Reference&quot;
262
+ path: &quot;reference&quot;
263
+ maxDepth: 6 # Discover up to 6 levels deep (default: 4, max: 10)
264
+ </code></pre>
265
+ <table>
266
+ <thead>
267
+ <tr>
268
+ <th>Setting</th>
269
+ <th>Default</th>
270
+ <th>Range</th>
271
+ <th>Effect</th>
272
+ </tr>
273
+ </thead>
274
+ <tbody><tr>
275
+ <td><code>maxDepth</code></td>
276
+ <td>4</td>
277
+ <td>1–10</td>
278
+ <td>How many directory levels to scan for <code>.md</code> files</td>
279
+ </tr>
280
+ </tbody></table>
281
+ <p>Deeper sections produce hierarchical sidebar navigation with collapsible groups. Set a lower <code>maxDepth</code> for sections where you want a flatter sidebar.</p>
282
+ <p><strong>Explicit files:</strong> For precise control over which files appear (bypasses auto-discovery):</p>
283
+ <pre><code class="language-yaml">sections:
284
+ - name: &quot;Overview&quot;
285
+ path: &quot;.&quot;
286
+ files: [&quot;README.md&quot;, &quot;CHANGELOG.md&quot;]
287
+ </code></pre>
288
+ <h3 id="footer">footer</h3>
289
+ <p>Footer has three zones: provenance (left), copyright and links (center), and Kitfly attribution (right). Each field is independent — setting one does not affect the others.</p>
290
+ <pre><code>v0.1.1 · Published 2026-02-10 © 2026 Acme Inc. · acme.com Built with Kitfly
291
+ ← provenance (automatic) ← copyright + links (configurable) → ← attribution →
292
+ </code></pre>
293
+ <table>
294
+ <thead>
295
+ <tr>
296
+ <th>Field</th>
297
+ <th>Default</th>
298
+ <th>What it controls</th>
299
+ </tr>
300
+ </thead>
301
+ <tbody><tr>
302
+ <td><code>copyright</code></td>
303
+ <td><code>© {publish-year} {brand.name}</code></td>
304
+ <td>The copyright text in the center zone</td>
305
+ </tr>
306
+ <tr>
307
+ <td><code>copyrightUrl</code></td>
308
+ <td><em>(none)</em></td>
309
+ <td>Makes the copyright text a clickable link</td>
310
+ </tr>
311
+ <tr>
312
+ <td><code>links</code></td>
313
+ <td>Your <code>brand.url</code> shown as a link</td>
314
+ <td>Links after the copyright text (max 10)</td>
315
+ </tr>
316
+ <tr>
317
+ <td><code>attribution</code></td>
318
+ <td><code>true</code></td>
319
+ <td>&quot;Built with Kitfly&quot; on the right</td>
320
+ </tr>
321
+ </tbody></table>
322
+ <p><strong>Common case: product name differs from copyright holder.</strong> The default copyright uses <code>brand.name</code>, which is your product title (shown in the header). If your legal entity is different, override it:</p>
323
+ <pre><code class="language-yaml"># brand.name is &quot;Acme Productbook&quot; — that&#39;s the product title
324
+ # but the copyright holder is the company
325
+ footer:
326
+ copyright: &quot;© 2026 Acme, Inc.&quot;
327
+ </code></pre>
328
+ <p>This changes only the copyright. The brand URL link and attribution are unaffected.</p>
329
+ <p><strong>Make the copyright clickable:</strong></p>
330
+ <pre><code class="language-yaml">footer:
331
+ copyright: &quot;© 2026 3 Leaps, LLC&quot;
332
+ copyrightUrl: &quot;https://3leaps.net&quot;
333
+ </code></pre>
334
+ <p>When <code>copyrightUrl</code> is set, the copyright text becomes a link. When omitted, it renders as plain text.</p>
335
+ <p><strong>Full example with all options:</strong></p>
336
+ <pre><code class="language-yaml">footer:
337
+ copyright: &quot;© 2026 My Company, Inc.&quot;
338
+ copyrightUrl: &quot;https://mycompany.com&quot;
339
+ links:
340
+ - text: &quot;Privacy&quot;
341
+ url: &quot;/privacy&quot;
342
+ - text: &quot;Terms&quot;
343
+ url: &quot;/terms&quot;
344
+ attribution: true
345
+ </code></pre>
346
+ <p>When <code>links</code> is set, it replaces the default brand URL link. When <code>links</code> is omitted, your <code>brand.url</code> appears as a link (with the protocol stripped — <code>https://acme.com</code> displays as <code>acme.com</code>).</p>
347
+ <p>Set <code>footer.attribution: false</code> to remove the &quot;Built with Kitfly&quot; text from the footer entirely.</p>
348
+ <h3 id="theme-layout-themeyaml">theme layout (<code>theme.yaml</code>)</h3>
349
+ <p><code>theme.yaml</code> can override layout variables, including sidebar width.</p>
350
+ <pre><code class="language-yaml"># theme.yaml
351
+ layout:
352
+ sidebarWidth: &quot;320px&quot;
353
+ </code></pre>
354
+ <p>This sets <code>--sidebar-width</code> and applies to dev server, static builds, and bundles.</p>
355
+ <p>Recommended range: <code>240px</code> to <code>400px</code> depending on logo and nav label length.</p>
356
+ <h2 id="frontmatter">Frontmatter</h2>
357
+ <p>Each markdown file can have YAML frontmatter:</p>
358
+ <pre><code class="language-yaml">---
359
+ title: &quot;Page Title&quot;
360
+ description: &quot;Brief description for meta tags&quot;
361
+ last_updated: &quot;2026-02-03&quot;
362
+ ---
363
+ # Content starts here
364
+ </code></pre>
365
+ <table>
366
+ <thead>
367
+ <tr>
368
+ <th>Field</th>
369
+ <th>Purpose</th>
370
+ </tr>
371
+ </thead>
372
+ <tbody><tr>
373
+ <td><code>title</code></td>
374
+ <td>Page title (overrides filename)</td>
375
+ </tr>
376
+ <tr>
377
+ <td><code>description</code></td>
378
+ <td>Meta description</td>
379
+ </tr>
380
+ <tr>
381
+ <td><code>last_updated</code></td>
382
+ <td>Shown in page footer</td>
383
+ </tr>
384
+ </tbody></table>
385
+ <h2 id="no-configuration">No Configuration</h2>
386
+ <p>If you don&#39;t create <code>site.yaml</code>, kitfly will:</p>
387
+ <ol>
388
+ <li>Look for <code>content/</code> directory</li>
389
+ <li>Auto-discover sections from subdirectories</li>
390
+ <li>Use default title (&quot;Documentation&quot;) and branding</li>
391
+ </ol>
392
+ <p>This means you can start with just markdown files and add <code>site.yaml</code> later.</p>
393
+ <h2 id="schema-validation">Schema Validation</h2>
394
+ <p>For editor autocomplete and validation, reference the schema:</p>
395
+ <pre><code class="language-yaml"># yaml-language-server: $schema=./schemas/v0/site.schema.json
396
+ </code></pre>
397
+ <p>The schema is in <code>schemas/v0/site.schema.json</code>.</p>
398
+
399
+ </article>
400
+ <aside class="toc"><span class="toc-title">On this page</span><ul><li><a href="#quick-reference">Quick Reference</a></li><li><a href="#settings">Settings</a></li><li class="toc-h3"><a href="#docroot">docroot</a></li><li class="toc-h3"><a href="#title">title</a></li><li class="toc-h3"><a href="#version">version</a></li><li class="toc-h3"><a href="#home">home</a></li><li class="toc-h3"><a href="#brand">brand</a></li><li class="toc-h3"><a href="#sections">sections</a></li><li class="toc-h3"><a href="#footer">footer</a></li><li><a href="#frontmatter">Frontmatter</a></li><li><a href="#no-configuration">No Configuration</a></li><li><a href="#schema-validation">Schema Validation</a></li></ul></aside>
401
+ </main>
402
+ </div>
403
+
404
+ <footer class="site-footer">
405
+ <div class="footer-content">
406
+ <div class="footer-left">
407
+ <span class="footer-version">v0.2.1</span>
408
+ <span class="footer-separator">·</span>
409
+ <span class="footer-commit" title="Commit: 30dfc01">Published 2026-02-15</span>
410
+ </div>
411
+ <div class="footer-center">
412
+ <span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
413
+ <span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
414
+ </div>
415
+ <div class="footer-right">
416
+ <a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
417
+ </div>
418
+ </div>
419
+ </footer>
420
+ <!-- Syntax highlighting - Prism.js -->
421
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
422
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
423
+ <!-- Mermaid diagram support -->
424
+ <script type="module">
425
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
426
+
427
+ function getMermaidTheme() {
428
+ const theme = document.documentElement.getAttribute('data-theme');
429
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
430
+ const isDark = theme === 'dark' || (!theme && prefersDark);
431
+ return isDark ? 'dark' : 'neutral';
432
+ }
433
+
434
+ mermaid.initialize({
435
+ startOnLoad: true,
436
+ theme: getMermaidTheme()
437
+ });
438
+
439
+ // Re-render mermaid diagrams when theme changes
440
+ window.reinitMermaid = async function() {
441
+ mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
442
+ const diagrams = document.querySelectorAll('.mermaid');
443
+ for (const el of diagrams) {
444
+ const code = el.getAttribute('data-mermaid-source');
445
+ if (code) {
446
+ el.innerHTML = code;
447
+ el.removeAttribute('data-processed');
448
+ }
449
+ }
450
+ await mermaid.run({ nodes: diagrams });
451
+ };
452
+ </script>
453
+
454
+ <script>
455
+ function toggleTheme() {
456
+ const html = document.documentElement;
457
+ const current = html.getAttribute('data-theme');
458
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
459
+
460
+ let next;
461
+ if (current === 'dark') {
462
+ next = 'light';
463
+ } else if (current === 'light') {
464
+ next = 'dark';
465
+ } else {
466
+ // No explicit theme set, toggle from system preference
467
+ next = prefersDark ? 'light' : 'dark';
468
+ }
469
+
470
+ html.setAttribute('data-theme', next);
471
+ localStorage.setItem('theme', next);
472
+
473
+ // Switch Prism theme
474
+ const prismLight = document.getElementById('prism-light');
475
+ const prismDark = document.getElementById('prism-dark');
476
+ if (next === 'dark') {
477
+ prismLight?.setAttribute('disabled', '');
478
+ prismDark?.removeAttribute('disabled');
479
+ } else {
480
+ prismLight?.removeAttribute('disabled');
481
+ prismDark?.setAttribute('disabled', '');
482
+ }
483
+
484
+ // Re-render mermaid diagrams with new theme
485
+ if (window.reinitMermaid) {
486
+ window.reinitMermaid();
487
+ }
488
+ }
489
+
490
+ // Slides mode hash routing
491
+ (function initSlidesMode() {
492
+ const shell = document.querySelector('.slides-shell');
493
+ if (!shell) return;
494
+
495
+ const slides = Array.from(document.querySelectorAll('.slide'));
496
+ if (!slides.length) return;
497
+
498
+ const prevBtn = document.querySelector('.slide-prev');
499
+ const nextBtn = document.querySelector('.slide-next');
500
+ const counter = document.querySelector('.slide-counter');
501
+ const progressBar = document.querySelector('.slide-progress-bar');
502
+ const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
503
+ let current = 0;
504
+
505
+ function setActive(n) {
506
+ current = Math.max(0, Math.min(n, slides.length - 1));
507
+ slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
508
+ navLinks.forEach((link) => {
509
+ const active = link.getAttribute('href') === '#' + slides[current].id;
510
+ link.classList.toggle('active', active);
511
+ });
512
+ if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
513
+ if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
514
+ if (prevBtn) prevBtn.disabled = current === 0;
515
+ if (nextBtn) nextBtn.disabled = current === slides.length - 1;
516
+ history.replaceState(null, '', '#' + slides[current].id);
517
+ }
518
+
519
+ function setFromHash() {
520
+ const hash = window.location.hash || '';
521
+ const idx = slides.findIndex((s) => '#' + s.id === hash);
522
+ if (idx >= 0) setActive(idx);
523
+ else setActive(0);
524
+ }
525
+
526
+ prevBtn?.addEventListener('click', () => setActive(current - 1));
527
+ nextBtn?.addEventListener('click', () => setActive(current + 1));
528
+
529
+ document.addEventListener('keydown', (e) => {
530
+ if (e.key === 'ArrowRight' || e.key === ' ') {
531
+ e.preventDefault();
532
+ setActive(current + 1);
533
+ } else if (e.key === 'ArrowLeft') {
534
+ e.preventDefault();
535
+ setActive(current - 1);
536
+ } else if (e.key === 'Home') {
537
+ e.preventDefault();
538
+ setActive(0);
539
+ } else if (e.key === 'End') {
540
+ e.preventDefault();
541
+ setActive(slides.length - 1);
542
+ }
543
+ });
544
+
545
+ window.addEventListener('hashchange', setFromHash);
546
+ setFromHash();
547
+ })();
548
+
549
+ // Copy code button
550
+ document.querySelectorAll('.prose pre code').forEach(block => {
551
+ const button = document.createElement('button');
552
+ button.className = 'copy-button';
553
+ button.textContent = 'Copy';
554
+ button.onclick = async () => {
555
+ await navigator.clipboard.writeText(block.textContent);
556
+ button.textContent = 'Copied!';
557
+ setTimeout(() => button.textContent = 'Copy', 2000);
558
+ };
559
+ block.parentElement.appendChild(button);
560
+ });
561
+
562
+ // Mobile nav toggle
563
+ function toggleNav() {
564
+ document.querySelector('.sidebar').classList.toggle('open');
565
+ }
566
+
567
+ // Close nav when clicking outside on mobile
568
+ document.addEventListener('click', (e) => {
569
+ const sidebar = document.querySelector('.sidebar');
570
+ const toggle = document.querySelector('.nav-toggle');
571
+ if (sidebar.classList.contains('open') &&
572
+ !sidebar.contains(e.target) &&
573
+ !toggle.contains(e.target)) {
574
+ sidebar.classList.remove('open');
575
+ }
576
+ });
577
+ </script>
578
+
579
+ </body>
580
+ </html>