@zseven-w/pen-types 0.5.2 → 0.7.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.
- package/README.md +1 -1
- package/package.json +4 -4
- package/src/__tests__/codegen.test.ts +36 -0
- package/src/canvas.ts +13 -13
- package/src/codegen.ts +147 -0
- package/src/design-md.ts +15 -15
- package/src/engine.ts +116 -0
- package/src/index.ts +47 -26
- package/src/layout.ts +6 -0
- package/src/pen.ts +141 -138
- package/src/styles.ts +57 -60
- package/src/theme-preset.ts +11 -11
- package/src/uikit.ts +15 -15
- package/src/variables.ts +5 -5
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ This package provides all TypeScript types and interfaces for the OpenPencil des
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
|
-
import type { PenDocument, PenNode, FrameNode } from '@zseven-w/pen-types'
|
|
26
|
+
import type { PenDocument, PenNode, FrameNode } from '@zseven-w/pen-types';
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
## License
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zseven-w/pen-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Type definitions for OpenPencil document model",
|
|
5
|
+
"files": [
|
|
6
|
+
"src"
|
|
7
|
+
],
|
|
5
8
|
"type": "module",
|
|
6
9
|
"exports": {
|
|
7
10
|
".": {
|
|
@@ -9,9 +12,6 @@
|
|
|
9
12
|
"import": "./src/index.ts"
|
|
10
13
|
}
|
|
11
14
|
},
|
|
12
|
-
"files": [
|
|
13
|
-
"src"
|
|
14
|
-
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"typecheck": "tsc --noEmit"
|
|
17
17
|
},
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { FRAMEWORKS } from '../codegen';
|
|
3
|
+
import type { NodeSnapshot, ResolvedDepContract } from '../codegen';
|
|
4
|
+
|
|
5
|
+
describe('codegen types', () => {
|
|
6
|
+
it('FRAMEWORKS contains all 8 frameworks', () => {
|
|
7
|
+
expect(FRAMEWORKS).toHaveLength(8);
|
|
8
|
+
expect(FRAMEWORKS).toContain('react');
|
|
9
|
+
expect(FRAMEWORKS).toContain('flutter');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('NodeSnapshot allows truncated children', () => {
|
|
13
|
+
const snapshot: NodeSnapshot = {
|
|
14
|
+
id: 'n1',
|
|
15
|
+
type: 'frame',
|
|
16
|
+
name: 'Test',
|
|
17
|
+
children: '...',
|
|
18
|
+
} as NodeSnapshot;
|
|
19
|
+
expect(snapshot.children).toBe('...');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('NodeSnapshot allows nested snapshots', () => {
|
|
23
|
+
const snapshot: NodeSnapshot = {
|
|
24
|
+
id: 'n1',
|
|
25
|
+
type: 'frame',
|
|
26
|
+
name: 'Parent',
|
|
27
|
+
children: [{ id: 'n2', type: 'rectangle', name: 'Child' } as NodeSnapshot],
|
|
28
|
+
} as NodeSnapshot;
|
|
29
|
+
expect(Array.isArray(snapshot.children)).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('ResolvedDepContract allows null', () => {
|
|
33
|
+
const resolved: ResolvedDepContract = null;
|
|
34
|
+
expect(resolved).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
});
|
package/src/canvas.ts
CHANGED
|
@@ -7,25 +7,25 @@ export type ToolType =
|
|
|
7
7
|
| 'polygon'
|
|
8
8
|
| 'path'
|
|
9
9
|
| 'text'
|
|
10
|
-
| 'hand'
|
|
10
|
+
| 'hand';
|
|
11
11
|
|
|
12
12
|
export interface ViewportState {
|
|
13
|
-
zoom: number
|
|
14
|
-
panX: number
|
|
15
|
-
panY: number
|
|
13
|
+
zoom: number;
|
|
14
|
+
panX: number;
|
|
15
|
+
panY: number;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export interface SelectionState {
|
|
19
|
-
selectedIds: string[]
|
|
20
|
-
activeId: string | null
|
|
21
|
-
hoveredId: string | null
|
|
22
|
-
enteredFrameId: string | null
|
|
23
|
-
enteredFrameStack: string[]
|
|
19
|
+
selectedIds: string[];
|
|
20
|
+
activeId: string | null;
|
|
21
|
+
hoveredId: string | null;
|
|
22
|
+
enteredFrameId: string | null;
|
|
23
|
+
enteredFrameStack: string[];
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface CanvasInteraction {
|
|
27
|
-
isDrawing: boolean
|
|
28
|
-
isPanning: boolean
|
|
29
|
-
isDragging: boolean
|
|
30
|
-
drawStartPoint: { x: number; y: number } | null
|
|
27
|
+
isDrawing: boolean;
|
|
28
|
+
isPanning: boolean;
|
|
29
|
+
isDragging: boolean;
|
|
30
|
+
drawStartPoint: { x: number; y: number } | null;
|
|
31
31
|
}
|
package/src/codegen.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { PenNode } from './pen.js';
|
|
2
|
+
|
|
3
|
+
// === Canonical framework type ===
|
|
4
|
+
|
|
5
|
+
export type Framework =
|
|
6
|
+
| 'react'
|
|
7
|
+
| 'vue'
|
|
8
|
+
| 'svelte'
|
|
9
|
+
| 'html'
|
|
10
|
+
| 'flutter'
|
|
11
|
+
| 'swiftui'
|
|
12
|
+
| 'compose'
|
|
13
|
+
| 'react-native';
|
|
14
|
+
|
|
15
|
+
export const FRAMEWORKS: Framework[] = [
|
|
16
|
+
'react',
|
|
17
|
+
'vue',
|
|
18
|
+
'svelte',
|
|
19
|
+
'html',
|
|
20
|
+
'flutter',
|
|
21
|
+
'swiftui',
|
|
22
|
+
'compose',
|
|
23
|
+
'react-native',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// === Step 1 output: AI planner returns this (no node data, minimal tokens) ===
|
|
27
|
+
|
|
28
|
+
export interface PlannedChunk {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
nodeIds: string[];
|
|
32
|
+
role: string;
|
|
33
|
+
suggestedComponentName: string;
|
|
34
|
+
dependencies: string[];
|
|
35
|
+
exposedSlots?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CodePlanFromAI {
|
|
39
|
+
chunks: PlannedChunk[];
|
|
40
|
+
sharedStyles: { name: string; description: string }[];
|
|
41
|
+
rootLayout: { direction: string; gap: number; responsive: boolean };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// === Runtime: hydrated with node data + execution order ===
|
|
45
|
+
|
|
46
|
+
export interface ExecutableChunk extends PlannedChunk {
|
|
47
|
+
nodes: PenNode[];
|
|
48
|
+
order: number;
|
|
49
|
+
depContracts: ChunkContract[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface CodeExecutionPlan {
|
|
53
|
+
chunks: ExecutableChunk[];
|
|
54
|
+
sharedStyles: { name: string; description: string }[];
|
|
55
|
+
rootLayout: { direction: string; gap: number; responsive: boolean };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// === Chunk contract: structured metadata output from each chunk ===
|
|
59
|
+
|
|
60
|
+
export interface ChunkContract {
|
|
61
|
+
chunkId: string;
|
|
62
|
+
componentName: string;
|
|
63
|
+
exportedProps: PropDef[];
|
|
64
|
+
slots: SlotDef[];
|
|
65
|
+
cssClasses: string[];
|
|
66
|
+
cssVariables: string[];
|
|
67
|
+
imports: ImportDef[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface PropDef {
|
|
71
|
+
name: string;
|
|
72
|
+
type: string;
|
|
73
|
+
required: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface SlotDef {
|
|
77
|
+
name: string;
|
|
78
|
+
description: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ImportDef {
|
|
82
|
+
source: string;
|
|
83
|
+
specifiers: string[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// === Chunk generation output ===
|
|
87
|
+
|
|
88
|
+
export interface ChunkResult {
|
|
89
|
+
chunkId: string;
|
|
90
|
+
code: string;
|
|
91
|
+
contract: ChunkContract;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// === Progress events ===
|
|
95
|
+
|
|
96
|
+
export type ChunkStatus = 'pending' | 'running' | 'done' | 'degraded' | 'failed' | 'skipped';
|
|
97
|
+
|
|
98
|
+
export type CodeGenProgress =
|
|
99
|
+
| {
|
|
100
|
+
step: 'planning';
|
|
101
|
+
status: 'running' | 'done' | 'failed';
|
|
102
|
+
plan?: CodePlanFromAI;
|
|
103
|
+
error?: string;
|
|
104
|
+
}
|
|
105
|
+
| {
|
|
106
|
+
step: 'chunk';
|
|
107
|
+
chunkId: string;
|
|
108
|
+
name: string;
|
|
109
|
+
status: ChunkStatus;
|
|
110
|
+
result?: ChunkResult;
|
|
111
|
+
error?: string;
|
|
112
|
+
}
|
|
113
|
+
| { step: 'assembly'; status: 'running' | 'done' | 'failed'; error?: string }
|
|
114
|
+
| { step: 'complete'; finalCode: string; degraded: boolean }
|
|
115
|
+
| { step: 'error'; message: string; chunkId?: string };
|
|
116
|
+
|
|
117
|
+
// === Contract validation ===
|
|
118
|
+
|
|
119
|
+
export interface ContractValidationResult {
|
|
120
|
+
valid: boolean;
|
|
121
|
+
issues: string[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// === Wire DTO types (MCP/CLI responses) ===
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Depth-limited node snapshot for wire transfer.
|
|
128
|
+
* When depth is exhausted, `children` is the string `"..."` instead of NodeSnapshot[].
|
|
129
|
+
*/
|
|
130
|
+
export type NodeSnapshot = Omit<PenNode, 'children'> & {
|
|
131
|
+
children?: NodeSnapshot[] | '...';
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Hydrated chunk payload returned by codegen_plan and codegen_submit_chunk.
|
|
136
|
+
* Uses NodeSnapshot (depth-limited) instead of PenNode[].
|
|
137
|
+
* depContracts entries may be null when a dependency chunk failed/was skipped.
|
|
138
|
+
*/
|
|
139
|
+
export interface ExecutableChunkPayload extends Omit<ExecutableChunk, 'nodes' | 'depContracts'> {
|
|
140
|
+
nodes: NodeSnapshot[];
|
|
141
|
+
depContracts: ResolvedDepContract[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A dependency contract that may be absent if the upstream chunk failed or was skipped.
|
|
146
|
+
*/
|
|
147
|
+
export type ResolvedDepContract = ChunkContract | null;
|
package/src/design-md.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
export interface DesignMdSpec {
|
|
2
2
|
/** Original markdown source (for round-trip fidelity) */
|
|
3
|
-
raw: string
|
|
4
|
-
projectName?: string
|
|
5
|
-
visualTheme?: string
|
|
6
|
-
colorPalette?: DesignMdColor[]
|
|
7
|
-
typography?: DesignMdTypography
|
|
8
|
-
componentStyles?: string
|
|
9
|
-
layoutPrinciples?: string
|
|
10
|
-
generationNotes?: string
|
|
3
|
+
raw: string;
|
|
4
|
+
projectName?: string;
|
|
5
|
+
visualTheme?: string;
|
|
6
|
+
colorPalette?: DesignMdColor[];
|
|
7
|
+
typography?: DesignMdTypography;
|
|
8
|
+
componentStyles?: string;
|
|
9
|
+
layoutPrinciples?: string;
|
|
10
|
+
generationNotes?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface DesignMdColor {
|
|
14
|
-
name: string
|
|
15
|
-
hex: string
|
|
16
|
-
role: string
|
|
14
|
+
name: string;
|
|
15
|
+
hex: string;
|
|
16
|
+
role: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface DesignMdTypography {
|
|
20
|
-
fontFamily?: string
|
|
21
|
-
headings?: string
|
|
22
|
-
body?: string
|
|
23
|
-
scale?: string
|
|
20
|
+
fontFamily?: string;
|
|
21
|
+
headings?: string;
|
|
22
|
+
body?: string;
|
|
23
|
+
scale?: string;
|
|
24
24
|
}
|
package/src/engine.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { PenDocument } from './pen.js';
|
|
2
|
+
import type { ViewportState, ToolType } from './canvas.js';
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Engine Options
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export interface DesignEngineOptions {
|
|
9
|
+
/** URL pattern for CanvasKit WASM files. */
|
|
10
|
+
canvasKitPath?: string | ((file: string) => string);
|
|
11
|
+
/** Base URL for bundled font files. */
|
|
12
|
+
fontBasePath?: string;
|
|
13
|
+
/** Custom Google Fonts CSS endpoint. */
|
|
14
|
+
googleFontsCssUrl?: string;
|
|
15
|
+
/** Icon lookup function for resolving icon names to SVG path data. */
|
|
16
|
+
iconLookup?: IconLookupFn;
|
|
17
|
+
/** Canvas background color. Default: '#1a1a1a' */
|
|
18
|
+
backgroundColor?: string;
|
|
19
|
+
/** Device pixel ratio override. */
|
|
20
|
+
devicePixelRatio?: number;
|
|
21
|
+
/** Maximum undo/redo history states. Default: 300 */
|
|
22
|
+
maxHistoryStates?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Engine Events
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
export interface DesignEngineEvents {
|
|
30
|
+
/** Fired after document mutation (batch-aware: only once per batch). Payload is immutable ref. */
|
|
31
|
+
'document:change': (doc: PenDocument) => void;
|
|
32
|
+
'selection:change': (ids: string[]) => void;
|
|
33
|
+
'viewport:change': (state: ViewportState) => void;
|
|
34
|
+
'tool:change': (tool: ToolType) => void;
|
|
35
|
+
'history:change': (state: { canUndo: boolean; canRedo: boolean }) => void;
|
|
36
|
+
'node:hover': (id: string | null) => void;
|
|
37
|
+
'page:change': (pageId: string) => void;
|
|
38
|
+
/** Fired after canvas re-render (browser adapter only). */
|
|
39
|
+
render: () => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Code Generation
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
export type CodePlatform =
|
|
47
|
+
| 'react'
|
|
48
|
+
| 'html'
|
|
49
|
+
| 'css'
|
|
50
|
+
| 'vue'
|
|
51
|
+
| 'svelte'
|
|
52
|
+
| 'flutter'
|
|
53
|
+
| 'swiftui'
|
|
54
|
+
| 'compose'
|
|
55
|
+
| 'react-native';
|
|
56
|
+
|
|
57
|
+
/** Structured code generation result. */
|
|
58
|
+
export interface CodeResult {
|
|
59
|
+
files: Array<{ filename: string; content: string; language: string }>;
|
|
60
|
+
/** CSS variables block if the document uses design variables. */
|
|
61
|
+
variables?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Icon Lookup
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
/** Injectable icon lookup function for resolving icon names to SVG path data. */
|
|
69
|
+
export interface IconLookupFn {
|
|
70
|
+
(name: string): { d: string; iconId: string; style: 'stroke' | 'fill' } | null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Canvas Interaction Types
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
export interface TextEditState {
|
|
78
|
+
nodeId: string;
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
w: number;
|
|
82
|
+
h: number;
|
|
83
|
+
content: string;
|
|
84
|
+
fontSize: number;
|
|
85
|
+
fontFamily: string;
|
|
86
|
+
fontWeight: number;
|
|
87
|
+
textAlign: string;
|
|
88
|
+
color: string;
|
|
89
|
+
lineHeight: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface AgentIndicatorEntry {
|
|
93
|
+
nodeId: string;
|
|
94
|
+
color: string;
|
|
95
|
+
name: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface AgentFrameEntry {
|
|
99
|
+
frameId: string;
|
|
100
|
+
color: string;
|
|
101
|
+
name: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface InsertionIndicator {
|
|
105
|
+
x: number;
|
|
106
|
+
y: number;
|
|
107
|
+
length: number;
|
|
108
|
+
orientation: 'horizontal' | 'vertical';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface ContainerHighlight {
|
|
112
|
+
x: number;
|
|
113
|
+
y: number;
|
|
114
|
+
w: number;
|
|
115
|
+
h: number;
|
|
116
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -12,22 +12,13 @@ export type {
|
|
|
12
12
|
ShadowEffect,
|
|
13
13
|
PenEffect,
|
|
14
14
|
StyledTextSegment,
|
|
15
|
-
} from './styles.js'
|
|
15
|
+
} from './styles.js';
|
|
16
16
|
|
|
17
17
|
// Variables
|
|
18
|
-
export type {
|
|
19
|
-
VariableDefinition,
|
|
20
|
-
VariableValue,
|
|
21
|
-
ThemedValue,
|
|
22
|
-
} from './variables.js'
|
|
18
|
+
export type { VariableDefinition, VariableValue, ThemedValue } from './variables.js';
|
|
23
19
|
|
|
24
20
|
// Canvas
|
|
25
|
-
export type {
|
|
26
|
-
ToolType,
|
|
27
|
-
ViewportState,
|
|
28
|
-
SelectionState,
|
|
29
|
-
CanvasInteraction,
|
|
30
|
-
} from './canvas.js'
|
|
21
|
+
export type { ToolType, ViewportState, SelectionState, CanvasInteraction } from './canvas.js';
|
|
31
22
|
|
|
32
23
|
// Document model
|
|
33
24
|
export type {
|
|
@@ -43,6 +34,9 @@ export type {
|
|
|
43
34
|
EllipseNode,
|
|
44
35
|
LineNode,
|
|
45
36
|
PolygonNode,
|
|
37
|
+
PenPathHandle,
|
|
38
|
+
PenPathAnchor,
|
|
39
|
+
PenPathPointType,
|
|
46
40
|
PathNode,
|
|
47
41
|
TextNode,
|
|
48
42
|
ImageFitMode,
|
|
@@ -50,24 +44,51 @@ export type {
|
|
|
50
44
|
IconFontNode,
|
|
51
45
|
RefNode,
|
|
52
46
|
PenNode,
|
|
53
|
-
} from './pen.js'
|
|
47
|
+
} from './pen.js';
|
|
54
48
|
|
|
55
49
|
// UIKit
|
|
56
|
-
export type {
|
|
57
|
-
ComponentCategory,
|
|
58
|
-
KitComponent,
|
|
59
|
-
UIKit,
|
|
60
|
-
} from './uikit.js'
|
|
50
|
+
export type { ComponentCategory, KitComponent, UIKit } from './uikit.js';
|
|
61
51
|
|
|
62
52
|
// Theme presets
|
|
63
|
-
export type {
|
|
64
|
-
ThemePreset,
|
|
65
|
-
ThemePresetFile,
|
|
66
|
-
} from './theme-preset.js'
|
|
53
|
+
export type { ThemePreset, ThemePresetFile } from './theme-preset.js';
|
|
67
54
|
|
|
68
55
|
// Design.md
|
|
56
|
+
export type { DesignMdSpec, DesignMdColor, DesignMdTypography } from './design-md.js';
|
|
57
|
+
|
|
58
|
+
// Layout
|
|
59
|
+
export type { Padding } from './layout.js';
|
|
60
|
+
|
|
61
|
+
// Engine
|
|
62
|
+
export type {
|
|
63
|
+
DesignEngineOptions,
|
|
64
|
+
DesignEngineEvents,
|
|
65
|
+
CodePlatform,
|
|
66
|
+
CodeResult,
|
|
67
|
+
TextEditState,
|
|
68
|
+
AgentIndicatorEntry,
|
|
69
|
+
AgentFrameEntry,
|
|
70
|
+
InsertionIndicator,
|
|
71
|
+
ContainerHighlight,
|
|
72
|
+
IconLookupFn,
|
|
73
|
+
} from './engine.js';
|
|
74
|
+
|
|
75
|
+
// Codegen
|
|
69
76
|
export type {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
Framework,
|
|
78
|
+
PlannedChunk,
|
|
79
|
+
CodePlanFromAI,
|
|
80
|
+
ExecutableChunk,
|
|
81
|
+
CodeExecutionPlan,
|
|
82
|
+
ChunkContract,
|
|
83
|
+
PropDef,
|
|
84
|
+
SlotDef,
|
|
85
|
+
ImportDef,
|
|
86
|
+
ChunkResult,
|
|
87
|
+
ChunkStatus,
|
|
88
|
+
CodeGenProgress,
|
|
89
|
+
ContractValidationResult,
|
|
90
|
+
NodeSnapshot,
|
|
91
|
+
ExecutableChunkPayload,
|
|
92
|
+
ResolvedDepContract,
|
|
93
|
+
} from './codegen.js';
|
|
94
|
+
export { FRAMEWORKS } from './codegen.js';
|
package/src/layout.ts
ADDED
package/src/pen.ts
CHANGED
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
PenStroke,
|
|
4
|
-
PenEffect,
|
|
5
|
-
StyledTextSegment,
|
|
6
|
-
} from './styles.js'
|
|
7
|
-
import type { VariableDefinition } from './variables.js'
|
|
1
|
+
import type { PenFill, PenStroke, PenEffect, StyledTextSegment } from './styles.js';
|
|
2
|
+
import type { VariableDefinition } from './variables.js';
|
|
8
3
|
|
|
9
4
|
// --- Page ---
|
|
10
5
|
|
|
11
6
|
export interface PenPage {
|
|
12
|
-
id: string
|
|
13
|
-
name: string
|
|
14
|
-
children: PenNode[]
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
children: PenNode[];
|
|
15
10
|
}
|
|
16
11
|
|
|
17
12
|
// --- Document Root ---
|
|
18
13
|
|
|
19
14
|
export interface PenDocument {
|
|
20
|
-
version: string
|
|
21
|
-
name?: string
|
|
22
|
-
themes?: Record<string, string[]
|
|
23
|
-
variables?: Record<string, VariableDefinition
|
|
24
|
-
pages?: PenPage[]
|
|
25
|
-
children: PenNode[]
|
|
15
|
+
version: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
themes?: Record<string, string[]>;
|
|
18
|
+
variables?: Record<string, VariableDefinition>;
|
|
19
|
+
pages?: PenPage[];
|
|
20
|
+
children: PenNode[];
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
// --- Node Types ---
|
|
@@ -38,171 +33,179 @@ export type PenNodeType =
|
|
|
38
33
|
| 'text'
|
|
39
34
|
| 'image'
|
|
40
35
|
| 'icon_font'
|
|
41
|
-
| 'ref'
|
|
36
|
+
| 'ref';
|
|
42
37
|
|
|
43
|
-
export type SizingBehavior = number | 'fit_content' | 'fill_container' | string
|
|
38
|
+
export type SizingBehavior = number | 'fit_content' | 'fill_container' | string;
|
|
44
39
|
|
|
45
40
|
// --- Base ---
|
|
46
41
|
|
|
47
42
|
export interface PenNodeBase {
|
|
48
|
-
id: string
|
|
49
|
-
type: PenNodeType
|
|
50
|
-
name?: string
|
|
51
|
-
role?: string // semantic role for AI generation ("button", "card", "heading", etc.)
|
|
52
|
-
x?: number
|
|
53
|
-
y?: number
|
|
54
|
-
rotation?: number
|
|
55
|
-
opacity?: number | string // number or $variable
|
|
56
|
-
enabled?: boolean | string
|
|
57
|
-
visible?: boolean // default true
|
|
58
|
-
locked?: boolean // default false
|
|
59
|
-
flipX?: boolean
|
|
60
|
-
flipY?: boolean
|
|
61
|
-
theme?: Record<string, string
|
|
43
|
+
id: string;
|
|
44
|
+
type: PenNodeType;
|
|
45
|
+
name?: string;
|
|
46
|
+
role?: string; // semantic role for AI generation ("button", "card", "heading", etc.)
|
|
47
|
+
x?: number;
|
|
48
|
+
y?: number;
|
|
49
|
+
rotation?: number;
|
|
50
|
+
opacity?: number | string; // number or $variable
|
|
51
|
+
enabled?: boolean | string;
|
|
52
|
+
visible?: boolean; // default true
|
|
53
|
+
locked?: boolean; // default false
|
|
54
|
+
flipX?: boolean;
|
|
55
|
+
flipY?: boolean;
|
|
56
|
+
theme?: Record<string, string>;
|
|
62
57
|
}
|
|
63
58
|
|
|
64
59
|
// --- Container (shared layout props) ---
|
|
65
60
|
|
|
66
61
|
export interface ContainerProps {
|
|
67
|
-
width?: SizingBehavior
|
|
68
|
-
height?: SizingBehavior
|
|
69
|
-
layout?: 'none' | 'vertical' | 'horizontal'
|
|
70
|
-
gap?: number | string
|
|
71
|
-
padding?:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
| 'space_between'
|
|
81
|
-
| 'space_around'
|
|
82
|
-
alignItems?: 'start' | 'center' | 'end'
|
|
83
|
-
clipContent?: boolean
|
|
84
|
-
children?: PenNode[]
|
|
85
|
-
cornerRadius?: number | [number, number, number, number]
|
|
86
|
-
fill?: PenFill[]
|
|
87
|
-
stroke?: PenStroke
|
|
88
|
-
effects?: PenEffect[]
|
|
62
|
+
width?: SizingBehavior;
|
|
63
|
+
height?: SizingBehavior;
|
|
64
|
+
layout?: 'none' | 'vertical' | 'horizontal';
|
|
65
|
+
gap?: number | string;
|
|
66
|
+
padding?: number | [number, number] | [number, number, number, number] | string;
|
|
67
|
+
justifyContent?: 'start' | 'center' | 'end' | 'space_between' | 'space_around';
|
|
68
|
+
alignItems?: 'start' | 'center' | 'end';
|
|
69
|
+
clipContent?: boolean;
|
|
70
|
+
children?: PenNode[];
|
|
71
|
+
cornerRadius?: number | [number, number, number, number];
|
|
72
|
+
fill?: PenFill[];
|
|
73
|
+
stroke?: PenStroke;
|
|
74
|
+
effects?: PenEffect[];
|
|
89
75
|
}
|
|
90
76
|
|
|
91
77
|
// --- Concrete Nodes ---
|
|
92
78
|
|
|
93
79
|
export interface FrameNode extends PenNodeBase, ContainerProps {
|
|
94
|
-
type: 'frame'
|
|
95
|
-
reusable?: boolean
|
|
96
|
-
slot?: string[]
|
|
80
|
+
type: 'frame';
|
|
81
|
+
reusable?: boolean;
|
|
82
|
+
slot?: string[];
|
|
97
83
|
}
|
|
98
84
|
|
|
99
85
|
export interface GroupNode extends PenNodeBase, ContainerProps {
|
|
100
|
-
type: 'group'
|
|
86
|
+
type: 'group';
|
|
101
87
|
}
|
|
102
88
|
|
|
103
89
|
export interface RectangleNode extends PenNodeBase, ContainerProps {
|
|
104
|
-
type: 'rectangle'
|
|
90
|
+
type: 'rectangle';
|
|
105
91
|
}
|
|
106
92
|
|
|
107
93
|
export interface EllipseNode extends PenNodeBase {
|
|
108
|
-
type: 'ellipse'
|
|
109
|
-
width?: SizingBehavior
|
|
110
|
-
height?: SizingBehavior
|
|
111
|
-
cornerRadius?: number
|
|
112
|
-
innerRadius?: number
|
|
113
|
-
startAngle?: number
|
|
114
|
-
sweepAngle?: number
|
|
115
|
-
fill?: PenFill[]
|
|
116
|
-
stroke?: PenStroke
|
|
117
|
-
effects?: PenEffect[]
|
|
94
|
+
type: 'ellipse';
|
|
95
|
+
width?: SizingBehavior;
|
|
96
|
+
height?: SizingBehavior;
|
|
97
|
+
cornerRadius?: number;
|
|
98
|
+
innerRadius?: number;
|
|
99
|
+
startAngle?: number;
|
|
100
|
+
sweepAngle?: number;
|
|
101
|
+
fill?: PenFill[];
|
|
102
|
+
stroke?: PenStroke;
|
|
103
|
+
effects?: PenEffect[];
|
|
118
104
|
}
|
|
119
105
|
|
|
120
106
|
export interface LineNode extends PenNodeBase {
|
|
121
|
-
type: 'line'
|
|
122
|
-
x2?: number
|
|
123
|
-
y2?: number
|
|
124
|
-
stroke?: PenStroke
|
|
125
|
-
effects?: PenEffect[]
|
|
107
|
+
type: 'line';
|
|
108
|
+
x2?: number;
|
|
109
|
+
y2?: number;
|
|
110
|
+
stroke?: PenStroke;
|
|
111
|
+
effects?: PenEffect[];
|
|
126
112
|
}
|
|
127
113
|
|
|
128
114
|
export interface PolygonNode extends PenNodeBase {
|
|
129
|
-
type: 'polygon'
|
|
130
|
-
polygonCount: number
|
|
131
|
-
width?: SizingBehavior
|
|
132
|
-
height?: SizingBehavior
|
|
133
|
-
cornerRadius?: number
|
|
134
|
-
fill?: PenFill[]
|
|
135
|
-
stroke?: PenStroke
|
|
136
|
-
effects?: PenEffect[]
|
|
115
|
+
type: 'polygon';
|
|
116
|
+
polygonCount: number;
|
|
117
|
+
width?: SizingBehavior;
|
|
118
|
+
height?: SizingBehavior;
|
|
119
|
+
cornerRadius?: number;
|
|
120
|
+
fill?: PenFill[];
|
|
121
|
+
stroke?: PenStroke;
|
|
122
|
+
effects?: PenEffect[];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface PenPathHandle {
|
|
126
|
+
x: number;
|
|
127
|
+
y: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export type PenPathPointType = 'corner' | 'mirrored' | 'independent';
|
|
131
|
+
|
|
132
|
+
export interface PenPathAnchor {
|
|
133
|
+
x: number;
|
|
134
|
+
y: number;
|
|
135
|
+
handleIn: PenPathHandle | null;
|
|
136
|
+
handleOut: PenPathHandle | null;
|
|
137
|
+
pointType?: PenPathPointType;
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
export interface PathNode extends PenNodeBase {
|
|
140
|
-
type: 'path'
|
|
141
|
-
iconId?: string // Iconify icon ID, e.g. "lucide:home"
|
|
142
|
-
d: string
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
type: 'path';
|
|
142
|
+
iconId?: string; // Iconify icon ID, e.g. "lucide:home"
|
|
143
|
+
d: string;
|
|
144
|
+
anchors?: PenPathAnchor[];
|
|
145
|
+
closed?: boolean;
|
|
146
|
+
width?: SizingBehavior;
|
|
147
|
+
height?: SizingBehavior;
|
|
148
|
+
fill?: PenFill[];
|
|
149
|
+
stroke?: PenStroke;
|
|
150
|
+
effects?: PenEffect[];
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
export interface TextNode extends PenNodeBase {
|
|
151
|
-
type: 'text'
|
|
152
|
-
width?: SizingBehavior
|
|
153
|
-
height?: SizingBehavior
|
|
154
|
-
content: string | StyledTextSegment[]
|
|
155
|
-
fontFamily?: string
|
|
156
|
-
fontSize?: number
|
|
157
|
-
fontWeight?: number | string
|
|
158
|
-
fontStyle?: 'normal' | 'italic'
|
|
159
|
-
letterSpacing?: number
|
|
160
|
-
lineHeight?: number
|
|
161
|
-
textAlign?: 'left' | 'center' | 'right' | 'justify'
|
|
162
|
-
textAlignVertical?: 'top' | 'middle' | 'bottom'
|
|
163
|
-
textGrowth?: 'auto' | 'fixed-width' | 'fixed-width-height'
|
|
164
|
-
underline?: boolean
|
|
165
|
-
strikethrough?: boolean
|
|
166
|
-
fill?: PenFill[]
|
|
167
|
-
effects?: PenEffect[]
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export type ImageFitMode = 'fill' | 'fit' | 'crop' | 'tile'
|
|
154
|
+
type: 'text';
|
|
155
|
+
width?: SizingBehavior;
|
|
156
|
+
height?: SizingBehavior;
|
|
157
|
+
content: string | StyledTextSegment[];
|
|
158
|
+
fontFamily?: string;
|
|
159
|
+
fontSize?: number;
|
|
160
|
+
fontWeight?: number | string;
|
|
161
|
+
fontStyle?: 'normal' | 'italic';
|
|
162
|
+
letterSpacing?: number;
|
|
163
|
+
lineHeight?: number;
|
|
164
|
+
textAlign?: 'left' | 'center' | 'right' | 'justify';
|
|
165
|
+
textAlignVertical?: 'top' | 'middle' | 'bottom';
|
|
166
|
+
textGrowth?: 'auto' | 'fixed-width' | 'fixed-width-height';
|
|
167
|
+
underline?: boolean;
|
|
168
|
+
strikethrough?: boolean;
|
|
169
|
+
fill?: PenFill[];
|
|
170
|
+
effects?: PenEffect[];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export type ImageFitMode = 'fill' | 'fit' | 'crop' | 'tile';
|
|
171
174
|
|
|
172
175
|
export interface ImageNode extends PenNodeBase {
|
|
173
|
-
type: 'image'
|
|
174
|
-
src: string
|
|
175
|
-
objectFit?: ImageFitMode
|
|
176
|
-
width?: SizingBehavior
|
|
177
|
-
height?: SizingBehavior
|
|
178
|
-
cornerRadius?: number | [number, number, number, number]
|
|
179
|
-
effects?: PenEffect[]
|
|
180
|
-
exposure?: number
|
|
181
|
-
contrast?: number
|
|
182
|
-
saturation?: number
|
|
183
|
-
temperature?: number // -100 to 100
|
|
184
|
-
tint?: number
|
|
185
|
-
highlights?: number
|
|
186
|
-
shadows?: number
|
|
187
|
-
imagePrompt?: string
|
|
188
|
-
imageSearchQuery?: string
|
|
176
|
+
type: 'image';
|
|
177
|
+
src: string;
|
|
178
|
+
objectFit?: ImageFitMode;
|
|
179
|
+
width?: SizingBehavior;
|
|
180
|
+
height?: SizingBehavior;
|
|
181
|
+
cornerRadius?: number | [number, number, number, number];
|
|
182
|
+
effects?: PenEffect[];
|
|
183
|
+
exposure?: number; // -100 to 100
|
|
184
|
+
contrast?: number; // -100 to 100
|
|
185
|
+
saturation?: number; // -100 to 100
|
|
186
|
+
temperature?: number; // -100 to 100
|
|
187
|
+
tint?: number; // -100 to 100
|
|
188
|
+
highlights?: number; // -100 to 100
|
|
189
|
+
shadows?: number; // -100 to 100
|
|
190
|
+
imagePrompt?: string; // Descriptive prompt for AI image generation (long)
|
|
191
|
+
imageSearchQuery?: string; // Short keywords for image search (e.g. "burger fries")
|
|
189
192
|
}
|
|
190
193
|
|
|
191
194
|
export interface IconFontNode extends PenNodeBase {
|
|
192
|
-
type: 'icon_font'
|
|
193
|
-
iconFontName: string
|
|
194
|
-
iconFontFamily?: string
|
|
195
|
-
width?: SizingBehavior
|
|
196
|
-
height?: SizingBehavior
|
|
197
|
-
fill?: PenFill[]
|
|
198
|
-
stroke?: PenStroke
|
|
195
|
+
type: 'icon_font';
|
|
196
|
+
iconFontName: string;
|
|
197
|
+
iconFontFamily?: string;
|
|
198
|
+
width?: SizingBehavior;
|
|
199
|
+
height?: SizingBehavior;
|
|
200
|
+
fill?: PenFill[];
|
|
201
|
+
stroke?: PenStroke;
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
export interface RefNode extends PenNodeBase {
|
|
202
|
-
type: 'ref'
|
|
203
|
-
ref: string
|
|
204
|
-
descendants?: Record<string, Partial<PenNode
|
|
205
|
-
children?: PenNode[]
|
|
205
|
+
type: 'ref';
|
|
206
|
+
ref: string;
|
|
207
|
+
descendants?: Record<string, Partial<PenNode>>;
|
|
208
|
+
children?: PenNode[];
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
// --- Union ---
|
|
@@ -218,4 +221,4 @@ export type PenNode =
|
|
|
218
221
|
| TextNode
|
|
219
222
|
| ImageNode
|
|
220
223
|
| IconFontNode
|
|
221
|
-
| RefNode
|
|
224
|
+
| RefNode;
|
package/src/styles.ts
CHANGED
|
@@ -9,100 +9,97 @@ export type BlendMode =
|
|
|
9
9
|
| 'hue'
|
|
10
10
|
| 'saturation'
|
|
11
11
|
| 'color'
|
|
12
|
-
| 'luminosity'
|
|
12
|
+
| 'luminosity';
|
|
13
13
|
|
|
14
14
|
// --- Fill Types ---
|
|
15
15
|
|
|
16
16
|
export interface SolidFill {
|
|
17
|
-
type: 'solid'
|
|
18
|
-
color: string // #RRGGBB or #RRGGBBAA
|
|
19
|
-
opacity?: number
|
|
20
|
-
blendMode?: BlendMode
|
|
17
|
+
type: 'solid';
|
|
18
|
+
color: string; // #RRGGBB or #RRGGBBAA
|
|
19
|
+
opacity?: number;
|
|
20
|
+
blendMode?: BlendMode;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface GradientStop {
|
|
24
|
-
offset: number // 0 to 1
|
|
25
|
-
color: string
|
|
24
|
+
offset: number; // 0 to 1
|
|
25
|
+
color: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export interface LinearGradientFill {
|
|
29
|
-
type: 'linear_gradient'
|
|
30
|
-
angle?: number
|
|
31
|
-
stops: GradientStop[]
|
|
32
|
-
opacity?: number
|
|
33
|
-
blendMode?: BlendMode
|
|
29
|
+
type: 'linear_gradient';
|
|
30
|
+
angle?: number;
|
|
31
|
+
stops: GradientStop[];
|
|
32
|
+
opacity?: number;
|
|
33
|
+
blendMode?: BlendMode;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface RadialGradientFill {
|
|
37
|
-
type: 'radial_gradient'
|
|
38
|
-
cx?: number
|
|
39
|
-
cy?: number
|
|
40
|
-
radius?: number
|
|
41
|
-
stops: GradientStop[]
|
|
42
|
-
opacity?: number
|
|
43
|
-
blendMode?: BlendMode
|
|
37
|
+
type: 'radial_gradient';
|
|
38
|
+
cx?: number;
|
|
39
|
+
cy?: number;
|
|
40
|
+
radius?: number;
|
|
41
|
+
stops: GradientStop[];
|
|
42
|
+
opacity?: number;
|
|
43
|
+
blendMode?: BlendMode;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export interface ImageFill {
|
|
47
|
-
type: 'image'
|
|
48
|
-
url: string
|
|
49
|
-
mode?: 'fill' | 'fit' | 'crop' | 'tile' | 'stretch'
|
|
50
|
-
opacity?: number
|
|
51
|
-
exposure?: number
|
|
52
|
-
contrast?: number
|
|
53
|
-
saturation?: number
|
|
54
|
-
temperature?: number // -100 to 100
|
|
55
|
-
tint?: number
|
|
56
|
-
highlights?: number
|
|
57
|
-
shadows?: number
|
|
47
|
+
type: 'image';
|
|
48
|
+
url: string;
|
|
49
|
+
mode?: 'fill' | 'fit' | 'crop' | 'tile' | 'stretch';
|
|
50
|
+
opacity?: number;
|
|
51
|
+
exposure?: number; // -100 to 100
|
|
52
|
+
contrast?: number; // -100 to 100
|
|
53
|
+
saturation?: number; // -100 to 100
|
|
54
|
+
temperature?: number; // -100 to 100
|
|
55
|
+
tint?: number; // -100 to 100
|
|
56
|
+
highlights?: number; // -100 to 100
|
|
57
|
+
shadows?: number; // -100 to 100
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
export type PenFill =
|
|
61
|
-
| SolidFill
|
|
62
|
-
| LinearGradientFill
|
|
63
|
-
| RadialGradientFill
|
|
64
|
-
| ImageFill
|
|
60
|
+
export type PenFill = SolidFill | LinearGradientFill | RadialGradientFill | ImageFill;
|
|
65
61
|
|
|
66
62
|
// --- Stroke ---
|
|
67
63
|
|
|
68
64
|
export interface PenStroke {
|
|
69
|
-
thickness: number | [number, number, number, number]
|
|
70
|
-
align?: 'inside' | 'center' | 'outside'
|
|
71
|
-
join?: 'miter' | 'bevel' | 'round'
|
|
72
|
-
cap?: 'none' | 'round' | 'square'
|
|
73
|
-
dashPattern?: number[]
|
|
74
|
-
|
|
65
|
+
thickness: number | [number, number, number, number];
|
|
66
|
+
align?: 'inside' | 'center' | 'outside';
|
|
67
|
+
join?: 'miter' | 'bevel' | 'round';
|
|
68
|
+
cap?: 'none' | 'round' | 'square';
|
|
69
|
+
dashPattern?: number[];
|
|
70
|
+
dashOffset?: number;
|
|
71
|
+
fill?: PenFill[];
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
// --- Effects ---
|
|
78
75
|
|
|
79
76
|
export interface BlurEffect {
|
|
80
|
-
type: 'blur' | 'background_blur'
|
|
81
|
-
radius: number
|
|
77
|
+
type: 'blur' | 'background_blur';
|
|
78
|
+
radius: number;
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
export interface ShadowEffect {
|
|
85
|
-
type: 'shadow'
|
|
86
|
-
inner?: boolean
|
|
87
|
-
offsetX: number
|
|
88
|
-
offsetY: number
|
|
89
|
-
blur: number
|
|
90
|
-
spread: number
|
|
91
|
-
color: string
|
|
82
|
+
type: 'shadow';
|
|
83
|
+
inner?: boolean;
|
|
84
|
+
offsetX: number;
|
|
85
|
+
offsetY: number;
|
|
86
|
+
blur: number;
|
|
87
|
+
spread: number;
|
|
88
|
+
color: string;
|
|
92
89
|
}
|
|
93
90
|
|
|
94
|
-
export type PenEffect = BlurEffect | ShadowEffect
|
|
91
|
+
export type PenEffect = BlurEffect | ShadowEffect;
|
|
95
92
|
|
|
96
93
|
// --- Text ---
|
|
97
94
|
|
|
98
95
|
export interface StyledTextSegment {
|
|
99
|
-
text: string
|
|
100
|
-
fontFamily?: string
|
|
101
|
-
fontSize?: number
|
|
102
|
-
fontWeight?: number
|
|
103
|
-
fontStyle?: 'normal' | 'italic'
|
|
104
|
-
fill?: string
|
|
105
|
-
underline?: boolean
|
|
106
|
-
strikethrough?: boolean
|
|
107
|
-
href?: string
|
|
96
|
+
text: string;
|
|
97
|
+
fontFamily?: string;
|
|
98
|
+
fontSize?: number;
|
|
99
|
+
fontWeight?: number;
|
|
100
|
+
fontStyle?: 'normal' | 'italic';
|
|
101
|
+
fill?: string;
|
|
102
|
+
underline?: boolean;
|
|
103
|
+
strikethrough?: boolean;
|
|
104
|
+
href?: string;
|
|
108
105
|
}
|
package/src/theme-preset.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import type { VariableDefinition } from './variables.js'
|
|
1
|
+
import type { VariableDefinition } from './variables.js';
|
|
2
2
|
|
|
3
3
|
export interface ThemePreset {
|
|
4
|
-
id: string
|
|
5
|
-
name: string
|
|
6
|
-
themes: Record<string, string[]
|
|
7
|
-
variables: Record<string, VariableDefinition
|
|
8
|
-
createdAt: number
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
themes: Record<string, string[]>;
|
|
7
|
+
variables: Record<string, VariableDefinition>;
|
|
8
|
+
createdAt: number;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export interface ThemePresetFile {
|
|
12
|
-
type: 'openpencil-theme-preset'
|
|
13
|
-
version: '1.0.0'
|
|
14
|
-
name: string
|
|
15
|
-
themes: Record<string, string[]
|
|
16
|
-
variables: Record<string, VariableDefinition
|
|
12
|
+
type: 'openpencil-theme-preset';
|
|
13
|
+
version: '1.0.0';
|
|
14
|
+
name: string;
|
|
15
|
+
themes: Record<string, string[]>;
|
|
16
|
+
variables: Record<string, VariableDefinition>;
|
|
17
17
|
}
|
package/src/uikit.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PenDocument } from './pen.js'
|
|
1
|
+
import type { PenDocument } from './pen.js';
|
|
2
2
|
|
|
3
3
|
export type ComponentCategory =
|
|
4
4
|
| 'buttons'
|
|
@@ -8,35 +8,35 @@ export type ComponentCategory =
|
|
|
8
8
|
| 'layout'
|
|
9
9
|
| 'feedback'
|
|
10
10
|
| 'data-display'
|
|
11
|
-
| 'other'
|
|
11
|
+
| 'other';
|
|
12
12
|
|
|
13
13
|
export interface KitComponent {
|
|
14
14
|
/** Node ID of the reusable FrameNode in the kit document */
|
|
15
|
-
id: string
|
|
15
|
+
id: string;
|
|
16
16
|
/** Display name */
|
|
17
|
-
name: string
|
|
17
|
+
name: string;
|
|
18
18
|
/** Category for organization in the browser */
|
|
19
|
-
category: ComponentCategory
|
|
19
|
+
category: ComponentCategory;
|
|
20
20
|
/** Tags for search */
|
|
21
|
-
tags: string[]
|
|
21
|
+
tags: string[];
|
|
22
22
|
/** Component dimensions for preview sizing */
|
|
23
|
-
width: number
|
|
24
|
-
height: number
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export interface UIKit {
|
|
28
28
|
/** Unique identifier */
|
|
29
|
-
id: string
|
|
29
|
+
id: string;
|
|
30
30
|
/** Display name */
|
|
31
|
-
name: string
|
|
31
|
+
name: string;
|
|
32
32
|
/** Optional description */
|
|
33
|
-
description?: string
|
|
33
|
+
description?: string;
|
|
34
34
|
/** Version string */
|
|
35
|
-
version: string
|
|
35
|
+
version: string;
|
|
36
36
|
/** Whether this is a built-in kit that ships with the app */
|
|
37
|
-
builtIn: boolean
|
|
37
|
+
builtIn: boolean;
|
|
38
38
|
/** Backing PenDocument containing the reusable nodes */
|
|
39
|
-
document: PenDocument
|
|
39
|
+
document: PenDocument;
|
|
40
40
|
/** Extracted component metadata for browsing */
|
|
41
|
-
components: KitComponent[]
|
|
41
|
+
components: KitComponent[];
|
|
42
42
|
}
|
package/src/variables.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export interface VariableDefinition {
|
|
2
|
-
type: 'color' | 'number' | 'boolean' | 'string'
|
|
3
|
-
value: VariableValue
|
|
2
|
+
type: 'color' | 'number' | 'boolean' | 'string';
|
|
3
|
+
value: VariableValue;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
export type VariableValue = string | number | boolean | ThemedValue[]
|
|
6
|
+
export type VariableValue = string | number | boolean | ThemedValue[];
|
|
7
7
|
|
|
8
8
|
export interface ThemedValue {
|
|
9
|
-
value: string | number | boolean
|
|
10
|
-
theme?: Record<string, string
|
|
9
|
+
value: string | number | boolean;
|
|
10
|
+
theme?: Record<string, string>;
|
|
11
11
|
}
|