@zseven-w/pen-types 0.0.1

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 ADDED
@@ -0,0 +1,31 @@
1
+ # @zseven-w/pen-types
2
+
3
+ Type definitions for the [OpenPencil](https://github.com/nicepkg/openpencil) document model.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @zseven-w/pen-types
9
+ ```
10
+
11
+ ## What's Included
12
+
13
+ This package provides all TypeScript types and interfaces for the OpenPencil design file format (`.op`):
14
+
15
+ - **Document model** — `PenDocument`, `PenPage`, `PenNode` and all node types (`FrameNode`, `RectangleNode`, `EllipseNode`, `TextNode`, `ImageNode`, `PathNode`, etc.)
16
+ - **Styles** — `PenFill` (solid, gradient, image), `PenStroke`, `PenEffect` (blur, shadow), `BlendMode`, `StyledTextSegment`
17
+ - **Variables & Themes** — `VariableDefinition`, `VariableValue`, `ThemedValue`
18
+ - **Canvas state** — `ToolType`, `ViewportState`, `SelectionState`, `CanvasInteraction`
19
+ - **UIKit** — `UIKit`, `KitComponent`, `ComponentCategory`
20
+ - **Theme presets** — `ThemePreset`, `ThemePresetFile`
21
+ - **Design spec** — `DesignMdSpec`, `DesignMdColor`, `DesignMdTypography`
22
+
23
+ ## Usage
24
+
25
+ ```ts
26
+ import type { PenDocument, PenNode, FrameNode } from '@zseven-w/pen-types'
27
+ ```
28
+
29
+ ## License
30
+
31
+ MIT
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@zseven-w/pen-types",
3
+ "version": "0.0.1",
4
+ "description": "Type definitions for OpenPencil document model",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./src/index.ts",
9
+ "import": "./src/index.ts"
10
+ }
11
+ },
12
+ "files": [
13
+ "src"
14
+ ],
15
+ "scripts": {
16
+ "typecheck": "tsc --noEmit"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.7.2"
20
+ }
21
+ }
package/src/canvas.ts ADDED
@@ -0,0 +1,31 @@
1
+ export type ToolType =
2
+ | 'select'
3
+ | 'frame'
4
+ | 'rectangle'
5
+ | 'ellipse'
6
+ | 'line'
7
+ | 'polygon'
8
+ | 'path'
9
+ | 'text'
10
+ | 'hand'
11
+
12
+ export interface ViewportState {
13
+ zoom: number
14
+ panX: number
15
+ panY: number
16
+ }
17
+
18
+ export interface SelectionState {
19
+ selectedIds: string[]
20
+ activeId: string | null
21
+ hoveredId: string | null
22
+ enteredFrameId: string | null
23
+ enteredFrameStack: string[]
24
+ }
25
+
26
+ export interface CanvasInteraction {
27
+ isDrawing: boolean
28
+ isPanning: boolean
29
+ isDragging: boolean
30
+ drawStartPoint: { x: number; y: number } | null
31
+ }
@@ -0,0 +1,24 @@
1
+ export interface DesignMdSpec {
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
11
+ }
12
+
13
+ export interface DesignMdColor {
14
+ name: string
15
+ hex: string
16
+ role: string
17
+ }
18
+
19
+ export interface DesignMdTypography {
20
+ fontFamily?: string
21
+ headings?: string
22
+ body?: string
23
+ scale?: string
24
+ }
package/src/index.ts ADDED
@@ -0,0 +1,73 @@
1
+ // Styles
2
+ export type {
3
+ BlendMode,
4
+ SolidFill,
5
+ GradientStop,
6
+ LinearGradientFill,
7
+ RadialGradientFill,
8
+ ImageFill,
9
+ PenFill,
10
+ PenStroke,
11
+ BlurEffect,
12
+ ShadowEffect,
13
+ PenEffect,
14
+ StyledTextSegment,
15
+ } from './styles.js'
16
+
17
+ // Variables
18
+ export type {
19
+ VariableDefinition,
20
+ VariableValue,
21
+ ThemedValue,
22
+ } from './variables.js'
23
+
24
+ // Canvas
25
+ export type {
26
+ ToolType,
27
+ ViewportState,
28
+ SelectionState,
29
+ CanvasInteraction,
30
+ } from './canvas.js'
31
+
32
+ // Document model
33
+ export type {
34
+ PenPage,
35
+ PenDocument,
36
+ PenNodeType,
37
+ SizingBehavior,
38
+ PenNodeBase,
39
+ ContainerProps,
40
+ FrameNode,
41
+ GroupNode,
42
+ RectangleNode,
43
+ EllipseNode,
44
+ LineNode,
45
+ PolygonNode,
46
+ PathNode,
47
+ TextNode,
48
+ ImageFitMode,
49
+ ImageNode,
50
+ IconFontNode,
51
+ RefNode,
52
+ PenNode,
53
+ } from './pen.js'
54
+
55
+ // UIKit
56
+ export type {
57
+ ComponentCategory,
58
+ KitComponent,
59
+ UIKit,
60
+ } from './uikit.js'
61
+
62
+ // Theme presets
63
+ export type {
64
+ ThemePreset,
65
+ ThemePresetFile,
66
+ } from './theme-preset.js'
67
+
68
+ // Design.md
69
+ export type {
70
+ DesignMdSpec,
71
+ DesignMdColor,
72
+ DesignMdTypography,
73
+ } from './design-md.js'
package/src/pen.ts ADDED
@@ -0,0 +1,221 @@
1
+ import type {
2
+ PenFill,
3
+ PenStroke,
4
+ PenEffect,
5
+ StyledTextSegment,
6
+ } from './styles.js'
7
+ import type { VariableDefinition } from './variables.js'
8
+
9
+ // --- Page ---
10
+
11
+ export interface PenPage {
12
+ id: string
13
+ name: string
14
+ children: PenNode[]
15
+ }
16
+
17
+ // --- Document Root ---
18
+
19
+ 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[]
26
+ }
27
+
28
+ // --- Node Types ---
29
+
30
+ export type PenNodeType =
31
+ | 'frame'
32
+ | 'group'
33
+ | 'rectangle'
34
+ | 'ellipse'
35
+ | 'line'
36
+ | 'polygon'
37
+ | 'path'
38
+ | 'text'
39
+ | 'image'
40
+ | 'icon_font'
41
+ | 'ref'
42
+
43
+ export type SizingBehavior = number | 'fit_content' | 'fill_container' | string
44
+
45
+ // --- Base ---
46
+
47
+ 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>
62
+ }
63
+
64
+ // --- Container (shared layout props) ---
65
+
66
+ export interface ContainerProps {
67
+ width?: SizingBehavior
68
+ height?: SizingBehavior
69
+ layout?: 'none' | 'vertical' | 'horizontal'
70
+ gap?: number | string
71
+ padding?:
72
+ | number
73
+ | [number, number]
74
+ | [number, number, number, number]
75
+ | string
76
+ justifyContent?:
77
+ | 'start'
78
+ | 'center'
79
+ | 'end'
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[]
89
+ }
90
+
91
+ // --- Concrete Nodes ---
92
+
93
+ export interface FrameNode extends PenNodeBase, ContainerProps {
94
+ type: 'frame'
95
+ reusable?: boolean
96
+ slot?: string[]
97
+ }
98
+
99
+ export interface GroupNode extends PenNodeBase, ContainerProps {
100
+ type: 'group'
101
+ }
102
+
103
+ export interface RectangleNode extends PenNodeBase, ContainerProps {
104
+ type: 'rectangle'
105
+ }
106
+
107
+ 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[]
118
+ }
119
+
120
+ export interface LineNode extends PenNodeBase {
121
+ type: 'line'
122
+ x2?: number
123
+ y2?: number
124
+ stroke?: PenStroke
125
+ effects?: PenEffect[]
126
+ }
127
+
128
+ 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[]
137
+ }
138
+
139
+ export interface PathNode extends PenNodeBase {
140
+ type: 'path'
141
+ iconId?: string // Iconify icon ID, e.g. "lucide:home"
142
+ d: string
143
+ width?: SizingBehavior
144
+ height?: SizingBehavior
145
+ fill?: PenFill[]
146
+ stroke?: PenStroke
147
+ effects?: PenEffect[]
148
+ }
149
+
150
+ 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'
171
+
172
+ 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 // -100 to 100
181
+ contrast?: number // -100 to 100
182
+ saturation?: number // -100 to 100
183
+ temperature?: number // -100 to 100
184
+ tint?: number // -100 to 100
185
+ highlights?: number // -100 to 100
186
+ shadows?: number // -100 to 100
187
+ imagePrompt?: string // Descriptive prompt for AI image generation (long)
188
+ imageSearchQuery?: string // Short keywords for image search (e.g. "burger fries")
189
+ }
190
+
191
+ 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
199
+ }
200
+
201
+ export interface RefNode extends PenNodeBase {
202
+ type: 'ref'
203
+ ref: string
204
+ descendants?: Record<string, Partial<PenNode>>
205
+ children?: PenNode[]
206
+ }
207
+
208
+ // --- Union ---
209
+
210
+ export type PenNode =
211
+ | FrameNode
212
+ | GroupNode
213
+ | RectangleNode
214
+ | EllipseNode
215
+ | LineNode
216
+ | PolygonNode
217
+ | PathNode
218
+ | TextNode
219
+ | ImageNode
220
+ | IconFontNode
221
+ | RefNode
package/src/styles.ts ADDED
@@ -0,0 +1,108 @@
1
+ export type BlendMode =
2
+ | 'normal'
3
+ | 'darken'
4
+ | 'multiply'
5
+ | 'screen'
6
+ | 'overlay'
7
+ | 'lighten'
8
+ | 'difference'
9
+ | 'hue'
10
+ | 'saturation'
11
+ | 'color'
12
+ | 'luminosity'
13
+
14
+ // --- Fill Types ---
15
+
16
+ export interface SolidFill {
17
+ type: 'solid'
18
+ color: string // #RRGGBB or #RRGGBBAA
19
+ opacity?: number
20
+ blendMode?: BlendMode
21
+ }
22
+
23
+ export interface GradientStop {
24
+ offset: number // 0 to 1
25
+ color: string
26
+ }
27
+
28
+ export interface LinearGradientFill {
29
+ type: 'linear_gradient'
30
+ angle?: number
31
+ stops: GradientStop[]
32
+ opacity?: number
33
+ blendMode?: BlendMode
34
+ }
35
+
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
44
+ }
45
+
46
+ export interface ImageFill {
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
+ }
59
+
60
+ export type PenFill =
61
+ | SolidFill
62
+ | LinearGradientFill
63
+ | RadialGradientFill
64
+ | ImageFill
65
+
66
+ // --- Stroke ---
67
+
68
+ 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
+ fill?: PenFill[]
75
+ }
76
+
77
+ // --- Effects ---
78
+
79
+ export interface BlurEffect {
80
+ type: 'blur' | 'background_blur'
81
+ radius: number
82
+ }
83
+
84
+ export interface ShadowEffect {
85
+ type: 'shadow'
86
+ inner?: boolean
87
+ offsetX: number
88
+ offsetY: number
89
+ blur: number
90
+ spread: number
91
+ color: string
92
+ }
93
+
94
+ export type PenEffect = BlurEffect | ShadowEffect
95
+
96
+ // --- Text ---
97
+
98
+ 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
108
+ }
@@ -0,0 +1,17 @@
1
+ import type { VariableDefinition } from './variables.js'
2
+
3
+ export interface ThemePreset {
4
+ id: string
5
+ name: string
6
+ themes: Record<string, string[]>
7
+ variables: Record<string, VariableDefinition>
8
+ createdAt: number
9
+ }
10
+
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>
17
+ }
package/src/uikit.ts ADDED
@@ -0,0 +1,42 @@
1
+ import type { PenDocument } from './pen.js'
2
+
3
+ export type ComponentCategory =
4
+ | 'buttons'
5
+ | 'inputs'
6
+ | 'cards'
7
+ | 'navigation'
8
+ | 'layout'
9
+ | 'feedback'
10
+ | 'data-display'
11
+ | 'other'
12
+
13
+ export interface KitComponent {
14
+ /** Node ID of the reusable FrameNode in the kit document */
15
+ id: string
16
+ /** Display name */
17
+ name: string
18
+ /** Category for organization in the browser */
19
+ category: ComponentCategory
20
+ /** Tags for search */
21
+ tags: string[]
22
+ /** Component dimensions for preview sizing */
23
+ width: number
24
+ height: number
25
+ }
26
+
27
+ export interface UIKit {
28
+ /** Unique identifier */
29
+ id: string
30
+ /** Display name */
31
+ name: string
32
+ /** Optional description */
33
+ description?: string
34
+ /** Version string */
35
+ version: string
36
+ /** Whether this is a built-in kit that ships with the app */
37
+ builtIn: boolean
38
+ /** Backing PenDocument containing the reusable nodes */
39
+ document: PenDocument
40
+ /** Extracted component metadata for browsing */
41
+ components: KitComponent[]
42
+ }
@@ -0,0 +1,11 @@
1
+ export interface VariableDefinition {
2
+ type: 'color' | 'number' | 'boolean' | 'string'
3
+ value: VariableValue
4
+ }
5
+
6
+ export type VariableValue = string | number | boolean | ThemedValue[]
7
+
8
+ export interface ThemedValue {
9
+ value: string | number | boolean
10
+ theme?: Record<string, string>
11
+ }