@mocanvas/editor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/index.d.ts +2066 -0
- package/dist/index.js +4801 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2066 @@
|
|
|
1
|
+
import * as _mocanvas_store from '@mocanvas/store';
|
|
2
|
+
import { RecordId, IndexKey, Store, MigrationSequence, SerializedStore, StoreSnapshot, StoreSchema, UnknownRecord } from '@mocanvas/store';
|
|
3
|
+
import { StyleWords, FrameBuffers, CameraState, EngineBridge, ClipRect } from '@mocanvas/wasm';
|
|
4
|
+
export { CameraState, ClipRect, EngineBridge, FLAG, PATH_OP, StyleWords, loadEngine, loadEngineSync } from '@mocanvas/wasm';
|
|
5
|
+
import * as react from 'react';
|
|
6
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
7
|
+
export { track, useAtom, useComputed, useQuickReactor, useReactor, useValue } from '@mocanvas/state/react';
|
|
8
|
+
|
|
9
|
+
interface VecLike {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}
|
|
13
|
+
interface BoxLike {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
w: number;
|
|
17
|
+
h: number;
|
|
18
|
+
}
|
|
19
|
+
declare class Vec {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
constructor(x?: number, y?: number);
|
|
23
|
+
static From(p: VecLike): Vec;
|
|
24
|
+
static Add(a: VecLike, b: VecLike): Vec;
|
|
25
|
+
static Sub(a: VecLike, b: VecLike): Vec;
|
|
26
|
+
static Mul(a: VecLike, s: number): Vec;
|
|
27
|
+
static Div(a: VecLike, s: number): Vec;
|
|
28
|
+
static Dist(a: VecLike, b: VecLike): number;
|
|
29
|
+
static Dist2(a: VecLike, b: VecLike): number;
|
|
30
|
+
static Len(a: VecLike): number;
|
|
31
|
+
static Dot(a: VecLike, b: VecLike): number;
|
|
32
|
+
static Cross(a: VecLike, b: VecLike): number;
|
|
33
|
+
static Lrp(a: VecLike, b: VecLike, t: number): Vec;
|
|
34
|
+
static Angle(a: VecLike, b: VecLike): number;
|
|
35
|
+
static Rot(a: VecLike, r: number): Vec;
|
|
36
|
+
static RotWith(a: VecLike, center: VecLike, r: number): Vec;
|
|
37
|
+
static Uni(a: VecLike): Vec;
|
|
38
|
+
static Per(a: VecLike): Vec;
|
|
39
|
+
static Equals(a: VecLike, b: VecLike): boolean;
|
|
40
|
+
static NearestPointOnLineSegment(a: VecLike, b: VecLike, p: VecLike): Vec;
|
|
41
|
+
static DistanceToLineSegment(a: VecLike, b: VecLike, p: VecLike): number;
|
|
42
|
+
add(b: VecLike): this;
|
|
43
|
+
sub(b: VecLike): this;
|
|
44
|
+
mul(s: number): this;
|
|
45
|
+
clone(): Vec;
|
|
46
|
+
toJson(): VecLike;
|
|
47
|
+
}
|
|
48
|
+
declare class Box {
|
|
49
|
+
x: number;
|
|
50
|
+
y: number;
|
|
51
|
+
w: number;
|
|
52
|
+
h: number;
|
|
53
|
+
constructor(x?: number, y?: number, w?: number, h?: number);
|
|
54
|
+
static From(b: BoxLike): Box;
|
|
55
|
+
static FromPoints(points: VecLike[]): Box;
|
|
56
|
+
static FromMinMax(minX: number, minY: number, maxX: number, maxY: number): Box;
|
|
57
|
+
static Common(boxes: BoxLike[]): Box;
|
|
58
|
+
static Contains(a: BoxLike, b: BoxLike): boolean;
|
|
59
|
+
static ContainsPoint(a: BoxLike, p: VecLike, margin?: number): boolean;
|
|
60
|
+
static Collides(a: BoxLike, b: BoxLike): boolean;
|
|
61
|
+
static Expand(a: BoxLike, d: number): Box;
|
|
62
|
+
get minX(): number;
|
|
63
|
+
get minY(): number;
|
|
64
|
+
get maxX(): number;
|
|
65
|
+
get maxY(): number;
|
|
66
|
+
get width(): number;
|
|
67
|
+
get height(): number;
|
|
68
|
+
get center(): Vec;
|
|
69
|
+
get corners(): Vec[];
|
|
70
|
+
clone(): Box;
|
|
71
|
+
toJson(): BoxLike;
|
|
72
|
+
}
|
|
73
|
+
interface Geometry2dOptions {
|
|
74
|
+
isFilled: boolean;
|
|
75
|
+
isClosed: boolean;
|
|
76
|
+
isLabel?: boolean;
|
|
77
|
+
}
|
|
78
|
+
/** Base class for shape geometry. */
|
|
79
|
+
declare abstract class Geometry2d {
|
|
80
|
+
readonly isFilled: boolean;
|
|
81
|
+
readonly isClosed: boolean;
|
|
82
|
+
readonly isLabel: boolean;
|
|
83
|
+
private _vertices;
|
|
84
|
+
private _bounds;
|
|
85
|
+
constructor(opts: Geometry2dOptions);
|
|
86
|
+
/** Flattened outline vertices in shape-local space. */
|
|
87
|
+
abstract getVertices(): Vec[];
|
|
88
|
+
/** Serialize to the engine's flat path encoding. */
|
|
89
|
+
abstract toPathWords(): number[];
|
|
90
|
+
get vertices(): Vec[];
|
|
91
|
+
get bounds(): Box;
|
|
92
|
+
get center(): Vec;
|
|
93
|
+
nearestPoint(point: VecLike): Vec;
|
|
94
|
+
distanceToPoint(point: VecLike, hitInside?: boolean): number;
|
|
95
|
+
hitTestPoint(point: VecLike, margin?: number, hitInside?: boolean): boolean;
|
|
96
|
+
}
|
|
97
|
+
declare function pointInPolygon(p: VecLike, poly: VecLike[]): boolean;
|
|
98
|
+
declare class Rectangle2d extends Geometry2d {
|
|
99
|
+
readonly x: number;
|
|
100
|
+
readonly y: number;
|
|
101
|
+
readonly w: number;
|
|
102
|
+
readonly h: number;
|
|
103
|
+
constructor(opts: {
|
|
104
|
+
x?: number;
|
|
105
|
+
y?: number;
|
|
106
|
+
width: number;
|
|
107
|
+
height: number;
|
|
108
|
+
isFilled: boolean;
|
|
109
|
+
isLabel?: boolean;
|
|
110
|
+
});
|
|
111
|
+
getVertices(): Vec[];
|
|
112
|
+
toPathWords(): number[];
|
|
113
|
+
}
|
|
114
|
+
declare class Ellipse2d extends Geometry2d {
|
|
115
|
+
readonly w: number;
|
|
116
|
+
readonly h: number;
|
|
117
|
+
constructor(opts: {
|
|
118
|
+
width: number;
|
|
119
|
+
height: number;
|
|
120
|
+
isFilled: boolean;
|
|
121
|
+
});
|
|
122
|
+
getVertices(): Vec[];
|
|
123
|
+
toPathWords(): number[];
|
|
124
|
+
}
|
|
125
|
+
declare class Circle2d extends Ellipse2d {
|
|
126
|
+
constructor(opts: {
|
|
127
|
+
radius: number;
|
|
128
|
+
isFilled: boolean;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
declare class Polygon2d extends Geometry2d {
|
|
132
|
+
readonly points: Vec[];
|
|
133
|
+
constructor(opts: {
|
|
134
|
+
points: VecLike[];
|
|
135
|
+
isFilled: boolean;
|
|
136
|
+
});
|
|
137
|
+
getVertices(): Vec[];
|
|
138
|
+
toPathWords(): number[];
|
|
139
|
+
}
|
|
140
|
+
declare class Polyline2d extends Geometry2d {
|
|
141
|
+
readonly points: Vec[];
|
|
142
|
+
constructor(opts: {
|
|
143
|
+
points: VecLike[];
|
|
144
|
+
});
|
|
145
|
+
getVertices(): Vec[];
|
|
146
|
+
toPathWords(): number[];
|
|
147
|
+
}
|
|
148
|
+
declare class Edge2d extends Polyline2d {
|
|
149
|
+
constructor(opts: {
|
|
150
|
+
start: VecLike;
|
|
151
|
+
end: VecLike;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/** Cubic bézier spline through control points, serialized natively. */
|
|
155
|
+
declare class CubicSpline2d extends Geometry2d {
|
|
156
|
+
readonly segments: {
|
|
157
|
+
p0: Vec;
|
|
158
|
+
c1: Vec;
|
|
159
|
+
c2: Vec;
|
|
160
|
+
p1: Vec;
|
|
161
|
+
}[];
|
|
162
|
+
constructor(opts: {
|
|
163
|
+
segments: {
|
|
164
|
+
p0: VecLike;
|
|
165
|
+
c1: VecLike;
|
|
166
|
+
c2: VecLike;
|
|
167
|
+
p1: VecLike;
|
|
168
|
+
}[];
|
|
169
|
+
isClosed?: boolean;
|
|
170
|
+
isFilled?: boolean;
|
|
171
|
+
});
|
|
172
|
+
getVertices(): Vec[];
|
|
173
|
+
toPathWords(): number[];
|
|
174
|
+
}
|
|
175
|
+
/** Several geometries drawn as one shape (e.g. a geo shape and its label). */
|
|
176
|
+
declare class Group2d extends Geometry2d {
|
|
177
|
+
readonly children: Geometry2d[];
|
|
178
|
+
constructor(opts: {
|
|
179
|
+
children: Geometry2d[];
|
|
180
|
+
});
|
|
181
|
+
getVertices(): Vec[];
|
|
182
|
+
toPathWords(): number[];
|
|
183
|
+
nearestPoint(point: VecLike): Vec;
|
|
184
|
+
hitTestPoint(point: VecLike, margin?: number, hitInside?: boolean): boolean;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
188
|
+
type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
189
|
+
[key: string]: JsonValue;
|
|
190
|
+
};
|
|
191
|
+
type JsonObject = {
|
|
192
|
+
[key: string]: JsonValue;
|
|
193
|
+
};
|
|
194
|
+
interface Document {
|
|
195
|
+
readonly id: DocumentId;
|
|
196
|
+
readonly typeName: "document";
|
|
197
|
+
gridSize: number;
|
|
198
|
+
name: string;
|
|
199
|
+
meta: JsonObject;
|
|
200
|
+
}
|
|
201
|
+
type DocumentId = RecordId<Document>;
|
|
202
|
+
declare const DocumentRecordType: _mocanvas_store.RecordType<Document, never>;
|
|
203
|
+
declare const DOCUMENT_ID: DocumentId;
|
|
204
|
+
interface Page {
|
|
205
|
+
readonly id: PageId;
|
|
206
|
+
readonly typeName: "page";
|
|
207
|
+
name: string;
|
|
208
|
+
index: IndexKey;
|
|
209
|
+
meta: JsonObject;
|
|
210
|
+
}
|
|
211
|
+
type PageId = RecordId<Page>;
|
|
212
|
+
declare const PageRecordType: _mocanvas_store.RecordType<Page, "name" | "index">;
|
|
213
|
+
declare function isPageId(id: string): id is PageId;
|
|
214
|
+
interface Camera {
|
|
215
|
+
readonly id: CameraId;
|
|
216
|
+
readonly typeName: "camera";
|
|
217
|
+
x: number;
|
|
218
|
+
y: number;
|
|
219
|
+
z: number;
|
|
220
|
+
meta: JsonObject;
|
|
221
|
+
}
|
|
222
|
+
type CameraId = RecordId<Camera>;
|
|
223
|
+
declare const CameraRecordType: _mocanvas_store.RecordType<Camera, never>;
|
|
224
|
+
interface Instance {
|
|
225
|
+
readonly id: InstanceId;
|
|
226
|
+
readonly typeName: "instance";
|
|
227
|
+
currentPageId: PageId;
|
|
228
|
+
followingUserId: string | null;
|
|
229
|
+
isFocusMode: boolean;
|
|
230
|
+
isDebugMode: boolean;
|
|
231
|
+
isToolLocked: boolean;
|
|
232
|
+
isGridMode: boolean;
|
|
233
|
+
isReadonly: boolean;
|
|
234
|
+
isFocused: boolean;
|
|
235
|
+
isPenMode: boolean;
|
|
236
|
+
isChangingStyle: boolean;
|
|
237
|
+
exportBackground: boolean;
|
|
238
|
+
screenBounds: {
|
|
239
|
+
x: number;
|
|
240
|
+
y: number;
|
|
241
|
+
w: number;
|
|
242
|
+
h: number;
|
|
243
|
+
};
|
|
244
|
+
insets: boolean[];
|
|
245
|
+
cursor: {
|
|
246
|
+
type: string;
|
|
247
|
+
rotation: number;
|
|
248
|
+
};
|
|
249
|
+
scribbles: Scribble[];
|
|
250
|
+
brush: {
|
|
251
|
+
x: number;
|
|
252
|
+
y: number;
|
|
253
|
+
w: number;
|
|
254
|
+
h: number;
|
|
255
|
+
} | null;
|
|
256
|
+
zoomBrush: {
|
|
257
|
+
x: number;
|
|
258
|
+
y: number;
|
|
259
|
+
w: number;
|
|
260
|
+
h: number;
|
|
261
|
+
} | null;
|
|
262
|
+
openMenus: string[];
|
|
263
|
+
devicePixelRatio: number;
|
|
264
|
+
isCoarsePointer: boolean;
|
|
265
|
+
isHoveringCanvas: boolean | null;
|
|
266
|
+
stylesForNextShape: Record<string, unknown>;
|
|
267
|
+
duplicateProps: {
|
|
268
|
+
shapeIds: string[];
|
|
269
|
+
offset: {
|
|
270
|
+
x: number;
|
|
271
|
+
y: number;
|
|
272
|
+
};
|
|
273
|
+
} | null;
|
|
274
|
+
meta: JsonObject;
|
|
275
|
+
}
|
|
276
|
+
type InstanceId = RecordId<Instance>;
|
|
277
|
+
interface Scribble {
|
|
278
|
+
id: string;
|
|
279
|
+
points: {
|
|
280
|
+
x: number;
|
|
281
|
+
y: number;
|
|
282
|
+
z?: number;
|
|
283
|
+
}[];
|
|
284
|
+
size: number;
|
|
285
|
+
color: string;
|
|
286
|
+
opacity: number;
|
|
287
|
+
state: "starting" | "paused" | "active" | "stopping";
|
|
288
|
+
delay: number;
|
|
289
|
+
shrink: number;
|
|
290
|
+
taper: boolean;
|
|
291
|
+
}
|
|
292
|
+
declare const InstanceRecordType: _mocanvas_store.RecordType<Instance, "currentPageId">;
|
|
293
|
+
declare const INSTANCE_ID: InstanceId;
|
|
294
|
+
interface InstancePageState {
|
|
295
|
+
readonly id: InstancePageStateId;
|
|
296
|
+
readonly typeName: "instance_page_state";
|
|
297
|
+
pageId: PageId;
|
|
298
|
+
selectedShapeIds: ShapeId[];
|
|
299
|
+
hintingShapeIds: ShapeId[];
|
|
300
|
+
erasingShapeIds: ShapeId[];
|
|
301
|
+
hoveredShapeId: ShapeId | null;
|
|
302
|
+
editingShapeId: ShapeId | null;
|
|
303
|
+
croppingShapeId: ShapeId | null;
|
|
304
|
+
focusedGroupId: ShapeId | null;
|
|
305
|
+
meta: JsonObject;
|
|
306
|
+
}
|
|
307
|
+
type InstancePageStateId = RecordId<InstancePageState>;
|
|
308
|
+
declare const InstancePageStateRecordType: _mocanvas_store.RecordType<InstancePageState, "pageId">;
|
|
309
|
+
interface BaseShape<Type extends string, Props extends object> {
|
|
310
|
+
readonly id: ShapeId;
|
|
311
|
+
readonly typeName: "shape";
|
|
312
|
+
type: Type;
|
|
313
|
+
x: number;
|
|
314
|
+
y: number;
|
|
315
|
+
rotation: number;
|
|
316
|
+
index: IndexKey;
|
|
317
|
+
parentId: ParentId;
|
|
318
|
+
isLocked: boolean;
|
|
319
|
+
opacity: number;
|
|
320
|
+
props: Props;
|
|
321
|
+
meta: JsonObject;
|
|
322
|
+
}
|
|
323
|
+
type UnknownShape = BaseShape<string, object>;
|
|
324
|
+
type Shape = UnknownShape;
|
|
325
|
+
type ShapeId = RecordId<UnknownShape>;
|
|
326
|
+
type ParentId = PageId | ShapeId;
|
|
327
|
+
declare const ShapeRecordType: _mocanvas_store.RecordType<UnknownShape, "props" | "type" | "index" | "parentId">;
|
|
328
|
+
declare function createShapeId(id?: string): ShapeId;
|
|
329
|
+
declare function isShapeId(id: unknown): id is ShapeId;
|
|
330
|
+
declare function isShape(record: unknown): record is UnknownShape;
|
|
331
|
+
type ShapePartial<T extends UnknownShape = UnknownShape> = {
|
|
332
|
+
id: ShapeId;
|
|
333
|
+
type: T["type"];
|
|
334
|
+
} & Partial<Omit<T, "id" | "type" | "props" | "meta">> & {
|
|
335
|
+
props?: Partial<T["props"]>;
|
|
336
|
+
meta?: Partial<T["meta"]>;
|
|
337
|
+
};
|
|
338
|
+
/** A shape partial without a required id (id and index are assigned on create). */
|
|
339
|
+
type ShapeCreate<T extends UnknownShape = UnknownShape> = Omit<ShapePartial<T>, "id"> & {
|
|
340
|
+
id?: ShapeId;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
interface VecModel {
|
|
344
|
+
x: number;
|
|
345
|
+
y: number;
|
|
346
|
+
z?: number;
|
|
347
|
+
}
|
|
348
|
+
type UiEventType = "pointer" | "click" | "keyboard" | "wheel" | "pinch" | "cancel" | "complete" | "interrupt" | "tick" | "misc";
|
|
349
|
+
type PointerEventName = "pointer_down" | "pointer_move" | "pointer_up" | "right_click" | "middle_click";
|
|
350
|
+
type ClickEventName = "double_click" | "triple_click" | "quadruple_click";
|
|
351
|
+
type KeyboardEventName = "key_down" | "key_up" | "key_repeat";
|
|
352
|
+
type PointerTarget = {
|
|
353
|
+
target: "canvas";
|
|
354
|
+
} | {
|
|
355
|
+
target: "shape";
|
|
356
|
+
shape: UnknownShape;
|
|
357
|
+
} | {
|
|
358
|
+
target: "selection";
|
|
359
|
+
handle?: SelectionHandle;
|
|
360
|
+
} | {
|
|
361
|
+
target: "handle";
|
|
362
|
+
shape: UnknownShape;
|
|
363
|
+
handle: ShapeHandle;
|
|
364
|
+
};
|
|
365
|
+
type SelectionHandle = "top" | "right" | "bottom" | "left" | "top_left" | "top_right" | "bottom_left" | "bottom_right" | "rotate" | "mobile_rotate" | "top_left_rotate" | "top_right_rotate" | "bottom_left_rotate" | "bottom_right_rotate";
|
|
366
|
+
interface ShapeHandle {
|
|
367
|
+
id: string;
|
|
368
|
+
type: "vertex" | "virtual" | "create" | "clone";
|
|
369
|
+
index: string;
|
|
370
|
+
x: number;
|
|
371
|
+
y: number;
|
|
372
|
+
}
|
|
373
|
+
interface BaseEventInfo {
|
|
374
|
+
type: UiEventType;
|
|
375
|
+
shiftKey: boolean;
|
|
376
|
+
altKey: boolean;
|
|
377
|
+
ctrlKey: boolean;
|
|
378
|
+
metaKey: boolean;
|
|
379
|
+
accelKey: boolean;
|
|
380
|
+
}
|
|
381
|
+
type PointerEventInfo = BaseEventInfo & PointerTarget & {
|
|
382
|
+
type: "pointer";
|
|
383
|
+
name: PointerEventName;
|
|
384
|
+
point: VecModel;
|
|
385
|
+
pointerId: number;
|
|
386
|
+
button: number;
|
|
387
|
+
isPen: boolean;
|
|
388
|
+
};
|
|
389
|
+
type ClickEventInfo = BaseEventInfo & PointerTarget & {
|
|
390
|
+
type: "click";
|
|
391
|
+
name: ClickEventName;
|
|
392
|
+
point: VecModel;
|
|
393
|
+
pointerId: number;
|
|
394
|
+
button: number;
|
|
395
|
+
isPen: boolean;
|
|
396
|
+
phase: "down" | "up" | "settle";
|
|
397
|
+
};
|
|
398
|
+
interface KeyboardEventInfo extends BaseEventInfo {
|
|
399
|
+
type: "keyboard";
|
|
400
|
+
name: KeyboardEventName;
|
|
401
|
+
key: string;
|
|
402
|
+
code: string;
|
|
403
|
+
}
|
|
404
|
+
interface WheelEventInfo extends BaseEventInfo {
|
|
405
|
+
type: "wheel";
|
|
406
|
+
name: "wheel";
|
|
407
|
+
delta: VecModel;
|
|
408
|
+
point: VecModel;
|
|
409
|
+
}
|
|
410
|
+
interface PinchEventInfo extends BaseEventInfo {
|
|
411
|
+
type: "pinch";
|
|
412
|
+
name: "pinch_start" | "pinch" | "pinch_end";
|
|
413
|
+
point: VecModel;
|
|
414
|
+
delta: VecModel;
|
|
415
|
+
}
|
|
416
|
+
interface CancelEventInfo extends BaseEventInfo {
|
|
417
|
+
type: "cancel";
|
|
418
|
+
name: "cancel";
|
|
419
|
+
}
|
|
420
|
+
interface CompleteEventInfo extends BaseEventInfo {
|
|
421
|
+
type: "complete";
|
|
422
|
+
name: "complete";
|
|
423
|
+
}
|
|
424
|
+
interface InterruptEventInfo extends BaseEventInfo {
|
|
425
|
+
type: "interrupt";
|
|
426
|
+
name: "interrupt";
|
|
427
|
+
}
|
|
428
|
+
interface TickEventInfo extends BaseEventInfo {
|
|
429
|
+
type: "tick";
|
|
430
|
+
name: "tick";
|
|
431
|
+
elapsed: number;
|
|
432
|
+
}
|
|
433
|
+
type EventInfo = PointerEventInfo | ClickEventInfo | KeyboardEventInfo | WheelEventInfo | PinchEventInfo | CancelEventInfo | CompleteEventInfo | InterruptEventInfo | TickEventInfo;
|
|
434
|
+
type EventHandlers = {
|
|
435
|
+
onPointerDown?(info: PointerEventInfo): void;
|
|
436
|
+
onPointerMove?(info: PointerEventInfo): void;
|
|
437
|
+
onPointerUp?(info: PointerEventInfo): void;
|
|
438
|
+
onRightClick?(info: PointerEventInfo): void;
|
|
439
|
+
onMiddleClick?(info: PointerEventInfo): void;
|
|
440
|
+
onDoubleClick?(info: ClickEventInfo): void;
|
|
441
|
+
onTripleClick?(info: ClickEventInfo): void;
|
|
442
|
+
onQuadrupleClick?(info: ClickEventInfo): void;
|
|
443
|
+
onKeyDown?(info: KeyboardEventInfo): void;
|
|
444
|
+
onKeyUp?(info: KeyboardEventInfo): void;
|
|
445
|
+
onKeyRepeat?(info: KeyboardEventInfo): void;
|
|
446
|
+
onWheel?(info: WheelEventInfo): void;
|
|
447
|
+
onCancel?(info: CancelEventInfo): void;
|
|
448
|
+
onComplete?(info: CompleteEventInfo): void;
|
|
449
|
+
onInterrupt?(info: InterruptEventInfo): void;
|
|
450
|
+
onTick?(info: TickEventInfo): void;
|
|
451
|
+
};
|
|
452
|
+
declare const EVENT_NAME_MAP: Record<string, keyof EventHandlers>;
|
|
453
|
+
interface EditorEvents {
|
|
454
|
+
mount: () => void;
|
|
455
|
+
"max-shapes": (info: {
|
|
456
|
+
name: string;
|
|
457
|
+
pageId: string;
|
|
458
|
+
count: number;
|
|
459
|
+
}) => void;
|
|
460
|
+
change: (info: {
|
|
461
|
+
source: "user" | "remote";
|
|
462
|
+
}) => void;
|
|
463
|
+
update: () => void;
|
|
464
|
+
event: (info: EventInfo) => void;
|
|
465
|
+
tick: (elapsed: number) => void;
|
|
466
|
+
frame: (info: {
|
|
467
|
+
drawn: number;
|
|
468
|
+
culled: number;
|
|
469
|
+
ms: number;
|
|
470
|
+
}) => void;
|
|
471
|
+
"select-all-text": (info: {
|
|
472
|
+
shapeId: ShapeId;
|
|
473
|
+
}) => void;
|
|
474
|
+
"stop-camera-animation": () => void;
|
|
475
|
+
"stop-following": () => void;
|
|
476
|
+
}
|
|
477
|
+
/** Minimal typed event emitter. */
|
|
478
|
+
declare class EventEmitter<Events extends {
|
|
479
|
+
[K in keyof Events]: (...args: any[]) => void;
|
|
480
|
+
}> {
|
|
481
|
+
private listeners;
|
|
482
|
+
on<K extends keyof Events>(name: K, fn: Events[K]): () => void;
|
|
483
|
+
once<K extends keyof Events>(name: K, fn: Events[K]): () => void;
|
|
484
|
+
off<K extends keyof Events>(name: K, fn: Events[K]): void;
|
|
485
|
+
emit<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): void;
|
|
486
|
+
removeAllListeners(): void;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
interface ShapeUtilConstructor<T extends UnknownShape = UnknownShape, U extends ShapeUtil<T> = ShapeUtil<T>> {
|
|
490
|
+
new (editor: Editor): U;
|
|
491
|
+
type: T["type"];
|
|
492
|
+
props?: Record<string, unknown>;
|
|
493
|
+
migrations?: unknown;
|
|
494
|
+
}
|
|
495
|
+
interface ResizeInfo<T extends UnknownShape> {
|
|
496
|
+
newPoint: VecLike;
|
|
497
|
+
handle: SelectionHandle;
|
|
498
|
+
mode: "scale_shape" | "resize_bounds";
|
|
499
|
+
scaleX: number;
|
|
500
|
+
scaleY: number;
|
|
501
|
+
initialBounds: {
|
|
502
|
+
x: number;
|
|
503
|
+
y: number;
|
|
504
|
+
w: number;
|
|
505
|
+
h: number;
|
|
506
|
+
};
|
|
507
|
+
initialShape: T;
|
|
508
|
+
}
|
|
509
|
+
interface TranslateInfo<T extends UnknownShape> {
|
|
510
|
+
initialShape: T;
|
|
511
|
+
}
|
|
512
|
+
/** Export-wide settings handed to `ShapeUtil.toSvg` / `toBackgroundSvg`. */
|
|
513
|
+
interface ShapeSvgContext {
|
|
514
|
+
/** Whether the export is being drawn against the dark theme. */
|
|
515
|
+
darkMode: boolean;
|
|
516
|
+
/** The page background colour of the export, as `#rrggbb`. */
|
|
517
|
+
background: string;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* What an SVG export callback may return: a raw markup string (inserted
|
|
521
|
+
* verbatim, so it must be well-formed and escaped by the util) or a React node
|
|
522
|
+
* the exporter serializes. `undefined` means "I have nothing to draw", which
|
|
523
|
+
* lets the exporter fall through to its own renderers.
|
|
524
|
+
*/
|
|
525
|
+
type ShapeSvgResult = string | ReactNode;
|
|
526
|
+
/**
|
|
527
|
+
* Describes how a shape type behaves: its default props, geometry, rendering
|
|
528
|
+
* and interaction callbacks. One instance per shape type per editor.
|
|
529
|
+
*/
|
|
530
|
+
declare abstract class ShapeUtil<T extends UnknownShape = UnknownShape> {
|
|
531
|
+
readonly editor: Editor;
|
|
532
|
+
static type: string;
|
|
533
|
+
static props?: Record<string, unknown>;
|
|
534
|
+
static migrations?: unknown;
|
|
535
|
+
constructor(editor: Editor);
|
|
536
|
+
get type(): T["type"];
|
|
537
|
+
abstract getDefaultProps(): T["props"];
|
|
538
|
+
abstract getGeometry(shape: T): Geometry2d;
|
|
539
|
+
abstract component(shape: T): ReactNode;
|
|
540
|
+
/**
|
|
541
|
+
* The outline drawn over the shape when it is selected or hovered, in
|
|
542
|
+
* shape-local coordinates. The indicators layer supplies `fill: none`, the
|
|
543
|
+
* selection colour and a zoom-independent stroke width, so an indicator is
|
|
544
|
+
* usually just a bare `<path>` or `<rect>`. Return `null` to fall back to a
|
|
545
|
+
* rectangle around the shape's geometry bounds.
|
|
546
|
+
*/
|
|
547
|
+
abstract indicator(shape: T): ReactNode;
|
|
548
|
+
/**
|
|
549
|
+
* The shape as SVG, in shape-local coordinates: the exporter wraps the
|
|
550
|
+
* result in a `<g>` carrying the shape's page transform and opacity.
|
|
551
|
+
* Implement it to make a custom shape exportable without registering
|
|
552
|
+
* anything; leave it undefined to fall through to the exporter's own
|
|
553
|
+
* renderers.
|
|
554
|
+
*/
|
|
555
|
+
toSvg?(shape: T, ctx: ShapeSvgContext): ShapeSvgResult;
|
|
556
|
+
/**
|
|
557
|
+
* Extra SVG drawn *behind* every exported shape (a backdrop, a drop shadow,
|
|
558
|
+
* a grid). Same coordinate space and wrapping as `toSvg`.
|
|
559
|
+
*/
|
|
560
|
+
toBackgroundSvg?(shape: T, ctx: ShapeSvgContext): ShapeSvgResult;
|
|
561
|
+
/**
|
|
562
|
+
* GPU style for the shape's geometry. Return `null` (the default) to render
|
|
563
|
+
* the shape through `component` in the DOM overlay instead.
|
|
564
|
+
*/
|
|
565
|
+
getRenderStyle(_shape: T): StyleWords | null;
|
|
566
|
+
/** Whether the shape should be drawn by the DOM overlay even if it has a render style (e.g. while editing). */
|
|
567
|
+
needsOverlay(shape: T): boolean;
|
|
568
|
+
/**
|
|
569
|
+
* Whether `component` should be rendered in the DOM overlay *in addition to*
|
|
570
|
+
* the GPU geometry (e.g. a text label on a filled shape).
|
|
571
|
+
*/
|
|
572
|
+
hasOverlayLabel(_shape: T): boolean;
|
|
573
|
+
/**
|
|
574
|
+
* Whether the shape clips its descendants to its own geometry bounds (e.g.
|
|
575
|
+
* frames). Children are then rendered with a scissor rect on the GPU.
|
|
576
|
+
*/
|
|
577
|
+
isClipShape(_shape: T): boolean;
|
|
578
|
+
canEdit(_shape: T): boolean;
|
|
579
|
+
canResize(_shape: T): boolean;
|
|
580
|
+
canBind(_opts: {
|
|
581
|
+
fromShapeType: string;
|
|
582
|
+
toShapeType: string;
|
|
583
|
+
bindingType: string;
|
|
584
|
+
}): boolean;
|
|
585
|
+
canCrop(_shape: T): boolean;
|
|
586
|
+
canScroll(_shape: T): boolean;
|
|
587
|
+
canSnap(_shape: T): boolean;
|
|
588
|
+
canReceiveNewChildrenOfType(_shape: T, _type: string): boolean;
|
|
589
|
+
canDropShapes(_shape: T, _shapes: UnknownShape[]): boolean;
|
|
590
|
+
hideRotateHandle(_shape: T): boolean;
|
|
591
|
+
hideResizeHandles(_shape: T): boolean;
|
|
592
|
+
hideSelectionBoundsBg(_shape: T): boolean;
|
|
593
|
+
hideSelectionBoundsFg(_shape: T): boolean;
|
|
594
|
+
isAspectRatioLocked(_shape: T): boolean;
|
|
595
|
+
getHandles?(shape: T): ShapeHandle[];
|
|
596
|
+
getText?(shape: T): string | undefined;
|
|
597
|
+
onBeforeCreate?(next: T): T | void;
|
|
598
|
+
onBeforeUpdate?(prev: T, next: T): T | void;
|
|
599
|
+
onResize?(shape: T, info: ResizeInfo<T>): Partial<T> | void;
|
|
600
|
+
onResizeStart?(shape: T): void;
|
|
601
|
+
onResizeEnd?(initial: T, current: T): void;
|
|
602
|
+
onTranslateStart?(shape: T): Partial<T> | void;
|
|
603
|
+
onTranslate?(initial: T, current: T): Partial<T> | void;
|
|
604
|
+
onTranslateEnd?(initial: T, current: T): Partial<T> | void;
|
|
605
|
+
onRotateStart?(shape: T): void;
|
|
606
|
+
onRotate?(initial: T, current: T): Partial<T> | void;
|
|
607
|
+
onRotateEnd?(initial: T, current: T): void;
|
|
608
|
+
onHandleDrag?(shape: T, info: {
|
|
609
|
+
handle: ShapeHandle;
|
|
610
|
+
isPrecise: boolean;
|
|
611
|
+
initial?: T;
|
|
612
|
+
}): Partial<T> | void;
|
|
613
|
+
onDoubleClick?(shape: T): Partial<T> | void;
|
|
614
|
+
onDoubleClickEdge?(shape: T): Partial<T> | void;
|
|
615
|
+
onDoubleClickHandle?(shape: T, handle: ShapeHandle): Partial<T> | void;
|
|
616
|
+
onEditEnd?(shape: T): void;
|
|
617
|
+
onChildrenChange?(shape: T): Partial<UnknownShape>[] | void;
|
|
618
|
+
onDragShapesOver?(shape: T, shapes: UnknownShape[]): void;
|
|
619
|
+
onDragShapesOut?(shape: T, shapes: UnknownShape[]): void;
|
|
620
|
+
onDropShapesOver?(shape: T, shapes: UnknownShape[]): void;
|
|
621
|
+
}
|
|
622
|
+
/** Shapes with `w` and `h` props. */
|
|
623
|
+
declare abstract class BaseBoxShapeUtil<T extends UnknownShape & {
|
|
624
|
+
props: {
|
|
625
|
+
w: number;
|
|
626
|
+
h: number;
|
|
627
|
+
};
|
|
628
|
+
}> extends ShapeUtil<T> {
|
|
629
|
+
onResize(shape: T, info: ResizeInfo<T>): Partial<T>;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** A directed relationship between two shapes (e.g. an arrow end attached to a shape). */
|
|
633
|
+
interface BaseBinding<Type extends string, Props extends object> {
|
|
634
|
+
readonly id: BindingId;
|
|
635
|
+
readonly typeName: "binding";
|
|
636
|
+
type: Type;
|
|
637
|
+
fromId: ShapeId;
|
|
638
|
+
toId: ShapeId;
|
|
639
|
+
props: Props;
|
|
640
|
+
meta: JsonObject;
|
|
641
|
+
}
|
|
642
|
+
type UnknownBinding = BaseBinding<string, object>;
|
|
643
|
+
type Binding = UnknownBinding;
|
|
644
|
+
type BindingId = RecordId<UnknownBinding>;
|
|
645
|
+
declare const BindingRecordType: _mocanvas_store.RecordType<UnknownBinding, "props" | "type" | "fromId" | "toId">;
|
|
646
|
+
declare function createBindingId(id?: string): BindingId;
|
|
647
|
+
declare function isBindingId(id: unknown): id is BindingId;
|
|
648
|
+
declare function isBinding(record: unknown): record is UnknownBinding;
|
|
649
|
+
type BindingCreate<B extends UnknownBinding = UnknownBinding> = {
|
|
650
|
+
id?: BindingId;
|
|
651
|
+
type: B["type"];
|
|
652
|
+
fromId: ShapeId;
|
|
653
|
+
toId: ShapeId;
|
|
654
|
+
props?: Partial<B["props"]>;
|
|
655
|
+
meta?: Partial<B["meta"]>;
|
|
656
|
+
};
|
|
657
|
+
type BindingPartial<B extends UnknownBinding = UnknownBinding> = {
|
|
658
|
+
id: BindingId;
|
|
659
|
+
type: B["type"];
|
|
660
|
+
} & Partial<Omit<B, "id" | "type" | "props" | "meta">> & {
|
|
661
|
+
props?: Partial<B["props"]>;
|
|
662
|
+
meta?: Partial<B["meta"]>;
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
interface BindingUtilConstructor<B extends UnknownBinding = UnknownBinding, U extends BindingUtil<B> = BindingUtil<B>> {
|
|
666
|
+
new (editor: Editor): U;
|
|
667
|
+
type: B["type"];
|
|
668
|
+
props?: Record<string, unknown>;
|
|
669
|
+
migrations?: unknown;
|
|
670
|
+
}
|
|
671
|
+
interface BindingOnCreateOptions<B extends UnknownBinding> {
|
|
672
|
+
binding: B;
|
|
673
|
+
}
|
|
674
|
+
interface BindingOnChangeOptions<B extends UnknownBinding> {
|
|
675
|
+
bindingBefore: B;
|
|
676
|
+
bindingAfter: B;
|
|
677
|
+
}
|
|
678
|
+
interface BindingOnShapeChangeOptions<B extends UnknownBinding> {
|
|
679
|
+
binding: B;
|
|
680
|
+
shapeBefore: UnknownShape;
|
|
681
|
+
shapeAfter: UnknownShape;
|
|
682
|
+
/** Why the shape changed: itself, or because an ancestor moved. */
|
|
683
|
+
reason: "self" | "ancestry";
|
|
684
|
+
}
|
|
685
|
+
interface BindingOnShapeIsolateOptions<B extends UnknownBinding> {
|
|
686
|
+
binding: B;
|
|
687
|
+
/** The shape leaving this binding's context (deleted, or copied without its partner). */
|
|
688
|
+
removedShape?: UnknownShape;
|
|
689
|
+
}
|
|
690
|
+
interface BindingOnShapeDeleteOptions<B extends UnknownBinding> {
|
|
691
|
+
binding: B;
|
|
692
|
+
shape: UnknownShape;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Describes how a binding type behaves. One instance per binding type per editor.
|
|
696
|
+
* Callbacks run inside the store transaction that caused them.
|
|
697
|
+
*/
|
|
698
|
+
declare abstract class BindingUtil<B extends UnknownBinding = UnknownBinding> {
|
|
699
|
+
readonly editor: Editor;
|
|
700
|
+
static type: string;
|
|
701
|
+
static props?: Record<string, unknown>;
|
|
702
|
+
static migrations?: unknown;
|
|
703
|
+
constructor(editor: Editor);
|
|
704
|
+
get type(): B["type"];
|
|
705
|
+
abstract getDefaultProps(): Partial<B["props"]>;
|
|
706
|
+
onBeforeCreate?(options: BindingOnCreateOptions<B>): B | void;
|
|
707
|
+
onAfterCreate?(options: BindingOnCreateOptions<B>): void;
|
|
708
|
+
onBeforeChange?(options: BindingOnChangeOptions<B>): B | void;
|
|
709
|
+
onAfterChange?(options: BindingOnChangeOptions<B>): void;
|
|
710
|
+
onBeforeDelete?(options: {
|
|
711
|
+
binding: B;
|
|
712
|
+
}): void;
|
|
713
|
+
onAfterDelete?(options: {
|
|
714
|
+
binding: B;
|
|
715
|
+
}): void;
|
|
716
|
+
/** The `from` shape changed (moved, resized, ...). */
|
|
717
|
+
onAfterChangeFromShape?(options: BindingOnShapeChangeOptions<B>): void;
|
|
718
|
+
/** The `to` shape changed. */
|
|
719
|
+
onAfterChangeToShape?(options: BindingOnShapeChangeOptions<B>): void;
|
|
720
|
+
/** The `from` shape is about to be deleted; the binding is deleted right after. */
|
|
721
|
+
onBeforeDeleteFromShape?(options: BindingOnShapeDeleteOptions<B>): void;
|
|
722
|
+
/** The `to` shape is about to be deleted; the binding is deleted right after. */
|
|
723
|
+
onBeforeDeleteToShape?(options: BindingOnShapeDeleteOptions<B>): void;
|
|
724
|
+
/** The `from` shape is being isolated (partner going away); typically the binding converts to a static value. */
|
|
725
|
+
onBeforeIsolateFromShape?(options: BindingOnShapeIsolateOptions<B>): void;
|
|
726
|
+
onBeforeIsolateToShape?(options: BindingOnShapeIsolateOptions<B>): void;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/** Pixel dimensions and source of an image file. `src` is a URL or data URL; `null` while uploading. */
|
|
730
|
+
interface ImageAssetProps {
|
|
731
|
+
w: number;
|
|
732
|
+
h: number;
|
|
733
|
+
name: string;
|
|
734
|
+
isAnimated: boolean;
|
|
735
|
+
mimeType: string | null;
|
|
736
|
+
src: string | null;
|
|
737
|
+
fileSize?: number;
|
|
738
|
+
}
|
|
739
|
+
type VideoAssetProps = ImageAssetProps;
|
|
740
|
+
interface BookmarkAssetProps {
|
|
741
|
+
title: string;
|
|
742
|
+
description: string;
|
|
743
|
+
image: string;
|
|
744
|
+
favicon: string;
|
|
745
|
+
src: string | null;
|
|
746
|
+
}
|
|
747
|
+
/** A document-scoped record describing external media referenced by shapes (images, videos, bookmarks). */
|
|
748
|
+
interface BaseAsset<Type extends string, Props extends object> {
|
|
749
|
+
readonly id: AssetId;
|
|
750
|
+
readonly typeName: "asset";
|
|
751
|
+
type: Type;
|
|
752
|
+
props: Props;
|
|
753
|
+
meta: JsonObject;
|
|
754
|
+
}
|
|
755
|
+
type ImageAsset = BaseAsset<"image", ImageAssetProps>;
|
|
756
|
+
type VideoAsset = BaseAsset<"video", VideoAssetProps>;
|
|
757
|
+
type BookmarkAsset = BaseAsset<"bookmark", BookmarkAssetProps>;
|
|
758
|
+
type Asset = ImageAsset | VideoAsset | BookmarkAsset;
|
|
759
|
+
type AssetType = Asset["type"];
|
|
760
|
+
type AssetId = RecordId<Asset>;
|
|
761
|
+
declare const AssetRecordType: _mocanvas_store.RecordType<Asset, "props" | "type">;
|
|
762
|
+
declare function createAssetId(id?: string): AssetId;
|
|
763
|
+
declare function isAssetId(id: unknown): id is AssetId;
|
|
764
|
+
declare function isAsset(record: unknown): record is Asset;
|
|
765
|
+
/** An asset to create: `id` and `meta` are optional, `props` must be complete. */
|
|
766
|
+
type AssetCreate<A extends Asset = Asset> = {
|
|
767
|
+
id?: AssetId;
|
|
768
|
+
type: A["type"];
|
|
769
|
+
props: A["props"];
|
|
770
|
+
meta?: Partial<A["meta"]>;
|
|
771
|
+
};
|
|
772
|
+
type AssetPartial<A extends Asset = Asset> = {
|
|
773
|
+
id: AssetId;
|
|
774
|
+
type: A["type"];
|
|
775
|
+
props?: Partial<A["props"]>;
|
|
776
|
+
meta?: Partial<A["meta"]>;
|
|
777
|
+
};
|
|
778
|
+
/** Content dropped or pasted onto the canvas. `point` is in page space; defaults to the viewport center. */
|
|
779
|
+
type ExternalContent = {
|
|
780
|
+
type: "files";
|
|
781
|
+
files: File[];
|
|
782
|
+
point?: VecLikeJson;
|
|
783
|
+
} | {
|
|
784
|
+
type: "text";
|
|
785
|
+
text: string;
|
|
786
|
+
point?: VecLikeJson;
|
|
787
|
+
} | {
|
|
788
|
+
type: "url";
|
|
789
|
+
url: string;
|
|
790
|
+
point?: VecLikeJson;
|
|
791
|
+
} | {
|
|
792
|
+
type: "svg-text";
|
|
793
|
+
text: string;
|
|
794
|
+
point?: VecLikeJson;
|
|
795
|
+
};
|
|
796
|
+
type ExternalContentType = ExternalContent["type"];
|
|
797
|
+
/** Something an asset can be produced from. */
|
|
798
|
+
type ExternalAssetContent = {
|
|
799
|
+
type: "file";
|
|
800
|
+
file: File;
|
|
801
|
+
} | {
|
|
802
|
+
type: "url";
|
|
803
|
+
url: string;
|
|
804
|
+
};
|
|
805
|
+
type ExternalAssetType = ExternalAssetContent["type"];
|
|
806
|
+
type ExternalContentHandler<T extends ExternalContentType = ExternalContentType> = (info: Extract<ExternalContent, {
|
|
807
|
+
type: T;
|
|
808
|
+
}>) => void | Promise<void>;
|
|
809
|
+
type ExternalAssetHandler<T extends ExternalAssetType = ExternalAssetType> = (info: Extract<ExternalAssetContent, {
|
|
810
|
+
type: T;
|
|
811
|
+
}>) => Asset | undefined | Promise<Asset | undefined>;
|
|
812
|
+
/** Plain `{ x, y }`; kept structural so records stay JSON-serializable. */
|
|
813
|
+
interface VecLikeJson {
|
|
814
|
+
x: number;
|
|
815
|
+
y: number;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
type StateNodeType = "branch" | "leaf" | "root";
|
|
819
|
+
interface StateNodeConstructor {
|
|
820
|
+
new (editor: Editor, parent?: StateNode): StateNode;
|
|
821
|
+
id: string;
|
|
822
|
+
initial?: string;
|
|
823
|
+
children?(): StateNodeConstructor[];
|
|
824
|
+
isLockable?: boolean;
|
|
825
|
+
useCoalescedEvents?: boolean;
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* A node in the tool state machine. Tools are trees of StateNodes: the root
|
|
829
|
+
* holds one child per tool, each tool holds its own states.
|
|
830
|
+
*/
|
|
831
|
+
declare abstract class StateNode {
|
|
832
|
+
static id: string;
|
|
833
|
+
static initial?: string;
|
|
834
|
+
static children?: () => StateNodeConstructor[];
|
|
835
|
+
static isLockable: boolean;
|
|
836
|
+
static useCoalescedEvents: boolean;
|
|
837
|
+
readonly id: string;
|
|
838
|
+
readonly type: StateNodeType;
|
|
839
|
+
readonly initial: string | undefined;
|
|
840
|
+
readonly children?: Record<string, StateNode>;
|
|
841
|
+
readonly parent: StateNode | undefined;
|
|
842
|
+
readonly editor: Editor;
|
|
843
|
+
private readonly _isActive;
|
|
844
|
+
private readonly _current;
|
|
845
|
+
private readonly _path;
|
|
846
|
+
/** Whether the shape kind this tool creates should be kept selected after creation etc. */
|
|
847
|
+
shapeType?: string;
|
|
848
|
+
/** Tools can declare the interaction they perform when locked. */
|
|
849
|
+
static get isLockableTool(): boolean;
|
|
850
|
+
constructor(editor: Editor, parent?: StateNode);
|
|
851
|
+
getPath(): string;
|
|
852
|
+
getCurrent(): StateNode | undefined;
|
|
853
|
+
getIsActive(): boolean;
|
|
854
|
+
getDescendant<T extends StateNode>(path: string): T | undefined;
|
|
855
|
+
/** Dispatch an event to this node, then to its active child. */
|
|
856
|
+
handleEvent(info: EventInfo): void;
|
|
857
|
+
/** Move this node's current child to `id`, exiting the old one and entering the new one. */
|
|
858
|
+
transition(id: string, info?: object): this;
|
|
859
|
+
enter(info: Record<string, unknown>, from: string): void;
|
|
860
|
+
exit(info: Record<string, unknown>, to: string): void;
|
|
861
|
+
/** Change the active tool from anywhere in the tree. */
|
|
862
|
+
setCurrentToolIdMask(_mask: string | undefined): void;
|
|
863
|
+
onEnter?(info: Record<string, unknown>, from: string): void;
|
|
864
|
+
onExit?(info: Record<string, unknown>, to: string): void;
|
|
865
|
+
onPointerDown?(info: PointerEventInfo): void;
|
|
866
|
+
onPointerMove?(info: PointerEventInfo): void;
|
|
867
|
+
onPointerUp?(info: PointerEventInfo): void;
|
|
868
|
+
onRightClick?(info: PointerEventInfo): void;
|
|
869
|
+
onMiddleClick?(info: PointerEventInfo): void;
|
|
870
|
+
onDoubleClick?(info: ClickEventInfo): void;
|
|
871
|
+
onTripleClick?(info: ClickEventInfo): void;
|
|
872
|
+
onQuadrupleClick?(info: ClickEventInfo): void;
|
|
873
|
+
onKeyDown?(info: KeyboardEventInfo): void;
|
|
874
|
+
onKeyUp?(info: KeyboardEventInfo): void;
|
|
875
|
+
onKeyRepeat?(info: KeyboardEventInfo): void;
|
|
876
|
+
onWheel?(info: WheelEventInfo): void;
|
|
877
|
+
onCancel?(info: CancelEventInfo): void;
|
|
878
|
+
onComplete?(info: CompleteEventInfo): void;
|
|
879
|
+
onInterrupt?(info: InterruptEventInfo): void;
|
|
880
|
+
onTick?(info: TickEventInfo): void;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/** Convenience for subclass typing. */
|
|
884
|
+
type StateNodeClass = typeof StateNode & StateNodeConstructor;
|
|
885
|
+
|
|
886
|
+
type RGBA = [number, number, number, number];
|
|
887
|
+
interface DrawOptions {
|
|
888
|
+
/** Clear color, normalized. */
|
|
889
|
+
background: RGBA;
|
|
890
|
+
}
|
|
891
|
+
/** Anything the GPU can sample as a texture. */
|
|
892
|
+
type TextureSource = TexImageSource | ImageData;
|
|
893
|
+
interface TextureOptions {
|
|
894
|
+
/**
|
|
895
|
+
* Whether `source` already holds premultiplied alpha. Defaults to `false`;
|
|
896
|
+
* the upload always produces a premultiplied texture, matching the blend mode.
|
|
897
|
+
*/
|
|
898
|
+
premultiplied?: boolean;
|
|
899
|
+
/** Bilinear filtering (default `true`). `false` gives nearest-neighbour sampling. */
|
|
900
|
+
smooth?: boolean;
|
|
901
|
+
}
|
|
902
|
+
/** A GPU backend that can draw one engine frame. Implementations: WebGL2 (now), WebGPU (later). */
|
|
903
|
+
interface RenderBackend {
|
|
904
|
+
readonly kind: "webgl2" | "webgpu";
|
|
905
|
+
/** Resize the drawing buffer. Sizes are CSS pixels; `dpr` is the device pixel ratio. */
|
|
906
|
+
resize(width: number, height: number, dpr: number): void;
|
|
907
|
+
/**
|
|
908
|
+
* Draw one frame. Batches are bound to the texture named by their texture
|
|
909
|
+
* word (0 = solid colour) and, when they carry a clip rect, scissored to it.
|
|
910
|
+
*/
|
|
911
|
+
draw(frame: FrameBuffers, camera: CameraState, options: DrawOptions): void;
|
|
912
|
+
/**
|
|
913
|
+
* Create or replace the texture with host id `id` (must be non-zero; 0 is the
|
|
914
|
+
* built-in solid texture). Shapes reference it through `StyleWords.texture`.
|
|
915
|
+
*/
|
|
916
|
+
uploadTexture(id: number, source: TextureSource, opts?: TextureOptions): void;
|
|
917
|
+
/** Release a texture. Batches still referencing `id` fall back to solid colour. */
|
|
918
|
+
deleteTexture(id: number): void;
|
|
919
|
+
dispose(): void;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/** Eight colours that stay readable on the canvas background in any order. */
|
|
923
|
+
declare const PRESENCE_COLORS: readonly ["#e0575b", "#ef8b3a", "#d8a72e", "#4f9d55", "#2fa39a", "#3f86d8", "#7a63d8", "#c05aa8"];
|
|
924
|
+
declare function randomPresenceColor(): string;
|
|
925
|
+
/**
|
|
926
|
+
* What one other person is doing right now: where their cursor is, what they
|
|
927
|
+
* have selected, which page they are on.
|
|
928
|
+
*
|
|
929
|
+
* Presence records live in the `presence` scope: they are shared with
|
|
930
|
+
* collaborators but never written to a `.tldr` file and never enter history.
|
|
931
|
+
*/
|
|
932
|
+
interface InstancePresence {
|
|
933
|
+
readonly id: InstancePresenceId;
|
|
934
|
+
readonly typeName: "instance_presence";
|
|
935
|
+
/** Stable id of the person (survives reconnects; one person may have many tabs). */
|
|
936
|
+
userId: string;
|
|
937
|
+
userName: string;
|
|
938
|
+
/** CSS colour used for their cursor, name chip and selection outlines. */
|
|
939
|
+
color: string;
|
|
940
|
+
currentPageId: PageId;
|
|
941
|
+
cursor: {
|
|
942
|
+
x: number;
|
|
943
|
+
y: number;
|
|
944
|
+
type: string;
|
|
945
|
+
rotation: number;
|
|
946
|
+
};
|
|
947
|
+
camera: {
|
|
948
|
+
x: number;
|
|
949
|
+
y: number;
|
|
950
|
+
z: number;
|
|
951
|
+
};
|
|
952
|
+
selectedShapeIds: ShapeId[];
|
|
953
|
+
brush: {
|
|
954
|
+
x: number;
|
|
955
|
+
y: number;
|
|
956
|
+
w: number;
|
|
957
|
+
h: number;
|
|
958
|
+
} | null;
|
|
959
|
+
scribbles: Scribble[];
|
|
960
|
+
followingUserId: string | null;
|
|
961
|
+
/** `Date.now()` on the sender when the record was produced. */
|
|
962
|
+
lastActivityTimestamp: number;
|
|
963
|
+
chatMessage: string;
|
|
964
|
+
meta: JsonObject;
|
|
965
|
+
}
|
|
966
|
+
type InstancePresenceId = RecordId<InstancePresence>;
|
|
967
|
+
declare const InstancePresenceRecordType: _mocanvas_store.RecordType<InstancePresence, "currentPageId" | "userId">;
|
|
968
|
+
declare function isInstancePresenceId(id: string): id is InstancePresenceId;
|
|
969
|
+
/**
|
|
970
|
+
* The local person's identity, held in a signal so that renaming or
|
|
971
|
+
* recolouring re-renders anything that shows it. Session-only: nothing here is
|
|
972
|
+
* persisted with the document.
|
|
973
|
+
*/
|
|
974
|
+
interface UserPreferences {
|
|
975
|
+
getId(): string;
|
|
976
|
+
getName(): string;
|
|
977
|
+
getColor(): string;
|
|
978
|
+
setName(name: string): void;
|
|
979
|
+
setColor(color: string): void;
|
|
980
|
+
}
|
|
981
|
+
interface UserPreferencesInit {
|
|
982
|
+
id?: string;
|
|
983
|
+
name?: string;
|
|
984
|
+
color?: string;
|
|
985
|
+
}
|
|
986
|
+
/** Create a `UserPreferences` backed by one session atom. */
|
|
987
|
+
declare function createUserPreferences(init?: UserPreferencesInit): UserPreferences;
|
|
988
|
+
|
|
989
|
+
type EditorRecord = Document | Page | UnknownShape | UnknownBinding | Asset | Camera | Instance | InstancePageState | InstancePresence;
|
|
990
|
+
type EditorStore = Store<EditorRecord, EditorStoreProps>;
|
|
991
|
+
type EditorStoreSnapshot = StoreSnapshot<EditorRecord>;
|
|
992
|
+
interface EditorStoreProps {
|
|
993
|
+
defaultName: string;
|
|
994
|
+
}
|
|
995
|
+
interface CreateStoreOptions {
|
|
996
|
+
migrations?: MigrationSequence[];
|
|
997
|
+
initialData?: SerializedStore<EditorRecord>;
|
|
998
|
+
snapshot?: EditorStoreSnapshot;
|
|
999
|
+
defaultName?: string;
|
|
1000
|
+
id?: string;
|
|
1001
|
+
}
|
|
1002
|
+
declare function createSchema(migrations?: MigrationSequence[]): StoreSchema<EditorRecord, EditorStoreProps>;
|
|
1003
|
+
/** Create a store with the editor's record types. */
|
|
1004
|
+
declare function createStore(options?: CreateStoreOptions): EditorStore;
|
|
1005
|
+
|
|
1006
|
+
/** Interns string ids to dense u32 engine handles. Handle 0 is reserved. */
|
|
1007
|
+
declare class HandleTable {
|
|
1008
|
+
private toHandle;
|
|
1009
|
+
private toId;
|
|
1010
|
+
private free;
|
|
1011
|
+
get size(): number;
|
|
1012
|
+
handle(id: string): number;
|
|
1013
|
+
peek(id: string): number | undefined;
|
|
1014
|
+
id(handle: number): string | undefined;
|
|
1015
|
+
release(id: string): number | undefined;
|
|
1016
|
+
clear(): void;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/** Produces the pixels for a texture. Rejecting marks the texture as failed. */
|
|
1020
|
+
type TextureLoader = () => Promise<TextureSource>;
|
|
1021
|
+
type TextureState = "pending" | "ready" | "error";
|
|
1022
|
+
interface TextureInfo {
|
|
1023
|
+
/** Host texture id, `1`-based. `StyleWords.texture` references it. */
|
|
1024
|
+
readonly id: number;
|
|
1025
|
+
/** Number of outstanding `acquire` calls. */
|
|
1026
|
+
readonly refs: number;
|
|
1027
|
+
readonly state: TextureState;
|
|
1028
|
+
/** Pixel size of the decoded source; `0` until it is ready. */
|
|
1029
|
+
readonly width: number;
|
|
1030
|
+
readonly height: number;
|
|
1031
|
+
}
|
|
1032
|
+
interface TextureManagerOptions {
|
|
1033
|
+
/**
|
|
1034
|
+
* Called (in a microtask) with the keys whose state just changed, so the host
|
|
1035
|
+
* can re-write the shapes holding them and redraw.
|
|
1036
|
+
*/
|
|
1037
|
+
onChange?: (keys: readonly string[]) => void;
|
|
1038
|
+
}
|
|
1039
|
+
/** Largest device-pixel scale a rasterized texture is rendered at. */
|
|
1040
|
+
declare const MAX_TEXTURE_RESOLUTION = 8;
|
|
1041
|
+
/** Largest zoom factor that still increases texture resolution. */
|
|
1042
|
+
declare const MAX_TEXTURE_ZOOM = 4;
|
|
1043
|
+
/**
|
|
1044
|
+
* `dpr × min(MAX_TEXTURE_ZOOM, zoom)` snapped up to a power of two, so panning
|
|
1045
|
+
* and zooming only re-rasterize a texture when it crosses a bucket boundary.
|
|
1046
|
+
*/
|
|
1047
|
+
declare function bucketTextureResolution(zoom: number, dpr?: number): number;
|
|
1048
|
+
/**
|
|
1049
|
+
* Owns the host's GPU textures: allocates ids, refcounts them by key, loads
|
|
1050
|
+
* their pixels asynchronously and uploads them through the current
|
|
1051
|
+
* `RenderBackend`.
|
|
1052
|
+
*
|
|
1053
|
+
* `acquire` hands back the id straight away so a shape can be written to the
|
|
1054
|
+
* engine in the same tick; the texture only appears once the load resolves, so
|
|
1055
|
+
* callers keep a DOM fallback until `isReady(key)`. A key whose load rejected
|
|
1056
|
+
* resolves to `0` (no texture — the shape falls back to its plain fill).
|
|
1057
|
+
*
|
|
1058
|
+
* Callers that re-derive their textures on every write (a `ShapeUtil` in
|
|
1059
|
+
* `getRenderStyle`) should acquire inside `withOwner`, which reconciles the
|
|
1060
|
+
* keys an owner holds instead of piling up references.
|
|
1061
|
+
*/
|
|
1062
|
+
declare class TextureManager {
|
|
1063
|
+
private readonly options;
|
|
1064
|
+
private readonly entries;
|
|
1065
|
+
/** owner id → keys that owner currently holds a reference to. */
|
|
1066
|
+
private readonly ownerKeys;
|
|
1067
|
+
/** key → owner ids, so the host can find the shapes to re-write. */
|
|
1068
|
+
private readonly keyOwners;
|
|
1069
|
+
private backend;
|
|
1070
|
+
private nextId;
|
|
1071
|
+
private disposed;
|
|
1072
|
+
private owner;
|
|
1073
|
+
private touched;
|
|
1074
|
+
constructor(options?: TextureManagerOptions);
|
|
1075
|
+
/** Number of live texture entries. */
|
|
1076
|
+
get size(): number;
|
|
1077
|
+
getBackend(): RenderBackend | null;
|
|
1078
|
+
/**
|
|
1079
|
+
* Reference the texture for `key`, starting `load` the first time. Returns
|
|
1080
|
+
* the host texture id, or `0` when the key previously failed to load.
|
|
1081
|
+
*/
|
|
1082
|
+
acquire(key: string, load: TextureLoader, opts?: TextureOptions): number;
|
|
1083
|
+
/** Drop one reference. The texture is deleted when the last one goes. */
|
|
1084
|
+
release(key: string): void;
|
|
1085
|
+
/** Whether the texture for `key` is uploaded and safe to draw. */
|
|
1086
|
+
isReady(key: string): boolean;
|
|
1087
|
+
getState(key: string): TextureState | undefined;
|
|
1088
|
+
/** Current id for `key`: `0` when unknown or failed. */
|
|
1089
|
+
getId(key: string): number;
|
|
1090
|
+
getInfo(key: string): TextureInfo | undefined;
|
|
1091
|
+
/** Owners (shape ids) currently holding `key`. */
|
|
1092
|
+
getOwners(key: string): readonly string[];
|
|
1093
|
+
/** Every owner holding at least one texture. */
|
|
1094
|
+
getAllOwners(): readonly string[];
|
|
1095
|
+
/**
|
|
1096
|
+
* Run `fn` with every `acquire` inside it attributed to `owner`. Keys the
|
|
1097
|
+
* owner held before but did not acquire again are released, so re-deriving a
|
|
1098
|
+
* shape's style neither leaks references nor keeps a stale texture alive.
|
|
1099
|
+
*/
|
|
1100
|
+
withOwner<T>(owner: string, fn: () => T): T;
|
|
1101
|
+
/** Release every key an owner holds (the shape was deleted). */
|
|
1102
|
+
releaseOwner(owner: string): void;
|
|
1103
|
+
/**
|
|
1104
|
+
* Point the manager at the backend that owns the GPU textures. Everything
|
|
1105
|
+
* already decoded is uploaded again; anything still loading uploads when it
|
|
1106
|
+
* resolves. The previous backend keeps (and frees) its own GL objects.
|
|
1107
|
+
*/
|
|
1108
|
+
setBackend(backend: RenderBackend | null): void;
|
|
1109
|
+
dispose(): void;
|
|
1110
|
+
private addOwner;
|
|
1111
|
+
private removeOwner;
|
|
1112
|
+
private load;
|
|
1113
|
+
private upload;
|
|
1114
|
+
private destroy;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/** Style value unions shared by the default shapes. Kept identical for file compatibility. */
|
|
1118
|
+
declare const DEFAULT_COLORS: readonly ["black", "grey", "light-violet", "violet", "blue", "light-blue", "yellow", "orange", "green", "light-green", "light-red", "red", "white"];
|
|
1119
|
+
type DefaultColorStyle$1 = (typeof DEFAULT_COLORS)[number];
|
|
1120
|
+
declare const DEFAULT_FILLS: readonly ["none", "semi", "solid", "pattern", "fill"];
|
|
1121
|
+
type DefaultFillStyle$1 = (typeof DEFAULT_FILLS)[number];
|
|
1122
|
+
declare const DEFAULT_DASHES: readonly ["draw", "solid", "dashed", "dotted"];
|
|
1123
|
+
type DefaultDashStyle$1 = (typeof DEFAULT_DASHES)[number];
|
|
1124
|
+
declare const DEFAULT_SIZES: readonly ["s", "m", "l", "xl"];
|
|
1125
|
+
type DefaultSizeStyle$1 = (typeof DEFAULT_SIZES)[number];
|
|
1126
|
+
declare const DEFAULT_FONTS: readonly ["draw", "sans", "serif", "mono"];
|
|
1127
|
+
type DefaultFontStyle$1 = (typeof DEFAULT_FONTS)[number];
|
|
1128
|
+
declare const DEFAULT_H_ALIGNS: readonly ["start", "middle", "end", "start-legacy", "end-legacy", "middle-legacy"];
|
|
1129
|
+
type DefaultHorizontalAlignStyle$1 = (typeof DEFAULT_H_ALIGNS)[number];
|
|
1130
|
+
declare const DEFAULT_V_ALIGNS: readonly ["start", "middle", "end"];
|
|
1131
|
+
type DefaultVerticalAlignStyle$1 = (typeof DEFAULT_V_ALIGNS)[number];
|
|
1132
|
+
declare const GEO_SHAPE_KINDS: readonly ["rectangle", "ellipse", "triangle", "diamond", "pentagon", "hexagon", "octagon", "star", "rhombus", "rhombus-2", "oval", "trapezoid", "arrow-right", "arrow-left", "arrow-up", "arrow-down", "x-box", "check-box", "cloud", "heart"];
|
|
1133
|
+
type GeoShapeKind = (typeof GEO_SHAPE_KINDS)[number];
|
|
1134
|
+
/** Stroke widths in page units per size, matching the classic look. */
|
|
1135
|
+
declare const STROKE_SIZES: Record<DefaultSizeStyle$1, number>;
|
|
1136
|
+
declare const FONT_SIZES: Record<DefaultSizeStyle$1, number>;
|
|
1137
|
+
interface ThemeColor {
|
|
1138
|
+
solid: string;
|
|
1139
|
+
semi: string;
|
|
1140
|
+
pattern: string;
|
|
1141
|
+
fill: string;
|
|
1142
|
+
note: {
|
|
1143
|
+
fill: string;
|
|
1144
|
+
text: string;
|
|
1145
|
+
};
|
|
1146
|
+
highlight: {
|
|
1147
|
+
srgb: string;
|
|
1148
|
+
p3: string;
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* A palette in the spirit of the classic light theme. Values are our own picks:
|
|
1153
|
+
* hue-matched, tuned for contrast on white and on the semi fills.
|
|
1154
|
+
*/
|
|
1155
|
+
declare const LIGHT_THEME: Record<DefaultColorStyle$1, ThemeColor> & {
|
|
1156
|
+
background: string;
|
|
1157
|
+
solid: string;
|
|
1158
|
+
text: string;
|
|
1159
|
+
};
|
|
1160
|
+
/** Parse `#rrggbb` or `#rrggbbaa` to the engine's `0xRRGGBBAA`. */
|
|
1161
|
+
declare function hexToRgba(hex: string, alpha?: number): number;
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* A shape prop that is a *style*: shared across shape types, remembered for
|
|
1165
|
+
* the next created shape, and editable for a whole selection at once.
|
|
1166
|
+
*/
|
|
1167
|
+
declare class StyleProp<T> {
|
|
1168
|
+
readonly id: string;
|
|
1169
|
+
readonly defaultValue: T;
|
|
1170
|
+
private readonly validator?;
|
|
1171
|
+
/** Define a style with a free-form value. */
|
|
1172
|
+
static define<T>(id: string, options: {
|
|
1173
|
+
defaultValue: T;
|
|
1174
|
+
validate?: (value: unknown) => T;
|
|
1175
|
+
}): StyleProp<T>;
|
|
1176
|
+
/** Define a style whose value is one of a fixed set of strings. */
|
|
1177
|
+
static defineEnum<const V extends readonly string[]>(id: string, options: {
|
|
1178
|
+
defaultValue: V[number];
|
|
1179
|
+
values: V;
|
|
1180
|
+
}): EnumStyleProp<V[number]>;
|
|
1181
|
+
protected constructor(id: string, defaultValue: T, validator?: ((value: unknown) => T) | undefined);
|
|
1182
|
+
validate(value: unknown): T;
|
|
1183
|
+
}
|
|
1184
|
+
declare class EnumStyleProp<T extends string> extends StyleProp<T> {
|
|
1185
|
+
readonly values: readonly T[];
|
|
1186
|
+
constructor(id: string, defaultValue: T, values: readonly T[]);
|
|
1187
|
+
}
|
|
1188
|
+
declare const GeoShapeGeoStyle: EnumStyleProp<"rectangle" | "ellipse" | "triangle" | "diamond" | "pentagon" | "hexagon" | "octagon" | "star" | "rhombus" | "rhombus-2" | "oval" | "trapezoid" | "arrow-right" | "arrow-left" | "arrow-up" | "arrow-down" | "x-box" | "check-box" | "cloud" | "heart">;
|
|
1189
|
+
|
|
1190
|
+
declare const DefaultColorStyle: EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
1191
|
+
type DefaultColorStyle = DefaultColorStyle$1;
|
|
1192
|
+
declare const DefaultLabelColorStyle: EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
1193
|
+
type DefaultLabelColorStyle = DefaultColorStyle$1;
|
|
1194
|
+
declare const DefaultFillStyle: EnumStyleProp<"fill" | "none" | "semi" | "solid" | "pattern">;
|
|
1195
|
+
type DefaultFillStyle = DefaultFillStyle$1;
|
|
1196
|
+
declare const DefaultDashStyle: EnumStyleProp<"solid" | "draw" | "dashed" | "dotted">;
|
|
1197
|
+
type DefaultDashStyle = DefaultDashStyle$1;
|
|
1198
|
+
declare const DefaultSizeStyle: EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
1199
|
+
type DefaultSizeStyle = DefaultSizeStyle$1;
|
|
1200
|
+
declare const DefaultFontStyle: EnumStyleProp<"draw" | "sans" | "serif" | "mono">;
|
|
1201
|
+
type DefaultFontStyle = DefaultFontStyle$1;
|
|
1202
|
+
declare const DefaultHorizontalAlignStyle: EnumStyleProp<"start" | "middle" | "end" | "start-legacy" | "end-legacy" | "middle-legacy">;
|
|
1203
|
+
type DefaultHorizontalAlignStyle = DefaultHorizontalAlignStyle$1;
|
|
1204
|
+
declare const DefaultVerticalAlignStyle: EnumStyleProp<"start" | "middle" | "end">;
|
|
1205
|
+
type DefaultVerticalAlignStyle = DefaultVerticalAlignStyle$1;
|
|
1206
|
+
type SharedStyle<T> = {
|
|
1207
|
+
type: "shared";
|
|
1208
|
+
value: T;
|
|
1209
|
+
} | {
|
|
1210
|
+
type: "mixed";
|
|
1211
|
+
};
|
|
1212
|
+
/** Styles shared by a set of shapes: one entry per style prop, `mixed` when values differ. */
|
|
1213
|
+
declare class SharedStyleMap {
|
|
1214
|
+
private readonly map;
|
|
1215
|
+
get size(): number;
|
|
1216
|
+
get<T>(prop: StyleProp<T>): SharedStyle<T> | undefined;
|
|
1217
|
+
getAsKnownValue<T>(prop: StyleProp<T>): T | undefined;
|
|
1218
|
+
has(prop: StyleProp<unknown>): boolean;
|
|
1219
|
+
/** Record a value seen on a shape. */
|
|
1220
|
+
applyValue<T>(prop: StyleProp<T>, value: T): void;
|
|
1221
|
+
[Symbol.iterator](): IterableIterator<[StyleProp<unknown>, SharedStyle<unknown>]>;
|
|
1222
|
+
keys(): IterableIterator<StyleProp<unknown>>;
|
|
1223
|
+
}
|
|
1224
|
+
/** Style props declared on a ShapeUtil's `static props` map, by prop key. */
|
|
1225
|
+
declare function getStylePropsOf(props: Record<string, unknown> | undefined): Map<string, StyleProp<unknown>>;
|
|
1226
|
+
|
|
1227
|
+
/**
|
|
1228
|
+
* Undo/redo over store diffs. Every user-sourced document change is recorded;
|
|
1229
|
+
* marks delimit undo steps. Undo applies the reverse of every diff back to the
|
|
1230
|
+
* previous mark.
|
|
1231
|
+
*/
|
|
1232
|
+
declare class HistoryManager<R extends UnknownRecord> {
|
|
1233
|
+
private readonly store;
|
|
1234
|
+
private readonly onBatchComplete?;
|
|
1235
|
+
private undos;
|
|
1236
|
+
private redos;
|
|
1237
|
+
private ignoring;
|
|
1238
|
+
private pendingDiff;
|
|
1239
|
+
private readonly _version;
|
|
1240
|
+
private readonly dispose;
|
|
1241
|
+
constructor(store: Store<R>, onBatchComplete?: (() => void) | undefined);
|
|
1242
|
+
private pushDiff;
|
|
1243
|
+
/** Reactive counter for UI. */
|
|
1244
|
+
getVersion(): number;
|
|
1245
|
+
getNumUndos(): number;
|
|
1246
|
+
getNumRedos(): number;
|
|
1247
|
+
/** Run `fn` without recording its changes. */
|
|
1248
|
+
ignore<T>(fn: () => T): T;
|
|
1249
|
+
/** Place a stopping point. Returns the mark id. */
|
|
1250
|
+
mark(id?: string): string;
|
|
1251
|
+
private apply;
|
|
1252
|
+
undo(): this;
|
|
1253
|
+
redo(): this;
|
|
1254
|
+
/** Undo to the previous mark and drop the undone entries (no redo). */
|
|
1255
|
+
bail(): this;
|
|
1256
|
+
/** Undo back to a specific mark and drop the entries. */
|
|
1257
|
+
bailToMark(id: string): this;
|
|
1258
|
+
/** Merge every diff since `id` into one step (the mark stays). */
|
|
1259
|
+
squashToMark(id: string): this;
|
|
1260
|
+
clear(): void;
|
|
1261
|
+
private step;
|
|
1262
|
+
destroy(): void;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
interface SnapLine {
|
|
1266
|
+
id: string;
|
|
1267
|
+
/** Page-space points along the line (at least two). */
|
|
1268
|
+
points: Vec[];
|
|
1269
|
+
}
|
|
1270
|
+
interface SnapResult {
|
|
1271
|
+
/** Adjustment to add to the proposed delta/position. */
|
|
1272
|
+
nudge: Vec;
|
|
1273
|
+
lines: SnapLine[];
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Edge and center snapping against nearby shapes. Candidate shapes are taken
|
|
1277
|
+
* from the engine's spatial index (viewport query), so cost does not grow with
|
|
1278
|
+
* page size.
|
|
1279
|
+
*/
|
|
1280
|
+
declare class SnapManager {
|
|
1281
|
+
private readonly editor;
|
|
1282
|
+
private readonly _lines;
|
|
1283
|
+
/** Snap distance in screen pixels. */
|
|
1284
|
+
threshold: number;
|
|
1285
|
+
constructor(editor: Editor);
|
|
1286
|
+
getLines(): SnapLine[];
|
|
1287
|
+
clearLines(): void;
|
|
1288
|
+
/** Bounds of shapes near the viewport that are not being moved. */
|
|
1289
|
+
private getSnapTargets;
|
|
1290
|
+
private static pointsOf;
|
|
1291
|
+
/**
|
|
1292
|
+
* Snap a moving box (already offset by the proposed delta) to nearby shapes.
|
|
1293
|
+
* Returns the extra nudge to apply and the guide lines to show.
|
|
1294
|
+
*/
|
|
1295
|
+
snapTranslate(moving: BoxLike, exclude: ReadonlySet<ShapeId>, opts?: {
|
|
1296
|
+
lockX?: boolean;
|
|
1297
|
+
lockY?: boolean;
|
|
1298
|
+
}): SnapResult;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
interface EditorOptions {
|
|
1302
|
+
store: EditorStore;
|
|
1303
|
+
shapeUtils: readonly ShapeUtilConstructor[];
|
|
1304
|
+
bindingUtils?: readonly BindingUtilConstructor[];
|
|
1305
|
+
tools: readonly StateNodeConstructor[];
|
|
1306
|
+
engine: EngineBridge;
|
|
1307
|
+
/** Id of the tool to start in. Defaults to the first tool. */
|
|
1308
|
+
initialState?: string;
|
|
1309
|
+
getContainer: () => HTMLElement;
|
|
1310
|
+
options?: Partial<EditorConfig>;
|
|
1311
|
+
}
|
|
1312
|
+
interface EditorConfig {
|
|
1313
|
+
maxShapesPerPage: number;
|
|
1314
|
+
dragDistanceSquared: number;
|
|
1315
|
+
/** Hit-test tolerance in screen pixels. */
|
|
1316
|
+
hitTestMargin: number;
|
|
1317
|
+
zoomMin: number;
|
|
1318
|
+
zoomMax: number;
|
|
1319
|
+
zoomSteps: number[];
|
|
1320
|
+
backgroundColor: [number, number, number, number];
|
|
1321
|
+
animationMediumMs: number;
|
|
1322
|
+
}
|
|
1323
|
+
declare const DEFAULT_EDITOR_CONFIG: EditorConfig;
|
|
1324
|
+
interface EditorInputs {
|
|
1325
|
+
originPagePoint: Vec;
|
|
1326
|
+
originScreenPoint: Vec;
|
|
1327
|
+
previousPagePoint: Vec;
|
|
1328
|
+
previousScreenPoint: Vec;
|
|
1329
|
+
currentPagePoint: Vec;
|
|
1330
|
+
currentScreenPoint: Vec;
|
|
1331
|
+
keys: Set<string>;
|
|
1332
|
+
buttons: Set<number>;
|
|
1333
|
+
isPen: boolean;
|
|
1334
|
+
shiftKey: boolean;
|
|
1335
|
+
ctrlKey: boolean;
|
|
1336
|
+
altKey: boolean;
|
|
1337
|
+
metaKey: boolean;
|
|
1338
|
+
accelKey: boolean;
|
|
1339
|
+
isDragging: boolean;
|
|
1340
|
+
isPointing: boolean;
|
|
1341
|
+
isPinching: boolean;
|
|
1342
|
+
isEditing: boolean;
|
|
1343
|
+
isPanning: boolean;
|
|
1344
|
+
pointerVelocity: Vec;
|
|
1345
|
+
}
|
|
1346
|
+
interface HitTestOptions {
|
|
1347
|
+
margin?: number;
|
|
1348
|
+
hitInside?: boolean;
|
|
1349
|
+
hitLocked?: boolean;
|
|
1350
|
+
hitFrameInside?: boolean;
|
|
1351
|
+
filter?: (shape: UnknownShape) => boolean;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* The editor: document access, selection, camera, tool dispatch, and the
|
|
1355
|
+
* bridge that mirrors the current page into the WASM engine.
|
|
1356
|
+
*/
|
|
1357
|
+
declare class Editor extends EventEmitter<EditorEvents> {
|
|
1358
|
+
readonly store: EditorStore;
|
|
1359
|
+
readonly engine: EngineBridge;
|
|
1360
|
+
readonly history: HistoryManager<EditorRecord>;
|
|
1361
|
+
readonly root: StateNode;
|
|
1362
|
+
readonly shapeUtils: Readonly<Record<string, ShapeUtil>>;
|
|
1363
|
+
readonly bindingUtils: Readonly<Record<string, BindingUtil>>;
|
|
1364
|
+
readonly options: EditorConfig;
|
|
1365
|
+
readonly inputs: EditorInputs;
|
|
1366
|
+
readonly handles: HandleTable;
|
|
1367
|
+
/** GPU textures referenced by shape styles (images, rasterized text). */
|
|
1368
|
+
readonly textures: TextureManager;
|
|
1369
|
+
readonly snaps: SnapManager;
|
|
1370
|
+
readonly getContainer: () => HTMLElement;
|
|
1371
|
+
readonly sideEffects: EditorStore["sideEffects"];
|
|
1372
|
+
private readonly kindIds;
|
|
1373
|
+
private readonly _frameEpoch;
|
|
1374
|
+
private readonly _overlayShapeIds;
|
|
1375
|
+
private readonly _overlayClips;
|
|
1376
|
+
private readonly _isDisposed;
|
|
1377
|
+
private readonly _lastFrame;
|
|
1378
|
+
private readonly disposables;
|
|
1379
|
+
private syncedPageId;
|
|
1380
|
+
/** Shapes whose last write to the engine threw; they are warned about once. */
|
|
1381
|
+
private readonly brokenShapeIds;
|
|
1382
|
+
constructor(opts: EditorOptions);
|
|
1383
|
+
dispose(): void;
|
|
1384
|
+
getIsDisposed(): boolean;
|
|
1385
|
+
private ensureBaseRecords;
|
|
1386
|
+
private ensurePageSessionRecords;
|
|
1387
|
+
/** Increments whenever the GPU frame must be redrawn. */
|
|
1388
|
+
getFrameEpoch(): number;
|
|
1389
|
+
private bumpFrame;
|
|
1390
|
+
/** Stats for the last rendered frame. */
|
|
1391
|
+
getLastFrameStats(): {
|
|
1392
|
+
drawn: number;
|
|
1393
|
+
culled: number;
|
|
1394
|
+
ms: number;
|
|
1395
|
+
};
|
|
1396
|
+
/** Shapes the DOM overlay must render this frame, in draw order. */
|
|
1397
|
+
getOverlayShapeIds(): readonly ShapeId[];
|
|
1398
|
+
/**
|
|
1399
|
+
* Page-space clip rects for the overlay shapes, parallel to
|
|
1400
|
+
* `getOverlayShapeIds()`; `undefined` where the shape is unclipped.
|
|
1401
|
+
*/
|
|
1402
|
+
getOverlayClips(): readonly (ClipRect | undefined)[];
|
|
1403
|
+
run<T>(fn: () => T, opts?: {
|
|
1404
|
+
history?: "record" | "ignore" | "record-preserveRedoStack";
|
|
1405
|
+
}): T;
|
|
1406
|
+
/** @deprecated use `run` */
|
|
1407
|
+
batch<T>(fn: () => T): T;
|
|
1408
|
+
markHistoryStoppingPoint(name?: string): string;
|
|
1409
|
+
/** Alias of `markHistoryStoppingPoint`. */
|
|
1410
|
+
mark(name?: string): string;
|
|
1411
|
+
undo(): this;
|
|
1412
|
+
redo(): this;
|
|
1413
|
+
bail(): this;
|
|
1414
|
+
bailToMark(id: string): this;
|
|
1415
|
+
squashToMark(id: string): this;
|
|
1416
|
+
getCanUndo(): boolean;
|
|
1417
|
+
getCanRedo(): boolean;
|
|
1418
|
+
getDocumentSettings(): Document;
|
|
1419
|
+
updateDocumentSettings(settings: Partial<Omit<Document, "id" | "typeName">>): this;
|
|
1420
|
+
private readonly _pages;
|
|
1421
|
+
getPages(): Page[];
|
|
1422
|
+
getPage(id: PageId): Page | undefined;
|
|
1423
|
+
getInstanceState(): Instance;
|
|
1424
|
+
updateInstanceState(partial: Partial<Omit<Instance, "id" | "typeName">>): this;
|
|
1425
|
+
getCurrentPageId(): PageId;
|
|
1426
|
+
getCurrentPage(): Page;
|
|
1427
|
+
setCurrentPage(pageId: PageId): this;
|
|
1428
|
+
createPage(page?: Partial<Omit<Page, "id" | "typeName" | "index">> & {
|
|
1429
|
+
id?: PageId;
|
|
1430
|
+
}): this;
|
|
1431
|
+
deletePage(id: PageId): this;
|
|
1432
|
+
renamePage(id: PageId, name: string): this;
|
|
1433
|
+
getCurrentPageState(): InstancePageState;
|
|
1434
|
+
updateCurrentPageState(partial: Partial<Omit<InstancePageState, "id" | "typeName" | "pageId">>): this;
|
|
1435
|
+
private readonly _allShapes;
|
|
1436
|
+
private readonly _currentPageShapes;
|
|
1437
|
+
private readonly _currentPageShapeIds;
|
|
1438
|
+
getShape<T extends UnknownShape = UnknownShape>(id: ShapeId | T): T | undefined;
|
|
1439
|
+
getShapeUtil<T extends UnknownShape>(shape: T | T["type"]): ShapeUtil<T>;
|
|
1440
|
+
hasShapeUtil(type: string): boolean;
|
|
1441
|
+
getCurrentPageShapes(): UnknownShape[];
|
|
1442
|
+
getCurrentPageShapeIds(): Set<ShapeId>;
|
|
1443
|
+
getCurrentPageShapesSorted(): UnknownShape[];
|
|
1444
|
+
getPageShapeIds(pageId: PageId): ShapeId[];
|
|
1445
|
+
getAncestorPageId(shape: UnknownShape | ShapeId | undefined): PageId | undefined;
|
|
1446
|
+
getShapeParent(shape: UnknownShape | ShapeId): UnknownShape | undefined;
|
|
1447
|
+
getSortedChildIdsForParent(parentId: ParentId): ShapeId[];
|
|
1448
|
+
getHighestIndexForParent(parentId: ParentId): IndexKey;
|
|
1449
|
+
getShapeGeometry<G extends Geometry2d = Geometry2d>(shape: UnknownShape | ShapeId): G;
|
|
1450
|
+
/** Local → parent transform components. */
|
|
1451
|
+
getShapeLocalTransform(shape: UnknownShape): {
|
|
1452
|
+
a: number;
|
|
1453
|
+
b: number;
|
|
1454
|
+
c: number;
|
|
1455
|
+
d: number;
|
|
1456
|
+
e: number;
|
|
1457
|
+
f: number;
|
|
1458
|
+
};
|
|
1459
|
+
getShapeParentTransform(shape: UnknownShape): {
|
|
1460
|
+
a: number;
|
|
1461
|
+
b: number;
|
|
1462
|
+
c: number;
|
|
1463
|
+
d: number;
|
|
1464
|
+
e: number;
|
|
1465
|
+
f: number;
|
|
1466
|
+
};
|
|
1467
|
+
getShapePageTransform(shape: UnknownShape | ShapeId): {
|
|
1468
|
+
a: number;
|
|
1469
|
+
b: number;
|
|
1470
|
+
c: number;
|
|
1471
|
+
d: number;
|
|
1472
|
+
e: number;
|
|
1473
|
+
f: number;
|
|
1474
|
+
};
|
|
1475
|
+
getShapePageBounds(shape: UnknownShape | ShapeId): Box | undefined;
|
|
1476
|
+
getShapeGeometryBounds(shape: UnknownShape | ShapeId): Box | undefined;
|
|
1477
|
+
/** Transform a page point into the shape's local space. */
|
|
1478
|
+
getPointInShapeSpace(shape: UnknownShape | ShapeId, point: VecLike): Vec;
|
|
1479
|
+
getPointInParentSpace(shape: UnknownShape | ShapeId, point: VecLike): Vec;
|
|
1480
|
+
getShapeParentAtPoint(_point: VecLike): UnknownShape | undefined;
|
|
1481
|
+
private hitFilterBits;
|
|
1482
|
+
getShapeAtPoint(point: VecLike, opts?: HitTestOptions): UnknownShape | undefined;
|
|
1483
|
+
/** Shapes under a point, topmost first. */
|
|
1484
|
+
getShapesAtPoint(point: VecLike, opts?: HitTestOptions): UnknownShape[];
|
|
1485
|
+
/** Shapes fully inside a page box, in draw order. */
|
|
1486
|
+
getShapesInsideBounds(box: BoxLike, opts?: HitTestOptions): UnknownShape[];
|
|
1487
|
+
/** Shapes whose outline touches a page box, in draw order. */
|
|
1488
|
+
getShapesIntersectingBounds(box: BoxLike, opts?: HitTestOptions): UnknownShape[];
|
|
1489
|
+
private handlesToShapes;
|
|
1490
|
+
/**
|
|
1491
|
+
* Union of every shape's page bounds on the current page, or undefined when
|
|
1492
|
+
* the page is empty.
|
|
1493
|
+
*
|
|
1494
|
+
* Geometry, not ink: this is the union of `getShapePageBounds()` and excludes
|
|
1495
|
+
* the half-stroke pad the engine keeps for culling. The pad is per-shape, so
|
|
1496
|
+
* including it would both inflate the box and shift its centre whenever the
|
|
1497
|
+
* outermost shapes carry different stroke widths.
|
|
1498
|
+
*/
|
|
1499
|
+
getCurrentPageBounds(): Box | undefined;
|
|
1500
|
+
createShape<T extends UnknownShape>(partial: ShapeCreate<T>): this;
|
|
1501
|
+
createShapes<T extends UnknownShape>(partials: readonly ShapeCreate<T>[]): this;
|
|
1502
|
+
updateShape<T extends UnknownShape>(partial: ShapePartial<T> | null | undefined): this;
|
|
1503
|
+
updateShapes<T extends UnknownShape>(partials: readonly (ShapePartial<T> | null | undefined)[]): this;
|
|
1504
|
+
deleteShape(id: ShapeId | UnknownShape): this;
|
|
1505
|
+
deleteShapes(ids: readonly (ShapeId | UnknownShape)[]): this;
|
|
1506
|
+
reparentShapes(ids: readonly ShapeId[], parentId: ParentId, insertIndex?: IndexKey): this;
|
|
1507
|
+
private readonly _allBindings;
|
|
1508
|
+
getBindingUtil<B extends UnknownBinding>(binding: B | B["type"]): BindingUtil<B>;
|
|
1509
|
+
hasBindingUtil(type: string): boolean;
|
|
1510
|
+
getBinding<B extends UnknownBinding = UnknownBinding>(id: BindingId): B | undefined;
|
|
1511
|
+
getBindingsFromShape<B extends UnknownBinding = UnknownBinding>(shape: UnknownShape | ShapeId, type?: B["type"]): B[];
|
|
1512
|
+
getBindingsToShape<B extends UnknownBinding = UnknownBinding>(shape: UnknownShape | ShapeId, type?: B["type"]): B[];
|
|
1513
|
+
getBindingsInvolvingShape<B extends UnknownBinding = UnknownBinding>(shape: UnknownShape | ShapeId, type?: B["type"]): B[];
|
|
1514
|
+
createBinding<B extends UnknownBinding>(partial: BindingCreate<B>): this;
|
|
1515
|
+
createBindings<B extends UnknownBinding>(partials: readonly BindingCreate<B>[]): this;
|
|
1516
|
+
updateBinding<B extends UnknownBinding>(partial: BindingPartial<B>): this;
|
|
1517
|
+
updateBindings<B extends UnknownBinding>(partials: readonly BindingPartial<B>[]): this;
|
|
1518
|
+
deleteBinding(id: BindingId | UnknownBinding, opts?: {
|
|
1519
|
+
isolateShapes?: boolean;
|
|
1520
|
+
}): this;
|
|
1521
|
+
deleteBindings(ids: readonly (BindingId | UnknownBinding)[], opts?: {
|
|
1522
|
+
isolateShapes?: boolean;
|
|
1523
|
+
}): this;
|
|
1524
|
+
/** Store side effects that keep bindings consistent with their shapes. */
|
|
1525
|
+
private registerBindingSideEffects;
|
|
1526
|
+
/** Duplicate shapes (and their descendants) with new ids, offset by `offset` in page space. Returns the new top-level ids. */
|
|
1527
|
+
duplicateShapes(ids?: readonly ShapeId[], offset?: VecLike): ShapeId[];
|
|
1528
|
+
/** Serializable content for the clipboard: shapes (with descendants) and bindings between them. */
|
|
1529
|
+
getContentFromCurrentPage(ids: readonly ShapeId[]): {
|
|
1530
|
+
shapes: UnknownShape[];
|
|
1531
|
+
bindings: UnknownBinding[];
|
|
1532
|
+
} | undefined;
|
|
1533
|
+
/** Insert clipboard content at a page point (centered), with fresh ids. Returns the new ids. */
|
|
1534
|
+
putContentOntoCurrentPage(content: {
|
|
1535
|
+
shapes: UnknownShape[];
|
|
1536
|
+
bindings?: UnknownBinding[];
|
|
1537
|
+
}, opts?: {
|
|
1538
|
+
point?: VecLike;
|
|
1539
|
+
select?: boolean;
|
|
1540
|
+
}): ShapeId[];
|
|
1541
|
+
private readonly stylePropCache;
|
|
1542
|
+
/** Style props declared by the ShapeUtil for a type, keyed by prop name. */
|
|
1543
|
+
getStylePropsForType(type: string): Map<string, StyleProp<unknown>>;
|
|
1544
|
+
getStyleForNextShape<T>(style: StyleProp<T>): T;
|
|
1545
|
+
setStyleForNextShapes<T>(style: StyleProp<T>, value: T): this;
|
|
1546
|
+
/** Set a style on every selected shape that declares it. */
|
|
1547
|
+
setStyleForSelectedShapes<T>(style: StyleProp<T>, value: T): this;
|
|
1548
|
+
/** Styles of the selection (or of the next shape when nothing is selected). */
|
|
1549
|
+
getSharedStyles(): SharedStyleMap;
|
|
1550
|
+
/** Move shapes by a page-space offset. */
|
|
1551
|
+
nudgeShapes(ids: readonly ShapeId[], offset: VecLike): this;
|
|
1552
|
+
/** Rotate shapes by `delta` radians around the center of their common page bounds. */
|
|
1553
|
+
rotateShapesBy(ids: readonly ShapeId[], delta: number, center?: VecLike): this;
|
|
1554
|
+
/** Mirror shapes across the center of their common bounds. Positions flip; geometry is not mirrored. */
|
|
1555
|
+
flipShapes(ids: readonly ShapeId[], operation: "horizontal" | "vertical"): this;
|
|
1556
|
+
/** Align shapes along an edge or center of their common bounds. */
|
|
1557
|
+
alignShapes(ids: readonly ShapeId[], operation: "left" | "center-horizontal" | "right" | "top" | "center-vertical" | "bottom"): this;
|
|
1558
|
+
/** Space shapes evenly between the first and last along an axis. */
|
|
1559
|
+
distributeShapes(ids: readonly ShapeId[], operation: "horizontal" | "vertical"): this;
|
|
1560
|
+
/** Stack shapes edge to edge with a fixed gap along an axis (order by current position). */
|
|
1561
|
+
stackShapes(ids: readonly ShapeId[], operation: "horizontal" | "vertical", gap?: number): this;
|
|
1562
|
+
/** Toggle the locked state of shapes. */
|
|
1563
|
+
toggleLock(ids?: readonly ShapeId[]): this;
|
|
1564
|
+
/** Wrap shapes in a new `group` shape (requires a registered "group" ShapeUtil). Returns the group id. */
|
|
1565
|
+
groupShapes(ids?: readonly ShapeId[], groupId?: ShapeId): ShapeId | undefined;
|
|
1566
|
+
/** Dissolve groups, re-parenting their children to the group's parent. */
|
|
1567
|
+
ungroupShapes(ids?: readonly ShapeId[]): this;
|
|
1568
|
+
/** The outermost group containing a shape, or undefined. */
|
|
1569
|
+
getOutermostSelectableShape(shape: UnknownShape | ShapeId): UnknownShape | undefined;
|
|
1570
|
+
bringToFront(ids?: readonly ShapeId[]): this;
|
|
1571
|
+
sendToBack(ids?: readonly ShapeId[]): this;
|
|
1572
|
+
bringForward(ids?: readonly ShapeId[]): this;
|
|
1573
|
+
sendBackward(ids?: readonly ShapeId[]): this;
|
|
1574
|
+
private reorder;
|
|
1575
|
+
getSelectedShapeIds(): ShapeId[];
|
|
1576
|
+
getSelectedShapes(): UnknownShape[];
|
|
1577
|
+
getOnlySelectedShape(): UnknownShape | undefined;
|
|
1578
|
+
setSelectedShapes(ids: readonly (ShapeId | UnknownShape)[]): this;
|
|
1579
|
+
select(...ids: (ShapeId | UnknownShape)[]): this;
|
|
1580
|
+
selectAll(): this;
|
|
1581
|
+
selectNone(): this;
|
|
1582
|
+
isShapeOrAncestorLocked(shape: UnknownShape | ShapeId): boolean;
|
|
1583
|
+
private readonly _selectionPageBounds;
|
|
1584
|
+
getSelectionPageBounds(): Box | undefined;
|
|
1585
|
+
getSelectionRotation(): number;
|
|
1586
|
+
getHoveredShapeId(): ShapeId | null;
|
|
1587
|
+
getHoveredShape(): UnknownShape | undefined;
|
|
1588
|
+
setHoveredShape(id: ShapeId | UnknownShape | null): this;
|
|
1589
|
+
getEditingShapeId(): ShapeId | null;
|
|
1590
|
+
getEditingShape(): UnknownShape | undefined;
|
|
1591
|
+
setEditingShape(id: ShapeId | UnknownShape | null): this;
|
|
1592
|
+
setErasingShapes(ids: readonly ShapeId[]): this;
|
|
1593
|
+
getErasingShapeIds(): ShapeId[];
|
|
1594
|
+
private cameraId;
|
|
1595
|
+
getCamera(): Camera;
|
|
1596
|
+
getZoomLevel(): number;
|
|
1597
|
+
setCamera(point: Partial<VecModel>, _opts?: {
|
|
1598
|
+
animation?: {
|
|
1599
|
+
duration: number;
|
|
1600
|
+
};
|
|
1601
|
+
}): this;
|
|
1602
|
+
getViewportScreenBounds(): Box;
|
|
1603
|
+
getViewportScreenCenter(): Vec;
|
|
1604
|
+
getViewportPageBounds(): Box;
|
|
1605
|
+
getViewportPageCenter(): Vec;
|
|
1606
|
+
updateViewportScreenBounds(bounds: Box | BoxLike, center?: boolean): this;
|
|
1607
|
+
/** Screen (container-relative) → page. */
|
|
1608
|
+
screenToPage(point: VecLike): Vec;
|
|
1609
|
+
/** Page → screen (container-relative). */
|
|
1610
|
+
pageToScreen(point: VecLike): Vec;
|
|
1611
|
+
/** Zoom keeping the given screen point fixed. */
|
|
1612
|
+
zoomToPointAt(screenPoint: VecLike, nextZoom: number): this;
|
|
1613
|
+
zoomIn(point?: VecLike): this;
|
|
1614
|
+
zoomOut(point?: VecLike): this;
|
|
1615
|
+
resetZoom(point?: VecLike): this;
|
|
1616
|
+
zoomToBounds(bounds: BoxLike, opts?: {
|
|
1617
|
+
inset?: number;
|
|
1618
|
+
targetZoom?: number;
|
|
1619
|
+
}): this;
|
|
1620
|
+
zoomToFit(): this;
|
|
1621
|
+
zoomToSelection(): this;
|
|
1622
|
+
centerOnPoint(point: VecLike): this;
|
|
1623
|
+
pan(offsetScreen: VecLike): this;
|
|
1624
|
+
getCurrentTool(): StateNode;
|
|
1625
|
+
getCurrentToolId(): string;
|
|
1626
|
+
setCurrentTool(id: string, info?: Record<string, unknown>): this;
|
|
1627
|
+
getPath(): string;
|
|
1628
|
+
isIn(path: string): boolean;
|
|
1629
|
+
isInAny(...paths: string[]): boolean;
|
|
1630
|
+
getStateDescendant<T extends StateNode>(path: string): T | undefined;
|
|
1631
|
+
cancel(): this;
|
|
1632
|
+
complete(): this;
|
|
1633
|
+
interrupt(): this;
|
|
1634
|
+
private modifierState;
|
|
1635
|
+
/** Route an event to the tool tree, updating `inputs` first. */
|
|
1636
|
+
dispatch(info: EventInfo): this;
|
|
1637
|
+
private updatePointer;
|
|
1638
|
+
private handleWheel;
|
|
1639
|
+
private kindId;
|
|
1640
|
+
/** Apply pending engine commands. Cheap when nothing is pending. */
|
|
1641
|
+
flushEngine(): void;
|
|
1642
|
+
private syncChanges;
|
|
1643
|
+
/** Clear the engine and upload every shape on `pageId`. */
|
|
1644
|
+
private syncPage;
|
|
1645
|
+
/**
|
|
1646
|
+
* Write one shape to the engine, isolating it from its siblings.
|
|
1647
|
+
*
|
|
1648
|
+
* A shape util can throw on a shape it does not fully understand (a prop a
|
|
1649
|
+
* file left out, a value it did not expect). That must cost that one shape,
|
|
1650
|
+
* never the rest of the page: the failure is logged once and the shape is
|
|
1651
|
+
* skipped, so a single bad record can no longer blank the canvas.
|
|
1652
|
+
*/
|
|
1653
|
+
private writeShapeToEngine;
|
|
1654
|
+
private writeShapeToEngineUnsafe;
|
|
1655
|
+
/** Build and draw one frame. Called by the canvas component inside rAF. */
|
|
1656
|
+
renderFrame(backend: RenderBackend): FrameBuffers;
|
|
1657
|
+
/**
|
|
1658
|
+
* Device-pixel scale at which rasterized textures (text labels) should be
|
|
1659
|
+
* drawn: the device pixel ratio times the zoom, bucketed to powers of two.
|
|
1660
|
+
*/
|
|
1661
|
+
getTextureResolution(): number;
|
|
1662
|
+
private textureResolution;
|
|
1663
|
+
/** Re-derive texture-backed styles when the resolution bucket changes. */
|
|
1664
|
+
private syncTextureResolution;
|
|
1665
|
+
private registerTextureSideEffects;
|
|
1666
|
+
/** A texture finished (or failed) loading: re-write its shapes and redraw. */
|
|
1667
|
+
private onTexturesChanged;
|
|
1668
|
+
private rewriteTextureOwners;
|
|
1669
|
+
/** Degrees → radians. */
|
|
1670
|
+
static degToRad(d: number): number;
|
|
1671
|
+
private readonly _allAssets;
|
|
1672
|
+
getAsset<A extends Asset = Asset>(id: AssetId | A): A | undefined;
|
|
1673
|
+
/** Every asset in the document (assets are not per page). */
|
|
1674
|
+
getAssets(): Asset[];
|
|
1675
|
+
createAsset<A extends Asset>(asset: AssetCreate<A>): this;
|
|
1676
|
+
createAssets<A extends Asset>(assets: readonly AssetCreate<A>[]): this;
|
|
1677
|
+
updateAsset<A extends Asset>(partial: AssetPartial<A>): this;
|
|
1678
|
+
updateAssets<A extends Asset>(partials: readonly AssetPartial<A>[]): this;
|
|
1679
|
+
deleteAsset(id: AssetId | Asset): this;
|
|
1680
|
+
deleteAssets(ids: readonly (AssetId | Asset)[]): this;
|
|
1681
|
+
private readonly externalContentHandlers;
|
|
1682
|
+
private readonly externalAssetHandlers;
|
|
1683
|
+
/**
|
|
1684
|
+
* Register the handler for one kind of dropped/pasted content. Replaces any
|
|
1685
|
+
* previous handler for that type; `null` removes it. Returns a function that
|
|
1686
|
+
* removes the handler again (only if it is still the registered one).
|
|
1687
|
+
*/
|
|
1688
|
+
registerExternalContentHandler<T extends ExternalContentType>(type: T, handler: ExternalContentHandler<T> | null): () => void;
|
|
1689
|
+
/** Register how an asset record is produced from a file or url. Returns a function that removes it again. */
|
|
1690
|
+
registerExternalAssetHandler<T extends ExternalAssetType>(type: T, handler: ExternalAssetHandler<T> | null): () => void;
|
|
1691
|
+
hasExternalContentHandler(type: ExternalContentType): boolean;
|
|
1692
|
+
hasExternalAssetHandler(type: ExternalAssetType): boolean;
|
|
1693
|
+
/**
|
|
1694
|
+
* Handle content dropped or pasted onto the canvas by dispatching to the
|
|
1695
|
+
* registered handler for `info.type`. Resolves once the handler is done;
|
|
1696
|
+
* resolves immediately when no handler is registered.
|
|
1697
|
+
*/
|
|
1698
|
+
putExternalContent(info: ExternalContent): Promise<void>;
|
|
1699
|
+
/**
|
|
1700
|
+
* Produce (but do not store) an asset record for a file or url through the
|
|
1701
|
+
* registered asset handler. `undefined` when there is no handler or the
|
|
1702
|
+
* handler declines the content.
|
|
1703
|
+
*/
|
|
1704
|
+
getAssetForExternalContent(info: ExternalAssetContent): Promise<Asset | undefined>;
|
|
1705
|
+
/**
|
|
1706
|
+
* The local person's identity: a random id for this session plus a name and
|
|
1707
|
+
* a colour that can be changed at any time. Session-only, never persisted.
|
|
1708
|
+
*/
|
|
1709
|
+
readonly user: UserPreferences;
|
|
1710
|
+
/** Presence records of everyone else in the room, in arrival order. */
|
|
1711
|
+
getCollaborators(): InstancePresence[];
|
|
1712
|
+
/** The subset of `getCollaborators()` looking at the page we are on. */
|
|
1713
|
+
getCollaboratorsOnCurrentPage(): InstancePresence[];
|
|
1714
|
+
/**
|
|
1715
|
+
* Scale one shape by `scale` about a point, letting its `ShapeUtil.onResize`
|
|
1716
|
+
* produce the prop change — the same contract the select tool's resize state
|
|
1717
|
+
* uses. The shape's origin is moved by the same scale about `scaleOrigin`
|
|
1718
|
+
* (its page bounds center by default), measured in a frame rotated by
|
|
1719
|
+
* `scaleAxisRotation`.
|
|
1720
|
+
*
|
|
1721
|
+
* Locked shapes and shapes whose util says `canResize` is false are left
|
|
1722
|
+
* alone. `onResizeStart` / `onResizeEnd` bracket the change; the util is
|
|
1723
|
+
* told the resize came from the `bottom_right` handle.
|
|
1724
|
+
*/
|
|
1725
|
+
resizeShape(id: ShapeId | UnknownShape, scale: VecLike, options?: ResizeShapeOptions): this;
|
|
1726
|
+
/**
|
|
1727
|
+
* Scale several shapes by the same factor about one point — by default the
|
|
1728
|
+
* center of their common page bounds, so the group scales as a unit.
|
|
1729
|
+
*/
|
|
1730
|
+
resizeShapes(ids: readonly (ShapeId | UnknownShape)[], scale: VecLike, options?: Omit<ResizeShapeOptions, "initialBounds">): this;
|
|
1731
|
+
/**
|
|
1732
|
+
* Resize shapes so each one spans the common bounds of all of them on one
|
|
1733
|
+
* axis. Every shape given contributes to the common bounds, but only the
|
|
1734
|
+
* unlocked, resizable ones are stretched.
|
|
1735
|
+
*/
|
|
1736
|
+
stretchShapes(ids: readonly ShapeId[], operation: "horizontal" | "vertical"): this;
|
|
1737
|
+
/**
|
|
1738
|
+
* Serialize shapes to an SVG string; `undefined` when there is nothing to
|
|
1739
|
+
* export. Defaults to the selection, or the whole page when nothing is
|
|
1740
|
+
* selected. Throws until an export implementation is registered.
|
|
1741
|
+
*/
|
|
1742
|
+
getSvgString(ids?: readonly ShapeId[], opts?: EditorSvgExportOptions): EditorSvgExportResult | undefined;
|
|
1743
|
+
/**
|
|
1744
|
+
* Render shapes to an image blob (`png` by default). Same shape selection
|
|
1745
|
+
* rules as `getSvgString`. Throws until an export implementation is
|
|
1746
|
+
* registered.
|
|
1747
|
+
*/
|
|
1748
|
+
toImage(ids?: readonly ShapeId[], opts?: EditorImageExportOptions): Promise<EditorImageExportResult>;
|
|
1749
|
+
/**
|
|
1750
|
+
* Measures runs of text the way the editor renders them. Installed through
|
|
1751
|
+
* the same seam as the export functions; throws until something registers
|
|
1752
|
+
* one.
|
|
1753
|
+
*/
|
|
1754
|
+
get textMeasure(): EditorTextMeasure;
|
|
1755
|
+
/** Which menus are open right now. Backed by the `instance` session record. */
|
|
1756
|
+
readonly menus: MenuManager;
|
|
1757
|
+
}
|
|
1758
|
+
interface ResizeShapeOptions {
|
|
1759
|
+
/** Shape-local bounds the scale is measured against. Defaults to the shape's geometry bounds. */
|
|
1760
|
+
initialBounds?: {
|
|
1761
|
+
x: number;
|
|
1762
|
+
y: number;
|
|
1763
|
+
w: number;
|
|
1764
|
+
h: number;
|
|
1765
|
+
};
|
|
1766
|
+
/** Page point that stays put. Defaults to the center of the shape's page bounds. */
|
|
1767
|
+
scaleOrigin?: VecLike;
|
|
1768
|
+
/** Rotation (radians) of the frame the scale axes are measured in. Defaults to 0. */
|
|
1769
|
+
scaleAxisRotation?: number;
|
|
1770
|
+
/** Force (or forbid) a uniform scale. Defaults to the util's `isAspectRatioLocked`. */
|
|
1771
|
+
isAspectRatioLocked?: boolean;
|
|
1772
|
+
/** Handed to `onResize`. Defaults to `"scale_shape"`. */
|
|
1773
|
+
mode?: "scale_shape" | "resize_bounds";
|
|
1774
|
+
}
|
|
1775
|
+
interface EditorSvgExportOptions {
|
|
1776
|
+
/** Page units added around the shapes' bounds. */
|
|
1777
|
+
padding?: number;
|
|
1778
|
+
/** Paint a full-size background rectangle. */
|
|
1779
|
+
background?: boolean;
|
|
1780
|
+
/** Multiplier applied to the output `width`/`height`. */
|
|
1781
|
+
scale?: number;
|
|
1782
|
+
/** Use the dark background colour. */
|
|
1783
|
+
darkMode?: boolean;
|
|
1784
|
+
}
|
|
1785
|
+
interface EditorSvgExportResult {
|
|
1786
|
+
svg: string;
|
|
1787
|
+
/** Output size in CSS pixels (bounds × scale). */
|
|
1788
|
+
width: number;
|
|
1789
|
+
height: number;
|
|
1790
|
+
}
|
|
1791
|
+
interface EditorImageExportOptions extends EditorSvgExportOptions {
|
|
1792
|
+
/** Output format. Implementations default to `"png"`. */
|
|
1793
|
+
format?: "svg" | "png" | "jpeg" | "webp";
|
|
1794
|
+
/** Encoder quality for lossy formats (0..1). */
|
|
1795
|
+
quality?: number;
|
|
1796
|
+
/** Device pixel ratio multiplier for raster output. */
|
|
1797
|
+
pixelRatio?: number;
|
|
1798
|
+
}
|
|
1799
|
+
interface EditorImageExportResult {
|
|
1800
|
+
blob: Blob;
|
|
1801
|
+
width: number;
|
|
1802
|
+
height: number;
|
|
1803
|
+
}
|
|
1804
|
+
interface EditorExportImplementation {
|
|
1805
|
+
getSvgString(editor: Editor, ids?: readonly ShapeId[], opts?: EditorSvgExportOptions): EditorSvgExportResult | undefined;
|
|
1806
|
+
toImage(editor: Editor, ids?: readonly ShapeId[], opts?: EditorImageExportOptions): Promise<EditorImageExportResult>;
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Install the implementation behind `Editor.getSvgString` and `Editor.toImage`.
|
|
1810
|
+
* `mocanvas` calls this at import time; pass `null` to remove it. Returns a
|
|
1811
|
+
* function that removes the implementation again (only if it is still the
|
|
1812
|
+
* registered one).
|
|
1813
|
+
*/
|
|
1814
|
+
declare function registerExportImplementation(impl: EditorExportImplementation | null): () => void;
|
|
1815
|
+
/** The registered export implementation, or `null` when nothing has registered one. */
|
|
1816
|
+
declare function getExportImplementation(): EditorExportImplementation | null;
|
|
1817
|
+
interface EditorTextMeasureOptions {
|
|
1818
|
+
fontFamily: string;
|
|
1819
|
+
fontSize: number;
|
|
1820
|
+
fontWeight?: string | number;
|
|
1821
|
+
/** Unitless line height (multiplier of the font size). */
|
|
1822
|
+
lineHeight: number;
|
|
1823
|
+
/** Wrap width in CSS px, including `padding`. Omit for a single unwrapped run per paragraph. */
|
|
1824
|
+
maxWidth?: number;
|
|
1825
|
+
/** Padding applied on every side; included in the returned `w`/`h`. */
|
|
1826
|
+
padding?: number;
|
|
1827
|
+
}
|
|
1828
|
+
interface EditorTextMeasurement {
|
|
1829
|
+
w: number;
|
|
1830
|
+
h: number;
|
|
1831
|
+
lineCount: number;
|
|
1832
|
+
}
|
|
1833
|
+
interface EditorTextMeasure {
|
|
1834
|
+
measureText(text: string, opts: EditorTextMeasureOptions): EditorTextMeasurement;
|
|
1835
|
+
}
|
|
1836
|
+
type EditorTextMeasureProvider = (editor: Editor) => EditorTextMeasure;
|
|
1837
|
+
/**
|
|
1838
|
+
* Install the measurer behind `Editor.textMeasure`. `mocanvas` calls this at
|
|
1839
|
+
* import time; pass `null` to remove it. Returns a function that removes the
|
|
1840
|
+
* provider again (only if it is still the registered one).
|
|
1841
|
+
*/
|
|
1842
|
+
declare function registerTextMeasureImplementation(provider: EditorTextMeasureProvider | null): () => void;
|
|
1843
|
+
/** The registered text measure provider, or `null` when nothing has registered one. */
|
|
1844
|
+
declare function getTextMeasureProvider(): EditorTextMeasureProvider | null;
|
|
1845
|
+
/**
|
|
1846
|
+
* The set of menus that are open, kept in the `instance` session record so it
|
|
1847
|
+
* survives tool changes, is visible to anything reading the store, and is
|
|
1848
|
+
* reactive: reading `getOpenMenus()` inside a signal re-runs it on every change.
|
|
1849
|
+
*
|
|
1850
|
+
* Ids are opaque strings owned by the UI; nothing here interprets them.
|
|
1851
|
+
*/
|
|
1852
|
+
declare class MenuManager {
|
|
1853
|
+
private readonly editor;
|
|
1854
|
+
constructor(editor: Editor);
|
|
1855
|
+
/** The open menu ids, in the order they were opened. */
|
|
1856
|
+
getOpenMenus(): string[];
|
|
1857
|
+
isMenuOpen(id: string): boolean;
|
|
1858
|
+
/** Mark a menu as open. Opening an already-open menu changes nothing. */
|
|
1859
|
+
addOpenMenu(id: string): this;
|
|
1860
|
+
/** Mark a menu as closed. Closing a menu that is not open changes nothing. */
|
|
1861
|
+
removeOpenMenu(id: string): this;
|
|
1862
|
+
/** Close every open menu. */
|
|
1863
|
+
clearOpenMenus(): this;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Half the size of a selection handle's pointer target, in screen pixels.
|
|
1868
|
+
* 12 gives a 24x24 target, the smallest that stays comfortable on a trackpad
|
|
1869
|
+
* and on touch. It shrinks on small selections — see `getHandleHitRadius`.
|
|
1870
|
+
*/
|
|
1871
|
+
declare const HANDLE_HIT_RADIUS = 12;
|
|
1872
|
+
declare const ROTATE_HANDLE_OFFSET = 20;
|
|
1873
|
+
/**
|
|
1874
|
+
* The effective hit radius for the current selection. On a selection smaller
|
|
1875
|
+
* than a few handles across, full-size targets would cover the whole shape and
|
|
1876
|
+
* leave nothing to drag, so the radius shrinks to keep an interior free.
|
|
1877
|
+
*/
|
|
1878
|
+
declare function getHandleHitRadius(screenW: number, screenH: number): number;
|
|
1879
|
+
interface SelectionHandleHit {
|
|
1880
|
+
handle: SelectionHandle;
|
|
1881
|
+
/** Screen-space position of the handle. */
|
|
1882
|
+
point: Vec;
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Screen-space positions of the selection handles for the current selection,
|
|
1886
|
+
* or null if nothing is selected / handles are hidden.
|
|
1887
|
+
*/
|
|
1888
|
+
declare function getSelectionHandlePositions(editor: Editor): {
|
|
1889
|
+
bounds: Box;
|
|
1890
|
+
rotation: number;
|
|
1891
|
+
handles: SelectionHandleHit[];
|
|
1892
|
+
screenW: number;
|
|
1893
|
+
screenH: number;
|
|
1894
|
+
} | null;
|
|
1895
|
+
/** The selection handle under a screen point, if any. */
|
|
1896
|
+
declare function hitTestSelectionHandles(editor: Editor, screenPoint: VecLike, radius?: number): SelectionHandle | undefined;
|
|
1897
|
+
/** Is the screen point inside the selection bounds (for dragging the selection as a whole)? */
|
|
1898
|
+
declare function hitTestSelectionBounds(editor: Editor, pagePoint: VecLike): boolean;
|
|
1899
|
+
|
|
1900
|
+
/**
|
|
1901
|
+
* Normalization for records read out of a `.tldr` file.
|
|
1902
|
+
*
|
|
1903
|
+
* Files written by different generations of the format spell the same
|
|
1904
|
+
* information in different ways: a shape's label may arrive as a plain string
|
|
1905
|
+
* or as a rich-text document, and a freehand stroke's points may arrive as an
|
|
1906
|
+
* array or as one packed binary blob. Nothing downstream should have to know
|
|
1907
|
+
* that, so the load path runs every record through here first and the shape
|
|
1908
|
+
* utils only ever see the props they declare.
|
|
1909
|
+
*
|
|
1910
|
+
* Everything in this module is pure and total: it never throws, and whatever it
|
|
1911
|
+
* cannot make sense of is reported as a warning string instead.
|
|
1912
|
+
*/
|
|
1913
|
+
|
|
1914
|
+
/**
|
|
1915
|
+
* Flatten a rich-text document to the plain string mocanvas stores in
|
|
1916
|
+
* `props.text`.
|
|
1917
|
+
*
|
|
1918
|
+
* The document is a tree of `{ type, content?, text? }` nodes. `text` nodes
|
|
1919
|
+
* contribute their string, `hardBreak` nodes a newline, top-level paragraphs
|
|
1920
|
+
* are joined by newlines, and any other node is recursed into so unknown marks
|
|
1921
|
+
* or wrappers still yield their text. `unknownNodeTypes`, when passed, collects
|
|
1922
|
+
* the node types that were not understood.
|
|
1923
|
+
*/
|
|
1924
|
+
declare function richTextToPlainText(doc: unknown, unknownNodeTypes?: Set<string>): string;
|
|
1925
|
+
/** A point of a freehand stroke: position plus pen pressure. */
|
|
1926
|
+
interface DecodedDrawPoint {
|
|
1927
|
+
x: number;
|
|
1928
|
+
y: number;
|
|
1929
|
+
z: number;
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Decode a packed freehand segment path into its points, or `null` when the
|
|
1933
|
+
* blob is not in the layout we understand.
|
|
1934
|
+
*
|
|
1935
|
+
* Layout, as observed in the files themselves: base64 of a little-endian
|
|
1936
|
+
* buffer holding the first point as three `float32` (x, y, pressure) followed
|
|
1937
|
+
* by one `float16` triple per further point, each a delta from the previous
|
|
1938
|
+
* point.
|
|
1939
|
+
*/
|
|
1940
|
+
declare function decodeDrawSegmentPath(path: string): DecodedDrawPoint[] | null;
|
|
1941
|
+
interface NormalizeOptions {
|
|
1942
|
+
/** The registered shape utils, keyed by shape type. Used for default props. */
|
|
1943
|
+
shapeUtils: Readonly<Record<string, Pick<ShapeUtil, "getDefaultProps">>>;
|
|
1944
|
+
}
|
|
1945
|
+
interface NormalizeResult {
|
|
1946
|
+
records: UnknownRecord[];
|
|
1947
|
+
warnings: string[];
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Rewrite loaded records so the shape utils see the props they declare.
|
|
1951
|
+
*
|
|
1952
|
+
* Per shape: `props.richText` becomes `props.text` (the rich-text document is
|
|
1953
|
+
* dropped, not kept alongside), packed freehand `segments[].path` becomes
|
|
1954
|
+
* `segments[].points`, and any prop the util declares a default for but the
|
|
1955
|
+
* file omits is filled in from that default. Props the util does not declare
|
|
1956
|
+
* are left untouched so a round trip preserves them, and shapes of a type with
|
|
1957
|
+
* no registered util are passed through unchanged.
|
|
1958
|
+
*
|
|
1959
|
+
* Records are only copied when something actually changes.
|
|
1960
|
+
*/
|
|
1961
|
+
declare function normalizeLoadedRecords(records: readonly UnknownRecord[], options: NormalizeOptions): NormalizeResult;
|
|
1962
|
+
|
|
1963
|
+
/** The root of the tool tree. Its children are the tools. */
|
|
1964
|
+
declare class RootState extends StateNode {
|
|
1965
|
+
static id: string;
|
|
1966
|
+
static initial: string;
|
|
1967
|
+
static children: () => StateNodeConstructor[];
|
|
1968
|
+
onKeyDown(info: KeyboardEventInfo): void;
|
|
1969
|
+
}
|
|
1970
|
+
/** Build a root state class with the given tools and initial tool id. */
|
|
1971
|
+
declare function createRootState(tools: readonly StateNodeConstructor[], initial: string): StateNodeConstructor;
|
|
1972
|
+
|
|
1973
|
+
/** WebGL2 backend: one interleaved VBO, one IBO, one program, one draw call per batch. */
|
|
1974
|
+
declare class WebGL2Backend implements RenderBackend {
|
|
1975
|
+
readonly canvas: HTMLCanvasElement;
|
|
1976
|
+
readonly kind: "webgl2";
|
|
1977
|
+
private readonly gl;
|
|
1978
|
+
private readonly program;
|
|
1979
|
+
private readonly vao;
|
|
1980
|
+
private readonly vbo;
|
|
1981
|
+
private readonly ibo;
|
|
1982
|
+
private readonly uCam;
|
|
1983
|
+
private readonly uVp;
|
|
1984
|
+
/** Texture 0: 1×1 opaque white, so solid batches use the same shader. */
|
|
1985
|
+
private readonly whiteTex;
|
|
1986
|
+
private readonly textures;
|
|
1987
|
+
private vboBytes;
|
|
1988
|
+
private iboBytes;
|
|
1989
|
+
/**
|
|
1990
|
+
* `FrameBuffers.version` currently sitting in the VBO/IBO, or -1 when they hold
|
|
1991
|
+
* nothing usable. The engine's vertex data is page-space, so panning and zooming
|
|
1992
|
+
* change only the camera uniform: while the version is unchanged this skips the
|
|
1993
|
+
* (multi-megabyte, at scale) `bufferSubData` and just re-issues the draw calls.
|
|
1994
|
+
*/
|
|
1995
|
+
private uploadedVersion;
|
|
1996
|
+
private width;
|
|
1997
|
+
private height;
|
|
1998
|
+
private pixelWidth;
|
|
1999
|
+
private pixelHeight;
|
|
2000
|
+
private dpr;
|
|
2001
|
+
private disposed;
|
|
2002
|
+
constructor(canvas: HTMLCanvasElement, options?: {
|
|
2003
|
+
antialias?: boolean;
|
|
2004
|
+
});
|
|
2005
|
+
resize(width: number, height: number, dpr: number): void;
|
|
2006
|
+
uploadTexture(id: number, source: TextureSource, opts?: TextureOptions): void;
|
|
2007
|
+
deleteTexture(id: number): void;
|
|
2008
|
+
draw(frame: FrameBuffers, camera: CameraState, options: DrawOptions): void;
|
|
2009
|
+
dispose(): void;
|
|
2010
|
+
}
|
|
2011
|
+
/**
|
|
2012
|
+
* Create the synchronous WebGL2 backend. For WebGPU (async device setup) or
|
|
2013
|
+
* automatic selection use `createBackendAsync` from `./createBackend`.
|
|
2014
|
+
*/
|
|
2015
|
+
declare function createBackend(canvas: HTMLCanvasElement): RenderBackend;
|
|
2016
|
+
|
|
2017
|
+
interface CanvasProps {
|
|
2018
|
+
editor: Editor;
|
|
2019
|
+
className?: string;
|
|
2020
|
+
style?: CSSProperties;
|
|
2021
|
+
/** Extra layers rendered above the canvas (UI, menus). */
|
|
2022
|
+
children?: ReactNode;
|
|
2023
|
+
/** Override the default selection/brush indicators. */
|
|
2024
|
+
components?: Partial<CanvasComponents>;
|
|
2025
|
+
}
|
|
2026
|
+
interface CanvasComponents {
|
|
2027
|
+
Indicators: (props: {
|
|
2028
|
+
editor: Editor;
|
|
2029
|
+
}) => ReactNode;
|
|
2030
|
+
Brush: (props: {
|
|
2031
|
+
editor: Editor;
|
|
2032
|
+
}) => ReactNode;
|
|
2033
|
+
Background: (props: {
|
|
2034
|
+
editor: Editor;
|
|
2035
|
+
}) => ReactNode;
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* The canvas: a WebGL2 surface driven by the engine, a DOM overlay for shapes
|
|
2039
|
+
* that render through `ShapeUtil.component`, and an SVG layer for indicators.
|
|
2040
|
+
*/
|
|
2041
|
+
declare function Canvas({ editor, className, style, children, components }: CanvasProps): react.JSX.Element;
|
|
2042
|
+
/**
|
|
2043
|
+
* The outline to draw over one shape: whatever its util's `indicator()`
|
|
2044
|
+
* returns, or a rectangle around the shape's geometry bounds when the util has
|
|
2045
|
+
* no indicator (or returns nothing). Both are expressed in *shape-local*
|
|
2046
|
+
* coordinates; the caller wraps them in the shape's page transform.
|
|
2047
|
+
*
|
|
2048
|
+
* Pure and exported so a custom `Indicators` component can reuse the same rule
|
|
2049
|
+
* instead of guessing at it.
|
|
2050
|
+
*/
|
|
2051
|
+
declare function getShapeIndicatorNode<T extends UnknownShape>(util: {
|
|
2052
|
+
indicator?(shape: T): ReactNode;
|
|
2053
|
+
}, shape: T, bounds: {
|
|
2054
|
+
x: number;
|
|
2055
|
+
y: number;
|
|
2056
|
+
w: number;
|
|
2057
|
+
h: number;
|
|
2058
|
+
} | undefined): ReactNode;
|
|
2059
|
+
|
|
2060
|
+
declare const EditorContext: react.Context<Editor | null>;
|
|
2061
|
+
/** The editor of the nearest `<EditorProvider>` / `<Canvas>`. */
|
|
2062
|
+
declare function useEditor(): Editor;
|
|
2063
|
+
declare function useMaybeEditor(): Editor | null;
|
|
2064
|
+
declare const EditorProvider: react.Provider<Editor | null>;
|
|
2065
|
+
|
|
2066
|
+
export { type Asset, type AssetCreate, type AssetId, type AssetPartial, AssetRecordType, type AssetType, type BaseAsset, type BaseBinding, BaseBoxShapeUtil, type BaseEventInfo, type BaseShape, type Binding, type BindingCreate, type BindingId, type BindingOnChangeOptions, type BindingOnCreateOptions, type BindingOnShapeChangeOptions, type BindingOnShapeDeleteOptions, type BindingOnShapeIsolateOptions, type BindingPartial, BindingRecordType, BindingUtil, type BindingUtilConstructor, type BookmarkAsset, type BookmarkAssetProps, Box, type BoxLike, type Camera, type CameraId, CameraRecordType, type CancelEventInfo, Canvas, type CanvasComponents, type CanvasProps, Circle2d, type ClickEventInfo, type ClickEventName, type DefaultColorStyle$1 as ColorValue, type CompleteEventInfo, type CreateStoreOptions, CubicSpline2d, DEFAULT_COLORS, DEFAULT_DASHES, DEFAULT_EDITOR_CONFIG, DEFAULT_FILLS, DEFAULT_FONTS, DEFAULT_H_ALIGNS, DEFAULT_SIZES, DEFAULT_V_ALIGNS, DOCUMENT_ID, type DefaultDashStyle$1 as DashValue, type DecodedDrawPoint, DefaultColorStyle, DefaultDashStyle, DefaultFillStyle, DefaultFontStyle, DefaultHorizontalAlignStyle, DefaultLabelColorStyle, DefaultSizeStyle, DefaultVerticalAlignStyle, type Document, type DocumentId, DocumentRecordType, type DrawOptions, EVENT_NAME_MAP, Edge2d, Editor, type EditorConfig, EditorContext, type EditorEvents, type EditorExportImplementation, type EditorImageExportOptions, type EditorImageExportResult, type EditorInputs, type EditorOptions, EditorProvider, type EditorRecord, type EditorStore, type EditorStoreProps, type EditorStoreSnapshot, type EditorSvgExportOptions, type EditorSvgExportResult, type EditorTextMeasure, type EditorTextMeasureOptions, type EditorTextMeasureProvider, type EditorTextMeasurement, Ellipse2d, EnumStyleProp, EventEmitter, type EventHandlers, type EventInfo, type ExternalAssetContent, type ExternalAssetHandler, type ExternalAssetType, type ExternalContent, type ExternalContentHandler, type ExternalContentType, FONT_SIZES, type DefaultFillStyle$1 as FillValue, type DefaultFontStyle$1 as FontValue, GEO_SHAPE_KINDS, GeoShapeGeoStyle, type GeoShapeKind, Geometry2d, type Geometry2dOptions, Group2d, HANDLE_HIT_RADIUS, type DefaultHorizontalAlignStyle$1 as HAlignValue, HandleTable, HistoryManager, type HitTestOptions, INSTANCE_ID, type ImageAsset, type ImageAssetProps, type Instance, type InstanceId, type InstancePageState, type InstancePageStateId, InstancePageStateRecordType, type InstancePresence, type InstancePresenceId, InstancePresenceRecordType, InstanceRecordType, type InterruptEventInfo, type JsonObject, type JsonPrimitive, type JsonValue, type KeyboardEventInfo, type KeyboardEventName, LIGHT_THEME, MAX_TEXTURE_RESOLUTION, MAX_TEXTURE_ZOOM, MenuManager, type NormalizeOptions, type NormalizeResult, PRESENCE_COLORS, type Page, type PageId, PageRecordType, type ParentId, type PinchEventInfo, type PointerEventInfo, type PointerEventName, type PointerTarget, Polygon2d, Polyline2d, type RGBA, ROTATE_HANDLE_OFFSET, Rectangle2d, type RenderBackend, type ResizeInfo, type ResizeShapeOptions, RootState, STROKE_SIZES, type Scribble, type SelectionHandle, type SelectionHandleHit, type Shape, type ShapeCreate, type ShapeHandle, type ShapeId, type ShapePartial, ShapeRecordType, type ShapeSvgContext, type ShapeSvgResult, ShapeUtil, type ShapeUtilConstructor, type SharedStyle, SharedStyleMap, type DefaultSizeStyle$1 as SizeValue, type SnapLine, SnapManager, type SnapResult, StateNode, type StateNodeClass, type StateNodeConstructor, type StateNodeType, StyleProp, type TextureInfo, type TextureLoader, TextureManager, type TextureManagerOptions, type TextureOptions, type TextureSource, type TextureState, type ThemeColor, type TickEventInfo, type TranslateInfo, type UiEventType, type UnknownBinding, type UnknownShape, type UserPreferences, type UserPreferencesInit, type DefaultVerticalAlignStyle$1 as VAlignValue, Vec, type VecLike, type VecModel, type VideoAsset, type VideoAssetProps, WebGL2Backend, type WheelEventInfo, bucketTextureResolution, createAssetId, createBackend, createBindingId, createRootState, createSchema, createShapeId, createStore, createUserPreferences, decodeDrawSegmentPath, getExportImplementation, getHandleHitRadius, getSelectionHandlePositions, getShapeIndicatorNode, getStylePropsOf, getTextMeasureProvider, hexToRgba, hitTestSelectionBounds, hitTestSelectionHandles, isAsset, isAssetId, isBinding, isBindingId, isInstancePresenceId, isPageId, isShape, isShapeId, normalizeLoadedRecords, pointInPolygon, randomPresenceColor, registerExportImplementation, registerTextMeasureImplementation, richTextToPlainText, useEditor, useMaybeEditor };
|