kitfly 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +63 -16
- package/VERSION +1 -1
- package/dist/_raw/content/deployment/preflight.md +134 -0
- package/dist/_raw/content/deployment/recipes/aws-s3.md +128 -0
- package/dist/_raw/content/deployment/recipes/cloudflare-pages.md +73 -0
- package/dist/_raw/content/deployment/recipes/cloudflare-r2.md +156 -0
- package/dist/_raw/content/deployment/recipes/fly-io.md +57 -0
- package/dist/_raw/content/deployment/recipes/github-pages.md +112 -0
- package/dist/_raw/content/deployment/recipes/netlify.md +99 -0
- package/dist/_raw/content/deployment/recipes/vercel.md +88 -0
- package/dist/_raw/content/deployment/secrets-and-env-vars.md +75 -0
- package/dist/_raw/content/deployment.md +128 -0
- package/dist/_raw/content/guide/approaches.md +182 -0
- package/dist/_raw/content/guide/features.md +121 -0
- package/dist/_raw/content/guide/getting-started.md +112 -0
- package/dist/_raw/content/guide/kitfly-overview.md +209 -0
- package/dist/_raw/content/reference/configuration.md +259 -0
- package/dist/_raw/content/reference/design-catalog.md +167 -0
- package/dist/_raw/content/reference/environment-variables.md +66 -0
- package/dist/_raw/content/reference/glossary.md +92 -0
- package/dist/_raw/content/reference/key-concepts.md +118 -0
- package/dist/_raw/content/reference/plugins.md +220 -0
- package/dist/_raw/content/reference/structure.md +166 -0
- package/dist/_raw/content/reference.md +19 -0
- package/dist/_raw/content/templates/crucible.md +192 -0
- package/dist/_raw/content/templates/handbook.md +83 -0
- package/dist/_raw/content/templates/minimal.md +138 -0
- package/dist/_raw/content/templates/overview.md +187 -0
- package/dist/_raw/content/templates/pipeline.md +151 -0
- package/dist/_raw/content/templates/productbook.md +187 -0
- package/dist/_raw/content/templates/runbook.md +193 -0
- package/dist/_raw/content/templates/servicebook.md +163 -0
- package/dist/_raw/docs/decisions/ADR-0001-minimalist-site-code.md +118 -0
- package/dist/_raw/docs/decisions/ADR-0002-ai-accessibility.md +153 -0
- package/dist/_raw/docs/decisions/ADR-0003-single-file-bundle.md +93 -0
- package/dist/_raw/docs/decisions/ADR-0004-bun-runtime.md +98 -0
- package/dist/_raw/docs/decisions/ADR-0005-plugin-contract-and-distribution.md +110 -0
- package/dist/_raw/docs/decisions/DDR-0001-viewport-locked-layout.md +111 -0
- package/dist/_raw/docs/decisions/DDR-0002-theme-system.md +131 -0
- package/dist/_raw/docs/decisions/DDR-0003-bounded-logo-slot.md +106 -0
- package/dist/_raw/docs/decisions/DDR-0004-slides-rendering-model.md +113 -0
- package/dist/_raw/docs/decisions/DDR-0005-deterministic-layout-boundary.md +107 -0
- package/dist/_raw/docs/userguide/cli/build.md +85 -0
- package/dist/_raw/docs/userguide/cli/bundle.md +81 -0
- package/dist/_raw/docs/userguide/cli/dev.md +92 -0
- package/dist/_raw/docs/userguide/cli/init.md +116 -0
- package/dist/_raw/docs/userguide/cli/servers.md +69 -0
- package/dist/_raw/docs/userguide/cli/stop.md +76 -0
- package/dist/_raw/docs/userguide/cli/update.md +78 -0
- package/dist/_raw/docs/userguide/cli/version.md +65 -0
- package/dist/_raw/docs/userguide/cli.md +34 -0
- package/dist/_raw/docs/userguide/sharing.md +94 -0
- package/dist/_raw/schemas/plugin-schemas-notes.md +71 -0
- package/dist/_raw/schemas.md +42 -0
- package/dist/assets/brand/kitfly-favicon-32.png +0 -0
- package/dist/assets/brand/kitfly-icon-64.png +0 -0
- package/dist/assets/brand/kitfly-logo-128.png +0 -0
- package/dist/assets/brand/kitfly-logo-512.png +0 -0
- package/dist/assets/brand/kitfly-logo.svg +12132 -0
- package/dist/assets/brand/kitfly-neon-128.png +0 -0
- package/dist/assets/brand/kitfly-neon-192.png +0 -0
- package/dist/assets/brand/kitfly-neon-256.png +0 -0
- package/dist/assets/brand/kitfly-neon.png +0 -0
- package/dist/assets/brand/palette.md +75 -0
- package/dist/content/deployment/index.html +11 -0
- package/dist/content/deployment/preflight.html +418 -0
- package/dist/content/deployment/recipes/aws-s3.html +421 -0
- package/dist/content/deployment/recipes/cloudflare-pages.html +372 -0
- package/dist/content/deployment/recipes/cloudflare-r2.html +443 -0
- package/dist/content/deployment/recipes/fly-io.html +356 -0
- package/dist/content/deployment/recipes/github-pages.html +414 -0
- package/dist/content/deployment/recipes/index.html +11 -0
- package/dist/content/deployment/recipes/netlify.html +394 -0
- package/dist/content/deployment/recipes/vercel.html +382 -0
- package/dist/content/deployment/secrets-and-env-vars.html +380 -0
- package/dist/content/deployment.html +426 -0
- package/dist/content/guide/approaches.html +501 -0
- package/dist/content/guide/features.html +436 -0
- package/dist/content/guide/getting-started.html +403 -0
- package/dist/content/guide/index.html +11 -0
- package/dist/content/guide/kitfly-overview.html +544 -0
- package/dist/content/index.html +11 -0
- package/dist/content/reference/configuration.html +580 -0
- package/dist/content/reference/design-catalog.html +449 -0
- package/dist/content/reference/environment-variables.html +367 -0
- package/dist/content/reference/glossary.html +368 -0
- package/dist/content/reference/index.html +11 -0
- package/dist/content/reference/key-concepts.html +399 -0
- package/dist/content/reference/plugins.html +491 -0
- package/dist/content/reference/structure.html +463 -0
- package/dist/content/reference.html +334 -0
- package/dist/content/templates/crucible.html +546 -0
- package/dist/content/templates/handbook.html +405 -0
- package/dist/content/templates/index.html +11 -0
- package/dist/content/templates/minimal.html +447 -0
- package/dist/content/templates/overview.html +558 -0
- package/dist/content/templates/pipeline.html +494 -0
- package/dist/content/templates/productbook.html +540 -0
- package/dist/content/templates/runbook.html +543 -0
- package/dist/content/templates/servicebook.html +523 -0
- package/dist/content-index.json +540 -0
- package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +491 -0
- package/dist/docs/decisions/ADR-0002-ai-accessibility.html +434 -0
- package/dist/docs/decisions/ADR-0003-single-file-bundle.html +412 -0
- package/dist/docs/decisions/ADR-0004-bun-runtime.html +409 -0
- package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +402 -0
- package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +459 -0
- package/dist/docs/decisions/DDR-0002-theme-system.html +452 -0
- package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +423 -0
- package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +399 -0
- package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +422 -0
- package/dist/docs/decisions/index.html +11 -0
- package/dist/docs/userguide/cli/build.html +408 -0
- package/dist/docs/userguide/cli/bundle.html +419 -0
- package/dist/docs/userguide/cli/dev.html +428 -0
- package/dist/docs/userguide/cli/index.html +11 -0
- package/dist/docs/userguide/cli/init.html +436 -0
- package/dist/docs/userguide/cli/servers.html +393 -0
- package/dist/docs/userguide/cli/stop.html +408 -0
- package/dist/docs/userguide/cli/update.html +406 -0
- package/dist/docs/userguide/cli/version.html +406 -0
- package/dist/docs/userguide/cli.html +386 -0
- package/dist/docs/userguide/index.html +11 -0
- package/dist/docs/userguide/sharing.html +465 -0
- package/dist/index.html +387 -0
- package/dist/llms.txt +18 -0
- package/dist/provenance.json +7 -0
- package/dist/schemas/index.html +11 -0
- package/dist/schemas/plugin-registry.schema.html +327 -0
- package/dist/schemas/plugin-schemas-notes.html +364 -0
- package/dist/schemas/plugin.schema.html +327 -0
- package/dist/schemas/plugins.schema.html +327 -0
- package/dist/schemas/v0/common.schema.html +386 -0
- package/dist/schemas/v0/index.html +11 -0
- package/dist/schemas/v0/plugin-registry.schema.html +547 -0
- package/dist/schemas/v0/plugin.schema.html +497 -0
- package/dist/schemas/v0/plugins.schema.html +406 -0
- package/dist/schemas/v0/site.schema.html +541 -0
- package/dist/schemas/v0/theme.schema.html +615 -0
- package/dist/schemas.html +351 -0
- package/dist/styles.css +1262 -0
- package/package.json +4 -2
- package/plugins-dist/callouts.css +32 -0
- package/plugins-dist/callouts.js +46 -0
- package/plugins-dist/slides-visuals.css +224 -0
- package/plugins-dist/slides-visuals.js +598 -0
- package/registry/plugins.yaml +35 -0
- package/schemas/README.md +10 -0
- package/schemas/plugin-registry.schema.json +5 -0
- package/schemas/plugin-schemas-notes.md +71 -0
- package/schemas/plugin.schema.json +5 -0
- package/schemas/plugins.schema.json +5 -0
- package/schemas/v0/common.schema.json +64 -0
- package/schemas/v0/plugin-registry.schema.json +225 -0
- package/schemas/v0/plugin.schema.json +175 -0
- package/schemas/v0/plugins.schema.json +84 -0
- package/schemas/v0/site.schema.json +56 -9
- package/schemas/v0/theme.schema.json +105 -22
- package/scripts/build.ts +155 -3
- package/scripts/bundle.ts +258 -95
- package/scripts/dev.ts +203 -1
- package/src/__tests__/build.test.ts +158 -1
- package/src/__tests__/bundle.test.ts +31 -0
- package/src/__tests__/cli.test.ts +14 -3
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/bad-list-indent.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/blank-line.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/compare-object-items.md +9 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/indented-fence.md +4 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/stat-grid-missing-fields.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/unknown-type.md +3 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/compare.md +10 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/comparison-table.md +14 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/funnel.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/kpi.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/layer-cake.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/pyramid.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/quadrant-grid.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/scorecard.md +13 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/stat-grid.md +8 -0
- package/src/__tests__/init.test.ts +35 -0
- package/src/__tests__/plugin-loader.test.ts +221 -0
- package/src/__tests__/shared.test.ts +428 -0
- package/src/__tests__/slides-visuals-fence-contract.test.ts +28 -0
- package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +114 -0
- package/src/__tests__/styles.test.ts +35 -0
- package/src/cli.ts +9 -4
- package/src/plugin-loader.ts +245 -0
- package/src/shared.ts +614 -7
- package/src/site/styles.css +331 -0
- package/src/site/template.html +66 -5
- package/src/templates/deck.ts +186 -0
- package/src/templates/driver.ts +11 -1
- package/src/templates/minimal.ts +1 -0
|
@@ -0,0 +1,543 @@
|
|
|
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>Runbook Template - 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.0</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" class="active">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/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/templates/crucible.html">Templates</a><span class="separator">›</span><span>runbook</span></nav>
|
|
139
|
+
|
|
140
|
+
<h1 id="runbook-template">Runbook Template</h1>
|
|
141
|
+
<p>The <code>runbook</code> template creates a structured site for operational documentation. It extends <code>minimal</code> with sections designed for on-call engineers, SREs, and operations teams.</p>
|
|
142
|
+
<h2 id="when-to-use">When to Use</h2>
|
|
143
|
+
<ul>
|
|
144
|
+
<li>Operations documentation</li>
|
|
145
|
+
<li>On-call runbooks</li>
|
|
146
|
+
<li>Incident response procedures</li>
|
|
147
|
+
<li>System administration guides</li>
|
|
148
|
+
<li>Deployment and maintenance checklists</li>
|
|
149
|
+
<li>Any docs that answer "what do I do when..."</li>
|
|
150
|
+
</ul>
|
|
151
|
+
<h2 id="what-you-get">What You Get</h2>
|
|
152
|
+
<p>Everything from <code>minimal</code>, plus:</p>
|
|
153
|
+
<pre><code>my-runbook/
|
|
154
|
+
├── site.yaml # Configured with ops sections
|
|
155
|
+
├── index.md # Home page with quick links
|
|
156
|
+
├── CUSTOMIZING.md # How to customize this site (AI + human friendly)
|
|
157
|
+
├── content/
|
|
158
|
+
│ ├── procedures/
|
|
159
|
+
│ │ └── deployment.md # Step-by-step procedures
|
|
160
|
+
│ ├── troubleshooting/
|
|
161
|
+
│ │ └── common-issues.md # Problem → solution guides
|
|
162
|
+
│ ├── reference/
|
|
163
|
+
│ │ ├── interfaces/
|
|
164
|
+
│ │ │ └── api-template.md # Integration/API documentation
|
|
165
|
+
│ │ ├── contacts/
|
|
166
|
+
│ │ │ └── directory.md # Team and vendor contacts
|
|
167
|
+
│ │ ├── checklists/
|
|
168
|
+
│ │ │ └── pre-deploy.md # Verification checklists
|
|
169
|
+
│ │ └── analytics/
|
|
170
|
+
│ │ └── dashboards.md # Metrics and KPI definitions
|
|
171
|
+
│ └── incidents/
|
|
172
|
+
│ └── escalation.md # Incident response paths
|
|
173
|
+
└── ...
|
|
174
|
+
</code></pre>
|
|
175
|
+
<h2 id="sections">Sections</h2>
|
|
176
|
+
<table>
|
|
177
|
+
<thead>
|
|
178
|
+
<tr>
|
|
179
|
+
<th>Section</th>
|
|
180
|
+
<th>Purpose</th>
|
|
181
|
+
<th>Typical Content</th>
|
|
182
|
+
</tr>
|
|
183
|
+
</thead>
|
|
184
|
+
<tbody><tr>
|
|
185
|
+
<td><strong>Procedures</strong></td>
|
|
186
|
+
<td>How to perform operational tasks</td>
|
|
187
|
+
<td>Deployment steps, maintenance tasks, migrations</td>
|
|
188
|
+
</tr>
|
|
189
|
+
<tr>
|
|
190
|
+
<td><strong>Troubleshooting</strong></td>
|
|
191
|
+
<td>Diagnose and fix issues</td>
|
|
192
|
+
<td>Error codes, symptoms → causes, remediation</td>
|
|
193
|
+
</tr>
|
|
194
|
+
<tr>
|
|
195
|
+
<td><strong>Reference</strong></td>
|
|
196
|
+
<td>Look-up information</td>
|
|
197
|
+
<td>Interfaces, contacts, checklists, glossary</td>
|
|
198
|
+
</tr>
|
|
199
|
+
<tr>
|
|
200
|
+
<td><strong>Incidents</strong></td>
|
|
201
|
+
<td>Emergency response</td>
|
|
202
|
+
<td>Escalation paths, severity definitions, post-mortems</td>
|
|
203
|
+
</tr>
|
|
204
|
+
</tbody></table>
|
|
205
|
+
<p>The <strong>Reference</strong> section consolidates supporting materials:</p>
|
|
206
|
+
<table>
|
|
207
|
+
<thead>
|
|
208
|
+
<tr>
|
|
209
|
+
<th>Subsection</th>
|
|
210
|
+
<th>Purpose</th>
|
|
211
|
+
</tr>
|
|
212
|
+
</thead>
|
|
213
|
+
<tbody><tr>
|
|
214
|
+
<td><code>reference/interfaces/</code></td>
|
|
215
|
+
<td>API specs, protocol docs, vendor integration details</td>
|
|
216
|
+
</tr>
|
|
217
|
+
<tr>
|
|
218
|
+
<td><code>reference/contacts/</code></td>
|
|
219
|
+
<td>Team directory, vendor contacts, escalation matrix</td>
|
|
220
|
+
</tr>
|
|
221
|
+
<tr>
|
|
222
|
+
<td><code>reference/checklists/</code></td>
|
|
223
|
+
<td>Pre/post deployment, audit, maintenance checklists</td>
|
|
224
|
+
</tr>
|
|
225
|
+
<tr>
|
|
226
|
+
<td><code>reference/analytics/</code></td>
|
|
227
|
+
<td>Dashboard links, KPIs, SLA definitions</td>
|
|
228
|
+
</tr>
|
|
229
|
+
</tbody></table>
|
|
230
|
+
<h2 id="usage">Usage</h2>
|
|
231
|
+
<pre><code class="language-bash">kitfly init ops-docs --template runbook
|
|
232
|
+
kitfly init ops-docs --template runbook --brand "Platform Team"
|
|
233
|
+
</code></pre>
|
|
234
|
+
<h2 id="design-principles">Design Principles</h2>
|
|
235
|
+
<p>Runbooks differ from handbooks in key ways:</p>
|
|
236
|
+
<table>
|
|
237
|
+
<thead>
|
|
238
|
+
<tr>
|
|
239
|
+
<th>Aspect</th>
|
|
240
|
+
<th>Handbook</th>
|
|
241
|
+
<th>Runbook</th>
|
|
242
|
+
</tr>
|
|
243
|
+
</thead>
|
|
244
|
+
<tbody><tr>
|
|
245
|
+
<td><strong>Audience</strong></td>
|
|
246
|
+
<td>Learning</td>
|
|
247
|
+
<td>Doing (often urgently)</td>
|
|
248
|
+
</tr>
|
|
249
|
+
<tr>
|
|
250
|
+
<td><strong>Reading mode</strong></td>
|
|
251
|
+
<td>Sequential</td>
|
|
252
|
+
<td>Jump to specific procedure</td>
|
|
253
|
+
</tr>
|
|
254
|
+
<tr>
|
|
255
|
+
<td><strong>Goal</strong></td>
|
|
256
|
+
<td>Understanding</td>
|
|
257
|
+
<td>Task completion</td>
|
|
258
|
+
</tr>
|
|
259
|
+
<tr>
|
|
260
|
+
<td><strong>Tone</strong></td>
|
|
261
|
+
<td>Explanatory</td>
|
|
262
|
+
<td>Direct, imperative</td>
|
|
263
|
+
</tr>
|
|
264
|
+
</tbody></table>
|
|
265
|
+
<h2 id="procedure-document-format">Procedure Document Format</h2>
|
|
266
|
+
<p>Each procedure should follow a consistent structure:</p>
|
|
267
|
+
<pre><code class="language-markdown"># Procedure Name
|
|
268
|
+
|
|
269
|
+
## Objective
|
|
270
|
+
|
|
271
|
+
What this procedure accomplishes and when to use it.
|
|
272
|
+
|
|
273
|
+
## Prerequisites
|
|
274
|
+
|
|
275
|
+
- [ ] Required access or permissions
|
|
276
|
+
- [ ] Tools or systems that must be available
|
|
277
|
+
- [ ] Related procedures to complete first
|
|
278
|
+
|
|
279
|
+
## Steps
|
|
280
|
+
|
|
281
|
+
1. **First action**
|
|
282
|
+
- Expected output: `what you should see`
|
|
283
|
+
|
|
284
|
+
2. **Second action**
|
|
285
|
+
- Verification: How to confirm success
|
|
286
|
+
|
|
287
|
+
## Verification
|
|
288
|
+
|
|
289
|
+
How to confirm the procedure completed successfully.
|
|
290
|
+
|
|
291
|
+
## Rollback
|
|
292
|
+
|
|
293
|
+
If something goes wrong, how to revert.
|
|
294
|
+
|
|
295
|
+
## Related
|
|
296
|
+
|
|
297
|
+
- Link to related procedures
|
|
298
|
+
- Escalation path if this fails
|
|
299
|
+
</code></pre>
|
|
300
|
+
<h2 id="growing-your-runbook">Growing Your Runbook</h2>
|
|
301
|
+
<p>As your runbook matures, consider:</p>
|
|
302
|
+
<p><strong>Numbered sections</strong> for ordering (enterprise pattern):</p>
|
|
303
|
+
<pre><code>content/
|
|
304
|
+
├── 00-bootstrap/ # Initial setup, first-run procedures
|
|
305
|
+
├── 01-operations/ # Day-to-day operational tasks
|
|
306
|
+
├── 02-incidents/ # Emergency response
|
|
307
|
+
└── 99-reference/ # Templates, glossaries, checklists
|
|
308
|
+
</code></pre>
|
|
309
|
+
<p><strong>Recipes</strong> - reusable procedures organized by topic:</p>
|
|
310
|
+
<pre><code>content/procedures/
|
|
311
|
+
├── security/
|
|
312
|
+
│ ├── rotate-credentials.md
|
|
313
|
+
│ └── access-review.md
|
|
314
|
+
├── database/
|
|
315
|
+
│ ├── backup.md
|
|
316
|
+
│ └── restore.md
|
|
317
|
+
└── deployment/
|
|
318
|
+
├── standard-deploy.md
|
|
319
|
+
└── hotfix-deploy.md
|
|
320
|
+
</code></pre>
|
|
321
|
+
<h2 id="the-customizing-guide">The Customizing Guide</h2>
|
|
322
|
+
<p>Every runbook includes <code>CUSTOMIZING.md</code> - a commissioning guide for both humans and AI assistants:</p>
|
|
323
|
+
<p><strong>What it covers:</strong></p>
|
|
324
|
+
<ul>
|
|
325
|
+
<li>How to add content to each section</li>
|
|
326
|
+
<li>Adding new sections to <code>site.yaml</code></li>
|
|
327
|
+
<li>Conventions used in this runbook</li>
|
|
328
|
+
<li>File linking and cross-references</li>
|
|
329
|
+
</ul>
|
|
330
|
+
<p><strong>Important limitations:</strong></p>
|
|
331
|
+
<ul>
|
|
332
|
+
<li>Content must live within the site folder (kitfly cannot include files from outside)</li>
|
|
333
|
+
<li>External resources should be linked via URL, not copied</li>
|
|
334
|
+
<li>Binary files (PDFs, images) go in <code>assets/</code> and are linked, not rendered</li>
|
|
335
|
+
</ul>
|
|
336
|
+
<p>This guide helps onboard team members and ensures AI coding assistants understand the structure when helping maintain the runbook.</p>
|
|
337
|
+
<h2 id="example-use-cases">Example Use Cases</h2>
|
|
338
|
+
<p><strong>Integration Runbook</strong> (like Blossman/Cargas)</p>
|
|
339
|
+
<ul>
|
|
340
|
+
<li>Procedures: Data sync, batch processing, error recovery</li>
|
|
341
|
+
<li>Troubleshooting: Connection failures, data format errors, timeout issues</li>
|
|
342
|
+
<li>Reference/Interfaces: ERP API specs (extracted from vendor PDF), protocol details</li>
|
|
343
|
+
<li>Reference/Contacts: Vendor support, internal integration team</li>
|
|
344
|
+
<li>Incidents: Sync failure response, data reconciliation</li>
|
|
345
|
+
</ul>
|
|
346
|
+
<p><strong>Platform Runbook</strong></p>
|
|
347
|
+
<ul>
|
|
348
|
+
<li>Procedures: Deploy service, rotate secrets, scale cluster</li>
|
|
349
|
+
<li>Troubleshooting: High CPU, memory leaks, connection timeouts</li>
|
|
350
|
+
<li>Reference/Checklists: Production deploy, database migration</li>
|
|
351
|
+
<li>Reference/Analytics: SLA dashboard links, key metrics</li>
|
|
352
|
+
<li>Incidents: P1 response, rollback procedure</li>
|
|
353
|
+
</ul>
|
|
354
|
+
<p><strong>Security Operations</strong></p>
|
|
355
|
+
<ul>
|
|
356
|
+
<li>Procedures: Credential rotation, access reviews, patch deployment</li>
|
|
357
|
+
<li>Troubleshooting: Auth failures, certificate expiry, firewall issues</li>
|
|
358
|
+
<li>Reference/Contacts: Security team, vendor security contacts</li>
|
|
359
|
+
<li>Incidents: Breach response, compromised credentials</li>
|
|
360
|
+
</ul>
|
|
361
|
+
|
|
362
|
+
</article>
|
|
363
|
+
<aside class="toc"><span class="toc-title">On this page</span><ul><li><a href="#when-to-use">When to Use</a></li><li><a href="#what-you-get">What You Get</a></li><li><a href="#sections">Sections</a></li><li><a href="#usage">Usage</a></li><li><a href="#design-principles">Design Principles</a></li><li><a href="#procedure-document-format">Procedure Document Format</a></li><li><a href="#growing-your-runbook">Growing Your Runbook</a></li><li><a href="#the-customizing-guide">The Customizing Guide</a></li><li><a href="#example-use-cases">Example Use Cases</a></li></ul></aside>
|
|
364
|
+
</main>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<footer class="site-footer">
|
|
368
|
+
<div class="footer-content">
|
|
369
|
+
<div class="footer-left">
|
|
370
|
+
<span class="footer-version">v0.2.0</span>
|
|
371
|
+
<span class="footer-separator">·</span>
|
|
372
|
+
<span class="footer-commit" title="Commit: 33ccd68">Published 2026-02-15</span>
|
|
373
|
+
</div>
|
|
374
|
+
<div class="footer-center">
|
|
375
|
+
<span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
|
|
376
|
+
<span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
|
|
377
|
+
</div>
|
|
378
|
+
<div class="footer-right">
|
|
379
|
+
<a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
</footer>
|
|
383
|
+
<!-- Syntax highlighting - Prism.js -->
|
|
384
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
|
|
385
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
|
|
386
|
+
<!-- Mermaid diagram support -->
|
|
387
|
+
<script type="module">
|
|
388
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
389
|
+
|
|
390
|
+
function getMermaidTheme() {
|
|
391
|
+
const theme = document.documentElement.getAttribute('data-theme');
|
|
392
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
393
|
+
const isDark = theme === 'dark' || (!theme && prefersDark);
|
|
394
|
+
return isDark ? 'dark' : 'neutral';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
mermaid.initialize({
|
|
398
|
+
startOnLoad: true,
|
|
399
|
+
theme: getMermaidTheme()
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Re-render mermaid diagrams when theme changes
|
|
403
|
+
window.reinitMermaid = async function() {
|
|
404
|
+
mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
|
|
405
|
+
const diagrams = document.querySelectorAll('.mermaid');
|
|
406
|
+
for (const el of diagrams) {
|
|
407
|
+
const code = el.getAttribute('data-mermaid-source');
|
|
408
|
+
if (code) {
|
|
409
|
+
el.innerHTML = code;
|
|
410
|
+
el.removeAttribute('data-processed');
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
await mermaid.run({ nodes: diagrams });
|
|
414
|
+
};
|
|
415
|
+
</script>
|
|
416
|
+
|
|
417
|
+
<script>
|
|
418
|
+
function toggleTheme() {
|
|
419
|
+
const html = document.documentElement;
|
|
420
|
+
const current = html.getAttribute('data-theme');
|
|
421
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
422
|
+
|
|
423
|
+
let next;
|
|
424
|
+
if (current === 'dark') {
|
|
425
|
+
next = 'light';
|
|
426
|
+
} else if (current === 'light') {
|
|
427
|
+
next = 'dark';
|
|
428
|
+
} else {
|
|
429
|
+
// No explicit theme set, toggle from system preference
|
|
430
|
+
next = prefersDark ? 'light' : 'dark';
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
html.setAttribute('data-theme', next);
|
|
434
|
+
localStorage.setItem('theme', next);
|
|
435
|
+
|
|
436
|
+
// Switch Prism theme
|
|
437
|
+
const prismLight = document.getElementById('prism-light');
|
|
438
|
+
const prismDark = document.getElementById('prism-dark');
|
|
439
|
+
if (next === 'dark') {
|
|
440
|
+
prismLight?.setAttribute('disabled', '');
|
|
441
|
+
prismDark?.removeAttribute('disabled');
|
|
442
|
+
} else {
|
|
443
|
+
prismLight?.removeAttribute('disabled');
|
|
444
|
+
prismDark?.setAttribute('disabled', '');
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Re-render mermaid diagrams with new theme
|
|
448
|
+
if (window.reinitMermaid) {
|
|
449
|
+
window.reinitMermaid();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Slides mode hash routing
|
|
454
|
+
(function initSlidesMode() {
|
|
455
|
+
const shell = document.querySelector('.slides-shell');
|
|
456
|
+
if (!shell) return;
|
|
457
|
+
|
|
458
|
+
const slides = Array.from(document.querySelectorAll('.slide'));
|
|
459
|
+
if (!slides.length) return;
|
|
460
|
+
|
|
461
|
+
const prevBtn = document.querySelector('.slide-prev');
|
|
462
|
+
const nextBtn = document.querySelector('.slide-next');
|
|
463
|
+
const counter = document.querySelector('.slide-counter');
|
|
464
|
+
const progressBar = document.querySelector('.slide-progress-bar');
|
|
465
|
+
const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
|
|
466
|
+
let current = 0;
|
|
467
|
+
|
|
468
|
+
function setActive(n) {
|
|
469
|
+
current = Math.max(0, Math.min(n, slides.length - 1));
|
|
470
|
+
slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
|
|
471
|
+
navLinks.forEach((link) => {
|
|
472
|
+
const active = link.getAttribute('href') === '#' + slides[current].id;
|
|
473
|
+
link.classList.toggle('active', active);
|
|
474
|
+
});
|
|
475
|
+
if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
|
|
476
|
+
if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
|
|
477
|
+
if (prevBtn) prevBtn.disabled = current === 0;
|
|
478
|
+
if (nextBtn) nextBtn.disabled = current === slides.length - 1;
|
|
479
|
+
history.replaceState(null, '', '#' + slides[current].id);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function setFromHash() {
|
|
483
|
+
const hash = window.location.hash || '';
|
|
484
|
+
const idx = slides.findIndex((s) => '#' + s.id === hash);
|
|
485
|
+
if (idx >= 0) setActive(idx);
|
|
486
|
+
else setActive(0);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
prevBtn?.addEventListener('click', () => setActive(current - 1));
|
|
490
|
+
nextBtn?.addEventListener('click', () => setActive(current + 1));
|
|
491
|
+
|
|
492
|
+
document.addEventListener('keydown', (e) => {
|
|
493
|
+
if (e.key === 'ArrowRight' || e.key === ' ') {
|
|
494
|
+
e.preventDefault();
|
|
495
|
+
setActive(current + 1);
|
|
496
|
+
} else if (e.key === 'ArrowLeft') {
|
|
497
|
+
e.preventDefault();
|
|
498
|
+
setActive(current - 1);
|
|
499
|
+
} else if (e.key === 'Home') {
|
|
500
|
+
e.preventDefault();
|
|
501
|
+
setActive(0);
|
|
502
|
+
} else if (e.key === 'End') {
|
|
503
|
+
e.preventDefault();
|
|
504
|
+
setActive(slides.length - 1);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
window.addEventListener('hashchange', setFromHash);
|
|
509
|
+
setFromHash();
|
|
510
|
+
})();
|
|
511
|
+
|
|
512
|
+
// Copy code button
|
|
513
|
+
document.querySelectorAll('.prose pre code').forEach(block => {
|
|
514
|
+
const button = document.createElement('button');
|
|
515
|
+
button.className = 'copy-button';
|
|
516
|
+
button.textContent = 'Copy';
|
|
517
|
+
button.onclick = async () => {
|
|
518
|
+
await navigator.clipboard.writeText(block.textContent);
|
|
519
|
+
button.textContent = 'Copied!';
|
|
520
|
+
setTimeout(() => button.textContent = 'Copy', 2000);
|
|
521
|
+
};
|
|
522
|
+
block.parentElement.appendChild(button);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Mobile nav toggle
|
|
526
|
+
function toggleNav() {
|
|
527
|
+
document.querySelector('.sidebar').classList.toggle('open');
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Close nav when clicking outside on mobile
|
|
531
|
+
document.addEventListener('click', (e) => {
|
|
532
|
+
const sidebar = document.querySelector('.sidebar');
|
|
533
|
+
const toggle = document.querySelector('.nav-toggle');
|
|
534
|
+
if (sidebar.classList.contains('open') &&
|
|
535
|
+
!sidebar.contains(e.target) &&
|
|
536
|
+
!toggle.contains(e.target)) {
|
|
537
|
+
sidebar.classList.remove('open');
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
</script>
|
|
541
|
+
|
|
542
|
+
</body>
|
|
543
|
+
</html>
|