@wire-dsl/engine 0.0.0-canary-20260221183600
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/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/index.cjs +8999 -0
- package/dist/index.d.cts +1380 -0
- package/dist/index.d.ts +1380 -0
- package/dist/index.js +8952 -0
- package/package.json +59 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SourceMap Types for Wire DSL
|
|
3
|
+
*
|
|
4
|
+
* Provides bidirectional mapping between code positions and AST nodes
|
|
5
|
+
* for features like code-canvas selection, visual editing, and error reporting.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Position in source code (1-based line, 0-based column)
|
|
10
|
+
*/
|
|
11
|
+
interface Position {
|
|
12
|
+
line: number;
|
|
13
|
+
column: number;
|
|
14
|
+
offset?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Range in source code
|
|
18
|
+
*/
|
|
19
|
+
interface CodeRange {
|
|
20
|
+
start: Position;
|
|
21
|
+
end: Position;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* SourceMap for a single property
|
|
25
|
+
* Captures ranges for precise property editing
|
|
26
|
+
*/
|
|
27
|
+
interface PropertySourceMap {
|
|
28
|
+
name: string;
|
|
29
|
+
value: any;
|
|
30
|
+
range: CodeRange;
|
|
31
|
+
nameRange: CodeRange;
|
|
32
|
+
valueRange: CodeRange;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Types of nodes in Wire DSL
|
|
36
|
+
*/
|
|
37
|
+
type SourceMapNodeType = 'project' | 'screen' | 'layout' | 'component' | 'component-definition' | 'layout-definition' | 'cell' | 'style' | 'mocks' | 'colors';
|
|
38
|
+
/**
|
|
39
|
+
* Main SourceMap entry - represents one node in the AST
|
|
40
|
+
*/
|
|
41
|
+
interface SourceMapEntry {
|
|
42
|
+
nodeId: string;
|
|
43
|
+
type: SourceMapNodeType;
|
|
44
|
+
range: CodeRange;
|
|
45
|
+
filePath: string;
|
|
46
|
+
parentId: string | null;
|
|
47
|
+
name?: string;
|
|
48
|
+
layoutType?: string;
|
|
49
|
+
componentType?: string;
|
|
50
|
+
indexInParent?: number;
|
|
51
|
+
isUserDefined?: boolean;
|
|
52
|
+
keywordRange?: CodeRange;
|
|
53
|
+
nameRange?: CodeRange;
|
|
54
|
+
bodyRange?: CodeRange;
|
|
55
|
+
properties?: Record<string, PropertySourceMap>;
|
|
56
|
+
insertionPoint?: InsertionPoint;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Insertion point for new children (FASE 3)
|
|
60
|
+
*/
|
|
61
|
+
interface InsertionPoint {
|
|
62
|
+
line: number;
|
|
63
|
+
column: number;
|
|
64
|
+
indentation: string;
|
|
65
|
+
after?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse result with SourceMap
|
|
69
|
+
*/
|
|
70
|
+
interface ParseResult {
|
|
71
|
+
ast: AST;
|
|
72
|
+
sourceMap: SourceMapEntry[];
|
|
73
|
+
diagnostics: ParseError[];
|
|
74
|
+
errors: ParseError[];
|
|
75
|
+
warnings: ParseError[];
|
|
76
|
+
hasErrors: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse result with diagnostics and optional AST/SourceMap
|
|
80
|
+
* Used by tolerant editor flows where diagnostics are needed even when parsing fails
|
|
81
|
+
*/
|
|
82
|
+
interface ParseDiagnosticsResult {
|
|
83
|
+
ast?: AST;
|
|
84
|
+
sourceMap?: SourceMapEntry[];
|
|
85
|
+
diagnostics: ParseError[];
|
|
86
|
+
errors: ParseError[];
|
|
87
|
+
warnings: ParseError[];
|
|
88
|
+
hasErrors: boolean;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse error with optional nodeId reference
|
|
92
|
+
*/
|
|
93
|
+
interface ParseError {
|
|
94
|
+
message: string;
|
|
95
|
+
range: CodeRange;
|
|
96
|
+
severity: 'error' | 'warning';
|
|
97
|
+
code?: string;
|
|
98
|
+
phase?: 'lexer' | 'parser' | 'semantic';
|
|
99
|
+
expected?: string;
|
|
100
|
+
actual?: string;
|
|
101
|
+
suggestion?: string;
|
|
102
|
+
nodeId?: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Captured tokens from Chevrotain parser (internal use)
|
|
106
|
+
* These are NOT stored in AST, only used temporarily during SourceMap building
|
|
107
|
+
*/
|
|
108
|
+
interface CapturedTokens {
|
|
109
|
+
keyword?: any;
|
|
110
|
+
name?: any;
|
|
111
|
+
paramList?: any;
|
|
112
|
+
properties?: any[];
|
|
113
|
+
body?: any;
|
|
114
|
+
children?: any[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
interface AST {
|
|
118
|
+
type: 'project';
|
|
119
|
+
name: string;
|
|
120
|
+
style: Record<string, string>;
|
|
121
|
+
mocks: Record<string, string>;
|
|
122
|
+
colors: Record<string, string>;
|
|
123
|
+
definedComponents: ASTDefinedComponent[];
|
|
124
|
+
definedLayouts: ASTDefinedLayout[];
|
|
125
|
+
screens: ASTScreen[];
|
|
126
|
+
_meta?: {
|
|
127
|
+
nodeId: string;
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
interface ASTDefinedComponent {
|
|
131
|
+
type: 'definedComponent';
|
|
132
|
+
name: string;
|
|
133
|
+
body: ASTLayout | ASTComponent;
|
|
134
|
+
_meta?: {
|
|
135
|
+
nodeId: string;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
interface ASTScreen {
|
|
139
|
+
type: 'screen';
|
|
140
|
+
name: string;
|
|
141
|
+
params: Record<string, string | number>;
|
|
142
|
+
layout: ASTLayout;
|
|
143
|
+
_meta?: {
|
|
144
|
+
nodeId: string;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
interface ASTLayout {
|
|
148
|
+
type: 'layout';
|
|
149
|
+
layoutType: string;
|
|
150
|
+
params: Record<string, string | number>;
|
|
151
|
+
children: (ASTComponent | ASTLayout | ASTCell)[];
|
|
152
|
+
_meta?: {
|
|
153
|
+
nodeId: string;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
interface ASTCell {
|
|
157
|
+
type: 'cell';
|
|
158
|
+
props: Record<string, string | number>;
|
|
159
|
+
children: (ASTComponent | ASTLayout)[];
|
|
160
|
+
_meta?: {
|
|
161
|
+
nodeId: string;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
interface ASTComponent {
|
|
165
|
+
type: 'component';
|
|
166
|
+
componentType: string;
|
|
167
|
+
props: Record<string, string | number>;
|
|
168
|
+
_meta?: {
|
|
169
|
+
nodeId: string;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
interface ParseWireDSLWithSourceMapOptions {
|
|
173
|
+
throwOnError?: boolean;
|
|
174
|
+
includeSemanticWarnings?: boolean;
|
|
175
|
+
}
|
|
176
|
+
interface ASTDefinedLayout {
|
|
177
|
+
type: 'definedLayout';
|
|
178
|
+
name: string;
|
|
179
|
+
body: ASTLayout;
|
|
180
|
+
_meta?: {
|
|
181
|
+
nodeId: string;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
declare function parseWireDSL(input: string): AST;
|
|
185
|
+
/**
|
|
186
|
+
* Parse Wire DSL with SourceMap generation
|
|
187
|
+
*
|
|
188
|
+
* Returns both AST and SourceMap for bidirectional code-canvas mapping
|
|
189
|
+
* Useful for:
|
|
190
|
+
* - Visual editors (Wire Studio)
|
|
191
|
+
* - Code navigation (click canvas → jump to code)
|
|
192
|
+
* - Error reporting with precise locations
|
|
193
|
+
* - Component inspection and manipulation
|
|
194
|
+
*
|
|
195
|
+
* @param input - Wire DSL source code
|
|
196
|
+
* @param filePath - Optional file path (default: "<input>") - used for stable nodeIds
|
|
197
|
+
* @returns ParseResult with AST, SourceMap, and errors
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const { ast, sourceMap } = parseWireDSLWithSourceMap(code, 'screens/Main.wire');
|
|
202
|
+
*
|
|
203
|
+
* // Find node by position
|
|
204
|
+
* const node = sourceMap.find(e =>
|
|
205
|
+
* e.range.start.line === 5 && e.range.start.column === 4
|
|
206
|
+
* );
|
|
207
|
+
*
|
|
208
|
+
* // Access AST node
|
|
209
|
+
* console.log(node.astNode.type); // 'component'
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
declare function parseWireDSLWithSourceMap(input: string, filePath?: string): ParseResult;
|
|
213
|
+
declare function parseWireDSLWithSourceMap(input: string, filePath: string | undefined, options: ParseWireDSLWithSourceMapOptions & {
|
|
214
|
+
throwOnError: false;
|
|
215
|
+
}): ParseDiagnosticsResult;
|
|
216
|
+
interface ParsedWireframe {
|
|
217
|
+
name: string;
|
|
218
|
+
components: ParsedComponent[];
|
|
219
|
+
}
|
|
220
|
+
interface ParsedComponent {
|
|
221
|
+
type: string;
|
|
222
|
+
id: string;
|
|
223
|
+
children?: ParsedComponent[];
|
|
224
|
+
properties?: Record<string, unknown>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Intermediate Representation (IR) Generator
|
|
229
|
+
*
|
|
230
|
+
* Transforms AST to normalized IR (JSON format)
|
|
231
|
+
* - Applies defaults from tokens
|
|
232
|
+
* - Generates unique IDs
|
|
233
|
+
* - Validates structure with Zod
|
|
234
|
+
*/
|
|
235
|
+
interface IRContract {
|
|
236
|
+
irVersion: string;
|
|
237
|
+
project: IRProject;
|
|
238
|
+
}
|
|
239
|
+
interface IRProject {
|
|
240
|
+
id: string;
|
|
241
|
+
name: string;
|
|
242
|
+
style: IRStyle;
|
|
243
|
+
mocks: Record<string, unknown>;
|
|
244
|
+
colors: Record<string, string>;
|
|
245
|
+
screens: IRScreen[];
|
|
246
|
+
nodes: Record<string, IRNode>;
|
|
247
|
+
}
|
|
248
|
+
interface IRStyle {
|
|
249
|
+
density: 'compact' | 'normal' | 'comfortable';
|
|
250
|
+
spacing: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
251
|
+
radius: 'none' | 'sm' | 'md' | 'lg' | 'full';
|
|
252
|
+
stroke: 'thin' | 'normal' | 'thick';
|
|
253
|
+
font: 'sm' | 'base' | 'lg';
|
|
254
|
+
background?: string;
|
|
255
|
+
theme?: string;
|
|
256
|
+
device?: string;
|
|
257
|
+
}
|
|
258
|
+
interface IRScreen {
|
|
259
|
+
id: string;
|
|
260
|
+
name: string;
|
|
261
|
+
viewport: {
|
|
262
|
+
width: number;
|
|
263
|
+
height: number;
|
|
264
|
+
};
|
|
265
|
+
background?: string;
|
|
266
|
+
root: {
|
|
267
|
+
ref: string;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
type IRNode = IRContainerNode | IRComponentNode | IRInstanceNode;
|
|
271
|
+
interface IRContainerNode {
|
|
272
|
+
id: string;
|
|
273
|
+
kind: 'container';
|
|
274
|
+
containerType: 'stack' | 'grid' | 'split' | 'panel' | 'card';
|
|
275
|
+
params: Record<string, string | number>;
|
|
276
|
+
children: Array<{
|
|
277
|
+
ref: string;
|
|
278
|
+
}>;
|
|
279
|
+
style: IRNodeStyle;
|
|
280
|
+
meta: IRMeta;
|
|
281
|
+
}
|
|
282
|
+
interface IRComponentNode {
|
|
283
|
+
id: string;
|
|
284
|
+
kind: 'component';
|
|
285
|
+
componentType: string;
|
|
286
|
+
props: Record<string, string | number>;
|
|
287
|
+
style: IRNodeStyle;
|
|
288
|
+
meta: IRMeta;
|
|
289
|
+
}
|
|
290
|
+
interface IRNodeStyle {
|
|
291
|
+
padding?: string;
|
|
292
|
+
gap?: string;
|
|
293
|
+
align?: 'left' | 'center' | 'right' | 'justify';
|
|
294
|
+
justify?: 'start' | 'center' | 'end';
|
|
295
|
+
background?: string;
|
|
296
|
+
}
|
|
297
|
+
interface IRMeta {
|
|
298
|
+
source?: string;
|
|
299
|
+
nodeId?: string;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Wraps an expanded user-defined component or layout instance.
|
|
303
|
+
* Preserves the call-site identity (nodeId) in the SVG so the canvas
|
|
304
|
+
* can select and edit the instance independently from its definition.
|
|
305
|
+
*/
|
|
306
|
+
interface IRInstanceNode {
|
|
307
|
+
id: string;
|
|
308
|
+
kind: 'instance';
|
|
309
|
+
/** Name of the user-defined component or layout (e.g. "MyComp") */
|
|
310
|
+
definitionName: string;
|
|
311
|
+
/** Whether it originated from a `define Component` or `define Layout` */
|
|
312
|
+
definitionKind: 'component' | 'layout';
|
|
313
|
+
/** Props/params passed at the call site (e.g. { text: "Hello" }) */
|
|
314
|
+
invocationProps: Record<string, string | number>;
|
|
315
|
+
/** Reference to the root IR node produced by expanding the definition */
|
|
316
|
+
expandedRoot: {
|
|
317
|
+
ref: string;
|
|
318
|
+
};
|
|
319
|
+
style: IRNodeStyle;
|
|
320
|
+
/** meta.nodeId = SourceMap nodeId of the call-site AST node */
|
|
321
|
+
meta: IRMeta;
|
|
322
|
+
}
|
|
323
|
+
declare class IRGenerator {
|
|
324
|
+
private idGen;
|
|
325
|
+
private nodes;
|
|
326
|
+
private definedComponents;
|
|
327
|
+
private definedLayouts;
|
|
328
|
+
private definedComponentIndices;
|
|
329
|
+
private undefinedComponentsUsed;
|
|
330
|
+
private warnings;
|
|
331
|
+
private errors;
|
|
332
|
+
private style;
|
|
333
|
+
generate(ast: AST): IRContract;
|
|
334
|
+
private applyStyle;
|
|
335
|
+
private convertScreen;
|
|
336
|
+
/**
|
|
337
|
+
* Validates that component definitions appear before their first usage
|
|
338
|
+
* Generates warnings (not errors) to allow flexible code organization
|
|
339
|
+
*/
|
|
340
|
+
private validateComponentOrder;
|
|
341
|
+
/**
|
|
342
|
+
* Recursively finds all component usages in a layout
|
|
343
|
+
*/
|
|
344
|
+
private findComponentsInLayout;
|
|
345
|
+
/**
|
|
346
|
+
* Get all warnings generated during IR generation
|
|
347
|
+
*/
|
|
348
|
+
getWarnings(): Array<{
|
|
349
|
+
message: string;
|
|
350
|
+
type: string;
|
|
351
|
+
}>;
|
|
352
|
+
private convertLayout;
|
|
353
|
+
private convertCell;
|
|
354
|
+
private convertComponent;
|
|
355
|
+
private expandDefinedComponent;
|
|
356
|
+
private expandDefinedLayout;
|
|
357
|
+
private resolveChildrenSlot;
|
|
358
|
+
private convertASTNode;
|
|
359
|
+
private resolveLayoutParams;
|
|
360
|
+
private normalizeSplitParams;
|
|
361
|
+
private resolveComponentProps;
|
|
362
|
+
private resolveBindingValue;
|
|
363
|
+
private isBindingTargetRequired;
|
|
364
|
+
private reportUnusedArguments;
|
|
365
|
+
private cleanParams;
|
|
366
|
+
private sanitizeId;
|
|
367
|
+
}
|
|
368
|
+
declare function generateIR(ast: AST): IRContract;
|
|
369
|
+
interface IRWireframe {
|
|
370
|
+
id: string;
|
|
371
|
+
name: string;
|
|
372
|
+
components: IRComponent[];
|
|
373
|
+
layout: IRLayout;
|
|
374
|
+
metadata: IRMetadata;
|
|
375
|
+
}
|
|
376
|
+
interface IRComponent {
|
|
377
|
+
id: string;
|
|
378
|
+
type: string;
|
|
379
|
+
label?: string;
|
|
380
|
+
props?: Record<string, unknown>;
|
|
381
|
+
children?: IRComponent[];
|
|
382
|
+
}
|
|
383
|
+
interface IRLayout {
|
|
384
|
+
width: number;
|
|
385
|
+
height: number;
|
|
386
|
+
gridCols: number;
|
|
387
|
+
gridRows: number;
|
|
388
|
+
}
|
|
389
|
+
interface IRMetadata {
|
|
390
|
+
version: string;
|
|
391
|
+
created: string;
|
|
392
|
+
author?: string;
|
|
393
|
+
description?: string;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Device viewport presets for multi-device wireframe rendering
|
|
398
|
+
*
|
|
399
|
+
* Defines standard viewport widths and minimum heights for different device
|
|
400
|
+
* categories. Final render height remains dynamic and grows with content.
|
|
401
|
+
*/
|
|
402
|
+
interface DevicePreset {
|
|
403
|
+
name: string;
|
|
404
|
+
width: number;
|
|
405
|
+
minHeight: number;
|
|
406
|
+
category: 'mobile' | 'desktop' | 'tablet' | 'print';
|
|
407
|
+
description: string;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Standard device viewport presets
|
|
411
|
+
*
|
|
412
|
+
* `minHeight` is used as the baseline viewport height. Renderers still
|
|
413
|
+
* expand dynamically when content exceeds this value.
|
|
414
|
+
*/
|
|
415
|
+
declare const DEVICE_PRESETS: Record<string, DevicePreset>;
|
|
416
|
+
/**
|
|
417
|
+
* Resolve device preset name to viewport dimensions
|
|
418
|
+
*
|
|
419
|
+
* @param device - Device preset name (case-insensitive)
|
|
420
|
+
* @returns Viewport width and minimum height in pixels
|
|
421
|
+
*/
|
|
422
|
+
declare function resolveDevicePreset(device: string): {
|
|
423
|
+
width: number;
|
|
424
|
+
minHeight: number;
|
|
425
|
+
};
|
|
426
|
+
/**
|
|
427
|
+
* Check if a device preset name is valid
|
|
428
|
+
*
|
|
429
|
+
* @param device - Device preset name to validate
|
|
430
|
+
* @returns True if device preset exists
|
|
431
|
+
*/
|
|
432
|
+
declare function isValidDevice(device: string): boolean;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Layout Engine
|
|
436
|
+
*
|
|
437
|
+
* Calculates positions and dimensions for all nodes
|
|
438
|
+
* Supports: stack (vertical/horizontal), grid (12-column)
|
|
439
|
+
*/
|
|
440
|
+
interface LayoutPosition {
|
|
441
|
+
x: number;
|
|
442
|
+
y: number;
|
|
443
|
+
width: number;
|
|
444
|
+
height: number;
|
|
445
|
+
}
|
|
446
|
+
interface LayoutResult {
|
|
447
|
+
[nodeId: string]: LayoutPosition;
|
|
448
|
+
}
|
|
449
|
+
declare class LayoutEngine {
|
|
450
|
+
private nodes;
|
|
451
|
+
private style;
|
|
452
|
+
private result;
|
|
453
|
+
private viewport;
|
|
454
|
+
private ir;
|
|
455
|
+
private parentContainerTypes;
|
|
456
|
+
constructor(ir: IRContract);
|
|
457
|
+
calculate(): LayoutResult;
|
|
458
|
+
private calculateNode;
|
|
459
|
+
private calculateContainer;
|
|
460
|
+
private calculateStack;
|
|
461
|
+
private calculateContainerHeight;
|
|
462
|
+
private calculateGrid;
|
|
463
|
+
private calculateSplit;
|
|
464
|
+
private calculatePanel;
|
|
465
|
+
private calculateCard;
|
|
466
|
+
/**
|
|
467
|
+
* Calculate layout for an instance node.
|
|
468
|
+
* The instance is a transparent wrapper — its bounding box equals the
|
|
469
|
+
* expanded root's bounding box. We calculate the expanded root first and
|
|
470
|
+
* then copy its position to the instance nodeId so the renderer can use it.
|
|
471
|
+
*/
|
|
472
|
+
private calculateInstance;
|
|
473
|
+
private calculateComponent;
|
|
474
|
+
private resolveSpacing;
|
|
475
|
+
private getSeparateSize;
|
|
476
|
+
private getComponentHeight;
|
|
477
|
+
private getTextMetricsForDensity;
|
|
478
|
+
private getButtonMetricsForDensity;
|
|
479
|
+
private getHeadingMetricsForDensity;
|
|
480
|
+
private wrapTextToLines;
|
|
481
|
+
private getIntrinsicComponentHeight;
|
|
482
|
+
private getControlLabelOffset;
|
|
483
|
+
private getIntrinsicComponentWidth;
|
|
484
|
+
private calculateChildHeight;
|
|
485
|
+
private calculateChildWidth;
|
|
486
|
+
private estimateTextWidth;
|
|
487
|
+
private parseBooleanProp;
|
|
488
|
+
private adjustNodeYPositions;
|
|
489
|
+
}
|
|
490
|
+
declare function calculateLayout(ir: IRContract): LayoutResult;
|
|
491
|
+
declare function resolveGridPosition(row: number, col: number, rowSpan?: number, colSpan?: number, gridWidth?: number, gridHeight?: number, gridCols?: number, gridRows?: number): LayoutPosition;
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Color Resolver
|
|
495
|
+
*
|
|
496
|
+
* Resolves color references from project colors, named colors, or hex values
|
|
497
|
+
*/
|
|
498
|
+
declare class ColorResolver {
|
|
499
|
+
private customColors;
|
|
500
|
+
private namedColors;
|
|
501
|
+
/**
|
|
502
|
+
* Set custom colors from project definition
|
|
503
|
+
*/
|
|
504
|
+
setCustomColors(colors: Record<string, string>): void;
|
|
505
|
+
/**
|
|
506
|
+
* Check if a color key/reference is resolvable.
|
|
507
|
+
*/
|
|
508
|
+
hasColor(colorRef: string): boolean;
|
|
509
|
+
/**
|
|
510
|
+
* Resolve a color reference to a hex value
|
|
511
|
+
* Priority: custom colors > named colors > hex validation > default
|
|
512
|
+
*/
|
|
513
|
+
resolveColor(colorRef: string, defaultColor?: string): string;
|
|
514
|
+
private resolveColorInternal;
|
|
515
|
+
/**
|
|
516
|
+
* Validate hex color (6-character hex code)
|
|
517
|
+
*/
|
|
518
|
+
private isValidHex;
|
|
519
|
+
/**
|
|
520
|
+
* Validate and return hex, or undefined if invalid
|
|
521
|
+
*/
|
|
522
|
+
private validateHex;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Design Tokens System
|
|
527
|
+
*
|
|
528
|
+
* Visual properties organized by density levels.
|
|
529
|
+
* These tokens are read from ir.project.style (density, spacing, radius, stroke, font)
|
|
530
|
+
* and used by all renderers to maintain consistent visual styling.
|
|
531
|
+
*/
|
|
532
|
+
interface DesignTokens {
|
|
533
|
+
spacing: {
|
|
534
|
+
xs: number;
|
|
535
|
+
sm: number;
|
|
536
|
+
md: number;
|
|
537
|
+
lg: number;
|
|
538
|
+
xl: number;
|
|
539
|
+
};
|
|
540
|
+
button: {
|
|
541
|
+
paddingX: number;
|
|
542
|
+
paddingY: number;
|
|
543
|
+
radius: number;
|
|
544
|
+
fontSize: number;
|
|
545
|
+
fontWeight: number;
|
|
546
|
+
};
|
|
547
|
+
input: {
|
|
548
|
+
paddingX: number;
|
|
549
|
+
paddingY: number;
|
|
550
|
+
radius: number;
|
|
551
|
+
fontSize: number;
|
|
552
|
+
};
|
|
553
|
+
card: {
|
|
554
|
+
padding: number;
|
|
555
|
+
radius: number;
|
|
556
|
+
strokeWidth: number;
|
|
557
|
+
};
|
|
558
|
+
heading: {
|
|
559
|
+
fontSize: number;
|
|
560
|
+
fontWeight: number;
|
|
561
|
+
marginBottom: number;
|
|
562
|
+
};
|
|
563
|
+
text: {
|
|
564
|
+
fontSize: number;
|
|
565
|
+
lineHeight: number;
|
|
566
|
+
};
|
|
567
|
+
badge: {
|
|
568
|
+
paddingX: number;
|
|
569
|
+
paddingY: number;
|
|
570
|
+
radius: number | 'pill';
|
|
571
|
+
fontSize: number;
|
|
572
|
+
};
|
|
573
|
+
table: {
|
|
574
|
+
cellPaddingX: number;
|
|
575
|
+
cellPaddingY: number;
|
|
576
|
+
headerFontWeight: number;
|
|
577
|
+
borderWidth: number;
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Token sets for each density level
|
|
582
|
+
*/
|
|
583
|
+
declare const DENSITY_TOKENS: Record<'compact' | 'normal' | 'comfortable', DesignTokens>;
|
|
584
|
+
/**
|
|
585
|
+
* Resolve design tokens based on IRStyle
|
|
586
|
+
*/
|
|
587
|
+
declare function resolveTokens(style: any): DesignTokens;
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* SVG Renderer
|
|
591
|
+
*
|
|
592
|
+
* Generates accessible, optimized SVG output from IR + Layout
|
|
593
|
+
*/
|
|
594
|
+
interface SVGRenderOptions {
|
|
595
|
+
width?: number;
|
|
596
|
+
height?: number;
|
|
597
|
+
theme?: 'light' | 'dark';
|
|
598
|
+
includeLabels?: boolean;
|
|
599
|
+
screenName?: string;
|
|
600
|
+
}
|
|
601
|
+
interface SVGComponent {
|
|
602
|
+
tag: string;
|
|
603
|
+
attrs: Record<string, string | number>;
|
|
604
|
+
children?: SVGComponent[];
|
|
605
|
+
text?: string;
|
|
606
|
+
}
|
|
607
|
+
declare const THEMES: {
|
|
608
|
+
light: {
|
|
609
|
+
bg: string;
|
|
610
|
+
cardBg: string;
|
|
611
|
+
border: string;
|
|
612
|
+
text: string;
|
|
613
|
+
textMuted: string;
|
|
614
|
+
primary: string;
|
|
615
|
+
primaryHover: string;
|
|
616
|
+
primaryLight: string;
|
|
617
|
+
};
|
|
618
|
+
dark: {
|
|
619
|
+
bg: string;
|
|
620
|
+
cardBg: string;
|
|
621
|
+
border: string;
|
|
622
|
+
text: string;
|
|
623
|
+
textMuted: string;
|
|
624
|
+
primary: string;
|
|
625
|
+
primaryHover: string;
|
|
626
|
+
primaryLight: string;
|
|
627
|
+
};
|
|
628
|
+
};
|
|
629
|
+
declare class SVGRenderer {
|
|
630
|
+
protected ir: IRContract;
|
|
631
|
+
private layout;
|
|
632
|
+
protected options: Required<Omit<SVGRenderOptions, 'screenName'>> & {
|
|
633
|
+
screenName?: string;
|
|
634
|
+
};
|
|
635
|
+
protected renderTheme: typeof THEMES.light;
|
|
636
|
+
protected tokens: DesignTokens;
|
|
637
|
+
private selectedScreenName?;
|
|
638
|
+
protected renderedNodeIds: Set<string>;
|
|
639
|
+
protected colorResolver: ColorResolver;
|
|
640
|
+
protected fontFamily: string;
|
|
641
|
+
private parentContainerByChildId;
|
|
642
|
+
constructor(ir: IRContract, layout: LayoutResult, options?: SVGRenderOptions);
|
|
643
|
+
/**
|
|
644
|
+
* Get list of available screens in the project
|
|
645
|
+
*/
|
|
646
|
+
getAvailableScreens(): Array<{
|
|
647
|
+
name: string;
|
|
648
|
+
id: string;
|
|
649
|
+
}>;
|
|
650
|
+
/**
|
|
651
|
+
* Get the currently selected or first screen
|
|
652
|
+
*/
|
|
653
|
+
protected getSelectedScreen(): {
|
|
654
|
+
screen: any;
|
|
655
|
+
name: string;
|
|
656
|
+
};
|
|
657
|
+
render(): string;
|
|
658
|
+
protected calculateContentHeight(): number;
|
|
659
|
+
protected renderNode(nodeId: string, output: string[]): void;
|
|
660
|
+
protected renderComponent(node: IRComponentNode, pos: {
|
|
661
|
+
x: number;
|
|
662
|
+
y: number;
|
|
663
|
+
width: number;
|
|
664
|
+
height: number;
|
|
665
|
+
}): string;
|
|
666
|
+
protected renderHeading(node: IRComponentNode, pos: any): string;
|
|
667
|
+
protected renderButton(node: IRComponentNode, pos: any): string;
|
|
668
|
+
protected renderLink(node: IRComponentNode, pos: any): string;
|
|
669
|
+
protected renderInput(node: IRComponentNode, pos: any): string;
|
|
670
|
+
protected renderTopbar(node: IRComponentNode, pos: any): string;
|
|
671
|
+
protected renderPanelBorder(node: IRNode, pos: any, output: string[]): void;
|
|
672
|
+
protected renderCardBorder(node: IRNode, pos: any, output: string[]): void;
|
|
673
|
+
protected renderSplitDecoration(node: IRNode, pos: any, output: string[]): void;
|
|
674
|
+
protected renderTable(node: IRComponentNode, pos: any): string;
|
|
675
|
+
protected renderChartPlaceholder(node: IRComponentNode, pos: any): string;
|
|
676
|
+
protected renderText(node: IRComponentNode, pos: any): string;
|
|
677
|
+
protected renderLabel(node: IRComponentNode, pos: any): string;
|
|
678
|
+
protected renderCode(node: IRComponentNode, pos: any): string;
|
|
679
|
+
protected renderTextarea(node: IRComponentNode, pos: any): string;
|
|
680
|
+
protected renderSelect(node: IRComponentNode, pos: any): string;
|
|
681
|
+
protected renderCheckbox(node: IRComponentNode, pos: any): string;
|
|
682
|
+
protected renderRadio(node: IRComponentNode, pos: any): string;
|
|
683
|
+
protected renderToggle(node: IRComponentNode, pos: any): string;
|
|
684
|
+
protected renderSidebar(node: IRComponentNode, pos: any): string;
|
|
685
|
+
protected renderTabs(node: IRComponentNode, pos: any): string;
|
|
686
|
+
protected renderDivider(node: IRComponentNode, pos: any): string;
|
|
687
|
+
protected renderSeparate(node: IRComponentNode, _pos: any): string;
|
|
688
|
+
protected renderAlert(node: IRComponentNode, pos: any): string;
|
|
689
|
+
protected renderBadge(node: IRComponentNode, pos: any): string;
|
|
690
|
+
protected renderModal(node: IRComponentNode, pos: any): string;
|
|
691
|
+
protected renderList(node: IRComponentNode, pos: any): string;
|
|
692
|
+
protected renderGenericComponent(node: IRComponentNode, pos: any): string;
|
|
693
|
+
protected renderStatCard(node: IRComponentNode, pos: any): string;
|
|
694
|
+
protected renderImage(node: IRComponentNode, pos: any): string;
|
|
695
|
+
protected renderBreadcrumbs(node: IRComponentNode, pos: any): string;
|
|
696
|
+
protected renderSidebarMenu(node: IRComponentNode, pos: any): string;
|
|
697
|
+
protected renderIcon(node: IRComponentNode, pos: any): string;
|
|
698
|
+
protected renderIconButton(node: IRComponentNode, pos: any): string;
|
|
699
|
+
/**
|
|
700
|
+
* Extract SVG path/element content from a full SVG string
|
|
701
|
+
* Removes the outer <svg> tag but keeps the content
|
|
702
|
+
*/
|
|
703
|
+
protected extractSvgContent(svgString: string): string;
|
|
704
|
+
protected resolveVariantColor(variant: string, fallback: string): string;
|
|
705
|
+
protected resolveAccentColor(): string;
|
|
706
|
+
protected resolveControlColor(): string;
|
|
707
|
+
protected resolveChartColor(): string;
|
|
708
|
+
protected resolveTextColor(): string;
|
|
709
|
+
protected resolveMutedColor(): string;
|
|
710
|
+
protected getSemanticVariantColor(variant: string): string | undefined;
|
|
711
|
+
protected hexToRgba(hex: string, alpha: number): string;
|
|
712
|
+
protected getIconSize(size?: string): number;
|
|
713
|
+
protected getIconButtonSize(size?: string): number;
|
|
714
|
+
protected resolveSpacing(spacing?: string): number;
|
|
715
|
+
protected wrapTextToLines(text: string, maxWidth: number, fontSize: number): string[];
|
|
716
|
+
protected clampControlWidth(idealWidth: number, availableWidth: number): number;
|
|
717
|
+
protected truncateTextToWidth(text: string, maxWidth: number, fontSize: number): string;
|
|
718
|
+
protected estimateTextWidth(text: string, fontSize: number): number;
|
|
719
|
+
protected generateUpwardTrendValues(count: number, start: number, end: number): number[];
|
|
720
|
+
protected getControlLabelOffset(label: string): number;
|
|
721
|
+
protected getControlLabelBaselineY(y: number): number;
|
|
722
|
+
protected getHeadingTypography(node: IRComponentNode): {
|
|
723
|
+
fontSize: number;
|
|
724
|
+
fontWeight: number;
|
|
725
|
+
lineHeight: number;
|
|
726
|
+
};
|
|
727
|
+
protected getHeadingFirstLineY(node: IRComponentNode, pos: {
|
|
728
|
+
y: number;
|
|
729
|
+
height: number;
|
|
730
|
+
}, fontSize: number, lineHeightPx: number, lineCount: number): number;
|
|
731
|
+
protected calculateTopbarLayout(node: IRComponentNode, pos: {
|
|
732
|
+
x: number;
|
|
733
|
+
y: number;
|
|
734
|
+
width: number;
|
|
735
|
+
height: number;
|
|
736
|
+
}, title: string, subtitle: string, actions: string, user: string): {
|
|
737
|
+
hasSubtitle: boolean;
|
|
738
|
+
titleY: number;
|
|
739
|
+
subtitleY: number;
|
|
740
|
+
textX: number;
|
|
741
|
+
titleMaxWidth: number;
|
|
742
|
+
visibleTitle: string;
|
|
743
|
+
visibleSubtitle: string;
|
|
744
|
+
leftIcon: null | {
|
|
745
|
+
badgeX: number;
|
|
746
|
+
badgeY: number;
|
|
747
|
+
badgeSize: number;
|
|
748
|
+
badgeRadius: number;
|
|
749
|
+
iconX: number;
|
|
750
|
+
iconY: number;
|
|
751
|
+
iconSize: number;
|
|
752
|
+
iconSvg: string;
|
|
753
|
+
};
|
|
754
|
+
actions: Array<{
|
|
755
|
+
x: number;
|
|
756
|
+
y: number;
|
|
757
|
+
width: number;
|
|
758
|
+
height: number;
|
|
759
|
+
label: string;
|
|
760
|
+
}>;
|
|
761
|
+
userBadge: null | {
|
|
762
|
+
x: number;
|
|
763
|
+
y: number;
|
|
764
|
+
width: number;
|
|
765
|
+
height: number;
|
|
766
|
+
label: string;
|
|
767
|
+
};
|
|
768
|
+
avatar: null | {
|
|
769
|
+
cx: number;
|
|
770
|
+
cy: number;
|
|
771
|
+
r: number;
|
|
772
|
+
};
|
|
773
|
+
};
|
|
774
|
+
protected parseBooleanProp(value: unknown, fallback?: boolean): boolean;
|
|
775
|
+
protected shouldButtonFillAvailableWidth(node: IRComponentNode): boolean;
|
|
776
|
+
private buildParentContainerIndex;
|
|
777
|
+
protected escapeXml(text: string): string;
|
|
778
|
+
/**
|
|
779
|
+
* Get data-node-id attribute string for SVG elements
|
|
780
|
+
* Enables bidirectional selection between code and canvas
|
|
781
|
+
*/
|
|
782
|
+
protected getDataNodeId(node: IRComponentNode | IRContainerNode | IRInstanceNode): string;
|
|
783
|
+
}
|
|
784
|
+
declare function renderToSVG(ir: IRContract, layout: LayoutResult, options?: SVGRenderOptions): string;
|
|
785
|
+
declare function createSVGElement(tag: string, attrs: Record<string, string | number>, children?: string[]): string;
|
|
786
|
+
declare function buildSVG(component: SVGComponent): string;
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Skeleton SVG Renderer
|
|
790
|
+
*
|
|
791
|
+
* Renders wireframes in a skeleton/loading state style:
|
|
792
|
+
* - Text/Heading: Gray rectangular blocks instead of text
|
|
793
|
+
* - Buttons: Shape outline only (no text, no fill)
|
|
794
|
+
* - Icons: Hidden completely
|
|
795
|
+
* - All text content: Gray blocks instead of actual text
|
|
796
|
+
*
|
|
797
|
+
* Used for:
|
|
798
|
+
* - Loading states
|
|
799
|
+
* - Content placeholders
|
|
800
|
+
* - Wireframe presentations without actual content
|
|
801
|
+
*/
|
|
802
|
+
|
|
803
|
+
declare class SkeletonSVGRenderer extends SVGRenderer {
|
|
804
|
+
/**
|
|
805
|
+
* Render button with same appearance as standard but without text
|
|
806
|
+
*/
|
|
807
|
+
protected renderButton(node: IRComponentNode, pos: any): string;
|
|
808
|
+
/**
|
|
809
|
+
* Render link as placeholder block + underline (no text)
|
|
810
|
+
*/
|
|
811
|
+
protected renderLink(node: IRComponentNode, pos: any): string;
|
|
812
|
+
/**
|
|
813
|
+
* Render breadcrumbs as skeleton blocks: <rect> / <rect> / <rect accent>
|
|
814
|
+
*/
|
|
815
|
+
protected renderBreadcrumbs(node: IRComponentNode, pos: any): string;
|
|
816
|
+
/**
|
|
817
|
+
* Render heading as gray block
|
|
818
|
+
*/
|
|
819
|
+
protected renderHeading(node: IRComponentNode, pos: any): string;
|
|
820
|
+
/**
|
|
821
|
+
* Render text as gray block
|
|
822
|
+
*/
|
|
823
|
+
protected renderText(node: IRComponentNode, pos: any): string;
|
|
824
|
+
/**
|
|
825
|
+
* Render label as gray block
|
|
826
|
+
*/
|
|
827
|
+
protected renderLabel(node: IRComponentNode, pos: any): string;
|
|
828
|
+
/**
|
|
829
|
+
* Render image as a plain skeleton rectangle — no icon, no placeholder label,
|
|
830
|
+
* just a filled block with the correct dimensions (aspect-ratio is preserved
|
|
831
|
+
* by the layout engine, so pos already has the right size).
|
|
832
|
+
*/
|
|
833
|
+
protected renderImage(node: IRComponentNode, pos: any): string;
|
|
834
|
+
/**
|
|
835
|
+
* Render badge as shape only (no text)
|
|
836
|
+
*/
|
|
837
|
+
protected renderBadge(node: IRComponentNode, pos: any): string;
|
|
838
|
+
/**
|
|
839
|
+
* Render alert as shape with gray block instead of message
|
|
840
|
+
*/
|
|
841
|
+
protected renderAlert(node: IRComponentNode, pos: any): string;
|
|
842
|
+
/**
|
|
843
|
+
* Render input with gray block for placeholder text
|
|
844
|
+
*/
|
|
845
|
+
protected renderInput(node: IRComponentNode, pos: any): string;
|
|
846
|
+
/**
|
|
847
|
+
* Render textarea with gray block for placeholder text
|
|
848
|
+
*/
|
|
849
|
+
protected renderTextarea(node: IRComponentNode, pos: any): string;
|
|
850
|
+
/**
|
|
851
|
+
* Render select as shape only (no placeholder text)
|
|
852
|
+
*/
|
|
853
|
+
protected renderSelect(node: IRComponentNode, pos: any): string;
|
|
854
|
+
/**
|
|
855
|
+
* Render checkbox as shape only (no label text)
|
|
856
|
+
*/
|
|
857
|
+
protected renderCheckbox(node: IRComponentNode, pos: any): string;
|
|
858
|
+
/**
|
|
859
|
+
* Render radio as shape only (no label text)
|
|
860
|
+
*/
|
|
861
|
+
protected renderRadio(node: IRComponentNode, pos: any): string;
|
|
862
|
+
/**
|
|
863
|
+
* Render toggle as shape only (no label text)
|
|
864
|
+
*/
|
|
865
|
+
protected renderToggle(node: IRComponentNode, pos: any): string;
|
|
866
|
+
/**
|
|
867
|
+
* Render code as shape with gray block instead of code text
|
|
868
|
+
*/
|
|
869
|
+
protected renderCode(node: IRComponentNode, pos: any): string;
|
|
870
|
+
/**
|
|
871
|
+
* Render table with gray blocks instead of text
|
|
872
|
+
*/
|
|
873
|
+
protected renderTable(node: IRComponentNode, pos: any): string;
|
|
874
|
+
/**
|
|
875
|
+
* Render topbar with gray blocks instead of text
|
|
876
|
+
*/
|
|
877
|
+
protected renderTopbar(node: IRComponentNode, pos: any): string;
|
|
878
|
+
/**
|
|
879
|
+
* Render StatCard with gray blocks instead of values
|
|
880
|
+
*/
|
|
881
|
+
protected renderStatCard(node: IRComponentNode, pos: any): string;
|
|
882
|
+
/**
|
|
883
|
+
* Render icon as gray square instead of hiding it
|
|
884
|
+
*/
|
|
885
|
+
protected renderIcon(node: IRComponentNode, pos: any): string;
|
|
886
|
+
/**
|
|
887
|
+
* Render IconButton with same appearance as standard but without icon
|
|
888
|
+
*/
|
|
889
|
+
protected renderIconButton(node: IRComponentNode, pos: any): string;
|
|
890
|
+
/**
|
|
891
|
+
* Render Sidebar with gray blocks instead of text
|
|
892
|
+
*/
|
|
893
|
+
protected renderSidebar(node: IRComponentNode, pos: any): string;
|
|
894
|
+
/**
|
|
895
|
+
* Render SidebarMenu with gray blocks instead of text and no icons
|
|
896
|
+
*/
|
|
897
|
+
protected renderSidebarMenu(node: IRComponentNode, pos: any): string;
|
|
898
|
+
/**
|
|
899
|
+
* Private helper: Render text as gray block
|
|
900
|
+
*/
|
|
901
|
+
private renderTextBlock;
|
|
902
|
+
private renderWrappedLineBlocks;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Sketch SVG Renderer
|
|
907
|
+
*
|
|
908
|
+
* Renders wireframes in a hand-drawn/sketch style:
|
|
909
|
+
* - Thicker borders with sketch appearance
|
|
910
|
+
* - For variant elements (Button, Badge): colored border instead of fill
|
|
911
|
+
* - Keeps text and icons visible
|
|
912
|
+
* - Traditional wireframe look
|
|
913
|
+
*
|
|
914
|
+
* Used for:
|
|
915
|
+
* - Low-fidelity wireframes
|
|
916
|
+
* - Traditional hand-drawn wireframe presentations
|
|
917
|
+
* - Early design mockups
|
|
918
|
+
*/
|
|
919
|
+
|
|
920
|
+
declare class SketchSVGRenderer extends SVGRenderer {
|
|
921
|
+
/**
|
|
922
|
+
* Override render to add sketch filter definitions
|
|
923
|
+
*/
|
|
924
|
+
render(): string;
|
|
925
|
+
/**
|
|
926
|
+
* Render button with colored border instead of fill
|
|
927
|
+
*/
|
|
928
|
+
protected renderButton(node: IRComponentNode, pos: any): string;
|
|
929
|
+
/**
|
|
930
|
+
* Render badge with colored border instead of fill
|
|
931
|
+
*/
|
|
932
|
+
protected renderBadge(node: IRComponentNode, pos: any): string;
|
|
933
|
+
/**
|
|
934
|
+
* Render IconButton with colored border instead of fill
|
|
935
|
+
*/
|
|
936
|
+
protected renderIconButton(node: IRComponentNode, pos: any): string;
|
|
937
|
+
/**
|
|
938
|
+
* Render alert with colored border
|
|
939
|
+
*/
|
|
940
|
+
protected renderAlert(node: IRComponentNode, pos: any): string;
|
|
941
|
+
/**
|
|
942
|
+
* Render input with thicker border
|
|
943
|
+
*/
|
|
944
|
+
protected renderInput(node: IRComponentNode, pos: any): string;
|
|
945
|
+
/**
|
|
946
|
+
* Render textarea with thicker border
|
|
947
|
+
*/
|
|
948
|
+
protected renderTextarea(node: IRComponentNode, pos: any): string;
|
|
949
|
+
/**
|
|
950
|
+
* Render card with thicker border and sketch filter
|
|
951
|
+
*/
|
|
952
|
+
protected renderCard(node: IRComponentNode, pos: any): string;
|
|
953
|
+
/**
|
|
954
|
+
* Render panel with thicker border and sketch filter
|
|
955
|
+
*/
|
|
956
|
+
protected renderPanel(node: IRComponentNode, pos: any): string;
|
|
957
|
+
/**
|
|
958
|
+
* Render heading with sketch filter and Comic Sans
|
|
959
|
+
*/
|
|
960
|
+
protected renderHeading(node: IRComponentNode, pos: any): string;
|
|
961
|
+
/**
|
|
962
|
+
* Render topbar with sketch filter and Comic Sans
|
|
963
|
+
*/
|
|
964
|
+
protected renderTopbar(node: IRComponentNode, pos: any): string;
|
|
965
|
+
/**
|
|
966
|
+
* Render table with sketch filter and Comic Sans
|
|
967
|
+
*/
|
|
968
|
+
protected renderTable(node: IRComponentNode, pos: any): string;
|
|
969
|
+
/**
|
|
970
|
+
* Render text with Comic Sans
|
|
971
|
+
*/
|
|
972
|
+
protected renderText(node: IRComponentNode, pos: any): string;
|
|
973
|
+
/**
|
|
974
|
+
* Render label with Comic Sans
|
|
975
|
+
*/
|
|
976
|
+
protected renderLabel(node: IRComponentNode, pos: any): string;
|
|
977
|
+
/**
|
|
978
|
+
* Render code with sketch filter and Comic Sans
|
|
979
|
+
*/
|
|
980
|
+
protected renderCode(node: IRComponentNode, pos: any): string;
|
|
981
|
+
/**
|
|
982
|
+
* Render select with sketch filter and Comic Sans
|
|
983
|
+
*/
|
|
984
|
+
protected renderSelect(node: IRComponentNode, pos: any): string;
|
|
985
|
+
/**
|
|
986
|
+
* Render checkbox with sketch filter and Comic Sans
|
|
987
|
+
*/
|
|
988
|
+
protected renderCheckbox(node: IRComponentNode, pos: any): string;
|
|
989
|
+
/**
|
|
990
|
+
* Render radio with sketch filter and Comic Sans
|
|
991
|
+
*/
|
|
992
|
+
protected renderRadio(node: IRComponentNode, pos: any): string;
|
|
993
|
+
/**
|
|
994
|
+
* Render toggle with sketch filter and Comic Sans
|
|
995
|
+
*/
|
|
996
|
+
protected renderToggle(node: IRComponentNode, pos: any): string;
|
|
997
|
+
/**
|
|
998
|
+
* Render sidebar with sketch filter and Comic Sans
|
|
999
|
+
*/
|
|
1000
|
+
protected renderSidebar(node: IRComponentNode, pos: any): string;
|
|
1001
|
+
/**
|
|
1002
|
+
* Render tabs with sketch filter and Comic Sans
|
|
1003
|
+
*/
|
|
1004
|
+
protected renderTabs(node: IRComponentNode, pos: any): string;
|
|
1005
|
+
/**
|
|
1006
|
+
* Render divider with sketch filter
|
|
1007
|
+
*/
|
|
1008
|
+
protected renderDivider(node: IRComponentNode, pos: any): string;
|
|
1009
|
+
/**
|
|
1010
|
+
* Render modal with sketch filter and Comic Sans
|
|
1011
|
+
*/
|
|
1012
|
+
protected renderModal(node: IRComponentNode, pos: any): string;
|
|
1013
|
+
/**
|
|
1014
|
+
* Render list with sketch filter and Comic Sans
|
|
1015
|
+
*/
|
|
1016
|
+
protected renderList(node: IRComponentNode, pos: any): string;
|
|
1017
|
+
/**
|
|
1018
|
+
* Render generic component with sketch filter and Comic Sans
|
|
1019
|
+
*/
|
|
1020
|
+
protected renderGenericComponent(node: IRComponentNode, pos: any): string;
|
|
1021
|
+
/**
|
|
1022
|
+
* Render stat card with sketch filter and Comic Sans
|
|
1023
|
+
*/
|
|
1024
|
+
protected renderStatCard(node: IRComponentNode, pos: any): string;
|
|
1025
|
+
/**
|
|
1026
|
+
* Render image with sketch filter
|
|
1027
|
+
*/
|
|
1028
|
+
protected renderImage(node: IRComponentNode, pos: any): string;
|
|
1029
|
+
/**
|
|
1030
|
+
* Render breadcrumbs with Comic Sans
|
|
1031
|
+
*/
|
|
1032
|
+
protected renderBreadcrumbs(node: IRComponentNode, pos: any): string;
|
|
1033
|
+
/**
|
|
1034
|
+
* Render sidebar menu with sketch filter and Comic Sans
|
|
1035
|
+
*/
|
|
1036
|
+
protected renderSidebarMenu(node: IRComponentNode, pos: any): string;
|
|
1037
|
+
/**
|
|
1038
|
+
* Render icon (same as base, icons don't need filter)
|
|
1039
|
+
*/
|
|
1040
|
+
protected renderIcon(node: IRComponentNode, pos: any): string;
|
|
1041
|
+
/**
|
|
1042
|
+
* Render chart placeholder with sketch filter and Comic Sans
|
|
1043
|
+
*/
|
|
1044
|
+
protected renderChartPlaceholder(node: IRComponentNode, pos: any): string;
|
|
1045
|
+
/**
|
|
1046
|
+
* Helper method to get icon SVG
|
|
1047
|
+
*/
|
|
1048
|
+
private getIconSvg;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Content-based hash generation for stable NodeIds
|
|
1053
|
+
*
|
|
1054
|
+
* Browser-safe implementation (no crypto/node dependencies)
|
|
1055
|
+
* Generates stable IDs that persist across parses if code hasn't changed
|
|
1056
|
+
*
|
|
1057
|
+
* **Uniqueness Strategy:**
|
|
1058
|
+
* Includes `indexInParent` in the hash to ensure identical components
|
|
1059
|
+
* in the same parent get unique IDs. This prevents nodeId collisions for:
|
|
1060
|
+
*
|
|
1061
|
+
* ```wire
|
|
1062
|
+
* layout stack {
|
|
1063
|
+
* component Button text: "Click" // index 0
|
|
1064
|
+
* component Button text: "Click" // index 1
|
|
1065
|
+
* component Button text: "Click" // index 2
|
|
1066
|
+
* }
|
|
1067
|
+
* ```
|
|
1068
|
+
*
|
|
1069
|
+
* Each button will have a different nodeId despite being identical.
|
|
1070
|
+
*/
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Generate a stable, content-based node ID
|
|
1074
|
+
*
|
|
1075
|
+
* The ID is based on:
|
|
1076
|
+
* - File path (distinguishes nodes across files)
|
|
1077
|
+
* - Line and column (unique position in file)
|
|
1078
|
+
* - Node type (component, layout, screen, etc.)
|
|
1079
|
+
* - Index in parent array (distinguishes identical siblings)
|
|
1080
|
+
* - Optional name (for named nodes like screens, defined components)
|
|
1081
|
+
*
|
|
1082
|
+
* Examples:
|
|
1083
|
+
* - "node-k7m2p9-component" (Icon component at Main.wire:5:4, index 0)
|
|
1084
|
+
* - "node-abc123-screen" (Main screen at Main.wire:2:0, index 0)
|
|
1085
|
+
* - "node-xyz789-layout" (stack layout at Main.wire:3:2, index 0)
|
|
1086
|
+
*
|
|
1087
|
+
* @param type - Type of AST node
|
|
1088
|
+
* @param filePath - Source file path
|
|
1089
|
+
* @param line - Line number (1-based)
|
|
1090
|
+
* @param column - Column number (0-based)
|
|
1091
|
+
* @param indexInParent - Index in parent's child array (0-based)
|
|
1092
|
+
* @param name - Optional name (for screens, defined components)
|
|
1093
|
+
* @returns Stable node ID
|
|
1094
|
+
*/
|
|
1095
|
+
declare function generateStableNodeId(type: SourceMapNodeType, filePath: string, line: number, column: number, indexInParent: number, name?: string): string;
|
|
1096
|
+
/**
|
|
1097
|
+
* Validate if a string looks like a valid node ID
|
|
1098
|
+
*
|
|
1099
|
+
* @param id - String to validate
|
|
1100
|
+
* @returns true if it matches the node ID pattern
|
|
1101
|
+
*/
|
|
1102
|
+
declare function isValidNodeId(id: string): boolean;
|
|
1103
|
+
/**
|
|
1104
|
+
* Extract node type from a node ID
|
|
1105
|
+
*
|
|
1106
|
+
* @param nodeId - Node ID to parse
|
|
1107
|
+
* @returns The node type, or null if invalid
|
|
1108
|
+
*/
|
|
1109
|
+
declare function getTypeFromNodeId(nodeId: string): SourceMapNodeType | null;
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* SourceMapBuilder
|
|
1113
|
+
*
|
|
1114
|
+
* Constructs SourceMap during AST traversal using semantic node IDs.
|
|
1115
|
+
*
|
|
1116
|
+
* **ID Strategy:**
|
|
1117
|
+
* - Uses human-readable, semantic IDs: `{type}-{subtype}-{counter}`
|
|
1118
|
+
* - Counter is per type-subtype to keep indices small
|
|
1119
|
+
* - Examples: `component-button-0`, `layout-stack-1`, `screen-0`
|
|
1120
|
+
*
|
|
1121
|
+
* **Stability:**
|
|
1122
|
+
* - IDs remain stable when editing properties ✅
|
|
1123
|
+
* - IDs change when reordering nodes ⚠️
|
|
1124
|
+
* - Ideal for editor UX where property editing is frequent
|
|
1125
|
+
*
|
|
1126
|
+
* **Generated IDs:**
|
|
1127
|
+
* - `project` - Single project (no counter)
|
|
1128
|
+
* - `screen-{n}` - Screens by index (0-based)
|
|
1129
|
+
* - `component-{type}-{n}` - Components by subtype (button-0, input-0, etc.)
|
|
1130
|
+
* - `layout-{type}-{n}` - Layouts by subtype (stack-0, grid-1, etc.)
|
|
1131
|
+
* - `cell-{n}` - Grid cells by index
|
|
1132
|
+
* - `define-{name}` - Component definitions by name
|
|
1133
|
+
*/
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Builder for constructing SourceMap entries during parsing
|
|
1137
|
+
* Uses semantic IDs based on type-subtype-counter (e.g., component-button-0)
|
|
1138
|
+
*/
|
|
1139
|
+
declare class SourceMapBuilder {
|
|
1140
|
+
private entries;
|
|
1141
|
+
private filePath;
|
|
1142
|
+
private sourceCode;
|
|
1143
|
+
private parentStack;
|
|
1144
|
+
private counters;
|
|
1145
|
+
constructor(filePath?: string, sourceCode?: string);
|
|
1146
|
+
/**
|
|
1147
|
+
* Add a node to the SourceMap
|
|
1148
|
+
* Generates semantic IDs like: project, screen-0, component-button-1, layout-stack-0
|
|
1149
|
+
*
|
|
1150
|
+
* @param type - Type of AST node
|
|
1151
|
+
* @param tokens - Captured tokens from parser
|
|
1152
|
+
* @param metadata - Optional metadata (name, layoutType, componentType)
|
|
1153
|
+
* @returns Generated nodeId
|
|
1154
|
+
*/
|
|
1155
|
+
addNode(type: SourceMapNodeType, tokens: CapturedTokens, metadata?: {
|
|
1156
|
+
name?: string;
|
|
1157
|
+
layoutType?: string;
|
|
1158
|
+
componentType?: string;
|
|
1159
|
+
isUserDefined?: boolean;
|
|
1160
|
+
}): string;
|
|
1161
|
+
/**
|
|
1162
|
+
* Generate semantic node ID based on type and subtype
|
|
1163
|
+
* Format: {type}-{subtype}-{counter} or {type}-{counter}
|
|
1164
|
+
*
|
|
1165
|
+
* Examples:
|
|
1166
|
+
* - project → "project"
|
|
1167
|
+
* - theme → "theme"
|
|
1168
|
+
* - mocks → "mocks"
|
|
1169
|
+
* - colors → "colors"
|
|
1170
|
+
* - screen → "screen-0", "screen-1"
|
|
1171
|
+
* - component Button → "component-button-0", "component-button-1"
|
|
1172
|
+
* - layout stack → "layout-stack-0", "layout-stack-1"
|
|
1173
|
+
* - cell → "cell-0", "cell-1"
|
|
1174
|
+
* - component-definition → "define-MyButton"
|
|
1175
|
+
* - layout-definition → "define-layout-MyShell"
|
|
1176
|
+
*/
|
|
1177
|
+
private generateNodeId;
|
|
1178
|
+
/**
|
|
1179
|
+
* Add a property to an existing node in the SourceMap
|
|
1180
|
+
* Captures precise ranges for property name and value for surgical editing
|
|
1181
|
+
*
|
|
1182
|
+
* @param nodeId - ID of the node that owns this property
|
|
1183
|
+
* @param propertyName - Name of the property (e.g., "text", "direction")
|
|
1184
|
+
* @param propertyValue - Parsed value of the property
|
|
1185
|
+
* @param tokens - Captured tokens for the property
|
|
1186
|
+
* @returns The PropertySourceMap entry created
|
|
1187
|
+
*/
|
|
1188
|
+
addProperty(nodeId: string, propertyName: string, propertyValue: any, tokens: {
|
|
1189
|
+
name?: any;
|
|
1190
|
+
value?: any;
|
|
1191
|
+
separator?: any;
|
|
1192
|
+
full?: any;
|
|
1193
|
+
}): PropertySourceMap;
|
|
1194
|
+
/**
|
|
1195
|
+
* Push a parent onto the stack (when entering a container node)
|
|
1196
|
+
*/
|
|
1197
|
+
pushParent(nodeId: string): void;
|
|
1198
|
+
/**
|
|
1199
|
+
* Pop a parent from the stack (when exiting a container node)
|
|
1200
|
+
*/
|
|
1201
|
+
popParent(): void;
|
|
1202
|
+
/**
|
|
1203
|
+
* Get the current parent nodeId (or null if at root)
|
|
1204
|
+
*/
|
|
1205
|
+
getCurrentParent(): string | null;
|
|
1206
|
+
/**
|
|
1207
|
+
* Build and return the final SourceMap
|
|
1208
|
+
*/
|
|
1209
|
+
build(): SourceMapEntry[];
|
|
1210
|
+
/**
|
|
1211
|
+
* Calculate insertionPoints for all container nodes
|
|
1212
|
+
* Container nodes: project, screen, layout, cell, component-definition, layout-definition
|
|
1213
|
+
*/
|
|
1214
|
+
private calculateAllInsertionPoints;
|
|
1215
|
+
/**
|
|
1216
|
+
* Calculate CodeRange from captured tokens
|
|
1217
|
+
* Finds the earliest start and latest end among all tokens
|
|
1218
|
+
*/
|
|
1219
|
+
private calculateRange;
|
|
1220
|
+
/**
|
|
1221
|
+
* Convert a single token to CodeRange
|
|
1222
|
+
*/
|
|
1223
|
+
private tokenToRange;
|
|
1224
|
+
/**
|
|
1225
|
+
* Calculate body range from closing brace token
|
|
1226
|
+
* Body range typically spans from opening brace to closing brace
|
|
1227
|
+
*/
|
|
1228
|
+
private calculateBodyRange;
|
|
1229
|
+
/**
|
|
1230
|
+
* Extract the first real token from a CST node (earliest by offset)
|
|
1231
|
+
* Recursively searches through children to find the token with smallest offset
|
|
1232
|
+
*/
|
|
1233
|
+
private getFirstToken;
|
|
1234
|
+
/**
|
|
1235
|
+
* Extract the last real token from a CST node (latest by offset)
|
|
1236
|
+
* Recursively searches through children to find the token with largest offset
|
|
1237
|
+
*/
|
|
1238
|
+
private getLastToken;
|
|
1239
|
+
/**
|
|
1240
|
+
* Extract start position from a Chevrotain token or CST node
|
|
1241
|
+
*/
|
|
1242
|
+
private getTokenStart;
|
|
1243
|
+
/**
|
|
1244
|
+
* Extract end position from a Chevrotain token or CST node
|
|
1245
|
+
*/
|
|
1246
|
+
private getTokenEnd;
|
|
1247
|
+
/**
|
|
1248
|
+
* Reset the builder (for reuse)
|
|
1249
|
+
*/
|
|
1250
|
+
reset(filePath?: string, sourceCode?: string): void;
|
|
1251
|
+
/**
|
|
1252
|
+
* Calculate insertion point for adding new children to a container node
|
|
1253
|
+
*
|
|
1254
|
+
* Strategy:
|
|
1255
|
+
* - If node has children: insert after last child, preserve indentation
|
|
1256
|
+
* - If node is empty: insert inside body, use parent indentation + 2 spaces
|
|
1257
|
+
*
|
|
1258
|
+
* @param nodeId - ID of the container node
|
|
1259
|
+
* @returns InsertionPoint with line, column, indentation, and optional after
|
|
1260
|
+
*/
|
|
1261
|
+
calculateInsertionPoint(nodeId: string): {
|
|
1262
|
+
line: number;
|
|
1263
|
+
column: number;
|
|
1264
|
+
indentation: string;
|
|
1265
|
+
after?: string;
|
|
1266
|
+
} | undefined;
|
|
1267
|
+
/**
|
|
1268
|
+
* Extract indentation (leading whitespace) from a line
|
|
1269
|
+
*/
|
|
1270
|
+
private extractIndentation;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* SourceMapResolver
|
|
1275
|
+
*
|
|
1276
|
+
* Provides query APIs for bidirectional code↔canvas selection
|
|
1277
|
+
*
|
|
1278
|
+
* **Use Cases:**
|
|
1279
|
+
* 1. Canvas → Code: Click SVG element with data-node-id → find code location
|
|
1280
|
+
* 2. Code → Canvas: Click code position → find corresponding node
|
|
1281
|
+
*
|
|
1282
|
+
* **Performance:**
|
|
1283
|
+
* - getNodeById: O(1) with Map index
|
|
1284
|
+
* - getNodeByPosition: O(n) linear search (could be optimized with interval tree)
|
|
1285
|
+
* - getChildren/getParent: O(1) with indexes
|
|
1286
|
+
*/
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Query result for position-based lookups
|
|
1290
|
+
*/
|
|
1291
|
+
interface PositionQueryResult extends SourceMapEntry {
|
|
1292
|
+
depth: number;
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* SourceMap query and navigation API
|
|
1296
|
+
*/
|
|
1297
|
+
declare class SourceMapResolver {
|
|
1298
|
+
private nodeMap;
|
|
1299
|
+
private childrenMap;
|
|
1300
|
+
private positionIndex;
|
|
1301
|
+
constructor(sourceMap: SourceMapEntry[]);
|
|
1302
|
+
/**
|
|
1303
|
+
* Find node by ID (Canvas → Code)
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* // User clicks SVG element with data-node-id="component-button-0"
|
|
1307
|
+
* const node = resolver.getNodeById("component-button-0");
|
|
1308
|
+
* editor.revealRange(node.range); // Jump to code
|
|
1309
|
+
*/
|
|
1310
|
+
getNodeById(nodeId: string): SourceMapEntry | null;
|
|
1311
|
+
/**
|
|
1312
|
+
* Find node at position (Code → Canvas)
|
|
1313
|
+
* Returns the most specific (deepest) node containing the position
|
|
1314
|
+
*
|
|
1315
|
+
* @example
|
|
1316
|
+
* // User clicks code at line 5, column 10
|
|
1317
|
+
* const node = resolver.getNodeByPosition(5, 10);
|
|
1318
|
+
* canvas.highlightElement(node.nodeId); // Highlight in canvas
|
|
1319
|
+
*/
|
|
1320
|
+
getNodeByPosition(line: number, column: number): SourceMapEntry | null;
|
|
1321
|
+
/**
|
|
1322
|
+
* Get all child nodes of a parent
|
|
1323
|
+
*
|
|
1324
|
+
* @example
|
|
1325
|
+
* const children = resolver.getChildren("layout-stack-0");
|
|
1326
|
+
* // Returns: [component-button-0, component-input-0, ...]
|
|
1327
|
+
*/
|
|
1328
|
+
getChildren(nodeId: string): SourceMapEntry[];
|
|
1329
|
+
/**
|
|
1330
|
+
* Get parent node
|
|
1331
|
+
*
|
|
1332
|
+
* @example
|
|
1333
|
+
* const parent = resolver.getParent("component-button-0");
|
|
1334
|
+
* // Returns: layout-stack-0
|
|
1335
|
+
*/
|
|
1336
|
+
getParent(nodeId: string): SourceMapEntry | null;
|
|
1337
|
+
/**
|
|
1338
|
+
* Get all nodes in the SourceMap
|
|
1339
|
+
*/
|
|
1340
|
+
getAllNodes(): SourceMapEntry[];
|
|
1341
|
+
/**
|
|
1342
|
+
* Get all nodes of a specific type
|
|
1343
|
+
*
|
|
1344
|
+
* @example
|
|
1345
|
+
* const buttons = resolver.getNodesByType("component", "Button");
|
|
1346
|
+
*/
|
|
1347
|
+
getNodesByType(type: SourceMapEntry['type'], subtype?: string): SourceMapEntry[];
|
|
1348
|
+
/**
|
|
1349
|
+
* Get siblings of a node (nodes with same parent)
|
|
1350
|
+
*/
|
|
1351
|
+
getSiblings(nodeId: string): SourceMapEntry[];
|
|
1352
|
+
/**
|
|
1353
|
+
* Get path from root to node (breadcrumb)
|
|
1354
|
+
*
|
|
1355
|
+
* @example
|
|
1356
|
+
* const path = resolver.getPath("component-button-0");
|
|
1357
|
+
* // Returns: [project, screen-0, layout-stack-0, component-button-0]
|
|
1358
|
+
*/
|
|
1359
|
+
getPath(nodeId: string): SourceMapEntry[];
|
|
1360
|
+
/**
|
|
1361
|
+
* Check if a position is within a node's range
|
|
1362
|
+
*/
|
|
1363
|
+
private containsPosition;
|
|
1364
|
+
/**
|
|
1365
|
+
* Calculate depth of a node in the tree (0 = root)
|
|
1366
|
+
*/
|
|
1367
|
+
private calculateDepth;
|
|
1368
|
+
/**
|
|
1369
|
+
* Get statistics about the SourceMap
|
|
1370
|
+
*/
|
|
1371
|
+
getStats(): {
|
|
1372
|
+
totalNodes: number;
|
|
1373
|
+
byType: Record<string, number>;
|
|
1374
|
+
maxDepth: number;
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
declare const version = "0.0.1";
|
|
1379
|
+
|
|
1380
|
+
export { type AST, type ASTCell, type ASTComponent, type ASTDefinedComponent, type ASTDefinedLayout, type ASTLayout, type ASTScreen, type CapturedTokens, type CodeRange, DENSITY_TOKENS, DEVICE_PRESETS, type DesignTokens, type DevicePreset, type IRComponent, type IRComponentNode, type IRContainerNode, type IRContract, IRGenerator, type IRInstanceNode, type IRLayout, type IRMeta, type IRMetadata, type IRNode, type IRNodeStyle, type IRProject, type IRScreen, type IRStyle, type IRWireframe, type InsertionPoint, LayoutEngine, type LayoutPosition, type LayoutResult, type ParseDiagnosticsResult, type ParseError, type ParseResult, type ParseWireDSLWithSourceMapOptions, type ParsedComponent, type ParsedWireframe, type Position, type PositionQueryResult, type PropertySourceMap, type SVGComponent, type SVGRenderOptions, SVGRenderer, SkeletonSVGRenderer, SketchSVGRenderer, SourceMapBuilder, type SourceMapEntry, type SourceMapNodeType, SourceMapResolver, buildSVG, calculateLayout, createSVGElement, generateIR, generateStableNodeId, getTypeFromNodeId, isValidDevice, isValidNodeId, parseWireDSL, parseWireDSLWithSourceMap, renderToSVG, resolveDevicePreset, resolveGridPosition, resolveTokens, version };
|