kitfly 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/README.md +136 -0
  4. package/VERSION +1 -0
  5. package/package.json +63 -0
  6. package/schemas/README.md +32 -0
  7. package/schemas/site.schema.json +5 -0
  8. package/schemas/theme.schema.json +5 -0
  9. package/schemas/v0/site.schema.json +172 -0
  10. package/schemas/v0/theme.schema.json +210 -0
  11. package/scripts/build-all.ts +121 -0
  12. package/scripts/build.ts +601 -0
  13. package/scripts/bundle.ts +781 -0
  14. package/scripts/dev.ts +777 -0
  15. package/scripts/generate-checksums.sh +78 -0
  16. package/scripts/release/export-release-key.sh +28 -0
  17. package/scripts/release/release-guard-tag-version.sh +79 -0
  18. package/scripts/release/sign-release-assets.sh +123 -0
  19. package/scripts/release/upload-release-assets.sh +76 -0
  20. package/scripts/release/upload-release-provenance.sh +52 -0
  21. package/scripts/release/verify-public-key.sh +48 -0
  22. package/scripts/release/verify-signatures.sh +117 -0
  23. package/scripts/version-sync.ts +82 -0
  24. package/src/__tests__/build.test.ts +240 -0
  25. package/src/__tests__/bundle.test.ts +786 -0
  26. package/src/__tests__/cli.test.ts +706 -0
  27. package/src/__tests__/crucible.test.ts +1043 -0
  28. package/src/__tests__/engine.test.ts +157 -0
  29. package/src/__tests__/init.test.ts +450 -0
  30. package/src/__tests__/pipeline.test.ts +1087 -0
  31. package/src/__tests__/productbook.test.ts +1206 -0
  32. package/src/__tests__/runbook.test.ts +974 -0
  33. package/src/__tests__/server-registry.test.ts +1251 -0
  34. package/src/__tests__/servicebook.test.ts +1248 -0
  35. package/src/__tests__/shared.test.ts +2005 -0
  36. package/src/__tests__/styles.test.ts +14 -0
  37. package/src/__tests__/theme-schema.test.ts +47 -0
  38. package/src/__tests__/theme.test.ts +554 -0
  39. package/src/cli.ts +582 -0
  40. package/src/commands/init.ts +92 -0
  41. package/src/commands/update.ts +444 -0
  42. package/src/engine.ts +20 -0
  43. package/src/logger.ts +15 -0
  44. package/src/migrations/0000_schema_versioning.ts +67 -0
  45. package/src/migrations/0001_server_port.ts +52 -0
  46. package/src/migrations/0002_brand_logo.ts +49 -0
  47. package/src/migrations/index.ts +26 -0
  48. package/src/migrations/schema.ts +24 -0
  49. package/src/server-registry.ts +405 -0
  50. package/src/shared.ts +1239 -0
  51. package/src/site/styles.css +931 -0
  52. package/src/site/template.html +193 -0
  53. package/src/templates/crucible.ts +1163 -0
  54. package/src/templates/driver.ts +876 -0
  55. package/src/templates/handbook.ts +339 -0
  56. package/src/templates/minimal.ts +139 -0
  57. package/src/templates/pipeline.ts +966 -0
  58. package/src/templates/productbook.ts +1032 -0
  59. package/src/templates/runbook.ts +829 -0
  60. package/src/templates/schema.ts +119 -0
  61. package/src/templates/servicebook.ts +1242 -0
  62. package/src/theme.ts +245 -0
package/src/theme.ts ADDED
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Theme loading and CSS generation for kitfly
3
+ */
4
+
5
+ import { readFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+
8
+ // Theme types
9
+ export interface ThemeColors {
10
+ background: string;
11
+ surface: string;
12
+ text: string;
13
+ textMuted?: string;
14
+ heading: string;
15
+ primary: string;
16
+ primaryHover?: string;
17
+ accent?: string;
18
+ border: string;
19
+ }
20
+
21
+ export interface Theme {
22
+ name?: string;
23
+ layout?: {
24
+ sidebarWidth?: string;
25
+ };
26
+ colors: {
27
+ light: ThemeColors;
28
+ dark: ThemeColors;
29
+ };
30
+ code?: {
31
+ light?: string;
32
+ dark?: string;
33
+ };
34
+ typography?: {
35
+ body?: string;
36
+ headings?: string;
37
+ code?: string;
38
+ baseSize?: string;
39
+ scale?: string;
40
+ };
41
+ }
42
+
43
+ // Default theme (kitfly brand)
44
+ export const DEFAULT_THEME: Theme = {
45
+ name: "Kitfly Default",
46
+ colors: {
47
+ light: {
48
+ background: "#ffffff",
49
+ surface: "#f5f7f8",
50
+ text: "#374151",
51
+ textMuted: "#6b7280",
52
+ heading: "#152F46",
53
+ primary: "#007182",
54
+ primaryHover: "#0a6172",
55
+ accent: "#D17059",
56
+ border: "#e5e7eb",
57
+ },
58
+ dark: {
59
+ background: "#0d1117",
60
+ surface: "#152F46",
61
+ text: "#e5e7eb",
62
+ textMuted: "#9ca3af",
63
+ heading: "#f9fafb",
64
+ primary: "#709EA6",
65
+ primaryHover: "#8fb5bc",
66
+ accent: "#e8947f",
67
+ border: "#374151",
68
+ },
69
+ },
70
+ code: {
71
+ light: "default",
72
+ dark: "okaidia",
73
+ },
74
+ layout: {
75
+ sidebarWidth: "280px",
76
+ },
77
+ typography: {
78
+ body: "system",
79
+ headings: "system",
80
+ code: "mono",
81
+ baseSize: "16px",
82
+ scale: "1.25",
83
+ },
84
+ };
85
+
86
+ // Font stack presets
87
+ const FONT_STACKS: Record<string, string> = {
88
+ system:
89
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
90
+ mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
91
+ serif: 'Georgia, Cambria, "Times New Roman", Times, serif',
92
+ readable: 'Charter, "Bitstream Charter", "Sitka Text", Cambria, serif',
93
+ };
94
+
95
+ // Prism theme CDN URLs
96
+ const PRISM_THEMES: Record<string, string> = {
97
+ default: "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.min.css",
98
+ coy: "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-coy.min.css",
99
+ "solarized-light": "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-solarizedlight.min.css",
100
+ tomorrow: "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-tomorrow.min.css",
101
+ okaidia: "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-okaidia.min.css",
102
+ "tomorrow-night": "https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-tomorrow.min.css",
103
+ nord: "https://cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-nord.min.css",
104
+ dracula: "https://cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-dracula.min.css",
105
+ "one-dark": "https://cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-one-dark.min.css",
106
+ synthwave84: "https://cdn.jsdelivr.net/npm/prism-themes@1/themes/prism-synthwave84.min.css",
107
+ };
108
+
109
+ // Simple YAML parser for theme files (reuses pattern from dev.ts)
110
+ function parseThemeYaml(content: string): Record<string, unknown> {
111
+ const result: Record<string, unknown> = {};
112
+ const lines = content.split("\n");
113
+ const stack: { obj: Record<string, unknown>; indent: number }[] = [{ obj: result, indent: -2 }];
114
+
115
+ for (const line of lines) {
116
+ if (line.trim().startsWith("#") || line.trim() === "") continue;
117
+
118
+ const indent = line.search(/\S/);
119
+ const trimmed = line.trim();
120
+
121
+ while (stack.length > 1 && indent <= stack[stack.length - 1].indent) {
122
+ stack.pop();
123
+ }
124
+
125
+ const colonIndex = trimmed.indexOf(":");
126
+ if (colonIndex > 0) {
127
+ const key = trimmed.slice(0, colonIndex).trim();
128
+ const value = trimmed.slice(colonIndex + 1).trim();
129
+ const parent = stack[stack.length - 1].obj;
130
+
131
+ if (value === "") {
132
+ const nested: Record<string, unknown> = {};
133
+ parent[key] = nested;
134
+ stack.push({ obj: nested, indent });
135
+ } else {
136
+ // Strip quotes
137
+ let v = value;
138
+ if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
139
+ v = v.slice(1, -1);
140
+ }
141
+ parent[key] = v;
142
+ }
143
+ }
144
+ }
145
+
146
+ return result;
147
+ }
148
+
149
+ // Load theme from file with fallback to defaults
150
+ export async function loadTheme(root: string): Promise<Theme> {
151
+ try {
152
+ const content = await readFile(join(root, "theme.yaml"), "utf-8");
153
+ const parsed = parseThemeYaml(content);
154
+ return deepMerge(DEFAULT_THEME, parsed as Partial<Theme>) as Theme;
155
+ } catch {
156
+ return DEFAULT_THEME;
157
+ }
158
+ }
159
+
160
+ // Deep merge utility
161
+ function deepMerge(target: unknown, source: unknown): unknown {
162
+ if (typeof target !== "object" || target === null) return source;
163
+ if (typeof source !== "object" || source === null) return source;
164
+
165
+ const result = { ...(target as Record<string, unknown>) };
166
+ for (const key of Object.keys(source as Record<string, unknown>)) {
167
+ const sourceVal = (source as Record<string, unknown>)[key];
168
+ const targetVal = result[key];
169
+ if (
170
+ typeof sourceVal === "object" &&
171
+ sourceVal !== null &&
172
+ typeof targetVal === "object" &&
173
+ targetVal !== null
174
+ ) {
175
+ result[key] = deepMerge(targetVal, sourceVal);
176
+ } else if (sourceVal !== undefined) {
177
+ result[key] = sourceVal;
178
+ }
179
+ }
180
+ return result;
181
+ }
182
+
183
+ // Generate CSS from theme
184
+ export function generateThemeCSS(theme: Theme): string {
185
+ const light = theme.colors.light;
186
+ const dark = theme.colors.dark;
187
+ const typo = theme.typography ?? DEFAULT_THEME.typography ?? {};
188
+ const layout = theme.layout ?? DEFAULT_THEME.layout ?? {};
189
+
190
+ const fontSans = FONT_STACKS[typo.body || "system"];
191
+ const fontHeadings = FONT_STACKS[typo.headings || "system"];
192
+ const fontMono = FONT_STACKS.mono;
193
+ const baseSize = typo.baseSize || "16px";
194
+ const sidebarWidth = layout.sidebarWidth || "280px";
195
+
196
+ // Map theme colors to CSS variables
197
+ const lightVars = `
198
+ --color-bg: ${light.background};
199
+ --color-bg-sidebar: ${light.surface};
200
+ --color-text: ${light.text};
201
+ --color-text-muted: ${light.textMuted || light.text};
202
+ --color-border: ${light.border};
203
+ --color-link: ${light.primary};
204
+ --color-link-hover: ${light.primaryHover || light.primary};
205
+ --color-accent: ${light.heading};
206
+ --color-code-bg: ${light.surface};
207
+ --color-logo: ${light.heading};
208
+ --sidebar-width: ${sidebarWidth};
209
+ --font-sans: ${fontSans};
210
+ --font-headings: ${fontHeadings};
211
+ --font-mono: ${fontMono};
212
+ `.trim();
213
+
214
+ const darkVars = `
215
+ --color-bg: ${dark.background};
216
+ --color-bg-sidebar: ${dark.surface};
217
+ --color-text: ${dark.text};
218
+ --color-text-muted: ${dark.textMuted || dark.text};
219
+ --color-border: ${dark.border};
220
+ --color-link: ${dark.primary};
221
+ --color-link-hover: ${dark.primaryHover || dark.primary};
222
+ --color-accent: ${dark.heading};
223
+ --color-code-bg: ${dark.surface};
224
+ --color-logo: ${dark.heading};
225
+ `.trim();
226
+
227
+ return `<style id="kitfly-theme">
228
+ :root { ${lightVars} }
229
+ html { font-size: ${baseSize}; }
230
+ @media (prefers-color-scheme: dark) {
231
+ :root:not([data-theme="light"]) { ${darkVars} }
232
+ }
233
+ [data-theme="dark"] { ${darkVars} }
234
+ [data-theme="light"] { ${lightVars} }
235
+ </style>`;
236
+ }
237
+
238
+ // Get Prism theme URLs
239
+ export function getPrismUrls(theme: Theme): { light: string; dark: string } {
240
+ const code = theme.code ?? DEFAULT_THEME.code ?? {};
241
+ return {
242
+ light: PRISM_THEMES[code.light || "default"] || PRISM_THEMES.default,
243
+ dark: PRISM_THEMES[code.dark || "okaidia"] || PRISM_THEMES.okaidia,
244
+ };
245
+ }