kitfly 0.2.1 → 0.2.3
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 +56 -0
- package/README.md +25 -10
- package/VERSION +1 -1
- package/dist/_raw/content/guide/branding.md +146 -0
- package/dist/_raw/content/guide/data-driven-content.md +204 -0
- package/dist/_raw/content/reference/configuration.md +145 -7
- package/dist/_raw/content/reference/environment-variables.md +26 -1
- package/dist/_raw/content/reference/glossary.md +25 -1
- package/dist/_raw/content/reference/key-concepts.md +30 -2
- package/dist/_raw/content/reference/plugins.md +14 -0
- package/dist/_raw/docs/decisions/ADR-0006-data-driven-content.md +350 -0
- package/dist/content/deployment/preflight.html +10 -6
- package/dist/content/deployment/recipes/aws-s3.html +10 -6
- package/dist/content/deployment/recipes/cloudflare-pages.html +10 -6
- package/dist/content/deployment/recipes/cloudflare-r2.html +10 -6
- package/dist/content/deployment/recipes/fly-io.html +10 -6
- package/dist/content/deployment/recipes/github-pages.html +10 -6
- package/dist/content/deployment/recipes/netlify.html +10 -6
- package/dist/content/deployment/recipes/vercel.html +10 -6
- package/dist/content/deployment/secrets-and-env-vars.html +10 -6
- package/dist/content/deployment.html +10 -6
- package/dist/content/guide/approaches.html +10 -6
- package/dist/content/guide/branding.html +510 -0
- package/dist/content/guide/data-driven-content.html +543 -0
- package/dist/content/guide/features.html +10 -6
- package/dist/content/guide/getting-started.html +10 -6
- package/dist/content/guide/kitfly-overview.html +10 -6
- package/dist/content/reference/configuration.html +135 -9
- package/dist/content/reference/design-catalog.html +10 -6
- package/dist/content/reference/environment-variables.html +50 -8
- package/dist/content/reference/glossary.html +24 -8
- package/dist/content/reference/key-concepts.html +33 -9
- package/dist/content/reference/plugins.html +22 -7
- package/dist/content/reference/slides-authoring-guidelines.html +10 -6
- package/dist/content/reference/structure.html +10 -6
- package/dist/content/reference.html +10 -6
- package/dist/content/templates/crucible.html +10 -6
- package/dist/content/templates/handbook.html +10 -6
- package/dist/content/templates/minimal.html +10 -6
- package/dist/content/templates/overview.html +10 -6
- package/dist/content/templates/pipeline.html +10 -6
- package/dist/content/templates/productbook.html +10 -6
- package/dist/content/templates/runbook.html +10 -6
- package/dist/content/templates/servicebook.html +10 -6
- package/dist/content-index.json +29 -2
- package/dist/docs/decisions/ADR-0001-minimalist-site-code.html +10 -6
- package/dist/docs/decisions/ADR-0002-ai-accessibility.html +10 -6
- package/dist/docs/decisions/ADR-0003-single-file-bundle.html +10 -6
- package/dist/docs/decisions/ADR-0004-bun-runtime.html +10 -6
- package/dist/docs/decisions/ADR-0005-plugin-contract-and-distribution.html +10 -6
- package/dist/docs/decisions/ADR-0006-data-driven-content.html +752 -0
- package/dist/docs/decisions/DDR-0001-viewport-locked-layout.html +10 -6
- package/dist/docs/decisions/DDR-0002-theme-system.html +10 -6
- package/dist/docs/decisions/DDR-0003-bounded-logo-slot.html +10 -6
- package/dist/docs/decisions/DDR-0004-slides-rendering-model.html +10 -6
- package/dist/docs/decisions/DDR-0005-deterministic-layout-boundary.html +10 -6
- package/dist/docs/userguide/cli/build.html +10 -6
- package/dist/docs/userguide/cli/bundle.html +10 -6
- package/dist/docs/userguide/cli/dev.html +10 -6
- package/dist/docs/userguide/cli/init.html +10 -6
- package/dist/docs/userguide/cli/servers.html +10 -6
- package/dist/docs/userguide/cli/stop.html +10 -6
- package/dist/docs/userguide/cli/update.html +10 -6
- package/dist/docs/userguide/cli/version.html +10 -6
- package/dist/docs/userguide/cli.html +10 -6
- package/dist/docs/userguide/sharing.html +10 -6
- package/dist/index.html +10 -6
- package/dist/llms.txt +3 -3
- package/dist/provenance.json +4 -4
- package/dist/schemas/plugin-registry.schema.html +10 -6
- package/dist/schemas/plugin-schemas-notes.html +10 -6
- package/dist/schemas/plugin.schema.html +10 -6
- package/dist/schemas/plugins.schema.html +10 -6
- package/dist/schemas/v0/common.schema.html +14 -10
- package/dist/schemas/v0/plugin-registry.schema.html +13 -9
- package/dist/schemas/v0/plugin.schema.html +13 -9
- package/dist/schemas/v0/plugins.schema.html +13 -9
- package/dist/schemas/v0/site.schema.html +67 -7
- package/dist/schemas/v0/theme.schema.html +21 -17
- package/dist/schemas.html +10 -6
- package/dist/styles.css +39 -4
- package/package.json +1 -1
- package/plugins-dist/latex-runtime.js +140 -0
- package/plugins-dist/latex.js +178 -0
- package/plugins-dist/slides-charts-lite-runtime.js +179 -0
- package/plugins-dist/slides-charts-lite.js +198 -0
- package/registry/plugins.yaml +25 -0
- package/schemas/v0/site.schema.json +56 -0
- package/scripts/build.ts +191 -69
- package/scripts/bundle.ts +118 -10
- package/scripts/dev.ts +245 -166
- package/src/__tests__/brief.test.ts +151 -0
- package/src/__tests__/build.test.ts +169 -1
- package/src/__tests__/bundle.test.ts +134 -0
- package/src/__tests__/init.test.ts +51 -2
- package/src/__tests__/latex-runtime.bun.test.ts +35 -0
- package/src/__tests__/shared.test.ts +598 -1
- package/src/__tests__/slides-charts-lite-runtime.bun.test.ts +45 -0
- package/src/cli.ts +11 -4
- package/src/commands/init.ts +1 -1
- package/src/shared.ts +725 -18
- package/src/site/styles.css +39 -4
- package/src/site/template.html +5 -2
- package/src/templates/brief.ts +486 -0
- package/src/templates/deck.ts +59 -0
- package/src/templates/driver.ts +46 -13
- package/src/templates/handbook.ts +32 -0
- package/src/templates/runbook.ts +32 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Kitfly are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.2.3] - 2026-02-17
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Content profiles: `--profile` flag and `KITFLY_PROFILE` env var for single-source multi-audience filtering via frontmatter `profile:` tags
|
|
10
|
+
- Data-driven bindings: `{{ key }}` value substitution and `{{ snippet:name }}` block injection from YAML/JSON data files bound via `data:` frontmatter
|
|
11
|
+
- Pre-build hooks: `prebuild:` commands in site.yaml that run before dev/build/bundle with watch-mode reruns
|
|
12
|
+
- Built-in formatters: `dollar`, `number`, `percent`, `round(n)`, `upper`, `lower` with pipe chaining (`{{ key | round(0) | dollar }}`)
|
|
13
|
+
- Optional schema validation for data files (JSON Schema structural checks)
|
|
14
|
+
- ADR-0006: Data-Driven Content architecture decision
|
|
15
|
+
- "Kitsite" terminology and "What's a Kitsite?" section in README
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Windows cross-platform compatibility: launcher script install (no symlinks), browser open dispatch, MSYS `/c/...` path normalization
|
|
20
|
+
- Profile filtering backward compatibility when no profiles configured and no active profile selected
|
|
21
|
+
- `KITFLY_PROFILE` environment variable propagation through kitfly CLI entrypoints
|
|
22
|
+
- YAML parser: block scalar (`|`/`>`) support for data file snippets
|
|
23
|
+
- YAML parser: direct list-item block scalars, chomping/indent indicator handling
|
|
24
|
+
- YAML parser: reject malformed block scalar headers (`|abc`, `>foo`) instead of silent empty strings
|
|
25
|
+
|
|
26
|
+
### Docs
|
|
27
|
+
|
|
28
|
+
- Windows contributor setup guide in docs/development.md
|
|
29
|
+
- Development docs table alignment fix
|
|
30
|
+
|
|
31
|
+
### Generator guidance (from dogfooding)
|
|
32
|
+
|
|
33
|
+
- `pages[].path` in data files must be relative to site root including `content/` prefix (e.g. `content/product/pricing.md`, not `product/pricing.md`)
|
|
34
|
+
- Generators must emit every snippet the template references, even if empty — the template is the contract
|
|
35
|
+
- `percent` formatter expects a decimal ratio (0.0–1.0), not an already-computed percentage
|
|
36
|
+
- Use JSON for generator-produced data files; reserve YAML for hand-authored data where readability matters
|
|
37
|
+
- Recommended layout: raw input in `data/raw/`, kitfly data files in `data/`
|
|
38
|
+
|
|
39
|
+
## [0.2.2] - 2026-02-16
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- `slides-charts-lite` plugin: bar/line/pie charts via Chart.js 4.4.7 CDN with fenced `chart` code blocks
|
|
44
|
+
- `latex` plugin: math typesetting via KaTeX 0.16.21 CDN ($inline$, $$display$$, fenced `math` blocks)
|
|
45
|
+
- `brief` template: external-audience product documentation with 4 sections and starter content
|
|
46
|
+
- Footer logo: `footer.logo`, `logoUrl`, `logoAlt`, `logoHeight` fields for image logos in the footer ribbon
|
|
47
|
+
- Dark mode logo variants: `brand.logoDark` and `footer.logoDark` fields with CSS show/hide swap (no JS)
|
|
48
|
+
- Hierarchical slide navigation: nested nav with collapsible groups for decks with subfolder content
|
|
49
|
+
- Branding guide (`content/guide/branding.md`): canonical reference for header logos, footer logos, dark mode variants
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- Plugin template injection: `$` replacement corruption that broke LaTeX delimiter rendering
|
|
54
|
+
- Plugin CDN loader: path resolution for LaTeX plugin assets
|
|
55
|
+
|
|
56
|
+
### Docs
|
|
57
|
+
|
|
58
|
+
- Configuration reference updated with footer logo and dark mode logo fields
|
|
59
|
+
- All templates (handbook, runbook, brief, deck) updated with expanded Brand Assets documentation
|
|
60
|
+
|
|
5
61
|
## [0.2.1] - 2026-02-15
|
|
6
62
|
|
|
7
63
|
### Added
|
package/README.md
CHANGED
|
@@ -31,21 +31,36 @@ Alternative: `npm install -g kitfly` (still requires Bun, because the CLI runs w
|
|
|
31
31
|
|
|
32
32
|
No global install: `bunx kitfly --version`
|
|
33
33
|
|
|
34
|
-
Note: `bun`/`npm` installs the latest published release. If you
|
|
34
|
+
Note: `bun`/`npm` installs the latest published release. If you're reading `main` before a release is cut, clone this repo for the newest features.
|
|
35
|
+
|
|
36
|
+
**Don't need the CLI?** `kitfly init` creates a [standalone site](#create-a-standalone-site-recommended) that runs with just Bun — no kitfly binary required after setup.
|
|
35
37
|
|
|
36
38
|
For contributor/development setup, see [docs/development.md](docs/development.md).
|
|
37
39
|
|
|
38
40
|
---
|
|
39
41
|
|
|
42
|
+
## What's a Kitsite?
|
|
43
|
+
|
|
44
|
+
Any site created or managed by kitfly is a **kitsite** — a self-contained workspace with your content, configuration, and (optionally) build scripts. A kitsite can operate in different **modes**:
|
|
45
|
+
|
|
46
|
+
| Mode | What it produces | Created with |
|
|
47
|
+
| -------- | --------------------------------- | ------------------------------------- |
|
|
48
|
+
| `docs` | Documentation site (default) | `kitfly init my-docs` |
|
|
49
|
+
| `slides` | Fixed-aspect slide deck (v0.2.0+) | `kitfly init my-deck --template deck` |
|
|
50
|
+
|
|
51
|
+
Every kitsite has the same structure: a `content/` directory with your markdown, a `site.yaml` for configuration, and static output you can host anywhere or bundle into a single HTML file. Standalone kitsites (created with `kitfly init`) include their own build scripts and run with just Bun — no kitfly CLI required after setup.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
40
55
|
## Three Ways to Use Kitfly
|
|
41
56
|
|
|
42
|
-
| Approach | Best For | What You Get
|
|
43
|
-
| ------------------------- | ------------- |
|
|
44
|
-
| **`kitfly init`** | New projects | Standalone
|
|
45
|
-
| **`kitfly dev ./folder`** | Existing docs | Quick preview without changing anything
|
|
46
|
-
| **Clone this repo** | Contributors | The kitfly engine itself
|
|
57
|
+
| Approach | Best For | What You Get |
|
|
58
|
+
| ------------------------- | ------------- | ------------------------------------------------- |
|
|
59
|
+
| **`kitfly init`** | New projects | Standalone kitsite with your own copy of the code |
|
|
60
|
+
| **`kitfly dev ./folder`** | Existing docs | Quick preview without changing anything |
|
|
61
|
+
| **Clone this repo** | Contributors | The kitfly engine itself |
|
|
47
62
|
|
|
48
|
-
### Create a Standalone
|
|
63
|
+
### Create a Standalone Kitsite (Recommended)
|
|
49
64
|
|
|
50
65
|
```bash
|
|
51
66
|
kitfly init my-docs
|
|
@@ -63,7 +78,7 @@ bun install
|
|
|
63
78
|
bun run dev
|
|
64
79
|
```
|
|
65
80
|
|
|
66
|
-
You get a complete, self-contained
|
|
81
|
+
You get a complete, self-contained kitsite: rendering code, template, styles, config. It's yours — modify it, version it, own it. No kitfly CLI required after setup.
|
|
67
82
|
|
|
68
83
|
### Preview Existing Docs
|
|
69
84
|
|
|
@@ -149,7 +164,7 @@ Or just drop markdown files in `content/` — sections auto-discover.
|
|
|
149
164
|
|
|
150
165
|
## Plugins
|
|
151
166
|
|
|
152
|
-
Plugins are optional CSS/JS add-ons (kept out of core) that you enable per-
|
|
167
|
+
Plugins are optional CSS/JS add-ons (kept out of core) that you enable per-kitsite.
|
|
153
168
|
|
|
154
169
|
- Config: `kitfly.plugins.yaml`
|
|
155
170
|
- Versions are pinned (`name@x.y.z`)
|
|
@@ -163,7 +178,7 @@ See `content/reference/plugins.md` for the contract and examples.
|
|
|
163
178
|
|
|
164
179
|
> **Rule of thumb**: If it can't be done with CSS, vanilla JS under 50 lines, or a marked plugin, it doesn't belong here.
|
|
165
180
|
|
|
166
|
-
Kitfly is intentionally limited. The goal is a
|
|
181
|
+
Kitfly is intentionally limited. The goal is a kitsite that stays simple and maintainable. When you outgrow it, migrate — your content is just markdown.
|
|
167
182
|
|
|
168
183
|
---
|
|
169
184
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.2.
|
|
1
|
+
0.2.3
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Branding
|
|
3
|
+
description: Configure header logos, footer logos, and dark mode variants
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Branding
|
|
7
|
+
|
|
8
|
+
Kitfly sites display your brand in two locations: the **header** (sidebar logo + site title) and the **footer ribbon** (optional logo, copyright, links). Both support light/dark mode variants.
|
|
9
|
+
|
|
10
|
+
## Header Logo
|
|
11
|
+
|
|
12
|
+
The header logo appears in the sidebar above the navigation. Configure it in `site.yaml`:
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
brand:
|
|
16
|
+
name: "My Project"
|
|
17
|
+
url: "/"
|
|
18
|
+
logo: "assets/brand/logo.png"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The logo renders inside a bounded slot that preserves aspect ratio. Both square icons and wide wordmarks work — set `logoType` to tell kitfly which you're using:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
brand:
|
|
25
|
+
logo: "assets/brand/logo.png"
|
|
26
|
+
logoType: "icon" # square mark (default)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
brand:
|
|
31
|
+
logo: "assets/brand/wordmark.svg"
|
|
32
|
+
logoType: "wordmark" # wide logo
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Breakpoint | Max Height | Max Width |
|
|
36
|
+
| ---------------- | ---------- | --------- |
|
|
37
|
+
| Desktop | 64px | 180px |
|
|
38
|
+
| Tablet (≤1024px) | 56px | 150px |
|
|
39
|
+
| Mobile (≤768px) | 48px | 130px |
|
|
40
|
+
|
|
41
|
+
SVG and PNG formats are supported. For SVGs, ensure the `viewBox` is tightly cropped to the artwork.
|
|
42
|
+
|
|
43
|
+
If the logo image is missing or fails to load, kitfly falls back to displaying the first letter of your brand name.
|
|
44
|
+
|
|
45
|
+
## Footer Logo
|
|
46
|
+
|
|
47
|
+
Add a logo to the footer ribbon — typically a parent company, client, or partner logo that differs from the header brand:
|
|
48
|
+
|
|
49
|
+
```yaml
|
|
50
|
+
footer:
|
|
51
|
+
logo: "assets/brand/footer-logo.png"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The footer logo renders at the leading edge of the ribbon, before the version and publish date.
|
|
55
|
+
|
|
56
|
+
### Footer Logo Options
|
|
57
|
+
|
|
58
|
+
| Field | Default | Description |
|
|
59
|
+
| ------------ | ---------------------------- | ----------------------------------- |
|
|
60
|
+
| `logo` | _(none)_ | Path to footer logo image |
|
|
61
|
+
| `logoUrl` | _(none)_ | Make the logo a clickable link |
|
|
62
|
+
| `logoAlt` | Copyright text or brand name | Alt text for accessibility |
|
|
63
|
+
| `logoHeight` | `20` | Max height in pixels (range: 10-40) |
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
footer:
|
|
67
|
+
logo: "assets/brand/footer-logo.png"
|
|
68
|
+
logoUrl: "https://example.com"
|
|
69
|
+
logoAlt: "Example Corp"
|
|
70
|
+
logoHeight: 24
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
If no `footer.logo` is set, the footer renders as text only (version, copyright, links, attribution) — no change from the default.
|
|
74
|
+
|
|
75
|
+
## Dark Mode Variants
|
|
76
|
+
|
|
77
|
+
By default, kitfly auto-adjusts logo brightness in dark mode using a CSS filter. This works for many logos but can distort brand colors or fail on dark logos with transparent backgrounds.
|
|
78
|
+
|
|
79
|
+
For precise control, provide a separate dark mode image:
|
|
80
|
+
|
|
81
|
+
### Header
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
brand:
|
|
85
|
+
logo: "assets/brand/logo.png"
|
|
86
|
+
logoDark: "assets/brand/logo-dark.png"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Footer
|
|
90
|
+
|
|
91
|
+
```yaml
|
|
92
|
+
footer:
|
|
93
|
+
logo: "assets/brand/footer-logo.png"
|
|
94
|
+
logoDark: "assets/brand/footer-logo-dark.png"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### How It Works
|
|
98
|
+
|
|
99
|
+
When `logoDark` is set, kitfly emits both images and uses CSS to show the correct one based on the active theme. No JavaScript is involved — the swap is instant on theme toggle.
|
|
100
|
+
|
|
101
|
+
| Configuration | Light Mode | Dark Mode |
|
|
102
|
+
| ------------------- | ---------- | --------------------------------- |
|
|
103
|
+
| `logo` only | Shows logo | Shows logo with brightness filter |
|
|
104
|
+
| `logo` + `logoDark` | Shows logo | Shows logoDark (no filter) |
|
|
105
|
+
|
|
106
|
+
### Recommendations
|
|
107
|
+
|
|
108
|
+
- Use the **single logo** approach when your logo works on both light and dark backgrounds (e.g. a colorful icon on transparent background)
|
|
109
|
+
- Use **light + dark variants** when your logo has a specific background assumption (e.g. dark wordmark that disappears on dark backgrounds)
|
|
110
|
+
- Keep both variants at the **same dimensions** so the layout doesn't shift on theme toggle
|
|
111
|
+
- SVG is ideal for both variants — resolution-independent and small file size
|
|
112
|
+
|
|
113
|
+
## Recommended Asset Files
|
|
114
|
+
|
|
115
|
+
| Asset | Location | Size / Format |
|
|
116
|
+
| ------------------- | ----------------------------------- | ------------------- |
|
|
117
|
+
| Logo (light) | `assets/brand/logo.png` | 200x50px or SVG |
|
|
118
|
+
| Logo (dark) | `assets/brand/logo-dark.png` | Same as logo |
|
|
119
|
+
| Footer logo (light) | `assets/brand/footer-logo.png` | Max height 20-40px |
|
|
120
|
+
| Footer logo (dark) | `assets/brand/footer-logo-dark.png` | Same as footer logo |
|
|
121
|
+
| Favicon | `assets/brand/favicon.ico` | 32x32px |
|
|
122
|
+
|
|
123
|
+
## Full Example
|
|
124
|
+
|
|
125
|
+
A site with separate header and footer logos, both with dark mode variants:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
# site.yaml
|
|
129
|
+
title: "Product Brief"
|
|
130
|
+
|
|
131
|
+
brand:
|
|
132
|
+
name: "Product Name"
|
|
133
|
+
url: "/"
|
|
134
|
+
logo: "assets/brand/product-logo.png"
|
|
135
|
+
logoDark: "assets/brand/product-logo-dark.png"
|
|
136
|
+
logoType: "wordmark"
|
|
137
|
+
|
|
138
|
+
footer:
|
|
139
|
+
copyright: "© 2026 Parent Company, Inc."
|
|
140
|
+
copyrightUrl: "https://example.com"
|
|
141
|
+
logo: "assets/brand/parent-logo.png"
|
|
142
|
+
logoDark: "assets/brand/parent-logo-dark.png"
|
|
143
|
+
logoAlt: "Parent Company"
|
|
144
|
+
logoHeight: 20
|
|
145
|
+
attribution: true
|
|
146
|
+
```
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Data-Driven Content"
|
|
3
|
+
description: "Use data bindings and generators to build pages from structured data"
|
|
4
|
+
last_updated: "2026-02-17"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Data-Driven Content
|
|
8
|
+
|
|
9
|
+
Some pages are driven by structured data — pricing tables, team rosters, metrics dashboards, product catalogs. Data-driven content lets you separate prose (markdown), computation (generators), and data (YAML/JSON files) so each layer does what it's good at.
|
|
10
|
+
|
|
11
|
+
## When to use this
|
|
12
|
+
|
|
13
|
+
Use data bindings when:
|
|
14
|
+
|
|
15
|
+
- The same values appear in multiple places on a page (DRY)
|
|
16
|
+
- A generator computes values that appear in prose (pricing math, discount brackets)
|
|
17
|
+
- Tables or blocks are produced by scripts and injected into narrative content
|
|
18
|
+
|
|
19
|
+
Don't use data bindings when:
|
|
20
|
+
|
|
21
|
+
- The page is pure prose with no external data
|
|
22
|
+
- You need loops, conditionals, or expressions — use a generator to produce the final markdown instead
|
|
23
|
+
|
|
24
|
+
## How it works
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
raw input → generator → data file → kitfly bindings → markdown → HTML
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
1. A **generator** (any script) reads raw input and writes a data file
|
|
31
|
+
2. A **data file** (YAML or JSON) declares globals, per-page values, and snippets
|
|
32
|
+
3. **Markdown templates** use `{{ key }}` and `{{ snippet:name }}` placeholders
|
|
33
|
+
4. Kitfly resolves bindings at build time, before markdown rendering
|
|
34
|
+
|
|
35
|
+
## Data file structure
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# data/pricing.yaml
|
|
39
|
+
globals:
|
|
40
|
+
company: "Acme Corp"
|
|
41
|
+
baseline_rate: "200"
|
|
42
|
+
credit_validity: "12"
|
|
43
|
+
|
|
44
|
+
pages:
|
|
45
|
+
- path: content/product/pricing.md
|
|
46
|
+
inject:
|
|
47
|
+
hero: "Implementation and operating costs"
|
|
48
|
+
discount_range: "5–20%"
|
|
49
|
+
snippets:
|
|
50
|
+
- slot: pricing-table
|
|
51
|
+
content: |
|
|
52
|
+
| Tier | Price | Features |
|
|
53
|
+
|------|-------|----------|
|
|
54
|
+
| Basic | $10/mo | Essentials |
|
|
55
|
+
| Pro | $50/mo | Full access |
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Path convention:** `pages[].path` must be relative to site root including the `content/` prefix. Use `content/product/pricing.md`, not `product/pricing.md`. Kitfly's error messages include the expected path if there's a mismatch.
|
|
59
|
+
|
|
60
|
+
### Resolution order
|
|
61
|
+
|
|
62
|
+
For `{{ key }}`: page `inject` first, then `globals`. Page-level values shadow globals on key collision.
|
|
63
|
+
|
|
64
|
+
For `{{ snippet:name }}`: matched by `slot` name from the page's `snippets` array.
|
|
65
|
+
|
|
66
|
+
## Binding syntax
|
|
67
|
+
|
|
68
|
+
### Value substitution
|
|
69
|
+
|
|
70
|
+
```markdown
|
|
71
|
+
Implementation rate: **{{ baseline_rate | dollar }}/hour**
|
|
72
|
+
|
|
73
|
+
Credits valid for {{ credit_validity }} months.
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Snippet injection
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
## Pricing Tiers
|
|
80
|
+
|
|
81
|
+
{{ snippet:pricing-table }}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Snippets are injected verbatim as markdown. After all bindings resolve, the full document passes through the markdown renderer as usual.
|
|
85
|
+
|
|
86
|
+
## Formatters
|
|
87
|
+
|
|
88
|
+
Apply with pipe syntax. Chaining composes left to right: `{{ key | round(0) | dollar }}`.
|
|
89
|
+
|
|
90
|
+
| Formatter | Input | Output | Notes |
|
|
91
|
+
| ---------- | ----------- | -------- | ------------------------------------------- |
|
|
92
|
+
| `dollar` | `"1500"` | `$1,500` | USD format; cents only if fractional |
|
|
93
|
+
| `number` | `"2500"` | `2,500` | Comma-separated |
|
|
94
|
+
| `percent` | `"0.15"` | `15%` | **Input must be a decimal ratio (0.0–1.0)** |
|
|
95
|
+
| `round(n)` | `"3.14159"` | `3.14` | Round to n decimal places |
|
|
96
|
+
| `upper` | `"hello"` | `HELLO` | Uppercase |
|
|
97
|
+
| `lower` | `"HELLO"` | `hello` | Lowercase |
|
|
98
|
+
|
|
99
|
+
**`percent` expects a decimal ratio.** `"0.15"` becomes `15%`. If your generator already computes integer percentages (like `"5"` for 5%), store them as pre-formatted strings (`"5%"`) and don't pipe through `percent` — that would yield `500%`.
|
|
100
|
+
|
|
101
|
+
All formatters are pure functions: string in, string out. The set is closed — adding a formatter requires a kitfly code change.
|
|
102
|
+
|
|
103
|
+
## Error handling
|
|
104
|
+
|
|
105
|
+
Kitfly fails loud on binding errors. These are build errors, not warnings:
|
|
106
|
+
|
|
107
|
+
| Condition | Error message |
|
|
108
|
+
| ----------------------- | -------------------------------------------------------- |
|
|
109
|
+
| Data file not found | `data file not found: data/pricing.yaml` |
|
|
110
|
+
| Unresolved `{{ key }}` | `unresolved binding "key" in content/product/pricing.md` |
|
|
111
|
+
| Unknown snippet slot | `unknown snippet "name" in content/product/pricing.md` |
|
|
112
|
+
| Formatter parse failure | `dollar formatter: "hello" is not a number ...` |
|
|
113
|
+
| Unknown formatter | `unknown formatter "custom_fn" ...` |
|
|
114
|
+
| Path escapes kitsite | `data path escapes kitsite: ../secrets/data.yaml` |
|
|
115
|
+
|
|
116
|
+
## Pre-build hooks
|
|
117
|
+
|
|
118
|
+
Declare generators in `site.yaml`:
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
prebuild:
|
|
122
|
+
- command: "bun run scripts/generate-pricing-data.ts"
|
|
123
|
+
watch: ["data/raw/pricing-input.json"]
|
|
124
|
+
- command: "bun run scripts/generate-team-data.ts"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
| Context | When hooks run |
|
|
128
|
+
| ---------------- | ----------------------------------------------------- |
|
|
129
|
+
| `bun run dev` | Once at startup, then again when watched files change |
|
|
130
|
+
| `bun run build` | Once before build starts |
|
|
131
|
+
| `bun run bundle` | Once before bundle starts |
|
|
132
|
+
|
|
133
|
+
Hooks run sequentially in declared order. A non-zero exit code halts the build with the hook's stderr as error context.
|
|
134
|
+
|
|
135
|
+
### Watch flow in dev mode
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
watched file changes → re-run matching hook → hook writes to data/ → data/ change triggers rebuild
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Content changes (edits to markdown) do NOT re-run hooks. Only watched file changes trigger hooks.
|
|
142
|
+
|
|
143
|
+
## Schema validation (optional)
|
|
144
|
+
|
|
145
|
+
If `data/pricing.schema.json` exists alongside `data/pricing.yaml`, kitfly validates the data at build time. JSON Schema structural checks cover required fields, value types, and pattern constraints.
|
|
146
|
+
|
|
147
|
+
Missing schema files are not an error — validation is opt-in.
|
|
148
|
+
|
|
149
|
+
## Generator best practices
|
|
150
|
+
|
|
151
|
+
These patterns emerged from real-world usage and apply to any non-trivial generator.
|
|
152
|
+
|
|
153
|
+
### The template is the contract
|
|
154
|
+
|
|
155
|
+
Every `{{ snippet:X }}` in a markdown template means the generator must always emit a snippet named `X` in the data file, even if the content is empty. Kitfly treats missing snippets as build errors — by design.
|
|
156
|
+
|
|
157
|
+
If a section is conditionally relevant (e.g. an implementation-tiers table that only applies to multi-tier configurations), the generator should emit the snippet with empty string content when the section doesn't apply. The blank line in the rendered output is acceptable and avoids conditional logic in the template.
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
# Generator always emits this, even when single-tier
|
|
161
|
+
snippets:
|
|
162
|
+
- slot: implementation-tiers
|
|
163
|
+
content: ""
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Use JSON for generator output
|
|
167
|
+
|
|
168
|
+
Generators should write JSON data files. `JSON.stringify` is deterministic in every language, there's no quoting ambiguity, and multiline strings use unambiguous `\n` escapes.
|
|
169
|
+
|
|
170
|
+
Reserve YAML for hand-authored data files (team rosters, simple config) where human readability matters.
|
|
171
|
+
|
|
172
|
+
### Separate raw input from kitfly data
|
|
173
|
+
|
|
174
|
+
Keep generator source files in `data/raw/` and kitfly data files in `data/`:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
data/
|
|
178
|
+
raw/
|
|
179
|
+
pricing-input.json ← generator reads this
|
|
180
|
+
team.csv ← generator reads this
|
|
181
|
+
pricing.json ← generator writes this (kitfly reads it)
|
|
182
|
+
team.json ← generator writes this (kitfly reads it)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
This prevents naming collisions and makes the data flow visible. The `prebuild:` hook `watch:` patterns point at `data/raw/` sources, and kitfly reads the generated files from `data/`.
|
|
186
|
+
|
|
187
|
+
### Generator language
|
|
188
|
+
|
|
189
|
+
Generators can be written in any language. Kitfly runs them as shell commands. The motivating use case was a Python generator in an otherwise TypeScript/Bun ecosystem — pre-build hooks eliminate the manual step and integrate it into the dev/build pipeline regardless of language.
|
|
190
|
+
|
|
191
|
+
## Interaction with content profiles
|
|
192
|
+
|
|
193
|
+
Data bindings and content profiles are independent features that compose naturally:
|
|
194
|
+
|
|
195
|
+
- Profile filtering runs first (after file collection, before rendering)
|
|
196
|
+
- Data binding resolution runs second (after reading markdown, before markdown rendering)
|
|
197
|
+
- A page excluded by profile filtering is never read, so its bindings are never resolved
|
|
198
|
+
- `KITFLY_PROFILE` is passed to pre-build hooks so generators can adapt output per profile
|
|
199
|
+
|
|
200
|
+
## Backwards compatibility
|
|
201
|
+
|
|
202
|
+
- Pages without `data:` frontmatter: no binding resolution, literal `{{ }}` passes through unchanged
|
|
203
|
+
- Sites without `prebuild:` in site.yaml: no hooks run, zero overhead
|
|
204
|
+
- Sites without a `data/` directory: no binding resolution, zero overhead
|