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.
- package/CHANGELOG.md +60 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/VERSION +1 -0
- package/package.json +63 -0
- package/schemas/README.md +32 -0
- package/schemas/site.schema.json +5 -0
- package/schemas/theme.schema.json +5 -0
- package/schemas/v0/site.schema.json +172 -0
- package/schemas/v0/theme.schema.json +210 -0
- package/scripts/build-all.ts +121 -0
- package/scripts/build.ts +601 -0
- package/scripts/bundle.ts +781 -0
- package/scripts/dev.ts +777 -0
- package/scripts/generate-checksums.sh +78 -0
- package/scripts/release/export-release-key.sh +28 -0
- package/scripts/release/release-guard-tag-version.sh +79 -0
- package/scripts/release/sign-release-assets.sh +123 -0
- package/scripts/release/upload-release-assets.sh +76 -0
- package/scripts/release/upload-release-provenance.sh +52 -0
- package/scripts/release/verify-public-key.sh +48 -0
- package/scripts/release/verify-signatures.sh +117 -0
- package/scripts/version-sync.ts +82 -0
- package/src/__tests__/build.test.ts +240 -0
- package/src/__tests__/bundle.test.ts +786 -0
- package/src/__tests__/cli.test.ts +706 -0
- package/src/__tests__/crucible.test.ts +1043 -0
- package/src/__tests__/engine.test.ts +157 -0
- package/src/__tests__/init.test.ts +450 -0
- package/src/__tests__/pipeline.test.ts +1087 -0
- package/src/__tests__/productbook.test.ts +1206 -0
- package/src/__tests__/runbook.test.ts +974 -0
- package/src/__tests__/server-registry.test.ts +1251 -0
- package/src/__tests__/servicebook.test.ts +1248 -0
- package/src/__tests__/shared.test.ts +2005 -0
- package/src/__tests__/styles.test.ts +14 -0
- package/src/__tests__/theme-schema.test.ts +47 -0
- package/src/__tests__/theme.test.ts +554 -0
- package/src/cli.ts +582 -0
- package/src/commands/init.ts +92 -0
- package/src/commands/update.ts +444 -0
- package/src/engine.ts +20 -0
- package/src/logger.ts +15 -0
- package/src/migrations/0000_schema_versioning.ts +67 -0
- package/src/migrations/0001_server_port.ts +52 -0
- package/src/migrations/0002_brand_logo.ts +49 -0
- package/src/migrations/index.ts +26 -0
- package/src/migrations/schema.ts +24 -0
- package/src/server-registry.ts +405 -0
- package/src/shared.ts +1239 -0
- package/src/site/styles.css +931 -0
- package/src/site/template.html +193 -0
- package/src/templates/crucible.ts +1163 -0
- package/src/templates/driver.ts +876 -0
- package/src/templates/handbook.ts +339 -0
- package/src/templates/minimal.ts +139 -0
- package/src/templates/pipeline.ts +966 -0
- package/src/templates/productbook.ts +1032 -0
- package/src/templates/runbook.ts +829 -0
- package/src/templates/schema.ts +119 -0
- package/src/templates/servicebook.ts +1242 -0
- 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
|
+
}
|