@pixerate/editor 0.31.0 → 0.33.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 +2 -1
- package/dist/agent/index.d.cts +3 -2
- package/dist/agent/index.d.ts +3 -2
- package/dist/{arrange-BICutYPk.d.cts → arrange-B6GH5Qcc.d.cts} +2 -216
- package/dist/{arrange-BICutYPk.d.ts → arrange-BPAXGdMm.d.ts} +2 -216
- package/dist/bezier-6KPx3w7Z.d.cts +547 -0
- package/dist/bezier-DJPYiVbA.d.ts +547 -0
- package/dist/{builtins-DxC3aUR2.d.cts → builtins-BLud97fC.d.cts} +1 -1
- package/dist/{builtins-NGxNxQfJ.d.ts → builtins-D0BDyAgw.d.ts} +1 -1
- package/dist/canvas/index.cjs +518 -3684
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +7 -547
- package/dist/canvas/index.d.ts +7 -547
- package/dist/canvas/index.js +126 -3292
- package/dist/canvas/index.js.map +1 -1
- package/dist/chunk-GQTXV2EQ.js +3335 -0
- package/dist/chunk-GQTXV2EQ.js.map +1 -0
- package/dist/chunk-M4XE4UMT.cjs +3335 -0
- package/dist/chunk-M4XE4UMT.cjs.map +1 -0
- package/dist/commands/index.d.cts +3 -2
- package/dist/commands/index.d.ts +3 -2
- package/dist/types-BFkYA4eF.d.cts +217 -0
- package/dist/types-BFkYA4eF.d.ts +217 -0
- package/dist/vector/index.cjs +3471 -0
- package/dist/vector/index.cjs.map +1 -0
- package/dist/vector/index.d.cts +1052 -0
- package/dist/vector/index.d.ts +1052 -0
- package/dist/vector/index.js +3471 -0
- package/dist/vector/index.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
import { C as CanvasGradientDef, A as AffineMatrix, B as BezierPathInput, a as BezierPath, S as SvgGradientDef } from '../bezier-6KPx3w7Z.cjs';
|
|
2
|
+
import { P as Point } from '../types-BFkYA4eF.cjs';
|
|
3
|
+
import { H as HistoryManager } from '../HistoryManager-DeQQMBCj.cjs';
|
|
4
|
+
import { C as CommandSpec } from '../registry-D0GADKc7.cjs';
|
|
5
|
+
import '../types-DsiXGmjo.cjs';
|
|
6
|
+
import '../schema-DYDGdYL9.cjs';
|
|
7
|
+
|
|
8
|
+
/** An affine matrix `[a, b, c, d, e, f]`: `x' = a·x + c·y + e`, `y' = b·x + d·y + f` (SVG order). */
|
|
9
|
+
type VectorMatrix = readonly [number, number, number, number, number, number];
|
|
10
|
+
/** A point in document coordinates. */
|
|
11
|
+
interface Vec2 {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* How an anchor's handles relate:
|
|
17
|
+
* - `corner`: handles move independently (or are absent).
|
|
18
|
+
* - `smooth`: handles stay on one line through the anchor, lengths independent.
|
|
19
|
+
* - `symmetric`: handles stay on one line with equal lengths.
|
|
20
|
+
*/
|
|
21
|
+
type VectorAnchorType = 'corner' | 'smooth' | 'symmetric';
|
|
22
|
+
/**
|
|
23
|
+
* One anchor of a subpath. Handles are absolute document positions; a missing (or `null`)
|
|
24
|
+
* handle sits on the anchor, so a segment with no handles at either end is a straight line.
|
|
25
|
+
*/
|
|
26
|
+
interface VectorAnchor {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
/** Control point of the segment arriving at this anchor. */
|
|
30
|
+
in?: Vec2 | null;
|
|
31
|
+
/** Control point of the segment leaving this anchor. */
|
|
32
|
+
out?: Vec2 | null;
|
|
33
|
+
/** Default `corner`. */
|
|
34
|
+
type?: VectorAnchorType;
|
|
35
|
+
}
|
|
36
|
+
/** A chain of anchors joined by line or cubic Bézier segments. */
|
|
37
|
+
interface VectorSubpath {
|
|
38
|
+
anchors: VectorAnchor[];
|
|
39
|
+
/** Joins the last anchor back to the first. */
|
|
40
|
+
closed: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** A fill or stroke: any CSS colour, a gradient, or `null` / `'none'` for no paint. */
|
|
43
|
+
type VectorPaint = string | CanvasGradientDef | null;
|
|
44
|
+
type VectorFillRule = 'nonzero' | 'evenodd';
|
|
45
|
+
type VectorLineCap = 'butt' | 'round' | 'square';
|
|
46
|
+
type VectorLineJoin = 'miter' | 'round' | 'bevel';
|
|
47
|
+
/** Paint and stroke settings of a path. */
|
|
48
|
+
interface VectorStyle {
|
|
49
|
+
fill?: VectorPaint;
|
|
50
|
+
stroke?: VectorPaint;
|
|
51
|
+
/** Default 1. */
|
|
52
|
+
strokeWidth?: number;
|
|
53
|
+
strokeLinecap?: VectorLineCap;
|
|
54
|
+
strokeLinejoin?: VectorLineJoin;
|
|
55
|
+
strokeDasharray?: number[];
|
|
56
|
+
/** Default `nonzero`. */
|
|
57
|
+
fillRule?: VectorFillRule;
|
|
58
|
+
}
|
|
59
|
+
interface VectorNodeBase {
|
|
60
|
+
id: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
/** Default true. */
|
|
63
|
+
visible?: boolean;
|
|
64
|
+
/** Locked nodes are drawn but cannot be picked or edited on the canvas. */
|
|
65
|
+
locked?: boolean;
|
|
66
|
+
/** 0–1, default 1. */
|
|
67
|
+
opacity?: number;
|
|
68
|
+
/**
|
|
69
|
+
* Placement in the parent's space: a rigid matrix (rotation + translation). Anchors and
|
|
70
|
+
* children are in the node's own space. Omitted for unrotated nodes, whose positions are
|
|
71
|
+
* stored directly. Scale and skew are never stored here — `transformVectorNode` bakes
|
|
72
|
+
* them into the geometry, so stroke widths and the rotated selection box stay exact.
|
|
73
|
+
*/
|
|
74
|
+
transform?: VectorMatrix;
|
|
75
|
+
}
|
|
76
|
+
/** A shape made of one or more subpaths (holes and compound shapes included). */
|
|
77
|
+
interface VectorPathNode extends VectorNodeBase, VectorStyle {
|
|
78
|
+
type: 'path';
|
|
79
|
+
subpaths: VectorSubpath[];
|
|
80
|
+
}
|
|
81
|
+
/** A group of nodes that select and transform together. */
|
|
82
|
+
interface VectorGroupNode extends VectorNodeBase {
|
|
83
|
+
type: 'group';
|
|
84
|
+
children: VectorNode[];
|
|
85
|
+
}
|
|
86
|
+
type VectorNode = VectorPathNode | VectorGroupNode;
|
|
87
|
+
/** A vector drawing: an artboard and its nodes, bottom to top. */
|
|
88
|
+
interface VectorDocument {
|
|
89
|
+
width: number;
|
|
90
|
+
height: number;
|
|
91
|
+
/** Artboard colour (default: transparent). */
|
|
92
|
+
background?: string;
|
|
93
|
+
nodes: VectorNode[];
|
|
94
|
+
}
|
|
95
|
+
/** An axis-aligned rectangle. */
|
|
96
|
+
interface VectorBounds {
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
width: number;
|
|
100
|
+
height: number;
|
|
101
|
+
}
|
|
102
|
+
/** Identifies one anchor of a path node. */
|
|
103
|
+
interface VectorAnchorRef {
|
|
104
|
+
nodeId: string;
|
|
105
|
+
subpath: number;
|
|
106
|
+
index: number;
|
|
107
|
+
}
|
|
108
|
+
/** Identifies one handle of an anchor. */
|
|
109
|
+
interface VectorHandleRef extends VectorAnchorRef {
|
|
110
|
+
handle: 'in' | 'out';
|
|
111
|
+
}
|
|
112
|
+
/** Canvas tools. `direct` edits anchors and handles; the shape tools drag out a new shape. */
|
|
113
|
+
type VectorTool = 'select' | 'direct' | 'pen' | 'pencil' | 'rect' | 'ellipse' | 'polygon' | 'star' | 'line';
|
|
114
|
+
declare const VECTOR_TOOLS: readonly VectorTool[];
|
|
115
|
+
/** Keyboard modifiers held during a pointer event. */
|
|
116
|
+
interface VectorModifiers {
|
|
117
|
+
shift?: boolean;
|
|
118
|
+
alt?: boolean;
|
|
119
|
+
/** Cmd on macOS, Ctrl elsewhere. */
|
|
120
|
+
mod?: boolean;
|
|
121
|
+
}
|
|
122
|
+
/** The eight resize handles of a selection box and the rotation handle. */
|
|
123
|
+
type VectorTransformHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'rotate';
|
|
124
|
+
declare const VECTOR_TRANSFORM_HANDLES: readonly VectorTransformHandle[];
|
|
125
|
+
/** Pan and zoom of the canvas: `screen = document × zoom + (x, y)`. */
|
|
126
|
+
interface VectorViewport {
|
|
127
|
+
/** Screen position of the document origin, in screen pixels. */
|
|
128
|
+
x: number;
|
|
129
|
+
y: number;
|
|
130
|
+
zoom: number;
|
|
131
|
+
}
|
|
132
|
+
/** What the next shape and pen path are drawn with. */
|
|
133
|
+
interface VectorDefaults extends VectorStyle {
|
|
134
|
+
/** Sides of the polygon tool (default 6). */
|
|
135
|
+
polygonSides?: number;
|
|
136
|
+
/** Points of the star tool (default 5). */
|
|
137
|
+
starPoints?: number;
|
|
138
|
+
/** Inner radius of the star tool as a fraction of the outer one (default 0.5). */
|
|
139
|
+
starInnerRatio?: number;
|
|
140
|
+
/** Corner radius of the rectangle tool (default 0). */
|
|
141
|
+
cornerRadius?: number;
|
|
142
|
+
}
|
|
143
|
+
type VectorAlign = 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom';
|
|
144
|
+
type VectorDistribute = 'horizontal' | 'vertical';
|
|
145
|
+
type VectorBooleanOperation = 'union' | 'subtract' | 'intersect' | 'exclude';
|
|
146
|
+
type VectorArrange = 'front' | 'forward' | 'backward' | 'back';
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Vector path geometry: anchors and handles to Bézier paths and back, bounds, hit-testing,
|
|
150
|
+
* affine transforms, segment splitting, anchor-type conversion and shape factories.
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
declare const cloneAnchor: (a: VectorAnchor) => VectorAnchor;
|
|
154
|
+
declare const cloneSubpath: (s: VectorSubpath) => VectorSubpath;
|
|
155
|
+
/** A deep copy of a node (ids kept). */
|
|
156
|
+
declare function cloneVectorNode<T extends VectorNode>(node: T): T;
|
|
157
|
+
declare function cloneVectorDocument(doc: VectorDocument): VectorDocument;
|
|
158
|
+
/** True when the paint draws something (not `null`, `'none'`, `'transparent'` or empty). */
|
|
159
|
+
declare function hasPaint(paint: VectorPaint | undefined): boolean;
|
|
160
|
+
/** Number of segments of a subpath (a closed one has a segment from the last anchor back to the first). */
|
|
161
|
+
declare function subpathSegmentCount(sp: VectorSubpath): number;
|
|
162
|
+
/** Control points of segment `i` of a subpath: 2 for a line, 4 for a cubic. */
|
|
163
|
+
declare function subpathSegment(sp: VectorSubpath, i: number): Point[];
|
|
164
|
+
/** Absolute `M` / `L` / `C` / `Z` commands of subpaths. */
|
|
165
|
+
declare function subpathsToBezier(subpaths: readonly VectorSubpath[]): BezierPath;
|
|
166
|
+
/** The node's outline as Bézier commands. Groups have none. */
|
|
167
|
+
declare function vectorNodeToBezier(node: VectorNode): BezierPath;
|
|
168
|
+
/** SVG path data (`d`) of a path node or subpaths. */
|
|
169
|
+
declare function vectorPathData(input: VectorPathNode | readonly VectorSubpath[], precision?: number): string;
|
|
170
|
+
/** Infers the anchor type from its handles. */
|
|
171
|
+
declare function inferAnchorType(a: VectorAnchor): VectorAnchorType;
|
|
172
|
+
/**
|
|
173
|
+
* Converts SVG path data or Bézier commands to editable subpaths. Quadratics become cubics,
|
|
174
|
+
* arcs become cubics, a closing anchor that repeats the first one is merged into it, and
|
|
175
|
+
* each anchor's type is inferred from its handles.
|
|
176
|
+
*/
|
|
177
|
+
declare function bezierToSubpaths(input: BezierPathInput): VectorSubpath[];
|
|
178
|
+
/** Calls `fn` for every node, depth first, parents before children. */
|
|
179
|
+
declare function walkVectorNodes(nodes: readonly VectorNode[], fn: (node: VectorNode, parent: VectorGroupNode | null, depth: number) => void): void;
|
|
180
|
+
/** Finds a node by id anywhere in the tree. */
|
|
181
|
+
declare function findVectorNode(nodes: readonly VectorNode[], id: string): VectorNode | undefined;
|
|
182
|
+
/** The list that contains node `id` (the root list or a group's children) and its index there. */
|
|
183
|
+
declare function locateVectorNode(nodes: VectorNode[], id: string): {
|
|
184
|
+
list: VectorNode[];
|
|
185
|
+
index: number;
|
|
186
|
+
parent: VectorGroupNode | null;
|
|
187
|
+
} | null;
|
|
188
|
+
/** Every path node in the subtree (a path node itself, or a group's descendants). */
|
|
189
|
+
declare function collectPathNodes(node: VectorNode): VectorPathNode[];
|
|
190
|
+
/** The node's local → parent matrix (identity when it has no `transform`). */
|
|
191
|
+
declare function nodeMatrix(node: VectorNode): AffineMatrix;
|
|
192
|
+
/** A path node's outline in the space `parent` maps to (default: its parent's space). */
|
|
193
|
+
declare function vectorPathWorldBezier(node: VectorPathNode, parent?: AffineMatrix): BezierPath;
|
|
194
|
+
/**
|
|
195
|
+
* Geometric bounds of a node (stroke width not included) in the space `parent` maps to —
|
|
196
|
+
* by default the node's parent space, which for top-level nodes is the document. Null when
|
|
197
|
+
* the node has no points.
|
|
198
|
+
*/
|
|
199
|
+
declare function vectorNodeBounds(node: VectorNode, parent?: AffineMatrix): VectorBounds | null;
|
|
200
|
+
/** The smallest box around all non-null boxes, or null. */
|
|
201
|
+
declare function unionBounds(list: readonly (VectorBounds | null | undefined)[]): VectorBounds | null;
|
|
202
|
+
/** True when two boxes overlap (touching counts). */
|
|
203
|
+
declare function boundsIntersect(a: VectorBounds, b: VectorBounds): boolean;
|
|
204
|
+
/** A box from two corners in any order. */
|
|
205
|
+
declare function boundsFromPoints(a: Vec2, b: Vec2): VectorBounds;
|
|
206
|
+
/**
|
|
207
|
+
* Whether `p` hits a node: inside its fill (when it has one) or within half its stroke
|
|
208
|
+
* width plus `tolerance` of its outline. A group is hit when any visible child is.
|
|
209
|
+
*/
|
|
210
|
+
declare function hitTestVectorNode(node: VectorNode, p: Vec2, tolerance?: number, parent?: AffineMatrix): boolean;
|
|
211
|
+
/** The topmost node hit by `p` (skipping hidden and locked ones), or null. */
|
|
212
|
+
declare function hitTestVectorNodes(nodes: readonly VectorNode[], p: Vec2, tolerance?: number): VectorNode | null;
|
|
213
|
+
interface NearestSegment {
|
|
214
|
+
subpath: number;
|
|
215
|
+
segment: number;
|
|
216
|
+
/** Curve parameter 0–1 on the segment. */
|
|
217
|
+
t: number;
|
|
218
|
+
point: Vec2;
|
|
219
|
+
distance: number;
|
|
220
|
+
}
|
|
221
|
+
/** The closest point on any segment of a path node (`p` and the result are in the node's own space). */
|
|
222
|
+
declare function nearestSegment(node: VectorPathNode, p: Vec2): NearestSegment | null;
|
|
223
|
+
declare const IDENTITY_MATRIX: AffineMatrix;
|
|
224
|
+
/** `a` then `b` (apply `a` first). */
|
|
225
|
+
declare function multiplyMatrices(a: AffineMatrix, b: AffineMatrix): AffineMatrix;
|
|
226
|
+
declare const translateMatrix: (dx: number, dy: number) => AffineMatrix;
|
|
227
|
+
declare function scaleMatrix(sx: number, sy: number, origin?: Vec2): AffineMatrix;
|
|
228
|
+
/** Rotation by `degrees` clockwise (y points down) around `origin`. */
|
|
229
|
+
declare function rotateMatrix(degrees: number, origin?: Vec2): AffineMatrix;
|
|
230
|
+
declare function applyMatrix(m: AffineMatrix, p: Vec2): Vec2;
|
|
231
|
+
declare function isIdentityMatrix(m: AffineMatrix, eps?: number): boolean;
|
|
232
|
+
/** The inverse matrix (the identity for a singular one). */
|
|
233
|
+
declare function invertMatrix(m: AffineMatrix): AffineMatrix;
|
|
234
|
+
/** Applies only the linear part (for directions and offsets). */
|
|
235
|
+
declare function applyMatrixLinear(m: AffineMatrix, v: Vec2): Vec2;
|
|
236
|
+
/**
|
|
237
|
+
* The rotation of a matrix in degrees, clockwise (y down), in (−180, 180]. For a mirrored
|
|
238
|
+
* matrix it is the rotation left after undoing a horizontal flip, so a flipped rotated shape
|
|
239
|
+
* keeps its angle.
|
|
240
|
+
*/
|
|
241
|
+
declare function matrixRotation(m: AffineMatrix): number;
|
|
242
|
+
/**
|
|
243
|
+
* Splits `m` into `residual` then `rigid` (`m = residual × rigid` in apply order): `rigid` is
|
|
244
|
+
* the nearest rotation plus the translation, `residual` the linear rest (scale, skew,
|
|
245
|
+
* reflection). An unrotated `m` gives an identity `rigid` and everything in `residual`.
|
|
246
|
+
*/
|
|
247
|
+
declare function decomposeRigid(m: AffineMatrix): {
|
|
248
|
+
rigid: AffineMatrix;
|
|
249
|
+
residual: AffineMatrix;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* A copy of the node moved by `m`, given in its parent's space. The node's rotation and
|
|
253
|
+
* translation go to its rigid `transform`; scale and skew are baked into its anchors (or its
|
|
254
|
+
* children), so the result draws exactly as `m` says. Stroke widths are kept.
|
|
255
|
+
*/
|
|
256
|
+
declare function transformVectorNode<T extends VectorNode>(node: T, m: AffineMatrix): T;
|
|
257
|
+
/** The node's rotation in degrees (from its `transform` and its ancestors' when `parent` is given). */
|
|
258
|
+
declare function vectorNodeRotation(node: VectorNode, parent?: AffineMatrix): number;
|
|
259
|
+
/** The local → document matrix of node `id` (ancestors' transforms included), or null. */
|
|
260
|
+
declare function vectorWorldMatrix(nodes: readonly VectorNode[], id: string): AffineMatrix | null;
|
|
261
|
+
/** The parent space → document matrix of node `id` (identity for top-level nodes), or null. */
|
|
262
|
+
declare function vectorParentMatrix(nodes: readonly VectorNode[], id: string): AffineMatrix | null;
|
|
263
|
+
/** Moves an anchor and its handles by (dx, dy). */
|
|
264
|
+
declare function translateAnchor(a: VectorAnchor, dx: number, dy: number): VectorAnchor;
|
|
265
|
+
/**
|
|
266
|
+
* Moves one handle to `pos`, keeping the other in line for `smooth` anchors and mirrored for
|
|
267
|
+
* `symmetric` ones. With `breakHandles` the anchor becomes a `corner` and only this handle moves.
|
|
268
|
+
*/
|
|
269
|
+
declare function moveAnchorHandle(a: VectorAnchor, side: 'in' | 'out', pos: Vec2, breakHandles?: boolean): VectorAnchor;
|
|
270
|
+
/**
|
|
271
|
+
* Converts anchor `index` of a subpath. `corner` removes both handles; `smooth` and
|
|
272
|
+
* `symmetric` keep existing handles in line, or add handles a third of the way to the
|
|
273
|
+
* neighbours along the line between them.
|
|
274
|
+
*/
|
|
275
|
+
declare function convertAnchor(sp: VectorSubpath, index: number, type: VectorAnchorType): VectorSubpath;
|
|
276
|
+
/**
|
|
277
|
+
* Inserts an anchor on segment `segment` at parameter `t` without changing the curve's
|
|
278
|
+
* shape (de Casteljau split). Returns the new subpath and the new anchor's index.
|
|
279
|
+
*/
|
|
280
|
+
declare function insertAnchor(sp: VectorSubpath, segment: number, t: number): {
|
|
281
|
+
subpath: VectorSubpath;
|
|
282
|
+
index: number;
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Removes anchors from a subpath. An open subpath with fewer than 2 anchors left, or a
|
|
286
|
+
* closed one with fewer than 2, is returned with whatever remains (callers drop empty ones).
|
|
287
|
+
*/
|
|
288
|
+
declare function removeAnchors(sp: VectorSubpath, indices: readonly number[]): VectorSubpath;
|
|
289
|
+
/** Reverses a subpath's direction (handles swap sides). */
|
|
290
|
+
declare function reverseSubpath(sp: VectorSubpath): VectorSubpath;
|
|
291
|
+
/** Handle length that makes a cubic approximate a quarter circle. */
|
|
292
|
+
declare const KAPPA = 0.5522847498307936;
|
|
293
|
+
/** A rectangle, optionally with rounded corners (clamped to half the shorter side). */
|
|
294
|
+
declare function rectSubpath(b: VectorBounds, radius?: number): VectorSubpath;
|
|
295
|
+
/** An ellipse inscribed in a box (four smooth anchors). */
|
|
296
|
+
declare function ellipseSubpath(b: VectorBounds): VectorSubpath;
|
|
297
|
+
/** A regular polygon inscribed in a box, first vertex at the top. */
|
|
298
|
+
declare function polygonSubpath(b: VectorBounds, sides?: number): VectorSubpath;
|
|
299
|
+
/** A star inscribed in a box, first point at the top. `innerRatio` is inner / outer radius. */
|
|
300
|
+
declare function starSubpath(b: VectorBounds, points?: number, innerRatio?: number): VectorSubpath;
|
|
301
|
+
/** A straight open line. */
|
|
302
|
+
declare function lineSubpath(a: Vec2, b: Vec2): VectorSubpath;
|
|
303
|
+
/** Rounds every coordinate to `precision` decimals (stable JSON and SVG output). */
|
|
304
|
+
declare function roundSubpaths(subpaths: readonly VectorSubpath[], precision?: number): VectorSubpath[];
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* SVG export and import for vector documents. Import is DOM-free (works in Node and
|
|
308
|
+
* workers): it reads `<svg>`, `<g>`, `<path>`, `<rect>`, `<circle>`, `<ellipse>`, `<line>`,
|
|
309
|
+
* `<polyline>`, `<polygon>`, linear / radial gradients, presentation attributes, `style`
|
|
310
|
+
* declarations and `transform`s. Text, images, clip paths, masks, filters and `<use>` are
|
|
311
|
+
* skipped.
|
|
312
|
+
*/
|
|
313
|
+
|
|
314
|
+
interface VectorSvgExportOptions {
|
|
315
|
+
/** Decimal places for coordinates (default 3). */
|
|
316
|
+
precision?: number;
|
|
317
|
+
/** Draw `document.background` as a full-size rect (default true). */
|
|
318
|
+
background?: boolean;
|
|
319
|
+
/** Include hidden nodes with `display="none"` instead of leaving them out (default false). */
|
|
320
|
+
includeHidden?: boolean;
|
|
321
|
+
/** Prefix for gradient ids (default `vg-`), to keep ids unique when several SVGs share a page. */
|
|
322
|
+
idPrefix?: string;
|
|
323
|
+
/** Indent nested elements (default true). */
|
|
324
|
+
pretty?: boolean;
|
|
325
|
+
}
|
|
326
|
+
/** A paint as an SVG attribute value plus the gradient def it needs (if any). */
|
|
327
|
+
declare function vectorPaintToSvg(paint: VectorPaint | undefined, gradientId: string): {
|
|
328
|
+
value: string;
|
|
329
|
+
gradient: SvgGradientDef | null;
|
|
330
|
+
};
|
|
331
|
+
/** Serializes a vector document as a standalone SVG string. */
|
|
332
|
+
declare function vectorDocumentToSvg(doc: VectorDocument, options?: VectorSvgExportOptions): string;
|
|
333
|
+
interface VectorSvgImportOptions {
|
|
334
|
+
/** Makes ids for elements without a usable `id` (default `node-1`, `node-2`, …). */
|
|
335
|
+
createId?: () => string;
|
|
336
|
+
}
|
|
337
|
+
/** Parses an SVG `transform` attribute into one matrix. */
|
|
338
|
+
declare function parseSvgTransform(value: string | undefined): AffineMatrix;
|
|
339
|
+
/** Parses SVG markup into a vector document. Throws when there is no `<svg>` element. */
|
|
340
|
+
declare function parseVectorSvg(svg: string, options?: VectorSvgImportOptions): VectorDocument;
|
|
341
|
+
|
|
342
|
+
interface VectorSnapOptions {
|
|
343
|
+
/** Grid size in document units (0 = off, default 0). */
|
|
344
|
+
grid: number;
|
|
345
|
+
}
|
|
346
|
+
interface VectorEditorOptions {
|
|
347
|
+
document?: VectorDocument;
|
|
348
|
+
/** History to record into, e.g. one shared with the rest of the app (default: a new one). */
|
|
349
|
+
history?: HistoryManager;
|
|
350
|
+
/** Starting tool (default `select`). */
|
|
351
|
+
tool?: VectorTool;
|
|
352
|
+
/** Style and settings for new shapes and paths. */
|
|
353
|
+
defaults?: VectorDefaults;
|
|
354
|
+
viewport?: Partial<VectorViewport>;
|
|
355
|
+
snap?: Partial<VectorSnapOptions>;
|
|
356
|
+
/** Pick distance for anchors, handles and outlines in screen pixels (default 6). */
|
|
357
|
+
hitRadius?: number;
|
|
358
|
+
/** Id factory for new nodes. */
|
|
359
|
+
createId?: () => string;
|
|
360
|
+
/** Called after the document changes (edits, undo, redo, load). */
|
|
361
|
+
onChange?: (document: VectorDocument) => void;
|
|
362
|
+
}
|
|
363
|
+
/** A pointer event as the controller needs it. Positions are in screen pixels relative to the canvas. */
|
|
364
|
+
interface VectorPointerInput extends VectorModifiers {
|
|
365
|
+
/** 0 = primary, 1 = middle (pans), 2 = secondary. Default 0. */
|
|
366
|
+
button?: number;
|
|
367
|
+
}
|
|
368
|
+
/** A key press as the controller needs it. */
|
|
369
|
+
interface VectorKeyInput extends VectorModifiers {
|
|
370
|
+
/** `KeyboardEvent.key`. */
|
|
371
|
+
key: string;
|
|
372
|
+
}
|
|
373
|
+
/** What the pointer is over. */
|
|
374
|
+
interface VectorHover {
|
|
375
|
+
nodeId: string | null;
|
|
376
|
+
anchor?: VectorAnchorRef;
|
|
377
|
+
handle?: VectorHandleRef;
|
|
378
|
+
segment?: {
|
|
379
|
+
nodeId: string;
|
|
380
|
+
subpath: number;
|
|
381
|
+
segment: number;
|
|
382
|
+
t: number;
|
|
383
|
+
point: Vec2;
|
|
384
|
+
};
|
|
385
|
+
transformHandle?: VectorTransformHandle;
|
|
386
|
+
}
|
|
387
|
+
/** The drag in progress, for overlays and status text. */
|
|
388
|
+
type VectorInteraction = {
|
|
389
|
+
type: 'marquee';
|
|
390
|
+
bounds: VectorBounds;
|
|
391
|
+
} | {
|
|
392
|
+
type: 'move';
|
|
393
|
+
dx: number;
|
|
394
|
+
dy: number;
|
|
395
|
+
} | {
|
|
396
|
+
type: 'scale';
|
|
397
|
+
handle: VectorTransformHandle;
|
|
398
|
+
sx: number;
|
|
399
|
+
sy: number;
|
|
400
|
+
} | {
|
|
401
|
+
type: 'rotate';
|
|
402
|
+
angle: number;
|
|
403
|
+
} | {
|
|
404
|
+
type: 'anchors';
|
|
405
|
+
dx: number;
|
|
406
|
+
dy: number;
|
|
407
|
+
} | {
|
|
408
|
+
type: 'handle';
|
|
409
|
+
ref: VectorHandleRef;
|
|
410
|
+
} | {
|
|
411
|
+
type: 'draw';
|
|
412
|
+
tool: VectorTool;
|
|
413
|
+
nodeId: string;
|
|
414
|
+
bounds: VectorBounds;
|
|
415
|
+
} | {
|
|
416
|
+
type: 'pen';
|
|
417
|
+
nodeId: string;
|
|
418
|
+
} | {
|
|
419
|
+
type: 'pan';
|
|
420
|
+
};
|
|
421
|
+
/** A snapshot of the editor. The same object is returned until something changes. */
|
|
422
|
+
interface VectorEditorState {
|
|
423
|
+
document: VectorDocument;
|
|
424
|
+
tool: VectorTool;
|
|
425
|
+
/** Selected node ids (top-level nodes or group members picked with the direct tool). */
|
|
426
|
+
selection: readonly string[];
|
|
427
|
+
/** Selected anchors (direct tool). */
|
|
428
|
+
selectedAnchors: readonly VectorAnchorRef[];
|
|
429
|
+
/** The path whose anchors are being edited, if any. */
|
|
430
|
+
editingId: string | null;
|
|
431
|
+
/** Axis-aligned bounds of the selected nodes in document units. */
|
|
432
|
+
selectionBounds: VectorBounds | null;
|
|
433
|
+
/** The selection box, rotated with the selection when its nodes share a rotation. */
|
|
434
|
+
selectionFrame: VectorSelectionFrame | null;
|
|
435
|
+
viewport: VectorViewport;
|
|
436
|
+
defaults: Readonly<Required<Pick<VectorDefaults, 'polygonSides' | 'starPoints' | 'starInnerRatio' | 'cornerRadius'>> & VectorStyle>;
|
|
437
|
+
snap: VectorSnapOptions;
|
|
438
|
+
hitRadius: number;
|
|
439
|
+
hover: VectorHover | null;
|
|
440
|
+
/** A CSS cursor for the canvas. */
|
|
441
|
+
cursor: string;
|
|
442
|
+
interaction: VectorInteraction | null;
|
|
443
|
+
/** The path the pen tool is adding to. */
|
|
444
|
+
pen: {
|
|
445
|
+
nodeId: string;
|
|
446
|
+
subpath: number;
|
|
447
|
+
} | null;
|
|
448
|
+
/** Last pointer position in document units (for the pen preview), or null when outside. */
|
|
449
|
+
pointer: Vec2 | null;
|
|
450
|
+
canUndo: boolean;
|
|
451
|
+
canRedo: boolean;
|
|
452
|
+
undoLabel: string;
|
|
453
|
+
redoLabel: string;
|
|
454
|
+
/** Bumped on every change. */
|
|
455
|
+
revision: number;
|
|
456
|
+
}
|
|
457
|
+
type VectorActionResult = {
|
|
458
|
+
ok: true;
|
|
459
|
+
} | {
|
|
460
|
+
ok: false;
|
|
461
|
+
message: string;
|
|
462
|
+
};
|
|
463
|
+
interface AddVectorNodeOptions {
|
|
464
|
+
/** Group to add into (default: the root). */
|
|
465
|
+
parentId?: string;
|
|
466
|
+
/** Position in the parent's list (default: on top). */
|
|
467
|
+
index?: number;
|
|
468
|
+
/** Select the new node (default true). */
|
|
469
|
+
select?: boolean;
|
|
470
|
+
/** Undo step label. */
|
|
471
|
+
label?: string;
|
|
472
|
+
}
|
|
473
|
+
/** A shape description for `addShape` (and the agent command of the same name). */
|
|
474
|
+
interface VectorShapeInput extends VectorStyle {
|
|
475
|
+
kind: 'rect' | 'ellipse' | 'polygon' | 'star' | 'line';
|
|
476
|
+
x: number;
|
|
477
|
+
y: number;
|
|
478
|
+
width: number;
|
|
479
|
+
height: number;
|
|
480
|
+
cornerRadius?: number;
|
|
481
|
+
sides?: number;
|
|
482
|
+
points?: number;
|
|
483
|
+
innerRatio?: number;
|
|
484
|
+
name?: string;
|
|
485
|
+
}
|
|
486
|
+
/** Clipboard payload type written by `copy()`. */
|
|
487
|
+
declare const VECTOR_CLIPBOARD_TYPE = "pixerate/vector";
|
|
488
|
+
/** Screen distance of the rotation handle above the selection box. */
|
|
489
|
+
declare const VECTOR_ROTATE_HANDLE_OFFSET = 24;
|
|
490
|
+
/** Ids of the path nodes whose anchors are shown (the edited path plus selected paths). */
|
|
491
|
+
declare function getEditablePathIds(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen'>): string[];
|
|
492
|
+
/** Every anchor drawn on the canvas. */
|
|
493
|
+
declare function getVisibleAnchors(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen'>): VectorAnchorRef[];
|
|
494
|
+
/**
|
|
495
|
+
* Handles drawn on the canvas: those of selected anchors and of the segments next to them
|
|
496
|
+
* (direct tool), or of the last anchor of the pen path.
|
|
497
|
+
*/
|
|
498
|
+
declare function getVisibleHandles(state: Pick<VectorEditorState, 'document' | 'selection' | 'editingId' | 'tool' | 'pen' | 'selectedAnchors'>): VectorHandleRef[];
|
|
499
|
+
/** Looks up an anchor (positions in its node's own space). */
|
|
500
|
+
declare function getAnchor(doc: VectorDocument, ref: VectorAnchorRef): VectorAnchor | undefined;
|
|
501
|
+
/** An anchor with its position and handles in document space (its node's transforms applied). */
|
|
502
|
+
declare function getAnchorWorld(doc: VectorDocument, ref: VectorAnchorRef): {
|
|
503
|
+
x: number;
|
|
504
|
+
y: number;
|
|
505
|
+
in?: Vec2;
|
|
506
|
+
out?: Vec2;
|
|
507
|
+
type?: VectorAnchorType;
|
|
508
|
+
} | undefined;
|
|
509
|
+
/**
|
|
510
|
+
* The selection box: `angle` is the shared rotation of the selected nodes (0 when they
|
|
511
|
+
* differ), `x / y / width / height` the bounds in the box's own frame (document space
|
|
512
|
+
* rotated by −angle). A frame point `q` is at `rotateMatrix(angle)` applied to `q`.
|
|
513
|
+
*/
|
|
514
|
+
interface VectorSelectionFrame {
|
|
515
|
+
x: number;
|
|
516
|
+
y: number;
|
|
517
|
+
width: number;
|
|
518
|
+
height: number;
|
|
519
|
+
angle: number;
|
|
520
|
+
}
|
|
521
|
+
/** The selection frame of nodes `ids` (see `VectorSelectionFrame`), or null when they have no points. */
|
|
522
|
+
declare function getVectorSelectionFrame(doc: VectorDocument, ids: readonly string[]): VectorSelectionFrame | null;
|
|
523
|
+
/** Screen positions of the selection box handles for a box given in screen pixels. */
|
|
524
|
+
declare function getTransformHandlePositions(box: VectorBounds): {
|
|
525
|
+
handle: VectorTransformHandle;
|
|
526
|
+
x: number;
|
|
527
|
+
y: number;
|
|
528
|
+
}[];
|
|
529
|
+
/**
|
|
530
|
+
* Screen geometry of a selection box. `box` and each handle's `x / y` are the unrotated box
|
|
531
|
+
* in screen pixels; draw them inside a group with `transform` (a `rotate(angle cx cy)`),
|
|
532
|
+
* or use `screen` for the true on-screen positions (hit-testing).
|
|
533
|
+
*/
|
|
534
|
+
interface VectorSelectionBoxGeometry {
|
|
535
|
+
box: VectorBounds;
|
|
536
|
+
angle: number;
|
|
537
|
+
transform: string;
|
|
538
|
+
handles: {
|
|
539
|
+
handle: VectorTransformHandle;
|
|
540
|
+
x: number;
|
|
541
|
+
y: number;
|
|
542
|
+
screen: Vec2;
|
|
543
|
+
}[];
|
|
544
|
+
/** The four corners on screen, clockwise from the top left. */
|
|
545
|
+
corners: Vec2[];
|
|
546
|
+
}
|
|
547
|
+
declare function getVectorSelectionBoxGeometry(frame: VectorSelectionFrame, viewport: VectorViewport): VectorSelectionBoxGeometry;
|
|
548
|
+
/** The resize cursor for a handle of a box rotated by `angle` degrees. */
|
|
549
|
+
declare function vectorResizeCursor(handle: VectorTransformHandle, angle?: number): string;
|
|
550
|
+
declare function vectorToolLabel(tool: VectorTool): string;
|
|
551
|
+
/** Keyboard shortcut of each tool (single letters, no modifiers). */
|
|
552
|
+
declare const VECTOR_TOOL_SHORTCUTS: Readonly<Record<VectorTool, string>>;
|
|
553
|
+
/** Short glyphs for tool buttons (pair them with `vectorToolLabel` for the accessible name). */
|
|
554
|
+
declare const VECTOR_TOOL_GLYPHS: Readonly<Record<VectorTool, string>>;
|
|
555
|
+
/**
|
|
556
|
+
* Headless state for a vector editor: a document of paths and groups, select / direct /
|
|
557
|
+
* pen / pencil / shape tools driven by pointer and key events, undo, transforms, boolean
|
|
558
|
+
* operations, alignment, grouping, SVG import / export and a viewport. Any UI forwards
|
|
559
|
+
* pointer events (in canvas pixels) and renders `getState()`; `getState()` returns the same
|
|
560
|
+
* object until something changes, so it works with `useSyncExternalStore` and Svelte runes.
|
|
561
|
+
*/
|
|
562
|
+
declare class VectorEditorController {
|
|
563
|
+
readonly history: HistoryManager;
|
|
564
|
+
private doc;
|
|
565
|
+
private selection;
|
|
566
|
+
private anchors;
|
|
567
|
+
private editingId;
|
|
568
|
+
private pen;
|
|
569
|
+
private tool;
|
|
570
|
+
private defaults;
|
|
571
|
+
private viewport;
|
|
572
|
+
private snap;
|
|
573
|
+
private hitRadius;
|
|
574
|
+
private hover;
|
|
575
|
+
private pointer;
|
|
576
|
+
private drag;
|
|
577
|
+
private interaction;
|
|
578
|
+
private spaceHeld;
|
|
579
|
+
private clipboard;
|
|
580
|
+
private readonly createId;
|
|
581
|
+
private readonly onChange?;
|
|
582
|
+
private readonly listeners;
|
|
583
|
+
private readonly cleanups;
|
|
584
|
+
private applying;
|
|
585
|
+
/** Set while a commit records a change that was already applied, so `restore` still reports it. */
|
|
586
|
+
private committing;
|
|
587
|
+
private revision;
|
|
588
|
+
private state;
|
|
589
|
+
constructor(options?: VectorEditorOptions);
|
|
590
|
+
/** Stops listening to the history. */
|
|
591
|
+
destroy(): void;
|
|
592
|
+
/** The current snapshot (stable until the next change). */
|
|
593
|
+
getState: () => VectorEditorState;
|
|
594
|
+
/** Calls `listener` after every change. Returns an unsubscribe function. */
|
|
595
|
+
subscribe: (listener: (state: VectorEditorState) => void) => (() => void);
|
|
596
|
+
getDocument(): VectorDocument;
|
|
597
|
+
getNode(id: string): VectorNode | undefined;
|
|
598
|
+
private computeState;
|
|
599
|
+
private notify;
|
|
600
|
+
private boundsOf;
|
|
601
|
+
private frameOf;
|
|
602
|
+
/** Local → document matrix of a node in `doc` (default: the current document). */
|
|
603
|
+
private worldOf;
|
|
604
|
+
/** A document point in node `id`'s own space. */
|
|
605
|
+
private toLocal;
|
|
606
|
+
private cursorFor;
|
|
607
|
+
private snapshot;
|
|
608
|
+
private restore;
|
|
609
|
+
/** Records the change from `before` to the current state as one undo step. */
|
|
610
|
+
private commit;
|
|
611
|
+
/** Runs `fn` (which updates fields) and records it as one undo step. */
|
|
612
|
+
private change;
|
|
613
|
+
private setDoc;
|
|
614
|
+
private replaceNode;
|
|
615
|
+
private freshIds;
|
|
616
|
+
private newId;
|
|
617
|
+
screenToDocument(p: Vec2): Vec2;
|
|
618
|
+
documentToScreen(p: Vec2): Vec2;
|
|
619
|
+
setViewport(viewport: Partial<VectorViewport>): void;
|
|
620
|
+
/** Zooms by `factor` keeping the document point under `screen` in place. */
|
|
621
|
+
zoomAt(screen: Vec2, factor: number): void;
|
|
622
|
+
panBy(dx: number, dy: number): void;
|
|
623
|
+
/** Fits the artboard (or `bounds`) into a canvas of `size` screen pixels. */
|
|
624
|
+
zoomToFit(size: {
|
|
625
|
+
width: number;
|
|
626
|
+
height: number;
|
|
627
|
+
}, options?: {
|
|
628
|
+
padding?: number;
|
|
629
|
+
bounds?: VectorBounds;
|
|
630
|
+
}): void;
|
|
631
|
+
/** Zoom at the canvas centre-ish: positive `delta` zooms out (wheel convention). Use with `wheel`. */
|
|
632
|
+
wheel(screen: Vec2, delta: {
|
|
633
|
+
x: number;
|
|
634
|
+
y: number;
|
|
635
|
+
}, mods?: VectorModifiers): void;
|
|
636
|
+
setTool(tool: VectorTool): void;
|
|
637
|
+
setDefaults(defaults: Partial<VectorDefaults>): void;
|
|
638
|
+
setSnap(snap: Partial<VectorSnapOptions>): void;
|
|
639
|
+
private snapPoint;
|
|
640
|
+
private snapDelta;
|
|
641
|
+
/** Replaces the document. Undoable unless `{ history: false }` (then history is cleared). */
|
|
642
|
+
setDocument(doc: VectorDocument, options?: {
|
|
643
|
+
history?: boolean;
|
|
644
|
+
label?: string;
|
|
645
|
+
}): void;
|
|
646
|
+
/** Changes the artboard size or background. */
|
|
647
|
+
setArtboard(patch: Partial<Pick<VectorDocument, 'width' | 'height' | 'background'>>): void;
|
|
648
|
+
/** Replaces the document with parsed SVG markup (one undo step). */
|
|
649
|
+
loadSvg(svg: string, options?: {
|
|
650
|
+
history?: boolean;
|
|
651
|
+
}): VectorActionResult;
|
|
652
|
+
/** Adds the nodes of SVG markup on top of the document, selected. */
|
|
653
|
+
importSvg(svg: string): VectorActionResult;
|
|
654
|
+
toSvg(options?: VectorSvgExportOptions): string;
|
|
655
|
+
toJSON(): VectorDocument;
|
|
656
|
+
/** Adds a node (ids are kept unless taken). Returns its id. */
|
|
657
|
+
addNode(node: VectorNode, options?: AddVectorNodeOptions): string;
|
|
658
|
+
/** Builds a path node for a shape with the current defaults (not added). */
|
|
659
|
+
createShapeNode(input: VectorShapeInput): VectorPathNode;
|
|
660
|
+
/** Adds a shape drawn with the current defaults. Returns its id. */
|
|
661
|
+
addShape(input: VectorShapeInput, options?: AddVectorNodeOptions): string;
|
|
662
|
+
/** Adds a path from SVG path data. Returns its id. */
|
|
663
|
+
addPath(d: string, style?: VectorStyle & {
|
|
664
|
+
name?: string;
|
|
665
|
+
}, options?: AddVectorNodeOptions): string;
|
|
666
|
+
private styleFor;
|
|
667
|
+
/** Merges `patch` into a node (paths take style and geometry; groups name / visibility / opacity). */
|
|
668
|
+
updateNode(id: string, patch: Partial<VectorPathNode> | Partial<VectorGroupNode>, options?: {
|
|
669
|
+
label?: string;
|
|
670
|
+
}): VectorActionResult;
|
|
671
|
+
/** Applies style (and opacity) to the selected (or given) nodes; groups pass it to every path inside. */
|
|
672
|
+
setStyle(style: VectorStyle & {
|
|
673
|
+
opacity?: number;
|
|
674
|
+
}, ids?: readonly string[], options?: {
|
|
675
|
+
label?: string;
|
|
676
|
+
}): VectorActionResult;
|
|
677
|
+
rename(id: string, name: string): VectorActionResult;
|
|
678
|
+
setVisible(id: string, visible: boolean): VectorActionResult;
|
|
679
|
+
setLocked(id: string, locked: boolean): VectorActionResult;
|
|
680
|
+
/** Removes nodes by id. */
|
|
681
|
+
removeNodes(ids: readonly string[], options?: {
|
|
682
|
+
label?: string;
|
|
683
|
+
}): VectorActionResult;
|
|
684
|
+
select(ids: readonly string[], options?: {
|
|
685
|
+
add?: boolean;
|
|
686
|
+
toggle?: boolean;
|
|
687
|
+
}): void;
|
|
688
|
+
selectAll(): void;
|
|
689
|
+
clearSelection(): void;
|
|
690
|
+
selectAnchors(refs: readonly VectorAnchorRef[], options?: {
|
|
691
|
+
add?: boolean;
|
|
692
|
+
}): void;
|
|
693
|
+
/** Top-level ids of the selection (members of selected groups are covered by the group). */
|
|
694
|
+
private selectedRoots;
|
|
695
|
+
/** `m`, given in a frame rotated by `angle`, as a document-space matrix. */
|
|
696
|
+
private inFrame;
|
|
697
|
+
/** Applies a matrix to the selected (or given) nodes as one undo step. */
|
|
698
|
+
transform(matrix: AffineMatrix, options?: {
|
|
699
|
+
ids?: readonly string[];
|
|
700
|
+
label?: string;
|
|
701
|
+
}): VectorActionResult;
|
|
702
|
+
/** `doc` with nodes `ids` moved by `m`, given in document space. */
|
|
703
|
+
private transformed;
|
|
704
|
+
translate(dx: number, dy: number, options?: {
|
|
705
|
+
ids?: readonly string[];
|
|
706
|
+
}): VectorActionResult;
|
|
707
|
+
/** The selection frame's centre in document space. */
|
|
708
|
+
private frameCenter;
|
|
709
|
+
/**
|
|
710
|
+
* Scales along the selection box's own axes (rotated with the selection when its nodes
|
|
711
|
+
* share a rotation) around `origin` (default: the box centre, in document space).
|
|
712
|
+
*/
|
|
713
|
+
scale(sx: number, sy?: number, options?: {
|
|
714
|
+
ids?: readonly string[];
|
|
715
|
+
origin?: Vec2;
|
|
716
|
+
}): VectorActionResult;
|
|
717
|
+
/** Rotates clockwise by `degrees` around `origin` (default: the selection centre). */
|
|
718
|
+
rotate(degrees: number, options?: {
|
|
719
|
+
ids?: readonly string[];
|
|
720
|
+
origin?: Vec2;
|
|
721
|
+
}): VectorActionResult;
|
|
722
|
+
/** Sets each selected (or given) node's rotation to `degrees`, turning it around its own centre. */
|
|
723
|
+
setRotation(degrees: number, options?: {
|
|
724
|
+
ids?: readonly string[];
|
|
725
|
+
}): VectorActionResult;
|
|
726
|
+
/** Mirrors along the selection box's own axes. */
|
|
727
|
+
flip(axis: 'horizontal' | 'vertical', options?: {
|
|
728
|
+
ids?: readonly string[];
|
|
729
|
+
}): VectorActionResult;
|
|
730
|
+
/**
|
|
731
|
+
* Aligns the selected nodes to their combined bounds, or to the artboard when one node is
|
|
732
|
+
* selected (or `to: 'artboard'`).
|
|
733
|
+
*/
|
|
734
|
+
align(edge: VectorAlign, options?: {
|
|
735
|
+
ids?: readonly string[];
|
|
736
|
+
to?: 'selection' | 'artboard';
|
|
737
|
+
}): VectorActionResult;
|
|
738
|
+
/** Spaces three or more selected nodes evenly (equal gaps). */
|
|
739
|
+
distribute(direction: VectorDistribute, options?: {
|
|
740
|
+
ids?: readonly string[];
|
|
741
|
+
}): VectorActionResult;
|
|
742
|
+
/** Copies the selection (offset by `offset`, default 10, 10) on top of each original. */
|
|
743
|
+
duplicate(options?: {
|
|
744
|
+
ids?: readonly string[];
|
|
745
|
+
offset?: Vec2;
|
|
746
|
+
}): string[];
|
|
747
|
+
private withDuplicates;
|
|
748
|
+
/** Groups the selected nodes (they must share a parent). Returns the group id. */
|
|
749
|
+
group(options?: {
|
|
750
|
+
ids?: readonly string[];
|
|
751
|
+
name?: string;
|
|
752
|
+
}): string | null;
|
|
753
|
+
/** Replaces selected groups with their children. */
|
|
754
|
+
ungroup(options?: {
|
|
755
|
+
ids?: readonly string[];
|
|
756
|
+
}): VectorActionResult;
|
|
757
|
+
/** Changes stacking order within each node's parent. */
|
|
758
|
+
arrange(op: VectorArrange, options?: {
|
|
759
|
+
ids?: readonly string[];
|
|
760
|
+
}): VectorActionResult;
|
|
761
|
+
/**
|
|
762
|
+
* Combines the selected paths (two or more) into one with a boolean operation. `subtract`
|
|
763
|
+
* removes the upper shapes from the lowest one. The result takes the lowest shape's style
|
|
764
|
+
* and place.
|
|
765
|
+
*/
|
|
766
|
+
booleanOp(op: VectorBooleanOperation, options?: {
|
|
767
|
+
ids?: readonly string[];
|
|
768
|
+
}): string | null;
|
|
769
|
+
/** Joins the selected paths into one compound path (subpaths kept, lowest style). */
|
|
770
|
+
combine(options?: {
|
|
771
|
+
ids?: readonly string[];
|
|
772
|
+
}): string | null;
|
|
773
|
+
/** A document-space path mapped into the parent space of node `id`. */
|
|
774
|
+
private intoParentSpace;
|
|
775
|
+
/** Splits selected compound paths into one path per subpath. */
|
|
776
|
+
breakApart(options?: {
|
|
777
|
+
ids?: readonly string[];
|
|
778
|
+
}): VectorActionResult;
|
|
779
|
+
private updateAnchors;
|
|
780
|
+
/** Moves anchors by a document-space offset (converted into each node's own space). */
|
|
781
|
+
private movedAnchors;
|
|
782
|
+
/** Moves one anchor to a document position (handles follow). */
|
|
783
|
+
moveAnchor(ref: VectorAnchorRef, to: Vec2): VectorActionResult;
|
|
784
|
+
/** Moves one handle to a document position (see `moveAnchorHandle` for how the opposite handle follows). */
|
|
785
|
+
moveHandle(ref: VectorHandleRef, to: Vec2, options?: {
|
|
786
|
+
breakHandles?: boolean;
|
|
787
|
+
}): VectorActionResult;
|
|
788
|
+
/** Converts the selected (or given) anchors between corner, smooth and symmetric. */
|
|
789
|
+
convertAnchors(type: VectorAnchorType, refs?: readonly VectorAnchorRef[]): VectorActionResult;
|
|
790
|
+
/** Adds an anchor on the nearest segment of `nodeId` to document point `point` (shape unchanged). */
|
|
791
|
+
insertAnchorAt(nodeId: string, point: Vec2): VectorAnchorRef | null;
|
|
792
|
+
/** Deletes the selected anchors; empty subpaths and paths go too. */
|
|
793
|
+
deleteAnchors(refs?: readonly VectorAnchorRef[]): VectorActionResult;
|
|
794
|
+
/** Opens or closes the subpaths that contain selected anchors (or all subpaths of `nodeId`). */
|
|
795
|
+
setClosed(closed: boolean, nodeId?: string): VectorActionResult;
|
|
796
|
+
/** Reverses the direction of every subpath of the selected paths. */
|
|
797
|
+
reversePaths(ids?: readonly string[]): VectorActionResult;
|
|
798
|
+
/** Ends the pen path being drawn (a lone anchor is removed). */
|
|
799
|
+
finishPath(): void;
|
|
800
|
+
/** Copies the selection. Returns JSON text for the system clipboard (or null). */
|
|
801
|
+
copy(): string | null;
|
|
802
|
+
cut(): string | null;
|
|
803
|
+
/**
|
|
804
|
+
* Pastes `text` (from `copy()`, or SVG markup) or the internal clipboard, offset by 10, 10.
|
|
805
|
+
* Returns the new ids.
|
|
806
|
+
*/
|
|
807
|
+
paste(text?: string, options?: {
|
|
808
|
+
offset?: Vec2;
|
|
809
|
+
}): string[];
|
|
810
|
+
undo(): boolean;
|
|
811
|
+
redo(): boolean;
|
|
812
|
+
/** Deletes selected anchors (direct tool) or nodes. */
|
|
813
|
+
deleteSelection(): VectorActionResult;
|
|
814
|
+
/** The fields the anchor / handle helpers read, without building a full state. */
|
|
815
|
+
private view;
|
|
816
|
+
private tolerance;
|
|
817
|
+
private hitTransformHandle;
|
|
818
|
+
private hitHandle;
|
|
819
|
+
private hitAnchor;
|
|
820
|
+
/** Deepest unlocked visible path under `p`. */
|
|
821
|
+
private hitPath;
|
|
822
|
+
/** Endpoints of open subpaths of unlocked visible paths (pen tool resumes from them). */
|
|
823
|
+
private openEndpoints;
|
|
824
|
+
private computeHover;
|
|
825
|
+
private sameHover;
|
|
826
|
+
/** Primary button down on the canvas at `screen` (pixels relative to the canvas). */
|
|
827
|
+
pointerDown(screen: Vec2, input?: VectorPointerInput): void;
|
|
828
|
+
private downSelect;
|
|
829
|
+
private downDirect;
|
|
830
|
+
private downPen;
|
|
831
|
+
private downPencil;
|
|
832
|
+
private downShape;
|
|
833
|
+
/** Pointer moved over the canvas (with or without a button down). */
|
|
834
|
+
pointerMove(screen: Vec2, input?: VectorPointerInput): void;
|
|
835
|
+
/** Primary button released. */
|
|
836
|
+
pointerUp(screen?: Vec2, input?: VectorPointerInput): void;
|
|
837
|
+
/** The pointer left the canvas. */
|
|
838
|
+
pointerLeave(): void;
|
|
839
|
+
/** Abandons the drag in progress (Escape, lost capture). */
|
|
840
|
+
cancelDrag(): void;
|
|
841
|
+
/** Double click: select tool enters path editing, direct tool toggles an anchor or adds one. */
|
|
842
|
+
doubleClick(screen: Vec2): void;
|
|
843
|
+
/**
|
|
844
|
+
* Handles editor shortcuts. Returns true when the key was used (call `preventDefault`).
|
|
845
|
+
* Do not forward keys typed into text inputs.
|
|
846
|
+
*/
|
|
847
|
+
keyDown(input: VectorKeyInput): boolean;
|
|
848
|
+
keyUp(input: VectorKeyInput): void;
|
|
849
|
+
}
|
|
850
|
+
/** Scale factors and origin for dragging a selection-box handle to `p`. */
|
|
851
|
+
declare function scaleFor(handle: VectorTransformHandle, b: VectorBounds, p: Vec2, mods?: VectorModifiers): {
|
|
852
|
+
sx: number;
|
|
853
|
+
sy: number;
|
|
854
|
+
origin: Vec2;
|
|
855
|
+
};
|
|
856
|
+
|
|
857
|
+
interface VectorRenderPath {
|
|
858
|
+
kind: 'path';
|
|
859
|
+
id: string;
|
|
860
|
+
/** SVG `transform` (`matrix(…)`) for a rotated node. */
|
|
861
|
+
transform?: string;
|
|
862
|
+
d: string;
|
|
863
|
+
/** `none`, a colour, or `url(#id)` of a gradient in `defs`. */
|
|
864
|
+
fill: string;
|
|
865
|
+
stroke: string;
|
|
866
|
+
strokeWidth: number;
|
|
867
|
+
strokeLinecap?: string;
|
|
868
|
+
strokeLinejoin?: string;
|
|
869
|
+
strokeDasharray?: string;
|
|
870
|
+
fillRule: 'nonzero' | 'evenodd';
|
|
871
|
+
opacity: number;
|
|
872
|
+
}
|
|
873
|
+
interface VectorRenderGroup {
|
|
874
|
+
kind: 'group';
|
|
875
|
+
id: string;
|
|
876
|
+
/** SVG `transform` (`matrix(…)`) for a rotated group. */
|
|
877
|
+
transform?: string;
|
|
878
|
+
opacity: number;
|
|
879
|
+
children: VectorRenderNode[];
|
|
880
|
+
}
|
|
881
|
+
type VectorRenderNode = VectorRenderPath | VectorRenderGroup;
|
|
882
|
+
interface VectorSceneRenderData {
|
|
883
|
+
/** SVG `transform` for the document layer: `translate(x y) scale(zoom)`. */
|
|
884
|
+
transform: string;
|
|
885
|
+
width: number;
|
|
886
|
+
height: number;
|
|
887
|
+
background?: string;
|
|
888
|
+
/** Gradients referenced by `fill` / `stroke` values. */
|
|
889
|
+
defs: SvgGradientDef[];
|
|
890
|
+
/** Visible nodes, bottom to top. */
|
|
891
|
+
nodes: VectorRenderNode[];
|
|
892
|
+
}
|
|
893
|
+
interface VectorRenderAnchor {
|
|
894
|
+
ref: VectorAnchorRef;
|
|
895
|
+
/** Screen position. */
|
|
896
|
+
x: number;
|
|
897
|
+
y: number;
|
|
898
|
+
type: VectorAnchorType;
|
|
899
|
+
selected: boolean;
|
|
900
|
+
hovered: boolean;
|
|
901
|
+
/** The pen tool closes the path when this anchor is clicked. */
|
|
902
|
+
closes: boolean;
|
|
903
|
+
}
|
|
904
|
+
interface VectorRenderHandle {
|
|
905
|
+
ref: VectorHandleRef;
|
|
906
|
+
x: number;
|
|
907
|
+
y: number;
|
|
908
|
+
/** Screen position of the handle's anchor (draw a line to it). */
|
|
909
|
+
anchorX: number;
|
|
910
|
+
anchorY: number;
|
|
911
|
+
hovered: boolean;
|
|
912
|
+
}
|
|
913
|
+
interface VectorOverlayRenderData {
|
|
914
|
+
/** Outlines (screen-space path data) of hovered and selected nodes. */
|
|
915
|
+
outlines: {
|
|
916
|
+
id: string;
|
|
917
|
+
d: string;
|
|
918
|
+
selected: boolean;
|
|
919
|
+
}[];
|
|
920
|
+
/**
|
|
921
|
+
* Selection box with resize and rotate handles (select tool). `x / y / width / height` and
|
|
922
|
+
* each handle's `x / y` are the unrotated box in screen pixels: draw them inside
|
|
923
|
+
* `<g transform={transform}>`. `corners` and each handle's `screen` are true positions.
|
|
924
|
+
*/
|
|
925
|
+
selectionBox: (VectorBounds & {
|
|
926
|
+
angle: number;
|
|
927
|
+
transform: string;
|
|
928
|
+
corners: Vec2[];
|
|
929
|
+
handles: VectorSelectionBoxGeometry['handles'];
|
|
930
|
+
}) | null;
|
|
931
|
+
anchors: VectorRenderAnchor[];
|
|
932
|
+
handles: VectorRenderHandle[];
|
|
933
|
+
/** Marquee rectangle, screen space. */
|
|
934
|
+
marquee: VectorBounds | null;
|
|
935
|
+
/** Rubber-band segment from the pen path's last anchor to the pointer, screen space. */
|
|
936
|
+
penPreview: string | null;
|
|
937
|
+
/** Artboard rectangle, screen space. */
|
|
938
|
+
artboard: VectorBounds;
|
|
939
|
+
/** A short status, e.g. "W 120 × H 80" or "45°". */
|
|
940
|
+
status: string | null;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* The document layer. Render the nodes inside `<g transform={transform}>` and `defs` in a
|
|
944
|
+
* `<defs>` block. Pass a unique `idPrefix` when several editors share a page.
|
|
945
|
+
*/
|
|
946
|
+
declare function getVectorSceneRenderData(state: Pick<VectorEditorState, 'document' | 'viewport'>, options?: {
|
|
947
|
+
idPrefix?: string;
|
|
948
|
+
}): VectorSceneRenderData;
|
|
949
|
+
/** The editing overlay in screen pixels. Render it on top of the scene, untransformed. */
|
|
950
|
+
declare function getVectorOverlayRenderData(state: VectorEditorState): VectorOverlayRenderData;
|
|
951
|
+
/** Colours and sizes (screen pixels) the bundled canvases draw the overlay with. */
|
|
952
|
+
declare const VECTOR_OVERLAY_THEME: {
|
|
953
|
+
readonly accent: "#0ea5e9";
|
|
954
|
+
readonly handleFill: "#ffffff";
|
|
955
|
+
readonly marqueeFill: "rgba(14, 165, 233, 0.08)";
|
|
956
|
+
readonly transformHandleSize: 8;
|
|
957
|
+
readonly anchorSize: 7;
|
|
958
|
+
readonly handleRadius: 3.5;
|
|
959
|
+
};
|
|
960
|
+
interface ModifierEventLike {
|
|
961
|
+
shiftKey?: boolean;
|
|
962
|
+
altKey?: boolean;
|
|
963
|
+
metaKey?: boolean;
|
|
964
|
+
ctrlKey?: boolean;
|
|
965
|
+
}
|
|
966
|
+
/** Modifiers of a mouse / pointer / key event (`mod` is Cmd or Ctrl). */
|
|
967
|
+
declare function toVectorModifiers(event: ModifierEventLike): {
|
|
968
|
+
shift: boolean;
|
|
969
|
+
alt: boolean;
|
|
970
|
+
mod: boolean;
|
|
971
|
+
};
|
|
972
|
+
/** A pointer event as `VectorEditorController.pointerDown/Move/Up` input. */
|
|
973
|
+
declare function toVectorPointerInput(event: ModifierEventLike & {
|
|
974
|
+
button?: number;
|
|
975
|
+
}): {
|
|
976
|
+
button: number | undefined;
|
|
977
|
+
shift: boolean;
|
|
978
|
+
alt: boolean;
|
|
979
|
+
mod: boolean;
|
|
980
|
+
};
|
|
981
|
+
/** A key event as `VectorEditorController.keyDown/keyUp` input. */
|
|
982
|
+
declare function toVectorKeyInput(event: ModifierEventLike & {
|
|
983
|
+
key: string;
|
|
984
|
+
}): {
|
|
985
|
+
shift: boolean;
|
|
986
|
+
alt: boolean;
|
|
987
|
+
mod: boolean;
|
|
988
|
+
key: string;
|
|
989
|
+
};
|
|
990
|
+
/** True for targets that should keep their own keys (inputs, textareas, contenteditable). */
|
|
991
|
+
declare function isVectorTextTarget(target: unknown): boolean;
|
|
992
|
+
/** Style shown in a style panel: the first selected path's, with `mixed` flags when paths differ. */
|
|
993
|
+
interface VectorSelectionStyle {
|
|
994
|
+
fill: VectorPaint;
|
|
995
|
+
stroke: VectorPaint;
|
|
996
|
+
strokeWidth: number;
|
|
997
|
+
opacity: number;
|
|
998
|
+
mixed: {
|
|
999
|
+
fill: boolean;
|
|
1000
|
+
stroke: boolean;
|
|
1001
|
+
strokeWidth: boolean;
|
|
1002
|
+
opacity: boolean;
|
|
1003
|
+
};
|
|
1004
|
+
/** Ids of the paths the style applies to. */
|
|
1005
|
+
pathIds: string[];
|
|
1006
|
+
}
|
|
1007
|
+
declare function getVectorSelectionStyle(state: Pick<VectorEditorState, 'document' | 'selection'>): VectorSelectionStyle | null;
|
|
1008
|
+
/** A paint as `#rrggbb` for `<input type="color">`, or null for none, gradients and other CSS colours. */
|
|
1009
|
+
declare function vectorPaintToHex(paint: VectorPaint | undefined): string | null;
|
|
1010
|
+
|
|
1011
|
+
interface VectorCommandOptions {
|
|
1012
|
+
/** Id prefix (default `vector.`); use one per editor when several are registered. */
|
|
1013
|
+
prefix?: string;
|
|
1014
|
+
/** Palette group (default `Vector`). */
|
|
1015
|
+
group?: string;
|
|
1016
|
+
/** Command scope. Omit for global commands. */
|
|
1017
|
+
scope?: string;
|
|
1018
|
+
}
|
|
1019
|
+
/** A node as listed for agents. */
|
|
1020
|
+
interface VectorNodeListing {
|
|
1021
|
+
id: string;
|
|
1022
|
+
type: 'path' | 'group';
|
|
1023
|
+
name?: string;
|
|
1024
|
+
depth: number;
|
|
1025
|
+
/** Axis-aligned bounds in document units. */
|
|
1026
|
+
bounds: {
|
|
1027
|
+
x: number;
|
|
1028
|
+
y: number;
|
|
1029
|
+
width: number;
|
|
1030
|
+
height: number;
|
|
1031
|
+
} | null;
|
|
1032
|
+
/** Rotation in degrees, clockwise (ancestors' rotations included). */
|
|
1033
|
+
rotation: number;
|
|
1034
|
+
visible: boolean;
|
|
1035
|
+
locked: boolean;
|
|
1036
|
+
selected: boolean;
|
|
1037
|
+
fill?: unknown;
|
|
1038
|
+
stroke?: unknown;
|
|
1039
|
+
strokeWidth?: number;
|
|
1040
|
+
/** SVG path data (paths only). */
|
|
1041
|
+
d?: string;
|
|
1042
|
+
children?: number;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Palette and agent commands for a vector editor: list, select, add shapes and paths, style,
|
|
1046
|
+
* transform, align, arrange, group, boolean operations and SVG import / export. Register them
|
|
1047
|
+
* in a `CommandRegistry` and agents get them through `createCommandToolProvider`. Failures
|
|
1048
|
+
* throw sentences, which the registry returns as `{ ok: false, code: 'failed', message }`.
|
|
1049
|
+
*/
|
|
1050
|
+
declare function createVectorCommands(controller: VectorEditorController, options?: VectorCommandOptions): CommandSpec[];
|
|
1051
|
+
|
|
1052
|
+
export { type AddVectorNodeOptions, AffineMatrix, IDENTITY_MATRIX, KAPPA, type NearestSegment, SvgGradientDef, VECTOR_CLIPBOARD_TYPE, VECTOR_OVERLAY_THEME, VECTOR_ROTATE_HANDLE_OFFSET, VECTOR_TOOLS, VECTOR_TOOL_GLYPHS, VECTOR_TOOL_SHORTCUTS, VECTOR_TRANSFORM_HANDLES, type Vec2, type VectorActionResult, type VectorAlign, type VectorAnchor, type VectorAnchorRef, type VectorAnchorType, type VectorArrange, type VectorBooleanOperation, type VectorBounds, type VectorCommandOptions, type VectorDefaults, type VectorDistribute, type VectorDocument, VectorEditorController, type VectorEditorOptions, type VectorEditorState, type VectorFillRule, type VectorGroupNode, type VectorHandleRef, type VectorHover, type VectorInteraction, type VectorKeyInput, type VectorLineCap, type VectorLineJoin, type VectorMatrix, type VectorModifiers, type VectorNode, type VectorNodeListing, type VectorOverlayRenderData, type VectorPaint, type VectorPathNode, type VectorPointerInput, type VectorRenderAnchor, type VectorRenderGroup, type VectorRenderHandle, type VectorRenderNode, type VectorRenderPath, type VectorSceneRenderData, type VectorSelectionBoxGeometry, type VectorSelectionFrame, type VectorSelectionStyle, type VectorShapeInput, type VectorSnapOptions, type VectorStyle, type VectorSubpath, type VectorSvgExportOptions, type VectorSvgImportOptions, type VectorTool, type VectorTransformHandle, type VectorViewport, applyMatrix, applyMatrixLinear, bezierToSubpaths, boundsFromPoints, boundsIntersect, cloneAnchor, cloneSubpath, cloneVectorDocument, cloneVectorNode, collectPathNodes, convertAnchor, createVectorCommands, decomposeRigid, ellipseSubpath, findVectorNode, getAnchor, getAnchorWorld, getEditablePathIds, getTransformHandlePositions, getVectorOverlayRenderData, getVectorSceneRenderData, getVectorSelectionBoxGeometry, getVectorSelectionFrame, getVectorSelectionStyle, getVisibleAnchors, getVisibleHandles, hasPaint, hitTestVectorNode, hitTestVectorNodes, inferAnchorType, insertAnchor, invertMatrix, isIdentityMatrix, isVectorTextTarget, lineSubpath, locateVectorNode, matrixRotation, moveAnchorHandle, multiplyMatrices, nearestSegment, nodeMatrix, parseSvgTransform, parseVectorSvg, polygonSubpath, rectSubpath, removeAnchors, reverseSubpath, rotateMatrix, roundSubpaths, scaleFor, scaleMatrix, starSubpath, subpathSegment, subpathSegmentCount, subpathsToBezier, toVectorKeyInput, toVectorModifiers, toVectorPointerInput, transformVectorNode, translateAnchor, translateMatrix, unionBounds, vectorDocumentToSvg, vectorNodeBounds, vectorNodeRotation, vectorNodeToBezier, vectorPaintToHex, vectorPaintToSvg, vectorParentMatrix, vectorPathData, vectorPathWorldBezier, vectorResizeCursor, vectorToolLabel, vectorWorldMatrix, walkVectorNodes };
|