@svg-gen/tech-svg-generator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpG,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG7E,OAAO,EAAE,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACjH,OAAO,EACL,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,OAAO,EACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,WAAW,GACjB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tech SVG Generator
3
+ * Generate clean, professional SVG illustrations for technical content
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ // Core generation
8
+ export { generateSVG, generateIllustration, detectScene, getAvailableScenes } from './generator.js';
9
+ export { SCENES } from './scenes.js';
10
+ export { THEMES, getTheme } from './themes.js';
11
+ export { ICONS } from './icons.js';
12
+ // Cartoon strip generation
13
+ export { generateCartoonStrip } from './cartoon.js';
14
+ export { renderCharacter, createCharacter, getCharacterPresets, getEmotions, CHARACTER_PRESETS } from './characters.js';
15
+ // YAML/JSON parsing
16
+ export { parseYAML, parseJSON, generateFromYAML, generateFromJSON, generateFromDescription, SCENE_YAML_EXAMPLE, CARTOON_YAML_EXAMPLE, CARTOON_JSON_EXAMPLE, } from './parser.js';
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,83 @@
1
+ /**
2
+ * YAML/JSON scene description parser
3
+ * Allows defining scenes and cartoon strips in declarative format
4
+ */
5
+ import type { SceneType } from './types.js';
6
+ /**
7
+ * Scene description format for technical illustrations
8
+ */
9
+ export interface SceneDescription {
10
+ type: 'scene';
11
+ title: string;
12
+ content?: string;
13
+ scene?: SceneType;
14
+ theme?: string;
15
+ width?: number;
16
+ height?: number;
17
+ }
18
+ /**
19
+ * Cartoon strip description format
20
+ */
21
+ export interface CartoonDescription {
22
+ type: 'cartoon';
23
+ title?: string;
24
+ theme?: string;
25
+ width?: number;
26
+ height?: number;
27
+ layout?: string;
28
+ characters: Record<string, {
29
+ name: string;
30
+ preset?: string;
31
+ style?: {
32
+ primary?: string;
33
+ secondary?: string;
34
+ skin?: string;
35
+ hairStyle?: 'short' | 'long' | 'bald' | 'spiky' | 'curly';
36
+ accessory?: 'none' | 'glasses' | 'hat' | 'headphones';
37
+ };
38
+ }>;
39
+ panels: Array<{
40
+ characters: string[];
41
+ caption?: string;
42
+ dialogue: Array<{
43
+ character: string;
44
+ text: string;
45
+ emotion?: string;
46
+ type?: 'speech' | 'thought' | 'shout';
47
+ }>;
48
+ }>;
49
+ }
50
+ export type Description = SceneDescription | CartoonDescription;
51
+ /**
52
+ * Parse YAML string into description object
53
+ */
54
+ export declare function parseYAML(yamlString: string): Description;
55
+ /**
56
+ * Parse JSON string into description object
57
+ */
58
+ export declare function parseJSON(jsonString: string): Description;
59
+ /**
60
+ * Generate SVG from description object
61
+ */
62
+ export declare function generateFromDescription(desc: Description): string;
63
+ /**
64
+ * Generate SVG from YAML string
65
+ */
66
+ export declare function generateFromYAML(yamlString: string): string;
67
+ /**
68
+ * Generate SVG from JSON string
69
+ */
70
+ export declare function generateFromJSON(jsonString: string): string;
71
+ /**
72
+ * Example YAML for a scene
73
+ */
74
+ export declare const SCENE_YAML_EXAMPLE: string;
75
+ /**
76
+ * Example YAML for a cartoon strip
77
+ */
78
+ export declare const CARTOON_YAML_EXAMPLE: string;
79
+ /**
80
+ * Example JSON for a cartoon strip
81
+ */
82
+ export declare const CARTOON_JSON_EXAMPLE: string;
83
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QACzB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE;YACN,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;YAC1D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,YAAY,CAAC;SACvD,CAAC;KACH,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACZ,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,KAAK,CAAC;YACd,SAAS,EAAE,MAAM,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;SACvC,CAAC,CAAC;KACJ,CAAC,CAAC;CACJ;AAED,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;AAEhE;;GAEG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAIzD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,CAIzD;AAoDD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAgCjE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAQvB,CAAC;AAET;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAoCzB,CAAC;AAET;;GAEG;AACH,eAAO,MAAM,oBAAoB,QA2BtB,CAAC"}
package/dist/parser.js ADDED
@@ -0,0 +1,204 @@
1
+ /**
2
+ * YAML/JSON scene description parser
3
+ * Allows defining scenes and cartoon strips in declarative format
4
+ */
5
+ import yaml from 'js-yaml';
6
+ import { generateSVG } from './generator.js';
7
+ import { generateCartoonStrip } from './cartoon.js';
8
+ /**
9
+ * Parse YAML string into description object
10
+ */
11
+ export function parseYAML(yamlString) {
12
+ const parsed = yaml.load(yamlString);
13
+ validateDescription(parsed);
14
+ return parsed;
15
+ }
16
+ /**
17
+ * Parse JSON string into description object
18
+ */
19
+ export function parseJSON(jsonString) {
20
+ const parsed = JSON.parse(jsonString);
21
+ validateDescription(parsed);
22
+ return parsed;
23
+ }
24
+ /**
25
+ * Validate description object
26
+ */
27
+ function validateDescription(desc) {
28
+ if (!desc || typeof desc !== 'object') {
29
+ throw new Error('Invalid description: must be an object');
30
+ }
31
+ if (!desc.type) {
32
+ throw new Error('Invalid description: missing "type" field (must be "scene" or "cartoon")');
33
+ }
34
+ if (desc.type === 'scene') {
35
+ if (!desc.title || typeof desc.title !== 'string') {
36
+ throw new Error('Invalid scene description: missing or invalid "title" field');
37
+ }
38
+ }
39
+ else if (desc.type === 'cartoon') {
40
+ if (!desc.characters || typeof desc.characters !== 'object') {
41
+ throw new Error('Invalid cartoon description: missing "characters" field');
42
+ }
43
+ if (!Array.isArray(desc.panels) || desc.panels.length === 0) {
44
+ throw new Error('Invalid cartoon description: missing or empty "panels" array');
45
+ }
46
+ // Validate panels
47
+ for (let i = 0; i < desc.panels.length; i++) {
48
+ const panel = desc.panels[i];
49
+ if (!Array.isArray(panel.characters)) {
50
+ throw new Error(`Invalid panel ${i}: missing "characters" array`);
51
+ }
52
+ if (!Array.isArray(panel.dialogue)) {
53
+ throw new Error(`Invalid panel ${i}: missing "dialogue" array`);
54
+ }
55
+ // Validate dialogue
56
+ for (let j = 0; j < panel.dialogue.length; j++) {
57
+ const line = panel.dialogue[j];
58
+ if (!line.character || typeof line.character !== 'string') {
59
+ throw new Error(`Invalid dialogue in panel ${i}, line ${j}: missing "character" field`);
60
+ }
61
+ if (!line.text || typeof line.text !== 'string') {
62
+ throw new Error(`Invalid dialogue in panel ${i}, line ${j}: missing "text" field`);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ else {
68
+ throw new Error(`Invalid description type: "${desc.type}" (must be "scene" or "cartoon")`);
69
+ }
70
+ }
71
+ /**
72
+ * Generate SVG from description object
73
+ */
74
+ export function generateFromDescription(desc) {
75
+ if (desc.type === 'scene') {
76
+ const options = {
77
+ width: desc.width,
78
+ height: desc.height,
79
+ theme: desc.theme,
80
+ scene: desc.scene,
81
+ };
82
+ return generateSVG(desc.title, desc.content || '', options).svg;
83
+ }
84
+ else if (desc.type === 'cartoon') {
85
+ const config = {
86
+ title: desc.title,
87
+ theme: desc.theme,
88
+ width: desc.width,
89
+ height: desc.height,
90
+ layout: desc.layout,
91
+ characters: desc.characters,
92
+ panels: desc.panels.map(p => ({
93
+ characters: p.characters,
94
+ caption: p.caption,
95
+ dialogue: p.dialogue.map(d => ({
96
+ character: d.character,
97
+ text: d.text,
98
+ emotion: d.emotion,
99
+ type: d.type,
100
+ })),
101
+ })),
102
+ };
103
+ return generateCartoonStrip(config);
104
+ }
105
+ throw new Error('Unknown description type');
106
+ }
107
+ /**
108
+ * Generate SVG from YAML string
109
+ */
110
+ export function generateFromYAML(yamlString) {
111
+ const desc = parseYAML(yamlString);
112
+ return generateFromDescription(desc);
113
+ }
114
+ /**
115
+ * Generate SVG from JSON string
116
+ */
117
+ export function generateFromJSON(jsonString) {
118
+ const desc = parseJSON(jsonString);
119
+ return generateFromDescription(desc);
120
+ }
121
+ /**
122
+ * Example YAML for a scene
123
+ */
124
+ export const SCENE_YAML_EXAMPLE = `
125
+ type: scene
126
+ title: "Database Migration Strategy"
127
+ content: "PostgreSQL replication and failover"
128
+ scene: database
129
+ theme: github-dark
130
+ width: 700
131
+ height: 420
132
+ `.trim();
133
+ /**
134
+ * Example YAML for a cartoon strip
135
+ */
136
+ export const CARTOON_YAML_EXAMPLE = `
137
+ type: cartoon
138
+ title: "The Code Review"
139
+ theme: github-dark
140
+ width: 800
141
+ height: 500
142
+ layout: "2x1"
143
+
144
+ characters:
145
+ alice:
146
+ name: Alice
147
+ preset: dev1
148
+ bob:
149
+ name: Bob
150
+ preset: dev2
151
+
152
+ panels:
153
+ - characters: [alice, bob]
154
+ caption: "Monday morning..."
155
+ dialogue:
156
+ - character: alice
157
+ text: "Did you see the PR I submitted?"
158
+ emotion: neutral
159
+ - character: bob
160
+ text: "The one with 2000 lines?"
161
+ emotion: surprised
162
+
163
+ - characters: [alice, bob]
164
+ caption: "Later..."
165
+ dialogue:
166
+ - character: bob
167
+ text: "Maybe we should split this up?"
168
+ emotion: thinking
169
+ - character: alice
170
+ text: "Good idea!"
171
+ emotion: happy
172
+ `.trim();
173
+ /**
174
+ * Example JSON for a cartoon strip
175
+ */
176
+ export const CARTOON_JSON_EXAMPLE = JSON.stringify({
177
+ type: 'cartoon',
178
+ title: 'Debugging Session',
179
+ theme: 'dracula',
180
+ width: 800,
181
+ height: 400,
182
+ layout: '2x1',
183
+ characters: {
184
+ dev: { name: 'Dev', preset: 'dev1' },
185
+ rubber: { name: 'Rubber Duck', preset: 'robot' }
186
+ },
187
+ panels: [
188
+ {
189
+ characters: ['dev', 'rubber'],
190
+ dialogue: [
191
+ { character: 'dev', text: "Why isn't this working?!", emotion: 'angry' },
192
+ { character: 'rubber', text: '...', emotion: 'neutral' }
193
+ ]
194
+ },
195
+ {
196
+ characters: ['dev', 'rubber'],
197
+ dialogue: [
198
+ { character: 'dev', text: 'Oh wait, I see it now!', emotion: 'excited' },
199
+ { character: 'rubber', text: '...', emotion: 'neutral' }
200
+ ]
201
+ }
202
+ ]
203
+ }, null, 2);
204
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * SVG primitive components for building illustrations
3
+ */
4
+ import type { ThemeColors } from './themes.js';
5
+ import type { CodeLine, TerminalLine } from './types.js';
6
+ /**
7
+ * Escape HTML entities
8
+ */
9
+ export declare function escapeHtml(text: string | null | undefined): string;
10
+ /**
11
+ * Render an icon at position
12
+ */
13
+ export declare function icon(name: string, x: number, y: number, size: number | undefined, color: string): string;
14
+ /**
15
+ * Card with icon and label
16
+ */
17
+ export declare function card(x: number, y: number, w: number, h: number, iconName: string, label: string, colors: ThemeColors, accentColor?: string, sublabel?: string): string;
18
+ /**
19
+ * Metric display box
20
+ */
21
+ export declare function metric(x: number, y: number, label: string, value: string, colors: ThemeColors, unit?: string, accentColor?: string): string;
22
+ /**
23
+ * Status indicator with text
24
+ */
25
+ export declare function status(x: number, y: number, state: 'ok' | 'warn' | 'error' | 'info', text: string, colors: ThemeColors): string;
26
+ /**
27
+ * Arrow connector between points
28
+ */
29
+ export declare function arrow(x1: number, y1: number, x2: number, y2: number, color: string, label?: string, dashed?: boolean, colors?: ThemeColors): string;
30
+ /**
31
+ * Code snippet block
32
+ */
33
+ export declare function codeSnippet(x: number, y: number, w: number, h: number, lines: CodeLine[], colors: ThemeColors, title?: string): string;
34
+ /**
35
+ * Terminal block
36
+ */
37
+ export declare function terminalBlock(x: number, y: number, w: number, h: number, lines: TerminalLine[], colors: ThemeColors): string;
38
+ /**
39
+ * Title bar at bottom with multi-line support
40
+ */
41
+ export declare function titleBar(text: string, width: number, height: number, colors: ThemeColors): string;
42
+ //# sourceMappingURL=primitives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAIzD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAKlE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAClB,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,IAAI,EAAE,MAAM,YAAK,EACjB,KAAK,EAAE,MAAM,GACZ,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,IAAI,CAClB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,MAAM,CACpB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE,MAAW,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,MAAM,CACpB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,EACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,GAClB,MAAM,CAWR;AAED;;GAEG;AACH,wBAAgB,KAAK,CACnB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,OAAe,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,WAAW,EACnB,KAAK,GAAE,MAAkB,GACxB,MAAM,CAUR;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,YAAY,EAAE,EACrB,MAAM,EAAE,WAAW,GAClB,MAAM,CASR;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,GAClB,MAAM,CA4BR"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * SVG primitive components for building illustrations
3
+ */
4
+ import { ICONS } from './icons.js';
5
+ const FONT = "'SF Mono', Menlo, Monaco, 'Courier New', monospace";
6
+ /**
7
+ * Escape HTML entities
8
+ */
9
+ export function escapeHtml(text) {
10
+ return String(text || '')
11
+ .replace(/&/g, '&amp;')
12
+ .replace(/</g, '&lt;')
13
+ .replace(/>/g, '&gt;');
14
+ }
15
+ /**
16
+ * Render an icon at position
17
+ */
18
+ export function icon(name, x, y, size = 24, color) {
19
+ const path = ICONS[name];
20
+ if (!path)
21
+ return '';
22
+ const scale = size / 24;
23
+ return `<g transform="translate(${x - size / 2}, ${y - size / 2}) scale(${scale})">
24
+ <path d="${path}" fill="none" stroke="${color}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
25
+ </g>`;
26
+ }
27
+ /**
28
+ * Card with icon and label
29
+ */
30
+ export function card(x, y, w, h, iconName, label, colors, accentColor, sublabel) {
31
+ const color = accentColor || colors.blue;
32
+ return `
33
+ <rect x="${x}" y="${y}" width="${w}" height="${h}" rx="12" fill="${colors.card}" stroke="${color}" stroke-width="2"/>
34
+ ${icon(iconName, x + w / 2, y + h / 2 - (sublabel ? 8 : 0), Math.min(w, h) * 0.35, color)}
35
+ <text x="${x + w / 2}" y="${y + h - 16}" text-anchor="middle" fill="${colors.text}" font-size="12" font-family="${FONT}">${escapeHtml(label.substring(0, 12))}</text>
36
+ ${sublabel ? `<text x="${x + w / 2}" y="${y + h - 4}" text-anchor="middle" fill="${colors.muted}" font-size="10" font-family="${FONT}">${escapeHtml(sublabel.substring(0, 15))}</text>` : ''}
37
+ `;
38
+ }
39
+ /**
40
+ * Metric display box
41
+ */
42
+ export function metric(x, y, label, value, colors, unit = '', accentColor) {
43
+ const color = accentColor || colors.blue;
44
+ return `
45
+ <rect x="${x}" y="${y}" width="110" height="60" rx="8" fill="${colors.card}" stroke="${colors.border}"/>
46
+ <text x="${x + 55}" y="${y + 22}" text-anchor="middle" fill="${colors.muted}" font-size="11" font-family="${FONT}">${escapeHtml(label.substring(0, 12))}</text>
47
+ <text x="${x + 55}" y="${y + 46}" text-anchor="middle" fill="${color}" font-size="18" font-family="${FONT}">${escapeHtml(value)}<tspan fill="${colors.muted}" font-size="11">${escapeHtml(unit)}</tspan></text>
48
+ `;
49
+ }
50
+ /**
51
+ * Status indicator with text
52
+ */
53
+ export function status(x, y, state, text, colors) {
54
+ const stateColors = {
55
+ ok: colors.green,
56
+ warn: colors.orange,
57
+ error: colors.red,
58
+ info: colors.cyan,
59
+ };
60
+ const color = stateColors[state] || colors.muted;
61
+ return `
62
+ <circle cx="${x}" cy="${y}" r="6" fill="${color}"/>
63
+ <text x="${x + 14}" y="${y + 4}" fill="${colors.text}" font-size="11" font-family="${FONT}">${escapeHtml(text.substring(0, 30))}</text>`;
64
+ }
65
+ /**
66
+ * Arrow connector between points
67
+ */
68
+ export function arrow(x1, y1, x2, y2, color, label = '', dashed = false, colors) {
69
+ const mx = (x1 + x2) / 2;
70
+ const my = (y1 + y2) / 2;
71
+ const dash = dashed ? 'stroke-dasharray="6 4"' : '';
72
+ const markerId = `ah${x1}${y1}${x2}${y2}`;
73
+ const cardBg = colors?.card || '#161b22';
74
+ const mutedColor = colors?.muted || '#8b949e';
75
+ return `
76
+ <defs><marker id="${markerId}" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
77
+ <path d="M0,0 L8,4 L0,8 Z" fill="${color}"/>
78
+ </marker></defs>
79
+ <line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${color}" stroke-width="2" ${dash} marker-end="url(#${markerId})"/>
80
+ ${label ? `<rect x="${mx - 25}" y="${my - 10}" width="50" height="20" rx="4" fill="${cardBg}"/>
81
+ <text x="${mx}" y="${my + 4}" text-anchor="middle" fill="${mutedColor}" font-size="10" font-family="${FONT}">${escapeHtml(label.substring(0, 8))}</text>` : ''}`;
82
+ }
83
+ /**
84
+ * Code snippet block
85
+ */
86
+ export function codeSnippet(x, y, w, h, lines, colors, title = 'code.ts') {
87
+ return `
88
+ <rect x="${x}" y="${y}" width="${w}" height="${h}" rx="8" fill="${colors.elevated}" stroke="${colors.border}"/>
89
+ <rect x="${x}" y="${y}" width="${w}" height="28" rx="8" fill="${colors.card}"/>
90
+ <circle cx="${x + 16}" cy="${y + 14}" r="5" fill="${colors.red}" opacity="0.8"/>
91
+ <circle cx="${x + 32}" cy="${y + 14}" r="5" fill="${colors.orange}" opacity="0.8"/>
92
+ <circle cx="${x + 48}" cy="${y + 14}" r="5" fill="${colors.green}" opacity="0.8"/>
93
+ <text x="${x + w / 2}" y="${y + 18}" text-anchor="middle" fill="${colors.dim}" font-size="10" font-family="${FONT}">${escapeHtml(title.substring(0, 20))}</text>
94
+ ${lines.slice(0, 5).map((l, i) => `<text x="${x + 12}" y="${y + 48 + i * 18}" fill="${l.hl ? colors.cyan : colors.text}" font-size="11" font-family="${FONT}">${escapeHtml(l.t.substring(0, 32))}</text>`).join('')}
95
+ `;
96
+ }
97
+ /**
98
+ * Terminal block
99
+ */
100
+ export function terminalBlock(x, y, w, h, lines, colors) {
101
+ return `
102
+ <rect x="${x}" y="${y}" width="${w}" height="${h}" rx="8" fill="${colors.bg}" stroke="${colors.green}"/>
103
+ <text x="${x + 12}" y="${y + 20}" fill="${colors.green}" font-size="11" font-family="${FONT}">$ terminal</text>
104
+ ${lines.slice(0, 4).map((l, i) => {
105
+ const col = l.err ? colors.red : l.ok ? colors.green : colors.text;
106
+ return `<text x="${x + 12}" y="${y + 44 + i * 18}" fill="${col}" font-size="11" font-family="${FONT}">${escapeHtml(l.t.substring(0, 35))}</text>`;
107
+ }).join('')}
108
+ `;
109
+ }
110
+ /**
111
+ * Title bar at bottom with multi-line support
112
+ */
113
+ export function titleBar(text, width, height, colors) {
114
+ const maxCharsPerLine = 60;
115
+ const words = text.split(' ');
116
+ const lines = [];
117
+ let currentLine = '';
118
+ for (const word of words) {
119
+ if ((currentLine + ' ' + word).trim().length <= maxCharsPerLine) {
120
+ currentLine = (currentLine + ' ' + word).trim();
121
+ }
122
+ else {
123
+ if (currentLine)
124
+ lines.push(currentLine);
125
+ currentLine = word;
126
+ }
127
+ }
128
+ if (currentLine)
129
+ lines.push(currentLine);
130
+ // Limit to 2 lines max
131
+ if (lines.length > 2) {
132
+ lines[1] = lines[1].substring(0, maxCharsPerLine - 3) + '...';
133
+ lines.length = 2;
134
+ }
135
+ const boxHeight = lines.length === 1 ? 36 : 50;
136
+ const startY = height - boxHeight - 8;
137
+ return `
138
+ <rect x="30" y="${startY}" width="${width - 60}" height="${boxHeight}" rx="8" fill="${colors.card}" stroke="${colors.border}"/>
139
+ ${lines.map((line, i) => `<text x="${width / 2}" y="${startY + 22 + i * 16}" text-anchor="middle" fill="${colors.text}" font-size="11" font-family="${FONT}">${escapeHtml(line)}</text>`).join('')}`;
140
+ }
141
+ //# sourceMappingURL=primitives.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Scene definitions for different technical contexts
3
+ */
4
+ import type { ThemeColors } from './themes.js';
5
+ type SceneRenderer = (title: string, colors: ThemeColors, width: number, height: number) => string;
6
+ /**
7
+ * All available scenes
8
+ */
9
+ export declare const SCENES: Record<string, SceneRenderer>;
10
+ export {};
11
+ //# sourceMappingURL=scenes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenes.d.ts","sourceRoot":"","sources":["../src/scenes.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;AAyPnG;;GAEG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAehD,CAAC"}