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,523 @@
|
|
|
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>Servicebook 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">runbook</a></li><li><a href="../../content/templates/servicebook.html" class="active">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>servicebook</span></nav>
|
|
139
|
+
|
|
140
|
+
<h1 id="servicebook-template">Servicebook Template</h1>
|
|
141
|
+
<p>The <code>servicebook</code> template creates a documentation site for <strong>professional services practices</strong> — what you sell, how you deliver, and proof of capability. It extends <code>minimal</code> with six sections organized around service offerings, delivery methodology, and engagement operations.</p>
|
|
142
|
+
<h2 id="when-to-use">When to Use</h2>
|
|
143
|
+
<ul>
|
|
144
|
+
<li>Consulting practices documenting their service portfolio</li>
|
|
145
|
+
<li>Professional services teams standardizing delivery methodology</li>
|
|
146
|
+
<li>Technical assessment or advisory services needing consistent scoping</li>
|
|
147
|
+
<li>Organizations building a body of case studies and industry expertise</li>
|
|
148
|
+
<li>Any situation where the deliverable is expertise, not a product</li>
|
|
149
|
+
</ul>
|
|
150
|
+
<h2 id="what-you-get">What You Get</h2>
|
|
151
|
+
<p>Everything from <code>minimal</code>, plus:</p>
|
|
152
|
+
<pre><code>my-servicebook/
|
|
153
|
+
├── site.yaml # Configured with 6 sections
|
|
154
|
+
├── index.md # Home page with service catalog summary
|
|
155
|
+
├── CUSTOMIZING.md # How to customize (AI + human friendly)
|
|
156
|
+
├── content/
|
|
157
|
+
│ ├── offerings/
|
|
158
|
+
│ │ ├── overview.md # Service tiers, engagement models
|
|
159
|
+
│ │ └── assess/
|
|
160
|
+
│ │ ├── overview.md # Example: assessment service
|
|
161
|
+
│ │ ├── deliverables.md # What the client receives
|
|
162
|
+
│ │ └── scoping.md # How we scope an engagement
|
|
163
|
+
│ ├── methodology/
|
|
164
|
+
│ │ ├── phases.md # Explore → Analyze → Synthesize → Deliver
|
|
165
|
+
│ │ ├── tools.md # Tools used in delivery
|
|
166
|
+
│ │ ├── frameworks.md # Assessment frameworks, scoring models
|
|
167
|
+
│ │ └── decisions/
|
|
168
|
+
│ │ ├── index.md # Methodology decision log
|
|
169
|
+
│ │ └── mdr-template.md # Decision record template
|
|
170
|
+
│ ├── delivery/
|
|
171
|
+
│ │ ├── engagement-lifecycle.md # End-to-end engagement flow
|
|
172
|
+
│ │ ├── client-onboarding.md # Prerequisites, setup, kickoff
|
|
173
|
+
│ │ ├── quality-gates.md # Checkpoints throughout delivery
|
|
174
|
+
│ │ └── templates/
|
|
175
|
+
│ │ └── index.md # Deliverable templates and report shells
|
|
176
|
+
│ ├── verticals/
|
|
177
|
+
│ │ └── overview.md # How to document industry context
|
|
178
|
+
│ ├── case-studies/
|
|
179
|
+
│ │ └── index.md # Case study catalog with template
|
|
180
|
+
│ └── reference/
|
|
181
|
+
│ ├── team-expertise.md # Capabilities, specializations
|
|
182
|
+
│ ├── pricing-models.md # Engagement pricing structures
|
|
183
|
+
│ └── contacts/
|
|
184
|
+
│ └── directory.md # Team contacts, escalation
|
|
185
|
+
└── ...
|
|
186
|
+
</code></pre>
|
|
187
|
+
<h2 id="sections">Sections</h2>
|
|
188
|
+
<table>
|
|
189
|
+
<thead>
|
|
190
|
+
<tr>
|
|
191
|
+
<th>Section</th>
|
|
192
|
+
<th>Purpose</th>
|
|
193
|
+
<th>Typical Content</th>
|
|
194
|
+
</tr>
|
|
195
|
+
</thead>
|
|
196
|
+
<tbody><tr>
|
|
197
|
+
<td><strong>Offerings</strong></td>
|
|
198
|
+
<td>What we deliver</td>
|
|
199
|
+
<td>Service tiers, engagement models, deliverables, scoping</td>
|
|
200
|
+
</tr>
|
|
201
|
+
<tr>
|
|
202
|
+
<td><strong>Methodology</strong></td>
|
|
203
|
+
<td>How we do it</td>
|
|
204
|
+
<td>Phases, tools, frameworks, methodology decisions</td>
|
|
205
|
+
</tr>
|
|
206
|
+
<tr>
|
|
207
|
+
<td><strong>Delivery</strong></td>
|
|
208
|
+
<td>Engagement operations</td>
|
|
209
|
+
<td>Lifecycle, onboarding, quality gates, templates</td>
|
|
210
|
+
</tr>
|
|
211
|
+
<tr>
|
|
212
|
+
<td><strong>Verticals</strong></td>
|
|
213
|
+
<td>Industry context</td>
|
|
214
|
+
<td>Regulations, terminology, common challenges per vertical</td>
|
|
215
|
+
</tr>
|
|
216
|
+
<tr>
|
|
217
|
+
<td><strong>Case Studies</strong></td>
|
|
218
|
+
<td>Proof of capability</td>
|
|
219
|
+
<td>Past engagements (anonymized), outcomes, lessons learned</td>
|
|
220
|
+
</tr>
|
|
221
|
+
<tr>
|
|
222
|
+
<td><strong>Reference</strong></td>
|
|
223
|
+
<td>Look-up material</td>
|
|
224
|
+
<td>Team expertise, pricing models, contacts</td>
|
|
225
|
+
</tr>
|
|
226
|
+
</tbody></table>
|
|
227
|
+
<h2 id="the-methodology-section">The Methodology Section</h2>
|
|
228
|
+
<p>This is what makes servicebook different. It captures <strong>how your practice delivers value</strong> — not just what you sell.</p>
|
|
229
|
+
<p>For a technical assessment practice, this might hold:</p>
|
|
230
|
+
<ul>
|
|
231
|
+
<li><strong>Phases</strong>: Explore (stakeholder interviews) → Analyze (gap analysis) → Synthesize (recommendations) → Deliver (presentation)</li>
|
|
232
|
+
<li><strong>Tools</strong>: Interview frameworks, scoring rubrics, analysis templates</li>
|
|
233
|
+
<li><strong>Frameworks</strong>: Maturity models, SWOT analysis, weighted scoring for vendor evaluation</li>
|
|
234
|
+
<li><strong>Decisions</strong>: Why we switched from 3-phase to 4-phase methodology (MDR-001)</li>
|
|
235
|
+
</ul>
|
|
236
|
+
<p>For a management consulting practice:</p>
|
|
237
|
+
<ul>
|
|
238
|
+
<li><strong>Phases</strong>: Current State → Target State → Gap Analysis → Roadmap</li>
|
|
239
|
+
<li><strong>Tools</strong>: Workshop facilitation tools, benchmarking databases, financial modeling</li>
|
|
240
|
+
<li><strong>Frameworks</strong>: Operating model canvas, capability mapping, value stream analysis</li>
|
|
241
|
+
<li><strong>Decisions</strong>: Why we standardized on a specific assessment framework</li>
|
|
242
|
+
</ul>
|
|
243
|
+
<p>The methodology section turns "how we work" from oral tradition into documented, repeatable process.</p>
|
|
244
|
+
<h2 id="usage">Usage</h2>
|
|
245
|
+
<pre><code class="language-bash">kitfly init consulting-docs --template servicebook
|
|
246
|
+
kitfly init consulting-docs --template servicebook --brand "Apex Advisory"
|
|
247
|
+
|
|
248
|
+
# With AI assistance instrumentation
|
|
249
|
+
kitfly init consulting-docs --template servicebook --standalone --ai-assist
|
|
250
|
+
</code></pre>
|
|
251
|
+
<h2 id="servicebook-vs-other-templates">Servicebook vs. Other Templates</h2>
|
|
252
|
+
<table>
|
|
253
|
+
<thead>
|
|
254
|
+
<tr>
|
|
255
|
+
<th>Aspect</th>
|
|
256
|
+
<th>Productbook</th>
|
|
257
|
+
<th>Servicebook</th>
|
|
258
|
+
<th>Handbook</th>
|
|
259
|
+
</tr>
|
|
260
|
+
</thead>
|
|
261
|
+
<tbody><tr>
|
|
262
|
+
<td><strong>Orientation</strong></td>
|
|
263
|
+
<td>What we build</td>
|
|
264
|
+
<td>What we sell & deliver</td>
|
|
265
|
+
<td>How we work</td>
|
|
266
|
+
</tr>
|
|
267
|
+
<tr>
|
|
268
|
+
<td><strong>Assumes</strong></td>
|
|
269
|
+
<td>One product, iterated</td>
|
|
270
|
+
<td>Multiple service offerings</td>
|
|
271
|
+
<td>Team exists</td>
|
|
272
|
+
</tr>
|
|
273
|
+
<tr>
|
|
274
|
+
<td><strong>Key section</strong></td>
|
|
275
|
+
<td>Domain</td>
|
|
276
|
+
<td>Methodology</td>
|
|
277
|
+
<td>Guides</td>
|
|
278
|
+
</tr>
|
|
279
|
+
<tr>
|
|
280
|
+
<td><strong>Audience</strong></td>
|
|
281
|
+
<td>Product team + stakeholders</td>
|
|
282
|
+
<td>Delivery team + clients</td>
|
|
283
|
+
<td>Team members</td>
|
|
284
|
+
</tr>
|
|
285
|
+
<tr>
|
|
286
|
+
<td><strong>Tone</strong></td>
|
|
287
|
+
<td>Analytical</td>
|
|
288
|
+
<td>Professional, prescriptive</td>
|
|
289
|
+
<td>Explanatory</td>
|
|
290
|
+
</tr>
|
|
291
|
+
<tr>
|
|
292
|
+
<td><strong>Lifecycle</strong></td>
|
|
293
|
+
<td>Product releases</td>
|
|
294
|
+
<td>Engagement lifecycle</td>
|
|
295
|
+
<td>Knowledge updates</td>
|
|
296
|
+
</tr>
|
|
297
|
+
</tbody></table>
|
|
298
|
+
<h2 id="growing-your-servicebook">Growing Your Servicebook</h2>
|
|
299
|
+
<p>As the practice matures, the sections grow naturally:</p>
|
|
300
|
+
<p><strong>Offerings expand as services are defined</strong> — each service gets its own folder with overview, deliverables, and scoping:</p>
|
|
301
|
+
<pre><code>content/offerings/
|
|
302
|
+
├── overview.md
|
|
303
|
+
├── assess/
|
|
304
|
+
│ ├── overview.md
|
|
305
|
+
│ ├── deliverables.md
|
|
306
|
+
│ └── scoping.md
|
|
307
|
+
├── advisory/
|
|
308
|
+
│ ├── overview.md
|
|
309
|
+
│ └── engagement-models.md
|
|
310
|
+
└── implement/
|
|
311
|
+
├── overview.md
|
|
312
|
+
├── deliverables.md
|
|
313
|
+
└── scoping.md
|
|
314
|
+
</code></pre>
|
|
315
|
+
<p><strong>Verticals deepen with experience</strong> — each industry vertical captures specialized knowledge:</p>
|
|
316
|
+
<pre><code>content/verticals/
|
|
317
|
+
├── overview.md
|
|
318
|
+
├── healthcare.md
|
|
319
|
+
├── financial-services.md
|
|
320
|
+
└── manufacturing.md
|
|
321
|
+
</code></pre>
|
|
322
|
+
<p><strong>Case studies accumulate</strong> — the portfolio of evidence grows with each completed engagement.</p>
|
|
323
|
+
<p><strong>Methodology evolves through decisions</strong> — the MDR log tracks why the practice changed its approach.</p>
|
|
324
|
+
<h2 id="example-use-cases">Example Use Cases</h2>
|
|
325
|
+
<p><strong>Technical Assessment Practice</strong></p>
|
|
326
|
+
<ul>
|
|
327
|
+
<li>Offerings: Security assessment, architecture review, cloud readiness evaluation</li>
|
|
328
|
+
<li>Methodology: Discovery interviews, tooling analysis, gap scoring, recommendations</li>
|
|
329
|
+
<li>Delivery: 2-4 week fixed-scope engagements with standardized quality gates</li>
|
|
330
|
+
<li>Verticals: Healthcare (HIPAA), finance (SOC 2), government (FedRAMP)</li>
|
|
331
|
+
<li>Case Studies: Anonymized assessment outcomes with measurable improvements</li>
|
|
332
|
+
</ul>
|
|
333
|
+
<p><strong>Management Consulting Practice</strong></p>
|
|
334
|
+
<ul>
|
|
335
|
+
<li>Offerings: Strategy advisory, operating model design, transformation planning</li>
|
|
336
|
+
<li>Methodology: Current-state analysis, target operating model, roadmap development</li>
|
|
337
|
+
<li>Delivery: Retainer-based advisory with milestone-based transformation work</li>
|
|
338
|
+
<li>Verticals: Retail, energy, telecommunications</li>
|
|
339
|
+
<li>Case Studies: Transformation outcomes, efficiency gains, capability maturation</li>
|
|
340
|
+
</ul>
|
|
341
|
+
|
|
342
|
+
</article>
|
|
343
|
+
<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="#the-methodology-section">The Methodology Section</a></li><li><a href="#usage">Usage</a></li><li><a href="#servicebook-vs-other-templates">Servicebook vs. Other Templates</a></li><li><a href="#growing-your-servicebook">Growing Your Servicebook</a></li><li><a href="#example-use-cases">Example Use Cases</a></li></ul></aside>
|
|
344
|
+
</main>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<footer class="site-footer">
|
|
348
|
+
<div class="footer-content">
|
|
349
|
+
<div class="footer-left">
|
|
350
|
+
<span class="footer-version">v0.2.0</span>
|
|
351
|
+
<span class="footer-separator">·</span>
|
|
352
|
+
<span class="footer-commit" title="Commit: 33ccd68">Published 2026-02-15</span>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="footer-center">
|
|
355
|
+
<span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
|
|
356
|
+
<span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="footer-right">
|
|
359
|
+
<a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
</footer>
|
|
363
|
+
<!-- Syntax highlighting - Prism.js -->
|
|
364
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
|
|
365
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
|
|
366
|
+
<!-- Mermaid diagram support -->
|
|
367
|
+
<script type="module">
|
|
368
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
369
|
+
|
|
370
|
+
function getMermaidTheme() {
|
|
371
|
+
const theme = document.documentElement.getAttribute('data-theme');
|
|
372
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
373
|
+
const isDark = theme === 'dark' || (!theme && prefersDark);
|
|
374
|
+
return isDark ? 'dark' : 'neutral';
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
mermaid.initialize({
|
|
378
|
+
startOnLoad: true,
|
|
379
|
+
theme: getMermaidTheme()
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Re-render mermaid diagrams when theme changes
|
|
383
|
+
window.reinitMermaid = async function() {
|
|
384
|
+
mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
|
|
385
|
+
const diagrams = document.querySelectorAll('.mermaid');
|
|
386
|
+
for (const el of diagrams) {
|
|
387
|
+
const code = el.getAttribute('data-mermaid-source');
|
|
388
|
+
if (code) {
|
|
389
|
+
el.innerHTML = code;
|
|
390
|
+
el.removeAttribute('data-processed');
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
await mermaid.run({ nodes: diagrams });
|
|
394
|
+
};
|
|
395
|
+
</script>
|
|
396
|
+
|
|
397
|
+
<script>
|
|
398
|
+
function toggleTheme() {
|
|
399
|
+
const html = document.documentElement;
|
|
400
|
+
const current = html.getAttribute('data-theme');
|
|
401
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
402
|
+
|
|
403
|
+
let next;
|
|
404
|
+
if (current === 'dark') {
|
|
405
|
+
next = 'light';
|
|
406
|
+
} else if (current === 'light') {
|
|
407
|
+
next = 'dark';
|
|
408
|
+
} else {
|
|
409
|
+
// No explicit theme set, toggle from system preference
|
|
410
|
+
next = prefersDark ? 'light' : 'dark';
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
html.setAttribute('data-theme', next);
|
|
414
|
+
localStorage.setItem('theme', next);
|
|
415
|
+
|
|
416
|
+
// Switch Prism theme
|
|
417
|
+
const prismLight = document.getElementById('prism-light');
|
|
418
|
+
const prismDark = document.getElementById('prism-dark');
|
|
419
|
+
if (next === 'dark') {
|
|
420
|
+
prismLight?.setAttribute('disabled', '');
|
|
421
|
+
prismDark?.removeAttribute('disabled');
|
|
422
|
+
} else {
|
|
423
|
+
prismLight?.removeAttribute('disabled');
|
|
424
|
+
prismDark?.setAttribute('disabled', '');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Re-render mermaid diagrams with new theme
|
|
428
|
+
if (window.reinitMermaid) {
|
|
429
|
+
window.reinitMermaid();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Slides mode hash routing
|
|
434
|
+
(function initSlidesMode() {
|
|
435
|
+
const shell = document.querySelector('.slides-shell');
|
|
436
|
+
if (!shell) return;
|
|
437
|
+
|
|
438
|
+
const slides = Array.from(document.querySelectorAll('.slide'));
|
|
439
|
+
if (!slides.length) return;
|
|
440
|
+
|
|
441
|
+
const prevBtn = document.querySelector('.slide-prev');
|
|
442
|
+
const nextBtn = document.querySelector('.slide-next');
|
|
443
|
+
const counter = document.querySelector('.slide-counter');
|
|
444
|
+
const progressBar = document.querySelector('.slide-progress-bar');
|
|
445
|
+
const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
|
|
446
|
+
let current = 0;
|
|
447
|
+
|
|
448
|
+
function setActive(n) {
|
|
449
|
+
current = Math.max(0, Math.min(n, slides.length - 1));
|
|
450
|
+
slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
|
|
451
|
+
navLinks.forEach((link) => {
|
|
452
|
+
const active = link.getAttribute('href') === '#' + slides[current].id;
|
|
453
|
+
link.classList.toggle('active', active);
|
|
454
|
+
});
|
|
455
|
+
if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
|
|
456
|
+
if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
|
|
457
|
+
if (prevBtn) prevBtn.disabled = current === 0;
|
|
458
|
+
if (nextBtn) nextBtn.disabled = current === slides.length - 1;
|
|
459
|
+
history.replaceState(null, '', '#' + slides[current].id);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function setFromHash() {
|
|
463
|
+
const hash = window.location.hash || '';
|
|
464
|
+
const idx = slides.findIndex((s) => '#' + s.id === hash);
|
|
465
|
+
if (idx >= 0) setActive(idx);
|
|
466
|
+
else setActive(0);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
prevBtn?.addEventListener('click', () => setActive(current - 1));
|
|
470
|
+
nextBtn?.addEventListener('click', () => setActive(current + 1));
|
|
471
|
+
|
|
472
|
+
document.addEventListener('keydown', (e) => {
|
|
473
|
+
if (e.key === 'ArrowRight' || e.key === ' ') {
|
|
474
|
+
e.preventDefault();
|
|
475
|
+
setActive(current + 1);
|
|
476
|
+
} else if (e.key === 'ArrowLeft') {
|
|
477
|
+
e.preventDefault();
|
|
478
|
+
setActive(current - 1);
|
|
479
|
+
} else if (e.key === 'Home') {
|
|
480
|
+
e.preventDefault();
|
|
481
|
+
setActive(0);
|
|
482
|
+
} else if (e.key === 'End') {
|
|
483
|
+
e.preventDefault();
|
|
484
|
+
setActive(slides.length - 1);
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
window.addEventListener('hashchange', setFromHash);
|
|
489
|
+
setFromHash();
|
|
490
|
+
})();
|
|
491
|
+
|
|
492
|
+
// Copy code button
|
|
493
|
+
document.querySelectorAll('.prose pre code').forEach(block => {
|
|
494
|
+
const button = document.createElement('button');
|
|
495
|
+
button.className = 'copy-button';
|
|
496
|
+
button.textContent = 'Copy';
|
|
497
|
+
button.onclick = async () => {
|
|
498
|
+
await navigator.clipboard.writeText(block.textContent);
|
|
499
|
+
button.textContent = 'Copied!';
|
|
500
|
+
setTimeout(() => button.textContent = 'Copy', 2000);
|
|
501
|
+
};
|
|
502
|
+
block.parentElement.appendChild(button);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// Mobile nav toggle
|
|
506
|
+
function toggleNav() {
|
|
507
|
+
document.querySelector('.sidebar').classList.toggle('open');
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Close nav when clicking outside on mobile
|
|
511
|
+
document.addEventListener('click', (e) => {
|
|
512
|
+
const sidebar = document.querySelector('.sidebar');
|
|
513
|
+
const toggle = document.querySelector('.nav-toggle');
|
|
514
|
+
if (sidebar.classList.contains('open') &&
|
|
515
|
+
!sidebar.contains(e.target) &&
|
|
516
|
+
!toggle.contains(e.target)) {
|
|
517
|
+
sidebar.classList.remove('open');
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
</script>
|
|
521
|
+
|
|
522
|
+
</body>
|
|
523
|
+
</html>
|