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.
- package/CHANGELOG.md +46 -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/slides-authoring-guidelines.md +129 -0
- package/dist/_raw/content/reference/structure.md +166 -0
- package/dist/_raw/content/reference.md +20 -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/slides-authoring-guidelines.html +418 -0
- package/dist/content/reference/structure.html +463 -0
- package/dist/content/reference.html +335 -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 +549 -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 +390 -0
- package/plugins-dist/slides-visuals.js +689 -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 +158 -3
- package/scripts/bundle.ts +261 -95
- package/scripts/dev.ts +301 -11
- package/src/__tests__/build.test.ts +220 -1
- package/src/__tests__/bundle.test.ts +31 -0
- package/src/__tests__/cli.test.ts +14 -3
- package/src/__tests__/dev-plugin-errors.test.ts +20 -0
- 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/flow-branching-no-source.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/flow-converging-no-target.md +6 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/indented-fence.md +4 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/staircase-empty-steps.md +3 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/stat-grid-missing-fields.md +5 -0
- package/src/__tests__/fixtures/fences/slides-visuals/invalid/timeline-horizontal-no-events.md +2 -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/flow-branching-no-split.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-branching.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging-no-merge.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/flow-converging.md +8 -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/staircase-down.md +7 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/staircase.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/stat-grid.md +8 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-horizontal.md +9 -0
- package/src/__tests__/fixtures/fences/slides-visuals/valid/timeline-vertical.md +10 -0
- package/src/__tests__/init.test.ts +35 -0
- package/src/__tests__/plugin-loader.test.ts +221 -0
- package/src/__tests__/shared.test.ts +451 -0
- package/src/__tests__/slides-visuals-fence-contract.test.ts +28 -0
- package/src/__tests__/slides-visuals-runtime-regressions.bun.test.ts +147 -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 +650 -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,491 @@
|
|
|
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>Plugins - Kitfly Docs</title>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="32x32" href="../../assets/brand/kitfly-favicon-32.png">
|
|
8
|
+
<link rel="icon" type="image/png" sizes="64x64" href="../../assets/brand/kitfly-neon-256.png">
|
|
9
|
+
<link rel="stylesheet" href="../../styles.css">
|
|
10
|
+
<style id="kitfly-theme">
|
|
11
|
+
:root { --color-bg: #ffffff;
|
|
12
|
+
--color-bg-sidebar: #f5f7f8;
|
|
13
|
+
--color-text: #374151;
|
|
14
|
+
--color-text-muted: #6b7280;
|
|
15
|
+
--color-border: #e5e7eb;
|
|
16
|
+
--color-link: #007182;
|
|
17
|
+
--color-link-hover: #0a6172;
|
|
18
|
+
--color-accent: #152F46;
|
|
19
|
+
--color-code-bg: #f5f7f8;
|
|
20
|
+
--color-logo: #152F46;
|
|
21
|
+
--sidebar-width: 280px;
|
|
22
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
23
|
+
--font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
24
|
+
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
|
|
25
|
+
html { font-size: 16px; }
|
|
26
|
+
@media (prefers-color-scheme: dark) {
|
|
27
|
+
:root:not([data-theme="light"]) { --color-bg: #0d1117;
|
|
28
|
+
--color-bg-sidebar: #152F46;
|
|
29
|
+
--color-text: #e5e7eb;
|
|
30
|
+
--color-text-muted: #9ca3af;
|
|
31
|
+
--color-border: #374151;
|
|
32
|
+
--color-link: #709EA6;
|
|
33
|
+
--color-link-hover: #8fb5bc;
|
|
34
|
+
--color-accent: #f9fafb;
|
|
35
|
+
--color-code-bg: #152F46;
|
|
36
|
+
--color-logo: #f9fafb; }
|
|
37
|
+
}
|
|
38
|
+
[data-theme="dark"] { --color-bg: #0d1117;
|
|
39
|
+
--color-bg-sidebar: #152F46;
|
|
40
|
+
--color-text: #e5e7eb;
|
|
41
|
+
--color-text-muted: #9ca3af;
|
|
42
|
+
--color-border: #374151;
|
|
43
|
+
--color-link: #709EA6;
|
|
44
|
+
--color-link-hover: #8fb5bc;
|
|
45
|
+
--color-accent: #f9fafb;
|
|
46
|
+
--color-code-bg: #152F46;
|
|
47
|
+
--color-logo: #f9fafb; }
|
|
48
|
+
[data-theme="light"] { --color-bg: #ffffff;
|
|
49
|
+
--color-bg-sidebar: #f5f7f8;
|
|
50
|
+
--color-text: #374151;
|
|
51
|
+
--color-text-muted: #6b7280;
|
|
52
|
+
--color-border: #e5e7eb;
|
|
53
|
+
--color-link: #007182;
|
|
54
|
+
--color-link-hover: #0a6172;
|
|
55
|
+
--color-accent: #152F46;
|
|
56
|
+
--color-code-bg: #f5f7f8;
|
|
57
|
+
--color-logo: #152F46;
|
|
58
|
+
--sidebar-width: 280px;
|
|
59
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
60
|
+
--font-headings: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
61
|
+
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
|
|
62
|
+
</style>
|
|
63
|
+
<!-- Syntax highlighting - Prism.js -->
|
|
64
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.min.css" id="prism-light">
|
|
65
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-okaidia.min.css" id="prism-dark" disabled>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
// Apply saved theme immediately to prevent flash
|
|
69
|
+
(function() {
|
|
70
|
+
const saved = localStorage.getItem('theme');
|
|
71
|
+
if (saved) {
|
|
72
|
+
document.documentElement.setAttribute('data-theme', saved);
|
|
73
|
+
}
|
|
74
|
+
// Set Prism theme based on saved or system preference
|
|
75
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
76
|
+
const isDark = saved === 'dark' || (!saved && prefersDark);
|
|
77
|
+
if (isDark) {
|
|
78
|
+
document.getElementById('prism-light')?.setAttribute('disabled', '');
|
|
79
|
+
document.getElementById('prism-dark')?.removeAttribute('disabled');
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
</script>
|
|
83
|
+
</head>
|
|
84
|
+
<body class="mode-docs">
|
|
85
|
+
<div class="mobile-header">
|
|
86
|
+
<button class="nav-toggle" onclick="toggleNav()" aria-label="Toggle navigation">
|
|
87
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
88
|
+
<path d="M3 12h18M3 6h18M3 18h18"/>
|
|
89
|
+
</svg>
|
|
90
|
+
</button>
|
|
91
|
+
<a href="../../" class="mobile-logo" title="Home" data-initial="K">
|
|
92
|
+
<img src="../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img logo-icon" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')"/>
|
|
93
|
+
</a>
|
|
94
|
+
<button class="mobile-theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
|
|
95
|
+
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
96
|
+
<circle cx="12" cy="12" r="5"/>
|
|
97
|
+
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
98
|
+
</svg>
|
|
99
|
+
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
100
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
|
101
|
+
</svg>
|
|
102
|
+
</button>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="layout">
|
|
105
|
+
<nav class="sidebar">
|
|
106
|
+
<div class="sidebar-header">
|
|
107
|
+
<div class="logo logo-icon">
|
|
108
|
+
<a href="/" class="logo-icon" data-initial="K">
|
|
109
|
+
<img src="../../assets/brand/kitfly-neon-256.png" alt="Kitfly" class="logo-img" onerror="this.onerror=null;this.style.display='none';this.parentElement.classList.add('logo-fallback')"/>
|
|
110
|
+
</a>
|
|
111
|
+
<span class="logo-text">
|
|
112
|
+
<a href="/" class="brand">Kitfly</a>
|
|
113
|
+
<a href="../../" class="product">Kitfly Docs</a>
|
|
114
|
+
</span>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="header-tools">
|
|
117
|
+
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">
|
|
118
|
+
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
119
|
+
<circle cx="12" cy="12" r="5"/>
|
|
120
|
+
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
121
|
+
</svg>
|
|
122
|
+
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
123
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
|
124
|
+
</svg>
|
|
125
|
+
</button>
|
|
126
|
+
<div class="sidebar-meta">
|
|
127
|
+
<span class="meta-version">v0.2.1</span>
|
|
128
|
+
<span class="meta-branch">HEAD</span>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="sidebar-nav">
|
|
133
|
+
<ul><li><a href="../../index.html" class="nav-home">Home</a></li><li><span class="nav-section">Guide</span><ul><li><a href="../../content/guide/approaches.html">approaches</a></li><li><a href="../../content/guide/features.html">features</a></li><li><a href="../../content/guide/getting-started.html">getting-started</a></li><li><a href="../../content/guide/kitfly-overview.html">kitfly-overview</a></li></ul></li><li><span class="nav-section">Templates</span><ul><li><a href="../../content/templates/crucible.html">crucible</a></li><li><a href="../../content/templates/handbook.html">handbook</a></li><li><a href="../../content/templates/minimal.html">minimal</a></li><li><a href="../../content/templates/overview.html">overview</a></li><li><a href="../../content/templates/pipeline.html">pipeline</a></li><li><a href="../../content/templates/productbook.html">productbook</a></li><li><a href="../../content/templates/runbook.html">runbook</a></li><li><a href="../../content/templates/servicebook.html">servicebook</a></li></ul></li><li><a href="../../content/reference.html" class="nav-section">Reference</a><ul><li><a href="../../content/reference/configuration.html">configuration</a></li><li><a href="../../content/reference/design-catalog.html">design-catalog</a></li><li><a href="../../content/reference/environment-variables.html">environment-variables</a></li><li><a href="../../content/reference/glossary.html">glossary</a></li><li><a href="../../content/reference/key-concepts.html">key-concepts</a></li><li><a href="../../content/reference/plugins.html" class="active">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>plugins</span></nav>
|
|
139
|
+
<div class="page-meta">Last updated: 2026-02-12</div>
|
|
140
|
+
<h1 id="plugins">Plugins</h1>
|
|
141
|
+
<p>Kitfly plugins are small, optional add-ons that inject CSS and/or JS into your generated HTML.</p>
|
|
142
|
+
<p>They are designed to stay minimal:</p>
|
|
143
|
+
<ul>
|
|
144
|
+
<li>No build pipeline required</li>
|
|
145
|
+
<li>Offline-friendly when assets are local</li>
|
|
146
|
+
<li>You own the files (standalone sites copy <code>registry/</code> + <code>plugins-dist/</code>)</li>
|
|
147
|
+
</ul>
|
|
148
|
+
<h2 id="enable-a-plugin">Enable a plugin</h2>
|
|
149
|
+
<p>Create <code>kitfly.plugins.yaml</code> in your site root:</p>
|
|
150
|
+
<pre><code class="language-yaml"># yaml-language-server: $schema=./schemas/v0/plugins.schema.json
|
|
151
|
+
plugins:
|
|
152
|
+
- callouts@0.2.0
|
|
153
|
+
</code></pre>
|
|
154
|
+
<p>Canonical plugins are referenced as pinned strings: <code>name@x.y.z</code>.</p>
|
|
155
|
+
<h2 id="registry-assets">Registry + assets</h2>
|
|
156
|
+
<p>Canonical plugins come from a registry file:</p>
|
|
157
|
+
<ul>
|
|
158
|
+
<li>Site registry: <code>registry/plugins.yaml</code> (preferred if present)</li>
|
|
159
|
+
<li>Engine registry: used as a fallback when the site does not include a registry</li>
|
|
160
|
+
</ul>
|
|
161
|
+
<p>Registry entries map a plugin id + version to asset locations (<code>assets.js</code> and/or <code>assets.css</code>) and per-asset checksums (<code>assetSha256</code>).</p>
|
|
162
|
+
<p>For local/offline registries, <code>baseUrl</code> may be an empty string.</p>
|
|
163
|
+
<h2 id="mode-allowlist-modes">Mode allowlist (<code>modes</code>)</h2>
|
|
164
|
+
<p>Registry entries may include an optional <code>modes</code> allowlist to control where a plugin runs:</p>
|
|
165
|
+
<ul>
|
|
166
|
+
<li>Omitted: allowed in <code>docs</code> and <code>slides</code></li>
|
|
167
|
+
<li>Present with values: allowed only in those modes (example: <code>["slides"]</code>)</li>
|
|
168
|
+
<li>Present but empty (<code>[]</code>): blocked in all modes (quick disable switch)</li>
|
|
169
|
+
</ul>
|
|
170
|
+
<p>Example:</p>
|
|
171
|
+
<pre><code class="language-yaml">plugins:
|
|
172
|
+
slides-widgets:
|
|
173
|
+
version: "0.2.0"
|
|
174
|
+
modes: ["slides"]
|
|
175
|
+
assets:
|
|
176
|
+
js: "plugins-dist/slides-widgets.js"
|
|
177
|
+
assetSha256:
|
|
178
|
+
js: "sha256:..."
|
|
179
|
+
</code></pre>
|
|
180
|
+
<h2 id="integrity-checks">Integrity checks</h2>
|
|
181
|
+
<p>Kitfly verifies every enabled asset against its <code>sha256:<hex></code> checksum. If a checksum does not match, the build/dev server fails with an integrity error.</p>
|
|
182
|
+
<p>When iterating on a local plugin (for example, editing <code>plugins-dist/slides-visuals.js</code>), you must also update the matching <code>assetSha256</code> in <code>registry/plugins.yaml</code> (or disable the plugin) for Kitfly to load it.</p>
|
|
183
|
+
<h2 id="triple-colon-fence-contract-slides-visuals">Triple-colon fence contract (<code>slides-visuals</code>)</h2>
|
|
184
|
+
<p>The <code>slides-visuals</code> plugin adds a <code>:::</code> block syntax for slides mode (widgets + figures).</p>
|
|
185
|
+
<p>To keep authoring predictable and make errors actionable, Kitfly defines a strict contract for these blocks.
|
|
186
|
+
When <code>slides-visuals</code> is enabled, Kitfly validates blocks before rendering and reports contract violations.</p>
|
|
187
|
+
<h3 id="valid-block-shape">Valid block shape</h3>
|
|
188
|
+
<ul>
|
|
189
|
+
<li>Opening fence: <code>:::<type></code> <strong>must</strong> start at column 0 and be the only content on the line.</li>
|
|
190
|
+
<li>Closing fence: <code>:::</code> <strong>must</strong> start at column 0 and be the only content on the line.</li>
|
|
191
|
+
<li>No blank lines inside a <code>:::</code> block.</li>
|
|
192
|
+
<li>Content is a narrow YAML subset:<ul>
|
|
193
|
+
<li>Scalar: <code>key: value</code></li>
|
|
194
|
+
<li>List: <code>key:</code> followed by list items<ul>
|
|
195
|
+
<li>List item start: exactly two spaces, then <code>- </code> (example: <code>␠␠- label: Users</code>)</li>
|
|
196
|
+
<li>Continuation lines (object fields): exactly four spaces, then <code>field: value</code></li>
|
|
197
|
+
</ul>
|
|
198
|
+
</li>
|
|
199
|
+
</ul>
|
|
200
|
+
</li>
|
|
201
|
+
</ul>
|
|
202
|
+
<h3 id="supported-types">Supported types</h3>
|
|
203
|
+
<p>Widgets:</p>
|
|
204
|
+
<ul>
|
|
205
|
+
<li><code>kpi</code> (scalar keys: <code>label</code>, <code>value</code>, optional <code>trend</code>)</li>
|
|
206
|
+
<li><code>stat-grid</code> (list key: <code>metrics</code> of <code>{label,value,trend?}</code> objects)</li>
|
|
207
|
+
<li><code>compare</code> (scalar keys: <code>left-title</code>, <code>right-title</code>; list keys: <code>left</code>, <code>right</code> as <strong>strings only</strong>)</li>
|
|
208
|
+
</ul>
|
|
209
|
+
<p>Notes:</p>
|
|
210
|
+
<ul>
|
|
211
|
+
<li><code>compare.left</code> and <code>compare.right</code> items are simple strings (not <code>{label: ..., value: ...}</code> objects).</li>
|
|
212
|
+
<li>If you need multi-field items, use <code>stat-grid</code> / <code>scorecard</code> instead.</li>
|
|
213
|
+
<li>If an item contains a colon (<code>:</code>), quote it as a string.</li>
|
|
214
|
+
</ul>
|
|
215
|
+
<p>Figures:</p>
|
|
216
|
+
<ul>
|
|
217
|
+
<li><code>quadrant-grid</code> (scalar keys: <code>axis-x</code>, <code>axis-y</code>, <code>tl</code>, <code>tr</code>, <code>bl</code>, <code>br</code>)</li>
|
|
218
|
+
<li><code>scorecard</code> (list key: <code>metrics</code> of <code>{label,value,trend?}</code> objects)</li>
|
|
219
|
+
<li><code>comparison-table</code> (list keys: <code>headers</code> (strings), <code>rows</code> (strings))</li>
|
|
220
|
+
<li><code>layer-cake</code> (list key: <code>layers</code> (strings))</li>
|
|
221
|
+
<li><code>pyramid</code> (list key: <code>levels</code> (strings))</li>
|
|
222
|
+
<li><code>funnel</code> (list key: <code>stages</code> (strings))</li>
|
|
223
|
+
</ul>
|
|
224
|
+
<h3 id="example-valid">Example (valid)</h3>
|
|
225
|
+
<pre><code class="language-markdown">:::stat-grid
|
|
226
|
+
metrics:
|
|
227
|
+
|
|
228
|
+
- label: Users
|
|
229
|
+
value: 1,234
|
|
230
|
+
- label: Uptime
|
|
231
|
+
value: 99.95%
|
|
232
|
+
trend: +0.3%
|
|
233
|
+
:::
|
|
234
|
+
</code></pre>
|
|
235
|
+
<h3 id="how-to-know-its-correct">How to know it’s correct</h3>
|
|
236
|
+
<p>When <code>slides-visuals@...</code> is enabled, Kitfly validates your <code>:::</code> blocks before it renders pages.</p>
|
|
237
|
+
<p>You know your fences are valid if:</p>
|
|
238
|
+
<ul>
|
|
239
|
+
<li><code>kitfly dev</code> starts successfully, and pages load normally</li>
|
|
240
|
+
<li><code>kitfly build</code> completes successfully</li>
|
|
241
|
+
<li><code>kitfly bundle</code> completes successfully</li>
|
|
242
|
+
</ul>
|
|
243
|
+
<p>If a block is invalid, Kitfly will fail fast with an error that points to the file and the specific contract rule you violated.</p>
|
|
244
|
+
<h3 id="common-mistakes-and-how-to-fix-them">Common mistakes (and how to fix them)</h3>
|
|
245
|
+
<ul>
|
|
246
|
+
<li><strong>Indented fences</strong>: <code>:::kpi</code> must start at column 0 (no spaces, no list indentation, no blockquote <code>></code>).</li>
|
|
247
|
+
<li><strong>Blank lines inside the block</strong>: remove empty lines between keys/items.</li>
|
|
248
|
+
<li><strong>Wrong list indentation</strong>:<ul>
|
|
249
|
+
<li>list items must start with exactly two spaces then <code>- </code></li>
|
|
250
|
+
<li>fields under an item must use exactly four spaces</li>
|
|
251
|
+
</ul>
|
|
252
|
+
</li>
|
|
253
|
+
<li><strong>Unknown type</strong>: the opening fence <code>:::<type></code> must be one of the supported types.</li>
|
|
254
|
+
</ul>
|
|
255
|
+
<h3 id="examples-invalid-fixed">Examples (invalid → fixed)</h3>
|
|
256
|
+
<h4 id="1-indented-fence-invalid">1) Indented fence (invalid)</h4>
|
|
257
|
+
<pre><code class="language-markdown">:::kpi
|
|
258
|
+
label: Users
|
|
259
|
+
value: 1,234
|
|
260
|
+
:::
|
|
261
|
+
</code></pre>
|
|
262
|
+
<p>Fixed:</p>
|
|
263
|
+
<pre><code class="language-markdown">:::kpi
|
|
264
|
+
label: Users
|
|
265
|
+
value: 1,234
|
|
266
|
+
:::
|
|
267
|
+
</code></pre>
|
|
268
|
+
<h4 id="2-blank-line-inside-block-invalid">2) Blank line inside block (invalid)</h4>
|
|
269
|
+
<pre><code class="language-markdown">:::kpi
|
|
270
|
+
label: Users
|
|
271
|
+
|
|
272
|
+
value: 1,234
|
|
273
|
+
:::
|
|
274
|
+
</code></pre>
|
|
275
|
+
<p>Fixed:</p>
|
|
276
|
+
<pre><code class="language-markdown">:::kpi
|
|
277
|
+
label: Users
|
|
278
|
+
value: 1,234
|
|
279
|
+
:::
|
|
280
|
+
</code></pre>
|
|
281
|
+
<h4 id="3-bad-list-indentation-invalid">3) Bad list indentation (invalid)</h4>
|
|
282
|
+
<pre><code class="language-markdown">:::stat-grid
|
|
283
|
+
metrics:
|
|
284
|
+
|
|
285
|
+
- label: Users
|
|
286
|
+
value: 1,234
|
|
287
|
+
:::
|
|
288
|
+
</code></pre>
|
|
289
|
+
<p>Fixed:</p>
|
|
290
|
+
<pre><code class="language-markdown">:::stat-grid
|
|
291
|
+
metrics:
|
|
292
|
+
|
|
293
|
+
- label: Users
|
|
294
|
+
value: 1,234
|
|
295
|
+
:::
|
|
296
|
+
</code></pre>
|
|
297
|
+
<h4 id="4-unknown-type-invalid">4) Unknown type (invalid)</h4>
|
|
298
|
+
<pre><code class="language-markdown">:::stats
|
|
299
|
+
label: Users
|
|
300
|
+
value: 1,234
|
|
301
|
+
:::
|
|
302
|
+
</code></pre>
|
|
303
|
+
<p>Fixed: use a supported type (for example <code>kpi</code>):</p>
|
|
304
|
+
<pre><code class="language-markdown">:::kpi
|
|
305
|
+
label: Users
|
|
306
|
+
value: 1,234
|
|
307
|
+
:::
|
|
308
|
+
</code></pre>
|
|
309
|
+
|
|
310
|
+
</article>
|
|
311
|
+
<aside class="toc"><span class="toc-title">On this page</span><ul><li><a href="#enable-a-plugin">Enable a plugin</a></li><li><a href="#registry-assets">Registry + assets</a></li><li><a href="#integrity-checks">Integrity checks</a></li><li class="toc-h3"><a href="#valid-block-shape">Valid block shape</a></li><li class="toc-h3"><a href="#supported-types">Supported types</a></li><li class="toc-h3"><a href="#example-valid">Example (valid)</a></li><li class="toc-h3"><a href="#how-to-know-its-correct">How to know it’s correct</a></li><li class="toc-h3"><a href="#common-mistakes-and-how-to-fix-them">Common mistakes (and how to fix them)</a></li><li class="toc-h3"><a href="#examples-invalid-fixed">Examples (invalid → fixed)</a></li></ul></aside>
|
|
312
|
+
</main>
|
|
313
|
+
</div>
|
|
314
|
+
|
|
315
|
+
<footer class="site-footer">
|
|
316
|
+
<div class="footer-content">
|
|
317
|
+
<div class="footer-left">
|
|
318
|
+
<span class="footer-version">v0.2.1</span>
|
|
319
|
+
<span class="footer-separator">·</span>
|
|
320
|
+
<span class="footer-commit" title="Commit: 30dfc01">Published 2026-02-15</span>
|
|
321
|
+
</div>
|
|
322
|
+
<div class="footer-center">
|
|
323
|
+
<span class="footer-copyright"><a href="https://3leaps.net" class="footer-link">© 2026 3 Leaps, LLC</a></span>
|
|
324
|
+
<span class="footer-separator">·</span><a href="/" class="footer-link">Kitfly</a>
|
|
325
|
+
</div>
|
|
326
|
+
<div class="footer-right">
|
|
327
|
+
<a href="https://kitfly.dev" class="footer-link">Built with Kitfly</a>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</footer>
|
|
331
|
+
<!-- Syntax highlighting - Prism.js -->
|
|
332
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-core.min.js"></script>
|
|
333
|
+
<script src="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js"></script>
|
|
334
|
+
<!-- Mermaid diagram support -->
|
|
335
|
+
<script type="module">
|
|
336
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
|
|
337
|
+
|
|
338
|
+
function getMermaidTheme() {
|
|
339
|
+
const theme = document.documentElement.getAttribute('data-theme');
|
|
340
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
341
|
+
const isDark = theme === 'dark' || (!theme && prefersDark);
|
|
342
|
+
return isDark ? 'dark' : 'neutral';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
mermaid.initialize({
|
|
346
|
+
startOnLoad: true,
|
|
347
|
+
theme: getMermaidTheme()
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// Re-render mermaid diagrams when theme changes
|
|
351
|
+
window.reinitMermaid = async function() {
|
|
352
|
+
mermaid.initialize({ startOnLoad: false, theme: getMermaidTheme() });
|
|
353
|
+
const diagrams = document.querySelectorAll('.mermaid');
|
|
354
|
+
for (const el of diagrams) {
|
|
355
|
+
const code = el.getAttribute('data-mermaid-source');
|
|
356
|
+
if (code) {
|
|
357
|
+
el.innerHTML = code;
|
|
358
|
+
el.removeAttribute('data-processed');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
await mermaid.run({ nodes: diagrams });
|
|
362
|
+
};
|
|
363
|
+
</script>
|
|
364
|
+
|
|
365
|
+
<script>
|
|
366
|
+
function toggleTheme() {
|
|
367
|
+
const html = document.documentElement;
|
|
368
|
+
const current = html.getAttribute('data-theme');
|
|
369
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
370
|
+
|
|
371
|
+
let next;
|
|
372
|
+
if (current === 'dark') {
|
|
373
|
+
next = 'light';
|
|
374
|
+
} else if (current === 'light') {
|
|
375
|
+
next = 'dark';
|
|
376
|
+
} else {
|
|
377
|
+
// No explicit theme set, toggle from system preference
|
|
378
|
+
next = prefersDark ? 'light' : 'dark';
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
html.setAttribute('data-theme', next);
|
|
382
|
+
localStorage.setItem('theme', next);
|
|
383
|
+
|
|
384
|
+
// Switch Prism theme
|
|
385
|
+
const prismLight = document.getElementById('prism-light');
|
|
386
|
+
const prismDark = document.getElementById('prism-dark');
|
|
387
|
+
if (next === 'dark') {
|
|
388
|
+
prismLight?.setAttribute('disabled', '');
|
|
389
|
+
prismDark?.removeAttribute('disabled');
|
|
390
|
+
} else {
|
|
391
|
+
prismLight?.removeAttribute('disabled');
|
|
392
|
+
prismDark?.setAttribute('disabled', '');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Re-render mermaid diagrams with new theme
|
|
396
|
+
if (window.reinitMermaid) {
|
|
397
|
+
window.reinitMermaid();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Slides mode hash routing
|
|
402
|
+
(function initSlidesMode() {
|
|
403
|
+
const shell = document.querySelector('.slides-shell');
|
|
404
|
+
if (!shell) return;
|
|
405
|
+
|
|
406
|
+
const slides = Array.from(document.querySelectorAll('.slide'));
|
|
407
|
+
if (!slides.length) return;
|
|
408
|
+
|
|
409
|
+
const prevBtn = document.querySelector('.slide-prev');
|
|
410
|
+
const nextBtn = document.querySelector('.slide-next');
|
|
411
|
+
const counter = document.querySelector('.slide-counter');
|
|
412
|
+
const progressBar = document.querySelector('.slide-progress-bar');
|
|
413
|
+
const navLinks = Array.from(document.querySelectorAll('.sidebar-nav a[href^="#slide-"]'));
|
|
414
|
+
let current = 0;
|
|
415
|
+
|
|
416
|
+
function setActive(n) {
|
|
417
|
+
current = Math.max(0, Math.min(n, slides.length - 1));
|
|
418
|
+
slides.forEach((slide, idx) => slide.classList.toggle('active', idx === current));
|
|
419
|
+
navLinks.forEach((link) => {
|
|
420
|
+
const active = link.getAttribute('href') === '#' + slides[current].id;
|
|
421
|
+
link.classList.toggle('active', active);
|
|
422
|
+
});
|
|
423
|
+
if (counter) counter.textContent = (current + 1) + ' / ' + slides.length;
|
|
424
|
+
if (progressBar) progressBar.style.width = (((current + 1) / slides.length) * 100) + '%';
|
|
425
|
+
if (prevBtn) prevBtn.disabled = current === 0;
|
|
426
|
+
if (nextBtn) nextBtn.disabled = current === slides.length - 1;
|
|
427
|
+
history.replaceState(null, '', '#' + slides[current].id);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function setFromHash() {
|
|
431
|
+
const hash = window.location.hash || '';
|
|
432
|
+
const idx = slides.findIndex((s) => '#' + s.id === hash);
|
|
433
|
+
if (idx >= 0) setActive(idx);
|
|
434
|
+
else setActive(0);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
prevBtn?.addEventListener('click', () => setActive(current - 1));
|
|
438
|
+
nextBtn?.addEventListener('click', () => setActive(current + 1));
|
|
439
|
+
|
|
440
|
+
document.addEventListener('keydown', (e) => {
|
|
441
|
+
if (e.key === 'ArrowRight' || e.key === ' ') {
|
|
442
|
+
e.preventDefault();
|
|
443
|
+
setActive(current + 1);
|
|
444
|
+
} else if (e.key === 'ArrowLeft') {
|
|
445
|
+
e.preventDefault();
|
|
446
|
+
setActive(current - 1);
|
|
447
|
+
} else if (e.key === 'Home') {
|
|
448
|
+
e.preventDefault();
|
|
449
|
+
setActive(0);
|
|
450
|
+
} else if (e.key === 'End') {
|
|
451
|
+
e.preventDefault();
|
|
452
|
+
setActive(slides.length - 1);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
window.addEventListener('hashchange', setFromHash);
|
|
457
|
+
setFromHash();
|
|
458
|
+
})();
|
|
459
|
+
|
|
460
|
+
// Copy code button
|
|
461
|
+
document.querySelectorAll('.prose pre code').forEach(block => {
|
|
462
|
+
const button = document.createElement('button');
|
|
463
|
+
button.className = 'copy-button';
|
|
464
|
+
button.textContent = 'Copy';
|
|
465
|
+
button.onclick = async () => {
|
|
466
|
+
await navigator.clipboard.writeText(block.textContent);
|
|
467
|
+
button.textContent = 'Copied!';
|
|
468
|
+
setTimeout(() => button.textContent = 'Copy', 2000);
|
|
469
|
+
};
|
|
470
|
+
block.parentElement.appendChild(button);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
// Mobile nav toggle
|
|
474
|
+
function toggleNav() {
|
|
475
|
+
document.querySelector('.sidebar').classList.toggle('open');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Close nav when clicking outside on mobile
|
|
479
|
+
document.addEventListener('click', (e) => {
|
|
480
|
+
const sidebar = document.querySelector('.sidebar');
|
|
481
|
+
const toggle = document.querySelector('.nav-toggle');
|
|
482
|
+
if (sidebar.classList.contains('open') &&
|
|
483
|
+
!sidebar.contains(e.target) &&
|
|
484
|
+
!toggle.contains(e.target)) {
|
|
485
|
+
sidebar.classList.remove('open');
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
</script>
|
|
489
|
+
|
|
490
|
+
</body>
|
|
491
|
+
</html>
|