@mocanvas/mocanvas 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 +114 -0
- package/dist/index.d.ts +1969 -0
- package/dist/index.js +6422 -0
- package/dist/index.js.map +1 -0
- package/dist/ui-62NV5N7K.css +550 -0
- package/package.json +77 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1969 @@
|
|
|
1
|
+
import * as _mocanvas_editor from '@mocanvas/editor';
|
|
2
|
+
import { BaseShape, GeoShapeKind, DefaultColorStyle, DefaultFillStyle, DefaultDashStyle, DefaultSizeStyle, DefaultFontStyle, DefaultHorizontalAlignStyle, DefaultVerticalAlignStyle, BaseBoxShapeUtil, Geometry2d, StyleWords, ShapeUtil, ResizeInfo, VecLike, ShapeHandle, Vec, Editor, UnknownShape, AssetId, BookmarkAsset, EnumStyleProp, ShapeUtilConstructor, BaseBinding, BindingUtil, BindingOnShapeChangeOptions, BindingOnShapeDeleteOptions, BindingOnShapeIsolateOptions, BindingUtilConstructor, StateNode, StateNodeConstructor, EditorStore, CanvasProps, Box, ShapeId, ShapeSvgContext, ShapeSvgResult, ExternalContent, ImageAsset } from '@mocanvas/editor';
|
|
3
|
+
export * from '@mocanvas/editor';
|
|
4
|
+
import * as react from 'react';
|
|
5
|
+
import { ReactNode, CSSProperties, ReactElement, RefObject } from 'react';
|
|
6
|
+
import * as _mocanvas_state from '@mocanvas/state';
|
|
7
|
+
import { ParseTldrFileResult } from '@mocanvas/store';
|
|
8
|
+
|
|
9
|
+
interface GeoShapeProps {
|
|
10
|
+
geo: GeoShapeKind;
|
|
11
|
+
w: number;
|
|
12
|
+
h: number;
|
|
13
|
+
color: DefaultColorStyle;
|
|
14
|
+
labelColor: DefaultColorStyle;
|
|
15
|
+
fill: DefaultFillStyle;
|
|
16
|
+
dash: DefaultDashStyle;
|
|
17
|
+
size: DefaultSizeStyle;
|
|
18
|
+
font: DefaultFontStyle;
|
|
19
|
+
align: DefaultHorizontalAlignStyle;
|
|
20
|
+
verticalAlign: DefaultVerticalAlignStyle;
|
|
21
|
+
growY: number;
|
|
22
|
+
url: string;
|
|
23
|
+
text: string;
|
|
24
|
+
scale: number;
|
|
25
|
+
}
|
|
26
|
+
type GeoShape = BaseShape<"geo", GeoShapeProps>;
|
|
27
|
+
declare const GEO_LABEL_PADDING = 16;
|
|
28
|
+
declare function horizontalAlignToFlex(align: DefaultHorizontalAlignStyle): CSSProperties["justifyContent"];
|
|
29
|
+
declare function horizontalAlignToTextAlign(align: DefaultHorizontalAlignStyle): CSSProperties["textAlign"];
|
|
30
|
+
declare function verticalAlignToFlex(align: DefaultVerticalAlignStyle): CSSProperties["alignItems"];
|
|
31
|
+
/**
|
|
32
|
+
* `shape.props` with every declared prop present and of the declared type.
|
|
33
|
+
* Geometry and rendering read through this so a record that arrived without a
|
|
34
|
+
* prop (or with a value from another editor's vocabulary) still draws.
|
|
35
|
+
*/
|
|
36
|
+
declare function readGeoProps(shape: {
|
|
37
|
+
props?: unknown;
|
|
38
|
+
}): GeoShapeProps;
|
|
39
|
+
/** Measured size of a geo label (padding included), wrapped at the shape width. */
|
|
40
|
+
declare function measureGeoLabel(props: Pick<GeoShapeProps, "text" | "font" | "size" | "scale" | "w">): TextMeasurement;
|
|
41
|
+
/** `growY` a geo shape needs so its label fits inside `h`. Empty labels never grow the shape. */
|
|
42
|
+
declare function getGeoGrowY(props: Pick<GeoShapeProps, "text" | "font" | "size" | "scale" | "w" | "h">): number;
|
|
43
|
+
declare class GeoShapeUtil extends BaseBoxShapeUtil<GeoShape> {
|
|
44
|
+
static type: "geo";
|
|
45
|
+
static props: {
|
|
46
|
+
geo: _mocanvas_editor.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">;
|
|
47
|
+
color: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
48
|
+
labelColor: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
49
|
+
fill: _mocanvas_editor.EnumStyleProp<"fill" | "none" | "semi" | "solid" | "pattern">;
|
|
50
|
+
dash: _mocanvas_editor.EnumStyleProp<"solid" | "draw" | "dashed" | "dotted">;
|
|
51
|
+
size: _mocanvas_editor.EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
52
|
+
font: _mocanvas_editor.EnumStyleProp<"draw" | "sans" | "serif" | "mono">;
|
|
53
|
+
align: _mocanvas_editor.EnumStyleProp<"start" | "middle" | "end" | "start-legacy" | "end-legacy" | "middle-legacy">;
|
|
54
|
+
verticalAlign: _mocanvas_editor.EnumStyleProp<"start" | "middle" | "end">;
|
|
55
|
+
};
|
|
56
|
+
getDefaultProps(): GeoShapeProps;
|
|
57
|
+
getGeometry(shape: GeoShape): Geometry2d;
|
|
58
|
+
/** Where the text label sits inside the body, in shape-local space. */
|
|
59
|
+
private getLabelRect;
|
|
60
|
+
getRenderStyle(shape: GeoShape): StyleWords;
|
|
61
|
+
component(shape: GeoShape): ReactNode;
|
|
62
|
+
indicator(shape: GeoShape): ReactNode;
|
|
63
|
+
/** The GPU keeps drawing the body while editing; only the label lives in the DOM. */
|
|
64
|
+
needsOverlay(_shape: GeoShape): boolean;
|
|
65
|
+
hasOverlayLabel(shape: GeoShape): boolean;
|
|
66
|
+
canEdit(_shape: GeoShape): boolean;
|
|
67
|
+
getText(shape: GeoShape): string;
|
|
68
|
+
onBeforeCreate(next: GeoShape): GeoShape | void;
|
|
69
|
+
onBeforeUpdate(prev: GeoShape, next: GeoShape): GeoShape | void;
|
|
70
|
+
onEditEnd(shape: GeoShape): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface DrawPoint {
|
|
74
|
+
x: number;
|
|
75
|
+
y: number;
|
|
76
|
+
z?: number;
|
|
77
|
+
}
|
|
78
|
+
interface DrawSegment {
|
|
79
|
+
type: "free" | "straight";
|
|
80
|
+
points: DrawPoint[];
|
|
81
|
+
}
|
|
82
|
+
interface DrawShapeProps {
|
|
83
|
+
segments: DrawSegment[];
|
|
84
|
+
color: DefaultColorStyle;
|
|
85
|
+
fill: DefaultFillStyle;
|
|
86
|
+
dash: DefaultDashStyle;
|
|
87
|
+
size: DefaultSizeStyle;
|
|
88
|
+
isComplete: boolean;
|
|
89
|
+
isClosed: boolean;
|
|
90
|
+
isPen: boolean;
|
|
91
|
+
scale: number;
|
|
92
|
+
}
|
|
93
|
+
type DrawShape = BaseShape<"draw", DrawShapeProps>;
|
|
94
|
+
/**
|
|
95
|
+
* The shape's segments in a usable form. A segment that carries no decoded
|
|
96
|
+
* `points` array — an encoding the load path could not read, say — reads as an
|
|
97
|
+
* empty run and contributes nothing, rather than throwing.
|
|
98
|
+
*/
|
|
99
|
+
declare function readDrawSegments(shape: {
|
|
100
|
+
props?: unknown;
|
|
101
|
+
}): DrawSegment[];
|
|
102
|
+
/** Flatten a draw shape's segments to one outline, smoothing freehand runs. */
|
|
103
|
+
declare function getDrawOutlinePoints(shape: DrawShape): VecLike[];
|
|
104
|
+
declare class DrawShapeUtil extends ShapeUtil<DrawShape> {
|
|
105
|
+
static type: "draw";
|
|
106
|
+
static props: {
|
|
107
|
+
color: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
108
|
+
fill: _mocanvas_editor.EnumStyleProp<"fill" | "none" | "semi" | "solid" | "pattern">;
|
|
109
|
+
dash: _mocanvas_editor.EnumStyleProp<"solid" | "draw" | "dashed" | "dotted">;
|
|
110
|
+
size: _mocanvas_editor.EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
111
|
+
};
|
|
112
|
+
getDefaultProps(): DrawShapeProps;
|
|
113
|
+
getGeometry(shape: DrawShape): Geometry2d;
|
|
114
|
+
getRenderStyle(shape: DrawShape): StyleWords;
|
|
115
|
+
component(_shape: DrawShape): ReactNode;
|
|
116
|
+
indicator(shape: DrawShape): ReactNode;
|
|
117
|
+
canResize(_shape: DrawShape): boolean;
|
|
118
|
+
hideResizeHandles(_shape: DrawShape): boolean;
|
|
119
|
+
onResize(shape: DrawShape, info: ResizeInfo<DrawShape>): Partial<DrawShape>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface LinePoint {
|
|
123
|
+
id: string;
|
|
124
|
+
index: string;
|
|
125
|
+
x: number;
|
|
126
|
+
y: number;
|
|
127
|
+
}
|
|
128
|
+
interface LineShapeProps {
|
|
129
|
+
color: DefaultColorStyle;
|
|
130
|
+
dash: DefaultDashStyle;
|
|
131
|
+
size: DefaultSizeStyle;
|
|
132
|
+
spline: "line" | "cubic";
|
|
133
|
+
points: Record<string, LinePoint>;
|
|
134
|
+
scale: number;
|
|
135
|
+
}
|
|
136
|
+
type LineShape = BaseShape<"line", LineShapeProps>;
|
|
137
|
+
/** The line's points in drawing order, skipping any entry that is not a point. */
|
|
138
|
+
declare function getLinePoints(shape: {
|
|
139
|
+
props?: unknown;
|
|
140
|
+
}): LinePoint[];
|
|
141
|
+
declare class LineShapeUtil extends ShapeUtil<LineShape> {
|
|
142
|
+
static type: "line";
|
|
143
|
+
static props: {
|
|
144
|
+
color: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
145
|
+
dash: _mocanvas_editor.EnumStyleProp<"solid" | "draw" | "dashed" | "dotted">;
|
|
146
|
+
size: _mocanvas_editor.EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
147
|
+
};
|
|
148
|
+
getDefaultProps(): LineShapeProps;
|
|
149
|
+
getGeometry(shape: LineShape): Geometry2d;
|
|
150
|
+
getRenderStyle(shape: LineShape): StyleWords;
|
|
151
|
+
component(_shape: LineShape): ReactNode;
|
|
152
|
+
indicator(shape: LineShape): ReactNode;
|
|
153
|
+
getHandles(shape: LineShape): ShapeHandle[];
|
|
154
|
+
onHandleDrag(shape: LineShape, info: {
|
|
155
|
+
handle: ShapeHandle;
|
|
156
|
+
}): Partial<LineShape>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Arrow body and arrowhead math.
|
|
161
|
+
*
|
|
162
|
+
* A body is a straight run, an arc, or — for an elbow arrow — the polyline
|
|
163
|
+
* routed by `elbow-helpers`. Everything below works on all three, so callers
|
|
164
|
+
* ask for a point, a tangent or a length without caring which one they hold.
|
|
165
|
+
*
|
|
166
|
+
* Bend convention: `bend` is the signed perpendicular offset of the arc's
|
|
167
|
+
* midpoint from the chord midpoint. The perpendicular is `Per(Uni(end-start))`
|
|
168
|
+
* = (-dy, dx), so for a left-to-right arrow a positive bend bows the arc toward
|
|
169
|
+
* +y (down in screen space).
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
type ArrowheadKind = "none" | "arrow" | "triangle" | "square" | "dot" | "diamond" | "inverted" | "bar" | "pipe";
|
|
173
|
+
interface StraightBody {
|
|
174
|
+
kind: "straight";
|
|
175
|
+
start: Vec;
|
|
176
|
+
end: Vec;
|
|
177
|
+
}
|
|
178
|
+
interface ArcBody {
|
|
179
|
+
kind: "arc";
|
|
180
|
+
center: Vec;
|
|
181
|
+
radius: number;
|
|
182
|
+
startAngle: number;
|
|
183
|
+
/** Signed sweep in radians. */
|
|
184
|
+
sweep: number;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* A body made of straight runs: the elbow arrow's axis-aligned route with its
|
|
188
|
+
* corners already rounded. Built by `getElbowBody` in `elbow-helpers`; every
|
|
189
|
+
* body operation below treats it as a polyline parameterized by arc length.
|
|
190
|
+
*/
|
|
191
|
+
interface ElbowBody {
|
|
192
|
+
kind: "elbow";
|
|
193
|
+
points: Vec[];
|
|
194
|
+
}
|
|
195
|
+
type ArrowBody = StraightBody | ArcBody | ElbowBody;
|
|
196
|
+
/** Describe the body running from `start` to `end`, bowed by `bend`. */
|
|
197
|
+
declare function getArrowBody(start: VecLike, end: VecLike, bend: number): ArrowBody;
|
|
198
|
+
declare function getBodyLength(body: ArrowBody): number;
|
|
199
|
+
/** Point at parameter `t` in [0, 1] along the body. */
|
|
200
|
+
declare function getPointOnBody(body: ArrowBody, t: number): Vec;
|
|
201
|
+
/**
|
|
202
|
+
* Unit direction of travel at parameter `t`. On an elbow that is the direction
|
|
203
|
+
* of the leg `t` falls on, so `t = 1` gives the direction of the final leg —
|
|
204
|
+
* which is where the end arrowhead points.
|
|
205
|
+
*/
|
|
206
|
+
declare function getTangentOnBody(body: ArrowBody, t: number): Vec;
|
|
207
|
+
/** Trim `startBy` / `endBy` page units from either end of the body. */
|
|
208
|
+
declare function shortenBody(body: ArrowBody, startBy: number, endBy: number): ArrowBody;
|
|
209
|
+
/** Geometry for the body: a polyline (straight or elbow) or a cubic approximation of the arc. */
|
|
210
|
+
declare function bodyToGeometry(body: ArrowBody): Geometry2d;
|
|
211
|
+
/** Arrowhead length for a stroke width, capped so two heads never overlap. */
|
|
212
|
+
declare function getArrowheadLength(strokeWidth: number, bodyLength: number): number;
|
|
213
|
+
/** How far the body must retreat from the tip so it ends at the head's base. */
|
|
214
|
+
declare function getArrowheadInset(kind: ArrowheadKind, length: number): number;
|
|
215
|
+
/**
|
|
216
|
+
* Arrowhead geometry with its tip at `tip`, pointing along unit `dir` (from
|
|
217
|
+
* the body outward). Closed heads are filled; open ones are polylines.
|
|
218
|
+
*/
|
|
219
|
+
declare function getArrowheadGeometry(kind: ArrowheadKind, tip: VecLike, dir: VecLike, length: number): Geometry2d | null;
|
|
220
|
+
/** Signed bend for a dragged midpoint handle at `point`. */
|
|
221
|
+
declare function getBendFromPoint(start: VecLike, end: VecLike, point: VecLike): number;
|
|
222
|
+
|
|
223
|
+
interface ArrowShapeProps {
|
|
224
|
+
/**
|
|
225
|
+
* How the body is routed: `"arc"` bows from start to end by `bend`,
|
|
226
|
+
* `"elbow"` runs in axis-aligned legs (and ignores `bend`).
|
|
227
|
+
*/
|
|
228
|
+
kind: ArrowKind;
|
|
229
|
+
/** Static start terminal in arrow-local space; ignored while the terminal is bound. */
|
|
230
|
+
start: {
|
|
231
|
+
x: number;
|
|
232
|
+
y: number;
|
|
233
|
+
};
|
|
234
|
+
/** Static end terminal in arrow-local space; ignored while the terminal is bound. */
|
|
235
|
+
end: {
|
|
236
|
+
x: number;
|
|
237
|
+
y: number;
|
|
238
|
+
};
|
|
239
|
+
bend: number;
|
|
240
|
+
/** Where an elbow's middle leg sits along the routing axis, `0..1`; unused by `"arc"`. */
|
|
241
|
+
elbowMidPoint: number;
|
|
242
|
+
color: DefaultColorStyle;
|
|
243
|
+
labelColor: DefaultColorStyle;
|
|
244
|
+
fill: DefaultFillStyle;
|
|
245
|
+
dash: DefaultDashStyle;
|
|
246
|
+
size: DefaultSizeStyle;
|
|
247
|
+
arrowheadStart: ArrowheadKind;
|
|
248
|
+
arrowheadEnd: ArrowheadKind;
|
|
249
|
+
font: DefaultFontStyle;
|
|
250
|
+
text: string;
|
|
251
|
+
labelPosition: number;
|
|
252
|
+
scale: number;
|
|
253
|
+
}
|
|
254
|
+
type ArrowShape = BaseShape<"arrow", ArrowShapeProps>;
|
|
255
|
+
declare const ARROW_LABEL_PADDING = 8;
|
|
256
|
+
/** Routing kinds an arrow can have. */
|
|
257
|
+
declare const ARROW_KINDS: readonly ["arc", "elbow"];
|
|
258
|
+
type ArrowKind = (typeof ARROW_KINDS)[number];
|
|
259
|
+
/**
|
|
260
|
+
* The shape with every declared prop present and of the declared type. Terminal
|
|
261
|
+
* resolution and binding code read the arrow as a whole, so the normalized copy
|
|
262
|
+
* is a whole shape rather than a loose props bag.
|
|
263
|
+
*/
|
|
264
|
+
declare function readArrowShape(shape: ArrowShape): ArrowShape;
|
|
265
|
+
/**
|
|
266
|
+
* `shape.props` with every declared prop present and of the declared type, so
|
|
267
|
+
* geometry and rendering survive a record that arrived without one.
|
|
268
|
+
*/
|
|
269
|
+
declare function readArrowProps(shape: {
|
|
270
|
+
props?: unknown;
|
|
271
|
+
}): ArrowShapeProps;
|
|
272
|
+
declare class ArrowShapeUtil extends ShapeUtil<ArrowShape> {
|
|
273
|
+
static type: "arrow";
|
|
274
|
+
/**
|
|
275
|
+
* `kind` is deliberately not among these. A style is shared across shape
|
|
276
|
+
* types, remembered for the next shape and applied to a whole selection at
|
|
277
|
+
* once; arc-versus-elbow is routing that belongs to the one arrow, and
|
|
278
|
+
* declaring it a style would put it in every mixed selection's shared styles
|
|
279
|
+
* (and in the style panel) with nothing else to share it with.
|
|
280
|
+
*/
|
|
281
|
+
static props: {
|
|
282
|
+
color: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
283
|
+
labelColor: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
284
|
+
fill: _mocanvas_editor.EnumStyleProp<"fill" | "none" | "semi" | "solid" | "pattern">;
|
|
285
|
+
dash: _mocanvas_editor.EnumStyleProp<"solid" | "draw" | "dashed" | "dotted">;
|
|
286
|
+
size: _mocanvas_editor.EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
287
|
+
font: _mocanvas_editor.EnumStyleProp<"draw" | "sans" | "serif" | "mono">;
|
|
288
|
+
};
|
|
289
|
+
getDefaultProps(): ArrowShapeProps;
|
|
290
|
+
/**
|
|
291
|
+
* The arrow's terminals and the body running between them, in arrow-local
|
|
292
|
+
* space. `"arc"` bows by `bend`; `"elbow"` routes axis-aligned legs, leaving
|
|
293
|
+
* a bound shape along its nearest edge's normal, with corners rounded in
|
|
294
|
+
* proportion to the stroke. The elbow's route is handed back as well, for the
|
|
295
|
+
* midpoint handle.
|
|
296
|
+
*/
|
|
297
|
+
private resolveBody;
|
|
298
|
+
getGeometry(shape: ArrowShape): Geometry2d;
|
|
299
|
+
getRenderStyle(shape: ArrowShape): StyleWords;
|
|
300
|
+
component(shape: ArrowShape): ReactNode;
|
|
301
|
+
indicator(shape: ArrowShape): ReactNode;
|
|
302
|
+
/** The GPU keeps drawing the arrow while its label is edited. */
|
|
303
|
+
needsOverlay(_shape: ArrowShape): boolean;
|
|
304
|
+
hasOverlayLabel(shape: ArrowShape): boolean;
|
|
305
|
+
canEdit(_shape: ArrowShape): boolean;
|
|
306
|
+
/** Nothing binds to an arrow (no arrow-to-arrow bindings). */
|
|
307
|
+
canBind(_opts: {
|
|
308
|
+
fromShapeType: string;
|
|
309
|
+
toShapeType: string;
|
|
310
|
+
bindingType: string;
|
|
311
|
+
}): boolean;
|
|
312
|
+
/** A selected arrow shows its handles instead of a selection box. */
|
|
313
|
+
hideSelectionBoundsBg(_shape: ArrowShape): boolean;
|
|
314
|
+
hideSelectionBoundsFg(_shape: ArrowShape): boolean;
|
|
315
|
+
hideResizeHandles(_shape: ArrowShape): boolean;
|
|
316
|
+
getText(shape: ArrowShape): string;
|
|
317
|
+
onEditEnd(shape: ArrowShape): void;
|
|
318
|
+
/**
|
|
319
|
+
* Start and end handles move (and bind) the terminals. Between them sits one
|
|
320
|
+
* virtual handle: an arc's `bend` handle rides the middle of the curve, while
|
|
321
|
+
* an elbow's `midpoint` handle sits on its middle leg and slides that leg.
|
|
322
|
+
* An elbow with no middle leg (an L route, or a straight run) has neither.
|
|
323
|
+
*/
|
|
324
|
+
getHandles(shape: ArrowShape): ShapeHandle[];
|
|
325
|
+
onHandleDrag(shape: ArrowShape, info: {
|
|
326
|
+
handle: ShapeHandle;
|
|
327
|
+
isPrecise: boolean;
|
|
328
|
+
initial?: ArrowShape;
|
|
329
|
+
}): Partial<ArrowShape> | void;
|
|
330
|
+
/**
|
|
331
|
+
* Move a terminal handle. When the handle lands on a bindable shape the
|
|
332
|
+
* terminal is bound to it (centered, or at the precise point under the
|
|
333
|
+
* pointer); otherwise any existing binding is dropped. The static point is
|
|
334
|
+
* always written so the arrow renders sensibly if the binding goes away.
|
|
335
|
+
* Holding Ctrl suppresses binding. Targets are found by hit-testing shape
|
|
336
|
+
* geometry directly (not `editor.getShapeAtPoint`) because the engine treats
|
|
337
|
+
* unfilled shapes as hollow, and arrows must bind into hollow shapes too.
|
|
338
|
+
*/
|
|
339
|
+
private dragTerminal;
|
|
340
|
+
/**
|
|
341
|
+
* Dragging the arrow on its own detaches it: bindings to shapes that are not
|
|
342
|
+
* part of the selection are dropped and their terminals frozen in place.
|
|
343
|
+
* Bindings to shapes moving along with the arrow are kept.
|
|
344
|
+
*/
|
|
345
|
+
onTranslateStart(shape: ArrowShape): void;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
type TextTextureAlign = "start" | "middle" | "end";
|
|
349
|
+
interface TextTextureSpec {
|
|
350
|
+
text: string;
|
|
351
|
+
fontFamily: string;
|
|
352
|
+
fontSize: number;
|
|
353
|
+
/** Any CSS color string. */
|
|
354
|
+
color: string;
|
|
355
|
+
align: TextTextureAlign;
|
|
356
|
+
verticalAlign: TextTextureAlign;
|
|
357
|
+
/** Unitless line-height multiplier. */
|
|
358
|
+
lineHeight: number;
|
|
359
|
+
/** The box the texture covers, in page units — the shape's local geometry bounds. */
|
|
360
|
+
width: number;
|
|
361
|
+
height: number;
|
|
362
|
+
/** Soft-wrap width in page units. Omit for no wrapping (one line per paragraph). */
|
|
363
|
+
maxWidth?: number;
|
|
364
|
+
/**
|
|
365
|
+
* Device pixels per page unit the canvas is rasterized at, i.e.
|
|
366
|
+
* `dpr × min(4, zoom)`. Callers bucket it to a power of two
|
|
367
|
+
* (`bucketTextureResolution` / `editor.getTextureResolution()`) so zooming
|
|
368
|
+
* does not re-rasterize every frame.
|
|
369
|
+
*/
|
|
370
|
+
resolution: number;
|
|
371
|
+
/** Padding inside the box, in page units. */
|
|
372
|
+
padding?: number;
|
|
373
|
+
}
|
|
374
|
+
/** Hard cap on either canvas dimension, so a huge label cannot blow up the GPU. */
|
|
375
|
+
declare const MAX_TEXT_TEXTURE_PX = 4096;
|
|
376
|
+
/**
|
|
377
|
+
* Cache key for a spec. Stable for equal specs and different for every field
|
|
378
|
+
* that changes the pixels — the resolution bucket included.
|
|
379
|
+
*/
|
|
380
|
+
declare function getTextTextureKey(spec: TextTextureSpec): string;
|
|
381
|
+
/**
|
|
382
|
+
* Break `spec.text` into the lines the label renders as: hard newlines always,
|
|
383
|
+
* plus greedy word wrapping at `maxWidth` (breaking inside a word only when a
|
|
384
|
+
* single word does not fit), mirroring `overflow-wrap: break-word`. Unlike the
|
|
385
|
+
* SVG exporter's `wrapTextLines`, widths come from the shared `TextMeasure`, so
|
|
386
|
+
* the raster matches the DOM label it replaces.
|
|
387
|
+
*/
|
|
388
|
+
declare function wrapTextTextureLines(spec: TextTextureSpec): string[];
|
|
389
|
+
/** Effective resolution after clamping the canvas to `MAX_TEXT_TEXTURE_PX`. */
|
|
390
|
+
declare function getTextTextureScale(spec: TextTextureSpec): number;
|
|
391
|
+
/**
|
|
392
|
+
* Draw the label onto a canvas covering the whole `width × height` box, so the
|
|
393
|
+
* engine's uv `0..1` quad over the shape's local bounds lands pixel-for-pixel.
|
|
394
|
+
* Throws where there is no `document` (Node, SSR) — callers keep the DOM path.
|
|
395
|
+
*/
|
|
396
|
+
declare function renderTextToCanvas(spec: TextTextureSpec): HTMLCanvasElement;
|
|
397
|
+
|
|
398
|
+
interface TextShapeProps {
|
|
399
|
+
color: DefaultColorStyle;
|
|
400
|
+
size: DefaultSizeStyle;
|
|
401
|
+
font: DefaultFontStyle;
|
|
402
|
+
textAlign: "start" | "middle" | "end";
|
|
403
|
+
w: number;
|
|
404
|
+
text: string;
|
|
405
|
+
scale: number;
|
|
406
|
+
autoSize: boolean;
|
|
407
|
+
}
|
|
408
|
+
type TextShape = BaseShape<"text", TextShapeProps>;
|
|
409
|
+
/**
|
|
410
|
+
* `shape.props` with every declared prop present and of the declared type, so
|
|
411
|
+
* measuring and rendering survive a record that arrived without one.
|
|
412
|
+
*/
|
|
413
|
+
declare function readTextProps(shape: {
|
|
414
|
+
props?: unknown;
|
|
415
|
+
}): TextShapeProps;
|
|
416
|
+
/** Measured size of a text shape: intrinsic when `autoSize`, else wrapped at `w`. */
|
|
417
|
+
declare function getTextShapeSizeFor(shape: TextShape): {
|
|
418
|
+
w: number;
|
|
419
|
+
h: number;
|
|
420
|
+
lineCount: number;
|
|
421
|
+
};
|
|
422
|
+
/** Height of the text block at its current width. */
|
|
423
|
+
declare function getTextShapeHeight(shape: TextShape): number;
|
|
424
|
+
/** The box `getGeometry` produces: what a texture for this shape must cover. */
|
|
425
|
+
declare function getTextShapeBox(shape: TextShape): {
|
|
426
|
+
w: number;
|
|
427
|
+
h: number;
|
|
428
|
+
};
|
|
429
|
+
/** The rasterization spec for a text shape at the editor's current resolution bucket. */
|
|
430
|
+
declare function getTextShapeTextureSpec(editor: Editor, shape: TextShape): TextTextureSpec;
|
|
431
|
+
declare class TextShapeUtil extends ShapeUtil<TextShape> {
|
|
432
|
+
static type: "text";
|
|
433
|
+
static props: {
|
|
434
|
+
color: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
435
|
+
size: _mocanvas_editor.EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
436
|
+
font: _mocanvas_editor.EnumStyleProp<"draw" | "sans" | "serif" | "mono">;
|
|
437
|
+
textAlign: _mocanvas_editor.EnumStyleProp<"start" | "middle" | "end" | "start-legacy" | "end-legacy" | "middle-legacy">;
|
|
438
|
+
};
|
|
439
|
+
getDefaultProps(): TextShapeProps;
|
|
440
|
+
getGeometry(shape: TextShape): Geometry2d;
|
|
441
|
+
/**
|
|
442
|
+
* A texture of the rasterized label, so the GPU draws the text instead of the
|
|
443
|
+
* DOM. The shape being edited (and any environment without a canvas) keeps
|
|
444
|
+
* the DOM path; `fill` is the text colour so the level-of-detail quad the
|
|
445
|
+
* engine draws below a few pixels still looks right.
|
|
446
|
+
*/
|
|
447
|
+
getRenderStyle(shape: TextShape): StyleWords | null;
|
|
448
|
+
/** The DOM label stands in while editing and until the texture is ready. */
|
|
449
|
+
needsOverlay(shape: TextShape): boolean;
|
|
450
|
+
private getTextureKey;
|
|
451
|
+
component(shape: TextShape): ReactNode;
|
|
452
|
+
indicator(shape: TextShape): ReactNode;
|
|
453
|
+
canEdit(_shape: TextShape): boolean;
|
|
454
|
+
isAspectRatioLocked(_shape: TextShape): boolean;
|
|
455
|
+
getText(shape: TextShape): string;
|
|
456
|
+
/** Auto-sized text keeps `w` in sync with its measured width. */
|
|
457
|
+
onBeforeCreate(next: TextShape): TextShape | void;
|
|
458
|
+
onBeforeUpdate(prev: TextShape, next: TextShape): TextShape | void;
|
|
459
|
+
private fitWidth;
|
|
460
|
+
/** A text shape left empty after editing is removed. */
|
|
461
|
+
onEditEnd(shape: TextShape): void;
|
|
462
|
+
/** Resizing a text shape changes its wrap width and turns auto-size off. */
|
|
463
|
+
onResize(shape: TextShape, info: ResizeInfo<TextShape>): Partial<TextShape>;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
interface NoteShapeProps {
|
|
467
|
+
color: DefaultColorStyle;
|
|
468
|
+
labelColor: DefaultColorStyle;
|
|
469
|
+
size: DefaultSizeStyle;
|
|
470
|
+
font: DefaultFontStyle;
|
|
471
|
+
fontSizeAdjustment: number;
|
|
472
|
+
align: DefaultHorizontalAlignStyle;
|
|
473
|
+
verticalAlign: DefaultVerticalAlignStyle;
|
|
474
|
+
growY: number;
|
|
475
|
+
url: string;
|
|
476
|
+
text: string;
|
|
477
|
+
scale: number;
|
|
478
|
+
}
|
|
479
|
+
type NoteShape = BaseShape<"note", NoteShapeProps>;
|
|
480
|
+
declare const NOTE_SIZE = 200;
|
|
481
|
+
declare const NOTE_PADDING = 16;
|
|
482
|
+
/**
|
|
483
|
+
* `shape.props` with every declared prop present and of the declared type, so
|
|
484
|
+
* geometry and rendering survive a record that arrived without one.
|
|
485
|
+
*/
|
|
486
|
+
declare function readNoteProps(shape: {
|
|
487
|
+
props?: unknown;
|
|
488
|
+
}): NoteShapeProps;
|
|
489
|
+
/**
|
|
490
|
+
* Effective font size: an explicit adjustment (auto-shrunk text) wins over the
|
|
491
|
+
* size style.
|
|
492
|
+
*
|
|
493
|
+
* `fontSizeAdjustment` is an absolute size in px, written when a label had to
|
|
494
|
+
* shrink to fit; `0` means "unset". Files exist that write a small placeholder
|
|
495
|
+
* there instead (a `1` on a note that was never shrunk), which would render the
|
|
496
|
+
* label at one pixel, so anything too small to be a font size is also read as
|
|
497
|
+
* unset and the size style takes over.
|
|
498
|
+
*/
|
|
499
|
+
declare function getNoteFontSize(shape: NoteShape): number;
|
|
500
|
+
/** `growY` a note needs so its (centered) text fits; the note keeps its square width. */
|
|
501
|
+
declare function getNoteGrowY(shape: NoteShape): number;
|
|
502
|
+
declare class NoteShapeUtil extends ShapeUtil<NoteShape> {
|
|
503
|
+
static type: "note";
|
|
504
|
+
static props: {
|
|
505
|
+
color: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
506
|
+
labelColor: _mocanvas_editor.EnumStyleProp<"black" | "grey" | "light-violet" | "violet" | "blue" | "light-blue" | "yellow" | "orange" | "green" | "light-green" | "light-red" | "red" | "white">;
|
|
507
|
+
size: _mocanvas_editor.EnumStyleProp<"s" | "m" | "l" | "xl">;
|
|
508
|
+
font: _mocanvas_editor.EnumStyleProp<"draw" | "sans" | "serif" | "mono">;
|
|
509
|
+
align: _mocanvas_editor.EnumStyleProp<"start" | "middle" | "end" | "start-legacy" | "end-legacy" | "middle-legacy">;
|
|
510
|
+
verticalAlign: _mocanvas_editor.EnumStyleProp<"start" | "middle" | "end">;
|
|
511
|
+
};
|
|
512
|
+
getDefaultProps(): NoteShapeProps;
|
|
513
|
+
getGeometry(shape: NoteShape): Geometry2d;
|
|
514
|
+
getRenderStyle(shape: NoteShape): StyleWords;
|
|
515
|
+
component(shape: NoteShape): ReactNode;
|
|
516
|
+
indicator(shape: NoteShape): ReactNode;
|
|
517
|
+
/**
|
|
518
|
+
* The GPU draws the sticky background even while editing; the overlay adds
|
|
519
|
+
* the gradient and the shadow over it (see `component`).
|
|
520
|
+
*/
|
|
521
|
+
needsOverlay(_shape: NoteShape): boolean;
|
|
522
|
+
hasOverlayLabel(_shape: NoteShape): boolean;
|
|
523
|
+
canEdit(_shape: NoteShape): boolean;
|
|
524
|
+
hideResizeHandles(_shape: NoteShape): boolean;
|
|
525
|
+
getText(shape: NoteShape): string;
|
|
526
|
+
onBeforeCreate(next: NoteShape): NoteShape | void;
|
|
527
|
+
onBeforeUpdate(prev: NoteShape, next: NoteShape): NoteShape | void;
|
|
528
|
+
onEditEnd(shape: NoteShape): void;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
interface FrameShapeProps {
|
|
532
|
+
w: number;
|
|
533
|
+
h: number;
|
|
534
|
+
name: string;
|
|
535
|
+
color?: DefaultColorStyle;
|
|
536
|
+
}
|
|
537
|
+
type FrameShape = BaseShape<"frame", FrameShapeProps>;
|
|
538
|
+
declare class FrameShapeUtil extends BaseBoxShapeUtil<FrameShape> {
|
|
539
|
+
static type: "frame";
|
|
540
|
+
getDefaultProps(): FrameShapeProps;
|
|
541
|
+
getGeometry(shape: FrameShape): Geometry2d;
|
|
542
|
+
getRenderStyle(_shape: FrameShape): StyleWords;
|
|
543
|
+
component(shape: FrameShape): ReactNode;
|
|
544
|
+
indicator(shape: FrameShape): ReactNode;
|
|
545
|
+
/** The GPU draws the frame body while its name is edited. */
|
|
546
|
+
needsOverlay(_shape: FrameShape): boolean;
|
|
547
|
+
hasOverlayLabel(_shape: FrameShape): boolean;
|
|
548
|
+
/** Descendants are clipped to the frame's bounds, on the GPU and in the overlay. */
|
|
549
|
+
isClipShape(_shape: FrameShape): boolean;
|
|
550
|
+
canEdit(_shape: FrameShape): boolean;
|
|
551
|
+
canReceiveNewChildrenOfType(_shape: FrameShape, _type: string): boolean;
|
|
552
|
+
canDropShapes(_shape: FrameShape, _shapes: UnknownShape[]): boolean;
|
|
553
|
+
getText(shape: FrameShape): string;
|
|
554
|
+
onEditEnd(shape: FrameShape): void;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** Normalized crop window: both corners in [0, 1] of the source image. */
|
|
558
|
+
interface ImageCrop {
|
|
559
|
+
topLeft: {
|
|
560
|
+
x: number;
|
|
561
|
+
y: number;
|
|
562
|
+
};
|
|
563
|
+
bottomRight: {
|
|
564
|
+
x: number;
|
|
565
|
+
y: number;
|
|
566
|
+
};
|
|
567
|
+
isCircle?: boolean;
|
|
568
|
+
}
|
|
569
|
+
interface ImageShapeProps {
|
|
570
|
+
w: number;
|
|
571
|
+
h: number;
|
|
572
|
+
/** The image asset holding the pixels; `null` while nothing is attached. */
|
|
573
|
+
assetId: AssetId | null;
|
|
574
|
+
/** Whether an animated image plays. */
|
|
575
|
+
playing: boolean;
|
|
576
|
+
/** A hyperlink attached to the shape (not the image source). */
|
|
577
|
+
url: string;
|
|
578
|
+
crop: ImageCrop | null;
|
|
579
|
+
flipX: boolean;
|
|
580
|
+
flipY: boolean;
|
|
581
|
+
altText: string;
|
|
582
|
+
}
|
|
583
|
+
type ImageShape = BaseShape<"image", ImageShapeProps>;
|
|
584
|
+
declare const IMAGE_PLACEHOLDER_FILL = "#eceff3";
|
|
585
|
+
declare const IMAGE_PLACEHOLDER_STROKE = "#9fa8b2";
|
|
586
|
+
/**
|
|
587
|
+
* The source the shape's pixels come from: the asset's `src` (a URL or data URL),
|
|
588
|
+
* or `null` when the shape has no asset yet or the asset is still uploading.
|
|
589
|
+
* `getImageTextureKey` turns it into a GPU texture; `component` draws the same
|
|
590
|
+
* source in the DOM overlay while that texture loads (and for crops/flips).
|
|
591
|
+
*/
|
|
592
|
+
declare function getImageTextureSource(editor: Editor, shape: ImageShape): string | null;
|
|
593
|
+
/**
|
|
594
|
+
* Texture cache key for the shape's pixels, or `null` when it must stay on the
|
|
595
|
+
* DOM overlay.
|
|
596
|
+
*
|
|
597
|
+
* The GPU path only covers the plain case. The engine maps uv `0..1` over the
|
|
598
|
+
* shape's local geometry bounds, so a crop window or a flip would need the quad
|
|
599
|
+
* to carry its own uvs; until the bridge does, cropped and flipped images (and
|
|
600
|
+
* anything without a decoded source) are drawn by `component`.
|
|
601
|
+
*
|
|
602
|
+
* Assets are treated as immutable, so the key is the asset id: replacing an
|
|
603
|
+
* asset's `src` in place keeps the cached texture.
|
|
604
|
+
*/
|
|
605
|
+
declare function getImageTextureKey(editor: Editor, shape: ImageShape): string | null;
|
|
606
|
+
/** Whether this environment can decode an image into a texture source at all. */
|
|
607
|
+
declare function canUploadImageTextures(): boolean;
|
|
608
|
+
/** Decode an image `src` (URL or data URL) into something the GPU can sample. */
|
|
609
|
+
declare function loadImageTextureSource(src: string): Promise<TexImageSource>;
|
|
610
|
+
/** CSS for the `<img>` so the crop window fills the shape's box; `null` crop shows the whole image. */
|
|
611
|
+
declare function getImageCropStyle(shape: ImageShape): CSSProperties;
|
|
612
|
+
declare class ImageShapeUtil extends BaseBoxShapeUtil<ImageShape> {
|
|
613
|
+
static type: "image";
|
|
614
|
+
getDefaultProps(): ImageShapeProps;
|
|
615
|
+
getGeometry(shape: ImageShape): Geometry2d;
|
|
616
|
+
/** A white textured quad over the shape's bounds, or `null` for the DOM overlay path. */
|
|
617
|
+
getRenderStyle(shape: ImageShape): StyleWords | null;
|
|
618
|
+
/** The DOM overlay draws the image until (and unless) its texture is ready. */
|
|
619
|
+
needsOverlay(shape: ImageShape): boolean;
|
|
620
|
+
component(shape: ImageShape): ReactNode;
|
|
621
|
+
indicator(shape: ImageShape): ReactNode;
|
|
622
|
+
isAspectRatioLocked(_shape: ImageShape): boolean;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
interface BookmarkShapeProps {
|
|
626
|
+
w: number;
|
|
627
|
+
h: number;
|
|
628
|
+
/** The `bookmark` asset holding the scraped preview; `null` when nothing was scraped. */
|
|
629
|
+
assetId: AssetId | null;
|
|
630
|
+
/** The page the card points at. Kept on the shape so a card survives a missing asset. */
|
|
631
|
+
url: string;
|
|
632
|
+
}
|
|
633
|
+
type BookmarkShape = BaseShape<"bookmark", BookmarkShapeProps>;
|
|
634
|
+
declare const BOOKMARK_WIDTH = 300;
|
|
635
|
+
declare const BOOKMARK_HEIGHT = 320;
|
|
636
|
+
declare const BOOKMARK_FILL = "#ffffff";
|
|
637
|
+
declare const BOOKMARK_STROKE = "#e8e9ea";
|
|
638
|
+
declare const BOOKMARK_STROKE_WIDTH = 1;
|
|
639
|
+
declare const BOOKMARK_RADIUS = 8;
|
|
640
|
+
/** The banner strip behind the asset's preview image (and its colour when there is none). */
|
|
641
|
+
declare const BOOKMARK_BANNER_HEIGHT = 160;
|
|
642
|
+
declare const BOOKMARK_BANNER_FILL = "#eceff3";
|
|
643
|
+
declare const BOOKMARK_PADDING = 12;
|
|
644
|
+
declare const BOOKMARK_GAP = 6;
|
|
645
|
+
declare const BOOKMARK_TITLE_HEIGHT = 38;
|
|
646
|
+
declare const BOOKMARK_META_HEIGHT = 16;
|
|
647
|
+
declare const BOOKMARK_FAVICON_SIZE = 14;
|
|
648
|
+
declare const BOOKMARK_TITLE_FONT_SIZE = 14;
|
|
649
|
+
declare const BOOKMARK_TEXT_FONT_SIZE = 12;
|
|
650
|
+
declare const BOOKMARK_META_FONT_SIZE = 11;
|
|
651
|
+
declare const BOOKMARK_TITLE_COLOR = "#1d1d1d";
|
|
652
|
+
declare const BOOKMARK_TEXT_COLOR = "#666666";
|
|
653
|
+
declare const BOOKMARK_META_COLOR = "#8f8f8f";
|
|
654
|
+
/** Body left under the banner even on a short card, so the text never disappears. */
|
|
655
|
+
declare const BOOKMARK_MIN_BODY_HEIGHT = 80;
|
|
656
|
+
/** A rectangle in shape-local space. */
|
|
657
|
+
interface BookmarkRect {
|
|
658
|
+
x: number;
|
|
659
|
+
y: number;
|
|
660
|
+
w: number;
|
|
661
|
+
h: number;
|
|
662
|
+
}
|
|
663
|
+
/** Where each part of the card sits, in shape-local space. Shared by the overlay and the SVG export. */
|
|
664
|
+
interface BookmarkLayout {
|
|
665
|
+
banner: BookmarkRect;
|
|
666
|
+
title: BookmarkRect;
|
|
667
|
+
description: BookmarkRect;
|
|
668
|
+
favicon: BookmarkRect;
|
|
669
|
+
hostname: BookmarkRect;
|
|
670
|
+
}
|
|
671
|
+
declare function getBookmarkLayout(w: number, h: number): BookmarkLayout;
|
|
672
|
+
/**
|
|
673
|
+
* The host a bookmark points at, without a `www.` prefix — the one line of a
|
|
674
|
+
* link card that is always available, since it comes from the shape's own url
|
|
675
|
+
* rather than from a scrape. An unparseable url reads as `""`.
|
|
676
|
+
*/
|
|
677
|
+
declare function getBookmarkHostname(url: string): string;
|
|
678
|
+
/** The shape's `bookmark` asset, or `null` when it has none (or one of another type). */
|
|
679
|
+
declare function getBookmarkAsset(editor: Editor, shape: BookmarkShape): BookmarkAsset | null;
|
|
680
|
+
/** Everything the card draws, with each field falling back to something renderable. */
|
|
681
|
+
interface BookmarkCard {
|
|
682
|
+
title: string;
|
|
683
|
+
description: string;
|
|
684
|
+
image: string;
|
|
685
|
+
favicon: string;
|
|
686
|
+
hostname: string;
|
|
687
|
+
url: string;
|
|
688
|
+
/** Whether the card has a scraped asset behind it; `false` renders the placeholder. */
|
|
689
|
+
hasAsset: boolean;
|
|
690
|
+
}
|
|
691
|
+
declare function getBookmarkCard(editor: Editor, shape: BookmarkShape): BookmarkCard;
|
|
692
|
+
declare class BookmarkShapeUtil extends BaseBoxShapeUtil<BookmarkShape> {
|
|
693
|
+
static type: "bookmark";
|
|
694
|
+
getDefaultProps(): BookmarkShapeProps;
|
|
695
|
+
getGeometry(shape: BookmarkShape): Geometry2d;
|
|
696
|
+
/**
|
|
697
|
+
* The card is html — a banner image over wrapped, ellipsised text — which no
|
|
698
|
+
* single textured quad can stand in for, so the shape always renders through
|
|
699
|
+
* the DOM overlay.
|
|
700
|
+
*/
|
|
701
|
+
getRenderStyle(_shape: BookmarkShape): StyleWords | null;
|
|
702
|
+
component(shape: BookmarkShape): ReactNode;
|
|
703
|
+
/** The scraped card: banner, title, description, favicon and host. */
|
|
704
|
+
private renderCard;
|
|
705
|
+
/** No asset: an empty banner and the host, so the shape still reads as a link to somewhere. */
|
|
706
|
+
private renderPlaceholder;
|
|
707
|
+
indicator(shape: BookmarkShape): ReactNode;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
interface EmbedShapeProps {
|
|
711
|
+
w: number;
|
|
712
|
+
h: number;
|
|
713
|
+
/** The page being embedded, as the document stored it (not the iframe's src). */
|
|
714
|
+
url: string;
|
|
715
|
+
}
|
|
716
|
+
type EmbedShape = BaseShape<"embed", EmbedShapeProps>;
|
|
717
|
+
declare const EMBED_WIDTH = 720;
|
|
718
|
+
declare const EMBED_HEIGHT = 500;
|
|
719
|
+
declare const EMBED_PLACEHOLDER_FILL = "#f5f6f8";
|
|
720
|
+
declare const EMBED_PLACEHOLDER_STROKE = "#9fa8b2";
|
|
721
|
+
declare const EMBED_PLACEHOLDER_TEXT = "#5f6670";
|
|
722
|
+
declare const EMBED_PLACEHOLDER_FONT_SIZE = 13;
|
|
723
|
+
declare const EMBED_PLACEHOLDER_PADDING = 16;
|
|
724
|
+
declare const EMBED_RADIUS = 6;
|
|
725
|
+
/**
|
|
726
|
+
* The sandbox an embedded page runs in. Scripts and same-origin are what makes
|
|
727
|
+
* a player work at all; popups let a "watch on the site" button escape. Nothing
|
|
728
|
+
* else is granted — no forms, no top-level navigation, no pointer lock.
|
|
729
|
+
*/
|
|
730
|
+
declare const EMBED_SANDBOX = "allow-scripts allow-same-origin allow-popups";
|
|
731
|
+
/**
|
|
732
|
+
* One embeddable service: the exact hosts it is recognised by, and how one of
|
|
733
|
+
* its page urls becomes an embeddable one.
|
|
734
|
+
*
|
|
735
|
+
* `hostnames` are matched exactly (case-insensitively, with a leading `www.`
|
|
736
|
+
* ignored), never as a suffix — `youtube.com.evil.test` is a different host and
|
|
737
|
+
* is not on the list. `toEmbedUrl` receives the parsed url and returns an
|
|
738
|
+
* absolute `https:` url, or `null` when this particular page of the service is
|
|
739
|
+
* not embeddable (a channel page rather than a video, say).
|
|
740
|
+
*/
|
|
741
|
+
interface EmbedDefinition {
|
|
742
|
+
/** Stable id for the service, e.g. `"youtube"`. */
|
|
743
|
+
type: string;
|
|
744
|
+
/** Human-readable name, shown on the placeholder card. */
|
|
745
|
+
title: string;
|
|
746
|
+
hostnames: readonly string[];
|
|
747
|
+
toEmbedUrl(url: URL): string | null;
|
|
748
|
+
}
|
|
749
|
+
/** A url that matched the permit list. */
|
|
750
|
+
interface EmbedMatch {
|
|
751
|
+
definition: EmbedDefinition;
|
|
752
|
+
/** Absolute `https:` url for the iframe's `src`. */
|
|
753
|
+
embedUrl: string;
|
|
754
|
+
}
|
|
755
|
+
/** The services recognised out of the box. */
|
|
756
|
+
declare const DEFAULT_EMBED_DEFINITIONS: readonly EmbedDefinition[];
|
|
757
|
+
/**
|
|
758
|
+
* The permit list `getEmbedDefinition` consults. It starts as the defaults and
|
|
759
|
+
* is meant to be extended by the app:
|
|
760
|
+
*
|
|
761
|
+
* ```ts
|
|
762
|
+
* embedDefinitions.push({
|
|
763
|
+
* type: "intranet",
|
|
764
|
+
* title: "Intranet",
|
|
765
|
+
* hostnames: ["wiki.example.com"],
|
|
766
|
+
* toEmbedUrl: (url) => `https://wiki.example.com${url.pathname}?embed=1`,
|
|
767
|
+
* })
|
|
768
|
+
* ```
|
|
769
|
+
*
|
|
770
|
+
* Nothing outside the list is ever put in an iframe.
|
|
771
|
+
*/
|
|
772
|
+
declare const embedDefinitions: EmbedDefinition[];
|
|
773
|
+
/**
|
|
774
|
+
* The permit-list entry for `url`, with the url to actually put in the iframe,
|
|
775
|
+
* or `null` when the url is not one we embed.
|
|
776
|
+
*
|
|
777
|
+
* Rejects anything that is not `http(s)` (a `javascript:` or `data:` url never
|
|
778
|
+
* reaches an iframe), and matches hosts exactly, so a lookalike host that
|
|
779
|
+
* merely *contains* a permitted one is not embedded.
|
|
780
|
+
*/
|
|
781
|
+
declare function getEmbedDefinition(url: string, definitions?: readonly EmbedDefinition[]): EmbedMatch | null;
|
|
782
|
+
declare class EmbedShapeUtil extends BaseBoxShapeUtil<EmbedShape> {
|
|
783
|
+
static type: "embed";
|
|
784
|
+
getDefaultProps(): EmbedShapeProps;
|
|
785
|
+
getGeometry(shape: EmbedShape): Geometry2d;
|
|
786
|
+
/** An iframe is not a quad: embeds always render through the DOM overlay. */
|
|
787
|
+
getRenderStyle(_shape: EmbedShape): StyleWords | null;
|
|
788
|
+
component(shape: EmbedShape): ReactNode;
|
|
789
|
+
indicator(shape: EmbedShape): ReactNode;
|
|
790
|
+
/** There is nothing to type into an embed; the shape has no text of its own. */
|
|
791
|
+
canEdit(_shape: EmbedShape): boolean;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
interface VideoShapeProps {
|
|
795
|
+
w: number;
|
|
796
|
+
h: number;
|
|
797
|
+
/** The `video` asset holding the file; `null` while nothing is attached. */
|
|
798
|
+
assetId: AssetId | null;
|
|
799
|
+
/** Playhead position, in seconds. */
|
|
800
|
+
time: number;
|
|
801
|
+
playing: boolean;
|
|
802
|
+
/** A hyperlink attached to the shape (not the video source). */
|
|
803
|
+
url: string;
|
|
804
|
+
altText: string;
|
|
805
|
+
}
|
|
806
|
+
type VideoShape = BaseShape<"video", VideoShapeProps>;
|
|
807
|
+
declare const VIDEO_WIDTH = 640;
|
|
808
|
+
declare const VIDEO_HEIGHT = 360;
|
|
809
|
+
declare const VIDEO_PLACEHOLDER_FILL = "#eceff3";
|
|
810
|
+
declare const VIDEO_PLACEHOLDER_STROKE = "#9fa8b2";
|
|
811
|
+
/** The play triangle drawn on the placeholder (and in an SVG export). */
|
|
812
|
+
declare const VIDEO_PLAY_COLOR = "#5f6670";
|
|
813
|
+
declare const VIDEO_PLAY_SIZE = 48;
|
|
814
|
+
/** Seeking to within this many seconds of the stored time is treated as already there. */
|
|
815
|
+
declare const VIDEO_TIME_EPSILON = 0.1;
|
|
816
|
+
/**
|
|
817
|
+
* The file the shape plays: the asset's `src`, or `null` when there is no
|
|
818
|
+
* asset yet, the asset is still uploading, or it is not a video.
|
|
819
|
+
*/
|
|
820
|
+
declare function getVideoSource(editor: Editor, shape: VideoShape): string | null;
|
|
821
|
+
/** The play triangle's points, centred in a `w`×`h` box. */
|
|
822
|
+
declare function getVideoPlayTriangle(w: number, h: number, size?: number): {
|
|
823
|
+
x: number;
|
|
824
|
+
y: number;
|
|
825
|
+
}[];
|
|
826
|
+
declare class VideoShapeUtil extends BaseBoxShapeUtil<VideoShape> {
|
|
827
|
+
static type: "video";
|
|
828
|
+
getDefaultProps(): VideoShapeProps;
|
|
829
|
+
getGeometry(shape: VideoShape): Geometry2d;
|
|
830
|
+
/** A moving picture is not a texture the engine owns: video renders in the DOM overlay. */
|
|
831
|
+
getRenderStyle(_shape: VideoShape): StyleWords | null;
|
|
832
|
+
component(shape: VideoShape): ReactNode;
|
|
833
|
+
indicator(shape: VideoShape): ReactNode;
|
|
834
|
+
/** Editing a video means taking its controls, not typing into it. */
|
|
835
|
+
canEdit(_shape: VideoShape): boolean;
|
|
836
|
+
isAspectRatioLocked(_shape: VideoShape): boolean;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* Theme lookups shared by the default shapes. Everything resolves against the
|
|
841
|
+
* light theme for now; a theme-aware variant can be threaded through later.
|
|
842
|
+
*/
|
|
843
|
+
|
|
844
|
+
/** Engine RGBA for a shape's stroke. */
|
|
845
|
+
declare function getStrokeRgba(color: DefaultColorStyle): number;
|
|
846
|
+
/**
|
|
847
|
+
* Engine RGBA for a shape's interior. `0` means "no fill".
|
|
848
|
+
*
|
|
849
|
+
* The fill *styles* and the palette *tokens* share names but are one step
|
|
850
|
+
* apart, which is easy to get wrong: `semi` paints the paper colour with a
|
|
851
|
+
* barely-there tint, `solid` paints the hue's pale tint (the `semi` token),
|
|
852
|
+
* and only `fill` paints the hue at full strength (the `solid` token). Reading
|
|
853
|
+
* the token whose name matches the style makes every filled shape a step too
|
|
854
|
+
* saturated. The stroke always uses the full-strength colour, so a `solid`
|
|
855
|
+
* fill reads as a pale body inside a saturated outline.
|
|
856
|
+
*/
|
|
857
|
+
declare function getFillRgba(color: DefaultColorStyle, fill: DefaultFillStyle): number;
|
|
858
|
+
/** Engine RGBA for a sticky note's background. */
|
|
859
|
+
declare function getNoteFillRgba(color: DefaultColorStyle): number;
|
|
860
|
+
/** CSS color string for text drawn in the DOM overlay. */
|
|
861
|
+
declare function getTextCssColor(color: DefaultColorStyle): string;
|
|
862
|
+
/** CSS color string for text on a sticky note. */
|
|
863
|
+
declare function getNoteTextCssColor(color: DefaultColorStyle): string;
|
|
864
|
+
/** Local-only font stacks; no web fonts are loaded. */
|
|
865
|
+
declare function getFontFamily(font: DefaultFontStyle): string;
|
|
866
|
+
/** Engine dash pattern id for a dash style (see `mocanvas-render::dash`). */
|
|
867
|
+
declare function getDashId(dash: string): number;
|
|
868
|
+
/** A frame's body. */
|
|
869
|
+
declare const FRAME_FILL = "#ffffff";
|
|
870
|
+
/**
|
|
871
|
+
* A frame's border.
|
|
872
|
+
*
|
|
873
|
+
* Measured off a reference render: a neutral grey hairline, not the blue-grey
|
|
874
|
+
* (`#9fa8b2`) this used to be. The difference reads as a colour cast along
|
|
875
|
+
* every frame edge, so it is worth being exact about.
|
|
876
|
+
*/
|
|
877
|
+
declare const FRAME_STROKE = "#717171";
|
|
878
|
+
/** Width of that border, in page units. */
|
|
879
|
+
declare const FRAME_STROKE_WIDTH = 1;
|
|
880
|
+
/**
|
|
881
|
+
* The frame's name, drawn in the strip above it. A shade darker than the
|
|
882
|
+
* border so the caption carries more weight than the hairline it labels.
|
|
883
|
+
*/
|
|
884
|
+
declare const FRAME_NAME_COLOR = "#5f5f5f";
|
|
885
|
+
declare const FRAME_NAME_FONT_SIZE = 12;
|
|
886
|
+
/** Height of the strip above the frame that the name is laid out in. */
|
|
887
|
+
declare const FRAME_NAME_OFFSET = 24;
|
|
888
|
+
/**
|
|
889
|
+
* Gap between the bottom of that strip and the frame's top edge. The name is
|
|
890
|
+
* bottom-aligned in the strip, so this is what sets how far above the frame it
|
|
891
|
+
* sits — measured two pixels higher than the four this used to be.
|
|
892
|
+
*/
|
|
893
|
+
declare const FRAME_NAME_GAP = 6;
|
|
894
|
+
/** Box the frame name is laid out in: the strip above the frame, minus the gap. */
|
|
895
|
+
declare const FRAME_NAME_HEIGHT: number;
|
|
896
|
+
/**
|
|
897
|
+
* A note body is not flat. It runs from a slightly deeper tint at the top to
|
|
898
|
+
* the palette's `note.fill` at the bottom — `#f7dc99` → `#fce19c` for the
|
|
899
|
+
* default yellow, i.e. the fill scaled by this factor channel-wise, which is
|
|
900
|
+
* the ratio measured off the reference render and holds for every hue.
|
|
901
|
+
*/
|
|
902
|
+
declare const NOTE_GRADIENT_TOP_SCALE = 0.9785;
|
|
903
|
+
/**
|
|
904
|
+
* The soft shadow that falls below a note. Chosen so the darkest pixel just
|
|
905
|
+
* under the body composites to `#c0c4c5` over the `#f9fafb` page and fades out
|
|
906
|
+
* about twelve pixels down, matching the reference.
|
|
907
|
+
*
|
|
908
|
+
* The shadow is decoration *outside* the body: it is drawn by the DOM overlay
|
|
909
|
+
* (and by a filter in SVG export) and never enters the shape's geometry,
|
|
910
|
+
* bounds or hit-testing.
|
|
911
|
+
*/
|
|
912
|
+
declare const NOTE_SHADOW_COLOR = "#152223";
|
|
913
|
+
declare const NOTE_SHADOW_OPACITY = 0.36;
|
|
914
|
+
/**
|
|
915
|
+
* A note's shadow falls almost entirely *below* it. A wide blur with no spread
|
|
916
|
+
* haloes the note on every side, which reads as a glow rather than as paper
|
|
917
|
+
* lifted off the page; the negative spread pulls the shadow rect in so the
|
|
918
|
+
* sides stay tight while the offset keeps the soft falloff underneath.
|
|
919
|
+
*/
|
|
920
|
+
declare const NOTE_SHADOW_OFFSET_Y = 12;
|
|
921
|
+
declare const NOTE_SHADOW_BLUR = 13;
|
|
922
|
+
declare const NOTE_SHADOW_SPREAD = -9;
|
|
923
|
+
/** `#rrggbb` as a CSS `rgba()` string at `alpha`. */
|
|
924
|
+
declare function hexToCssRgba(hex: string, alpha: number): string;
|
|
925
|
+
/** CSS colour at the bottom of a note's body gradient (the palette's note fill). */
|
|
926
|
+
declare function getNoteFillCssColor(color: DefaultColorStyle): string;
|
|
927
|
+
/**
|
|
928
|
+
* Top of a note's body gradient, derived from the colour at its bottom. Takes
|
|
929
|
+
* a `#rrggbb`; anything else (an `#rrggbbaa`, a named colour) is returned
|
|
930
|
+
* unchanged, so the gradient degrades to a flat body rather than to garbage.
|
|
931
|
+
*/
|
|
932
|
+
declare function getNoteGradientTopFrom(fill: string): string;
|
|
933
|
+
/** CSS colour at the top of a note's body gradient. */
|
|
934
|
+
declare function getNoteGradientTopCssColor(color: DefaultColorStyle): string;
|
|
935
|
+
/** CSS `background` for a note body: the vertical gradient, top to bottom. */
|
|
936
|
+
declare function getNoteBodyGradientCss(color: DefaultColorStyle): string;
|
|
937
|
+
/** CSS `box-shadow` for a note body, in shape-local units at `scale`. */
|
|
938
|
+
declare function getNoteShadowCss(scale?: number): string;
|
|
939
|
+
/**
|
|
940
|
+
* The note shadow's geometry for an SVG export, in shape-local units.
|
|
941
|
+
* `feDropShadow` has no spread, so the exporter draws the shadow as its own
|
|
942
|
+
* rect — the body inset by the spread and offset down — behind the body, and
|
|
943
|
+
* blurs just that rect.
|
|
944
|
+
*/
|
|
945
|
+
declare function getNoteShadowSvgRect(w: number, h: number, scale?: number): {
|
|
946
|
+
x: number;
|
|
947
|
+
y: number;
|
|
948
|
+
w: number;
|
|
949
|
+
h: number;
|
|
950
|
+
stdDeviation: number;
|
|
951
|
+
};
|
|
952
|
+
|
|
953
|
+
/** Curve construction shared by the line, arrow and geo shapes. */
|
|
954
|
+
|
|
955
|
+
interface CubicSegment {
|
|
956
|
+
p0: VecLike;
|
|
957
|
+
c1: VecLike;
|
|
958
|
+
c2: VecLike;
|
|
959
|
+
p1: VecLike;
|
|
960
|
+
}
|
|
961
|
+
/** A straight line expressed as a cubic (control points at 1/3 and 2/3). */
|
|
962
|
+
declare function lineSegment(a: VecLike, b: VecLike): CubicSegment;
|
|
963
|
+
/**
|
|
964
|
+
* Approximate a circular arc with `count` cubic béziers.
|
|
965
|
+
* `sweep` is signed: positive sweeps toward increasing angle (clockwise in
|
|
966
|
+
* screen space where +y points down).
|
|
967
|
+
*/
|
|
968
|
+
declare function arcToCubicSegments(center: VecLike, radius: number, startAngle: number, sweep: number, count?: number): CubicSegment[];
|
|
969
|
+
/**
|
|
970
|
+
* Convert a polyline into a smooth cubic spline that passes through every
|
|
971
|
+
* point (uniform Catmull-Rom, tension 0.5, converted to bézier handles).
|
|
972
|
+
* Endpoints are clamped so the curve starts and ends exactly on the first and
|
|
973
|
+
* last points.
|
|
974
|
+
*/
|
|
975
|
+
declare function catmullRomToBezier(points: readonly VecLike[], closed?: boolean): CubicSegment[];
|
|
976
|
+
/** Evaluate a cubic segment at `t`. */
|
|
977
|
+
declare function pointOnCubic(s: CubicSegment, t: number): Vec;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Outline math for the `geo` shape. Every polygon is inscribed in the w×h box
|
|
981
|
+
* so that its bounds are exactly the shape's bounds.
|
|
982
|
+
*/
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Inner radius of the five-pointed star, as a fraction of the outer radius.
|
|
986
|
+
* Measured off a reference render (interior-IoU and outline least-squares fits
|
|
987
|
+
* of the same star both put it at 0.51 ± 0.02, calibrated against a render of
|
|
988
|
+
* known ratio); a plain half reads the same to well under a pixel at any
|
|
989
|
+
* sensible size. A smaller ratio makes the arms too thin.
|
|
990
|
+
*/
|
|
991
|
+
declare const STAR_INNER_RATIO = 0.5;
|
|
992
|
+
/**
|
|
993
|
+
* Fraction of the box height spanned by the hexagon's two vertical sides. They
|
|
994
|
+
* are centred, so they run from `(1 - span) / 2` to `(1 + span) / 2` of the
|
|
995
|
+
* height, and the distance across them is the full box width.
|
|
996
|
+
*/
|
|
997
|
+
declare const HEXAGON_FLAT_SIDE_SPAN = 0.5;
|
|
998
|
+
/** Affinely stretch points so their bounding box becomes exactly [0,w]×[0,h]. */
|
|
999
|
+
declare function fitPointsToBox(points: readonly VecLike[], w: number, h: number): VecLike[];
|
|
1000
|
+
/**
|
|
1001
|
+
* Polygon vertices for the polygonal geo kinds, or `null` for the curved kinds
|
|
1002
|
+
* (ellipse, oval, cloud, heart). `x-box` and `check-box` return their frame;
|
|
1003
|
+
* see `getGeoDecorations` for the marks inside.
|
|
1004
|
+
*/
|
|
1005
|
+
declare function getGeoPolygonPoints(kind: GeoShapeKind, w: number, h: number): VecLike[] | null;
|
|
1006
|
+
/** Open polylines drawn inside the outline (the X of an x-box, the tick of a check-box). */
|
|
1007
|
+
declare function getGeoDecorations(kind: GeoShapeKind, w: number, h: number): VecLike[][];
|
|
1008
|
+
/** Stadium / capsule: two semicircles joined by straight sides. */
|
|
1009
|
+
declare function getStadiumSegments(w: number, h: number): CubicSegment[];
|
|
1010
|
+
/** A ring of round bumps around an inner ellipse, fitted to the box. */
|
|
1011
|
+
declare function getCloudSegments(w: number, h: number): CubicSegment[];
|
|
1012
|
+
/** Classic two-lobed heart, six cubics, bounds exactly w×h. */
|
|
1013
|
+
declare function getHeartSegments(w: number, h: number): CubicSegment[];
|
|
1014
|
+
/** Build the outline geometry for a geo kind inside a w×h box. */
|
|
1015
|
+
declare function getGeoGeometry(kind: GeoShapeKind, w: number, h: number, isFilled: boolean): Geometry2d;
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* One pass of a light 1-2-1 moving average over a freehand stroke. Endpoints
|
|
1019
|
+
* are kept fixed so the stroke still starts and ends where the pen did.
|
|
1020
|
+
* Extra properties on each point (e.g. pressure `z`) are preserved.
|
|
1021
|
+
*/
|
|
1022
|
+
declare function smoothPoints<P extends VecLike>(points: readonly P[]): P[];
|
|
1023
|
+
|
|
1024
|
+
/** Cheap text metrics used until real measurement lands (phase 2 text layout). */
|
|
1025
|
+
declare const LINE_HEIGHT = 1.3;
|
|
1026
|
+
/** Average glyph advance as a fraction of the font size. */
|
|
1027
|
+
declare const AVG_CHAR_WIDTH = 0.6;
|
|
1028
|
+
interface TextSizeEstimate {
|
|
1029
|
+
w: number;
|
|
1030
|
+
h: number;
|
|
1031
|
+
lines: number;
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Estimate the box a run of text occupies at `fontSize`, wrapping each
|
|
1035
|
+
* paragraph at `maxWidth` using an average character width.
|
|
1036
|
+
*/
|
|
1037
|
+
declare function estimateTextSize(text: string, fontSize: number, maxWidth?: number): TextSizeEstimate;
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Tolerant prop reads for the default shape utils.
|
|
1041
|
+
*
|
|
1042
|
+
* A shape can reach a util with props that do not match its declared shape:
|
|
1043
|
+
* a file written by another editor, a record hand-built by an integrator, a
|
|
1044
|
+
* partial update. Geometry and rendering must survive that, so the utils read
|
|
1045
|
+
* through these helpers rather than trusting the declared types (and rather
|
|
1046
|
+
* than sprinkling `?.` and `??` over every access).
|
|
1047
|
+
*
|
|
1048
|
+
* Each helper takes the shape's props object and returns a usable value of the
|
|
1049
|
+
* expected kind, falling back to the util's default when the stored value is
|
|
1050
|
+
* missing or of the wrong type.
|
|
1051
|
+
*/
|
|
1052
|
+
|
|
1053
|
+
type Props = Record<string, unknown>;
|
|
1054
|
+
/** A shape's props as a bag of unknowns, tolerating a missing or non-object `props`. */
|
|
1055
|
+
declare function propsOf(shape: {
|
|
1056
|
+
props?: unknown;
|
|
1057
|
+
}): Props;
|
|
1058
|
+
declare function readString(props: unknown, key: string, fallback: string): string;
|
|
1059
|
+
/** A string prop constrained to a known set; anything else falls back. */
|
|
1060
|
+
declare function readEnum<T extends string>(props: unknown, key: string, allowed: readonly T[], fallback: T): T;
|
|
1061
|
+
/** A style prop's value, validated against the style's own set of values. */
|
|
1062
|
+
declare function readStyle<T extends string>(props: unknown, key: string, style: EnumStyleProp<T>, fallback?: T): T;
|
|
1063
|
+
declare function readNumber(props: unknown, key: string, fallback: number): number;
|
|
1064
|
+
declare function readBoolean(props: unknown, key: string, fallback: boolean): boolean;
|
|
1065
|
+
/**
|
|
1066
|
+
* A shape's label. Prefers `props.text`; a record that still carries a
|
|
1067
|
+
* rich-text document (one that skipped the load-time normalization) is
|
|
1068
|
+
* flattened here so it renders rather than throwing.
|
|
1069
|
+
*/
|
|
1070
|
+
declare function readText(props: unknown, key?: string): string;
|
|
1071
|
+
/** A `{ x, y }` prop, with each coordinate falling back independently. */
|
|
1072
|
+
declare function readPoint(props: unknown, key: string, fallback: {
|
|
1073
|
+
x: number;
|
|
1074
|
+
y: number;
|
|
1075
|
+
}): {
|
|
1076
|
+
x: number;
|
|
1077
|
+
y: number;
|
|
1078
|
+
};
|
|
1079
|
+
/** An array prop; a missing or non-array value reads as empty. */
|
|
1080
|
+
declare function readArray(props: unknown, key: string): unknown[];
|
|
1081
|
+
/** A record-of-objects prop (a line's points, say); anything else reads as empty. */
|
|
1082
|
+
declare function readRecord(props: unknown, key: string): Record<string, unknown>;
|
|
1083
|
+
|
|
1084
|
+
/** Convert the engine's flat path encoding to an SVG `d` attribute (for indicators). */
|
|
1085
|
+
declare function pathWordsToSvgD(words: readonly number[]): string;
|
|
1086
|
+
|
|
1087
|
+
type GroupShape = BaseShape<"group", Record<string, never>>;
|
|
1088
|
+
/** A container with no visuals of its own; its bounds are the union of its children. */
|
|
1089
|
+
declare class GroupShapeUtil extends ShapeUtil<GroupShape> {
|
|
1090
|
+
static type: "group";
|
|
1091
|
+
getDefaultProps(): GroupShape["props"];
|
|
1092
|
+
getGeometry(shape: GroupShape): Geometry2d;
|
|
1093
|
+
getRenderStyle(_shape: GroupShape): StyleWords;
|
|
1094
|
+
canResize(): boolean;
|
|
1095
|
+
hideResizeHandles(): boolean;
|
|
1096
|
+
hideSelectionBoundsBg(): boolean;
|
|
1097
|
+
canReceiveNewChildrenOfType(): boolean;
|
|
1098
|
+
component(): null;
|
|
1099
|
+
indicator(shape: GroupShape): react.JSX.Element;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Elbow arrow routing.
|
|
1104
|
+
*
|
|
1105
|
+
* An elbow arrow runs from its start terminal to its end terminal along
|
|
1106
|
+
* axis-aligned legs instead of an arc. The route is decided by two axes — the
|
|
1107
|
+
* axis the body leaves the start on and the axis it arrives at the end on:
|
|
1108
|
+
*
|
|
1109
|
+
* - same axis on both ends: three legs (H-V-H or V-H-V), with the middle leg
|
|
1110
|
+
* placed at `midPoint` along that axis (0 hard against the start, 1 against
|
|
1111
|
+
* the end);
|
|
1112
|
+
* - different axes: two legs (an L, H-V or V-H).
|
|
1113
|
+
*
|
|
1114
|
+
* A free terminal uses the *dominant* axis — whichever of `|dx|` and `|dy|` is
|
|
1115
|
+
* larger — so a wide, short arrow leaves horizontally and a tall, narrow one
|
|
1116
|
+
* leaves vertically. A bound terminal uses the axis of the bound shape's
|
|
1117
|
+
* nearest edge normal (see `getBoundElbowAxes`), so the arrow leaves the shape
|
|
1118
|
+
* square-on rather than diagonally.
|
|
1119
|
+
*
|
|
1120
|
+
* Corners are then rounded with a quarter-turn whose radius is clamped to half
|
|
1121
|
+
* of each adjacent leg, so two corners sharing a leg can never overlap.
|
|
1122
|
+
*/
|
|
1123
|
+
|
|
1124
|
+
/** Which coordinate a leg varies: `"x"` for a horizontal leg, `"y"` for a vertical one. */
|
|
1125
|
+
type ElbowAxis = "x" | "y";
|
|
1126
|
+
/** Corner radius as a multiple of the stroke width. */
|
|
1127
|
+
declare const ELBOW_CORNER_STROKES = 2.5;
|
|
1128
|
+
interface ElbowRouteOptions {
|
|
1129
|
+
/** Where the middle leg sits along the shared axis, clamped to `0..1`. */
|
|
1130
|
+
midPoint?: number;
|
|
1131
|
+
/** Axis the body must leave the start on; defaults to the dominant axis. */
|
|
1132
|
+
startAxis?: ElbowAxis | undefined;
|
|
1133
|
+
/** Axis the body must arrive at the end on; defaults to the dominant axis. */
|
|
1134
|
+
endAxis?: ElbowAxis | undefined;
|
|
1135
|
+
/** Corner radius before per-corner clamping. */
|
|
1136
|
+
cornerRadius?: number;
|
|
1137
|
+
}
|
|
1138
|
+
interface ElbowRoute {
|
|
1139
|
+
/** Corner points from start to end, before the corners are rounded. */
|
|
1140
|
+
corners: Vec[];
|
|
1141
|
+
/** The drawn polyline: `corners` with each corner replaced by a fillet. */
|
|
1142
|
+
points: Vec[];
|
|
1143
|
+
/** The middle leg, when the route has one; `null` for an L or a straight run. */
|
|
1144
|
+
midLeg: [Vec, Vec] | null;
|
|
1145
|
+
/** The axis the middle leg slides along, `null` when there is no middle leg. */
|
|
1146
|
+
slideAxis: ElbowAxis | null;
|
|
1147
|
+
}
|
|
1148
|
+
/** The axis a free terminal leaves on: the one the terminals are further apart on. */
|
|
1149
|
+
declare function getDominantAxis(start: VecLike, end: VecLike): ElbowAxis;
|
|
1150
|
+
/** `corners` with every interior corner replaced by a fillet of `radius`. */
|
|
1151
|
+
declare function roundElbowCorners(corners: Vec[], radius: number): Vec[];
|
|
1152
|
+
/**
|
|
1153
|
+
* Route an elbow from `start` to `end`. See the module comment for the rule.
|
|
1154
|
+
*/
|
|
1155
|
+
declare function getElbowRoute(start: VecLike, end: VecLike, options?: ElbowRouteOptions): ElbowRoute;
|
|
1156
|
+
/** The elbow body for an arrow: the rounded route as a polyline body. */
|
|
1157
|
+
declare function getElbowBody(start: VecLike, end: VecLike, options?: ElbowRouteOptions): ElbowBody;
|
|
1158
|
+
/**
|
|
1159
|
+
* `elbowMidPoint` for a midpoint handle dragged to `point`: how far along
|
|
1160
|
+
* `axis` it sits between the terminals, clamped to `0..1`.
|
|
1161
|
+
*/
|
|
1162
|
+
declare function getElbowMidPointFromPoint(start: VecLike, end: VecLike, point: VecLike, axis: ElbowAxis): number;
|
|
1163
|
+
|
|
1164
|
+
declare const defaultShapeUtils: ShapeUtilConstructor[];
|
|
1165
|
+
|
|
1166
|
+
type ArrowTerminal = "start" | "end";
|
|
1167
|
+
/**
|
|
1168
|
+
* Props of an `arrow` binding. The record layout matches what `.tldr` files
|
|
1169
|
+
* store for arrow bindings so documents round-trip.
|
|
1170
|
+
*/
|
|
1171
|
+
interface ArrowBindingProps {
|
|
1172
|
+
/** Which end of the arrow (`fromId`) is attached to the bound shape (`toId`). */
|
|
1173
|
+
terminal: ArrowTerminal;
|
|
1174
|
+
/** Anchor inside the bound shape's geometry bounds, normalized to `[0, 1]`. */
|
|
1175
|
+
normalizedAnchor: {
|
|
1176
|
+
x: number;
|
|
1177
|
+
y: number;
|
|
1178
|
+
};
|
|
1179
|
+
/** When true the terminal sits exactly on the anchor instead of on the shape's outline. */
|
|
1180
|
+
isExact: boolean;
|
|
1181
|
+
/** When true the anchor was placed deliberately (Alt or a lingering drag) rather than snapped to the center. */
|
|
1182
|
+
isPrecise: boolean;
|
|
1183
|
+
}
|
|
1184
|
+
type ArrowBinding = BaseBinding<"arrow", ArrowBindingProps>;
|
|
1185
|
+
/**
|
|
1186
|
+
* Keeps arrows attached to the shapes their terminals are bound to.
|
|
1187
|
+
*
|
|
1188
|
+
* - When the bound shape changes, the arrow is rewritten so its geometry is
|
|
1189
|
+
* re-derived (and its static fallback stays current).
|
|
1190
|
+
* - When the bound shape goes away, or the binding is deleted with
|
|
1191
|
+
* `isolateShapes`, the terminal is frozen at its current page position.
|
|
1192
|
+
*/
|
|
1193
|
+
declare class ArrowBindingUtil extends BindingUtil<ArrowBinding> {
|
|
1194
|
+
static type: "arrow";
|
|
1195
|
+
getDefaultProps(): ArrowBindingProps;
|
|
1196
|
+
onAfterChangeToShape({ binding }: BindingOnShapeChangeOptions<ArrowBinding>): void;
|
|
1197
|
+
onAfterChangeFromShape({ binding, shapeBefore, shapeAfter }: BindingOnShapeChangeOptions<ArrowBinding>): void;
|
|
1198
|
+
onBeforeDeleteToShape({ binding }: BindingOnShapeDeleteOptions<ArrowBinding>): void;
|
|
1199
|
+
onBeforeIsolateFromShape({ binding }: BindingOnShapeIsolateOptions<ArrowBinding>): void;
|
|
1200
|
+
onBeforeIsolateToShape({ binding }: BindingOnShapeIsolateOptions<ArrowBinding>): void;
|
|
1201
|
+
/** Write the resolved terminals into the arrow so its geometry is rebuilt. */
|
|
1202
|
+
private refreshArrow;
|
|
1203
|
+
/**
|
|
1204
|
+
* Convert the bound terminal into a static point at its current resolved
|
|
1205
|
+
* position. Idempotent: calling it twice for the same binding is a no-op the
|
|
1206
|
+
* second time.
|
|
1207
|
+
*/
|
|
1208
|
+
private freezeTerminal;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Arrow terminal resolution.
|
|
1213
|
+
*
|
|
1214
|
+
* An arrow terminal is either static (`props.start` / `props.end`, in
|
|
1215
|
+
* arrow-local space) or bound to another shape through an `arrow` binding.
|
|
1216
|
+
* A bound terminal is derived from the bound shape every time it is read, so
|
|
1217
|
+
* the arrow follows the shape without anyone copying coordinates around.
|
|
1218
|
+
*/
|
|
1219
|
+
|
|
1220
|
+
/** Affine transform components as returned by `Editor.getShapePageTransform`. */
|
|
1221
|
+
interface TransformLike {
|
|
1222
|
+
a: number;
|
|
1223
|
+
b: number;
|
|
1224
|
+
c: number;
|
|
1225
|
+
d: number;
|
|
1226
|
+
e: number;
|
|
1227
|
+
f: number;
|
|
1228
|
+
}
|
|
1229
|
+
declare function applyTransform(m: TransformLike, p: VecLike): Vec;
|
|
1230
|
+
interface ArrowBindings {
|
|
1231
|
+
start?: ArrowBinding;
|
|
1232
|
+
end?: ArrowBinding;
|
|
1233
|
+
}
|
|
1234
|
+
/** The arrow's terminal bindings, keyed by terminal. */
|
|
1235
|
+
declare function getArrowBindings(editor: Editor, arrow: ArrowShape): ArrowBindings;
|
|
1236
|
+
/** Map a normalized anchor `(0..1, 0..1)` into a shape's geometry bounds (shape-local space). */
|
|
1237
|
+
declare function getAnchorInShapeSpace(editor: Editor, shape: UnknownShape, normalizedAnchor: VecLike): Vec;
|
|
1238
|
+
/**
|
|
1239
|
+
* The topmost shape an arrow terminal dropped at `pagePoint` should bind to,
|
|
1240
|
+
* or `undefined`. Candidates must accept arrow bindings, may not be arrows,
|
|
1241
|
+
* the arrow itself, or one of the arrow's ancestors, and are hit inside their
|
|
1242
|
+
* outline (or within the hit-test margin of it) regardless of fill, so an arrow
|
|
1243
|
+
* can attach to a hollow rectangle by pointing into it.
|
|
1244
|
+
*/
|
|
1245
|
+
declare function getArrowBindingTargetAtPoint(editor: Editor, arrow: UnknownShape, pagePoint: VecLike): UnknownShape | undefined;
|
|
1246
|
+
/** Normalize a page point into a shape's geometry bounds, clamped to `[0, 1]`. */
|
|
1247
|
+
declare function getNormalizedAnchor(editor: Editor, shape: UnknownShape, pagePoint: VecLike): Vec;
|
|
1248
|
+
/**
|
|
1249
|
+
* Flattened outline of a geometry as line segments, in the geometry's own
|
|
1250
|
+
* space. Groups are walked recursively; label geometry is skipped so arrows
|
|
1251
|
+
* end on the shape body rather than on its text box.
|
|
1252
|
+
*/
|
|
1253
|
+
declare function getOutlineSegments(geometry: Geometry2d): [Vec, Vec][];
|
|
1254
|
+
/**
|
|
1255
|
+
* Intersection point of segments `a1-a2` and `b1-b2`, or `null` when they do
|
|
1256
|
+
* not cross. Parallel and degenerate segments never intersect.
|
|
1257
|
+
*/
|
|
1258
|
+
declare function intersectSegments(a1: VecLike, a2: VecLike, b1: VecLike, b2: VecLike): Vec | null;
|
|
1259
|
+
interface ArrowTerminals {
|
|
1260
|
+
start: Vec;
|
|
1261
|
+
end: Vec;
|
|
1262
|
+
}
|
|
1263
|
+
/** Which axis an elbow arrow leaves a terminal on, when that terminal is bound. */
|
|
1264
|
+
interface ElbowTerminalAxes {
|
|
1265
|
+
start?: "x" | "y";
|
|
1266
|
+
end?: "x" | "y";
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* For each bound terminal, the axis of the normal of the bound shape's nearest
|
|
1270
|
+
* edge: `"x"` on a left or right edge, `"y"` on a top or bottom one. An elbow
|
|
1271
|
+
* arrow routes its first and last leg along these, so it leaves and enters a
|
|
1272
|
+
* bound shape square-on instead of diagonally. A free terminal is absent from
|
|
1273
|
+
* the result and the router falls back to the dominant axis.
|
|
1274
|
+
*
|
|
1275
|
+
* The bound shape's bounds are measured in arrow space, so a rotated shape is
|
|
1276
|
+
* compared against the box it occupies from the arrow's point of view — which
|
|
1277
|
+
* is the frame the route itself is built in.
|
|
1278
|
+
*/
|
|
1279
|
+
declare function getBoundElbowAxes(editor: Editor, arrow: ArrowShape, terminals: ArrowTerminals): ElbowTerminalAxes;
|
|
1280
|
+
/**
|
|
1281
|
+
* How far a bound arrow stops short of the shape it points at, as a multiple
|
|
1282
|
+
* of its stroke width. An arrowhead resting on a shape's border reads as part
|
|
1283
|
+
* of that border; a small gap reads as pointing at it.
|
|
1284
|
+
*/
|
|
1285
|
+
declare const ARROW_TERMINAL_GAP_STROKES = 2.7;
|
|
1286
|
+
/** The gap in page units for an arrow of a given size style. */
|
|
1287
|
+
declare function getArrowTerminalGap(size: DefaultSizeStyle, scale?: number): number;
|
|
1288
|
+
/**
|
|
1289
|
+
* Resolve both terminals of an arrow in arrow-local space.
|
|
1290
|
+
*
|
|
1291
|
+
* Unbound terminals come straight from `props`. A bound terminal starts at
|
|
1292
|
+
* its anchor inside the bound shape's bounds; unless the binding is exact it
|
|
1293
|
+
* is then pulled back to where the arrow body first crosses the bound shape's
|
|
1294
|
+
* outline on its way from the opposite terminal. If the body never crosses
|
|
1295
|
+
* the outline (e.g. the other terminal is inside the shape) the anchor is used.
|
|
1296
|
+
*/
|
|
1297
|
+
declare function getArrowTerminalsInArrowSpace(editor: Editor, arrow: ArrowShape): ArrowTerminals;
|
|
1298
|
+
|
|
1299
|
+
/** The built-in binding utils, in the order they are registered. */
|
|
1300
|
+
declare const defaultBindingUtils: BindingUtilConstructor[];
|
|
1301
|
+
|
|
1302
|
+
/** Drag to draw an arrow; dropping the end on a shape binds it to that shape. */
|
|
1303
|
+
declare class ArrowTool extends StateNode {
|
|
1304
|
+
static id: string;
|
|
1305
|
+
static initial: string;
|
|
1306
|
+
static children: () => StateNodeConstructor[];
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/** Freehand drawing. */
|
|
1310
|
+
declare class DrawTool extends StateNode {
|
|
1311
|
+
static id: string;
|
|
1312
|
+
static initial: string;
|
|
1313
|
+
static children: () => StateNodeConstructor[];
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/** Delete shapes by dragging over them. */
|
|
1317
|
+
declare class EraserTool extends StateNode {
|
|
1318
|
+
static id: string;
|
|
1319
|
+
static initial: string;
|
|
1320
|
+
static children: () => StateNodeConstructor[];
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
/** Drag out a frame, or click to place a default-sized one. */
|
|
1324
|
+
declare class FrameTool extends StateNode {
|
|
1325
|
+
static id: string;
|
|
1326
|
+
static initial: string;
|
|
1327
|
+
static children: () => StateNodeConstructor[];
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/** Draw a geo shape (rectangle, ellipse, ...) by dragging. Pick the kind with `editor.setCurrentTool('geo', { geo })`. */
|
|
1331
|
+
declare class GeoTool extends StateNode {
|
|
1332
|
+
static id: string;
|
|
1333
|
+
static initial: string;
|
|
1334
|
+
static children: () => StateNodeConstructor[];
|
|
1335
|
+
geo: GeoShapeKind;
|
|
1336
|
+
onEnter(info: Record<string, unknown>): void;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
/** Pan the camera by dragging. */
|
|
1340
|
+
declare class HandTool extends StateNode {
|
|
1341
|
+
static id: string;
|
|
1342
|
+
static initial: string;
|
|
1343
|
+
static children: () => StateNodeConstructor[];
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
/** Drag for a straight line, or click repeatedly for a multi-segment one. */
|
|
1347
|
+
declare class LineTool extends StateNode {
|
|
1348
|
+
static id: string;
|
|
1349
|
+
static initial: string;
|
|
1350
|
+
static children: () => StateNodeConstructor[];
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
/** Click to place a sticky note. */
|
|
1354
|
+
declare class NoteTool extends StateNode {
|
|
1355
|
+
static id: string;
|
|
1356
|
+
static initial: string;
|
|
1357
|
+
static children: () => StateNodeConstructor[];
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
/** Selection, brush select, drag-to-move, resize, rotate, and shape handle dragging. */
|
|
1361
|
+
declare class SelectTool extends StateNode {
|
|
1362
|
+
static id: string;
|
|
1363
|
+
static initial: string;
|
|
1364
|
+
static children: () => StateNodeConstructor[];
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/** Click to place a text shape and start typing. */
|
|
1368
|
+
declare class TextTool extends StateNode {
|
|
1369
|
+
static id: string;
|
|
1370
|
+
static initial: string;
|
|
1371
|
+
static children: () => StateNodeConstructor[];
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
declare const defaultTools: StateNodeConstructor[];
|
|
1375
|
+
|
|
1376
|
+
interface TextMeasureOptions {
|
|
1377
|
+
fontFamily: string;
|
|
1378
|
+
fontSize: number;
|
|
1379
|
+
fontWeight?: string | number;
|
|
1380
|
+
/** Unitless line height (multiplier of the font size). */
|
|
1381
|
+
lineHeight: number;
|
|
1382
|
+
/** Wrap width in CSS px, including `padding`. Omit for a single unwrapped run per paragraph. */
|
|
1383
|
+
maxWidth?: number;
|
|
1384
|
+
/** Padding applied on every side; included in the returned `w`/`h`. */
|
|
1385
|
+
padding?: number;
|
|
1386
|
+
}
|
|
1387
|
+
interface TextMeasurement {
|
|
1388
|
+
w: number;
|
|
1389
|
+
h: number;
|
|
1390
|
+
lineCount: number;
|
|
1391
|
+
}
|
|
1392
|
+
/** Text exactly as the static label / measuring element must render it to occupy the right box. */
|
|
1393
|
+
declare function toDisplayText(text: string): string;
|
|
1394
|
+
/**
|
|
1395
|
+
* Measures runs of text with a hidden DOM element, mirroring the CSS the
|
|
1396
|
+
* `<TextLabel>` component uses (pre-wrap + break-word, border-box padding).
|
|
1397
|
+
* Falls back to `estimateTextSize` where no `document` exists (tests, SSR).
|
|
1398
|
+
*/
|
|
1399
|
+
declare class TextMeasure {
|
|
1400
|
+
private element;
|
|
1401
|
+
private readonly cache;
|
|
1402
|
+
measureText(text: string, opts: TextMeasureOptions): TextMeasurement;
|
|
1403
|
+
/** Number of cached measurements (for tests and debugging). */
|
|
1404
|
+
get cacheSize(): number;
|
|
1405
|
+
clearCache(): void;
|
|
1406
|
+
dispose(): void;
|
|
1407
|
+
private estimate;
|
|
1408
|
+
private measureDom;
|
|
1409
|
+
private getElement;
|
|
1410
|
+
}
|
|
1411
|
+
/** The shared measurer. */
|
|
1412
|
+
declare function getTextMeasure(): TextMeasure;
|
|
1413
|
+
|
|
1414
|
+
/** Pure text layout helpers shared by the label-bearing shapes. DOM-free so they are testable in Node. */
|
|
1415
|
+
|
|
1416
|
+
/**
|
|
1417
|
+
* Extra height a box needs so its label fits: `labelHeight - boxHeight`,
|
|
1418
|
+
* never negative. `growY` is stored on the shape and added to `h`.
|
|
1419
|
+
*/
|
|
1420
|
+
declare function computeGrowY(labelHeight: number, boxHeight: number): number;
|
|
1421
|
+
/** Trim whitespace at the end of a label (kept inside the run, e.g. indentation on later lines). */
|
|
1422
|
+
declare function trimTrailingWhitespace(text: string): string;
|
|
1423
|
+
interface LabelMeasureOptions {
|
|
1424
|
+
font: DefaultFontStyle;
|
|
1425
|
+
fontSize: number;
|
|
1426
|
+
/** Wrap width in px including padding; omit for no soft wrapping. */
|
|
1427
|
+
maxWidth?: number;
|
|
1428
|
+
padding?: number;
|
|
1429
|
+
}
|
|
1430
|
+
/** Measure a shape label with the shared measurer using the default label typography. */
|
|
1431
|
+
declare function measureLabel(text: string, opts: LabelMeasureOptions): TextMeasurement;
|
|
1432
|
+
/** Minimum width of an auto-sized text shape so an empty one still shows a caret area. */
|
|
1433
|
+
declare const TEXT_SHAPE_MIN_WIDTH = 8;
|
|
1434
|
+
interface TextShapeSizeInput {
|
|
1435
|
+
text: string;
|
|
1436
|
+
font: DefaultFontStyle;
|
|
1437
|
+
fontSize: number;
|
|
1438
|
+
autoSize: boolean;
|
|
1439
|
+
/** Current wrap width, used when `autoSize` is off. */
|
|
1440
|
+
w: number;
|
|
1441
|
+
}
|
|
1442
|
+
/** Width and height of a text shape: measured (auto-size) or wrapped at `w`. */
|
|
1443
|
+
declare function getTextShapeSize(input: TextShapeSizeInput): {
|
|
1444
|
+
w: number;
|
|
1445
|
+
h: number;
|
|
1446
|
+
lineCount: number;
|
|
1447
|
+
};
|
|
1448
|
+
|
|
1449
|
+
interface TextLabelProps {
|
|
1450
|
+
shape: UnknownShape;
|
|
1451
|
+
text: string;
|
|
1452
|
+
isEditing: boolean;
|
|
1453
|
+
font: DefaultFontStyle;
|
|
1454
|
+
fontSize: number;
|
|
1455
|
+
/** CSS color of the text. */
|
|
1456
|
+
color: string;
|
|
1457
|
+
align: DefaultHorizontalAlignStyle;
|
|
1458
|
+
verticalAlign: DefaultVerticalAlignStyle;
|
|
1459
|
+
/** Soft-wrap at `width`. When false the label only breaks at explicit newlines. */
|
|
1460
|
+
wrap: boolean;
|
|
1461
|
+
/** Box the label is laid out in, in shape-local px. Omit for an intrinsically sized label. */
|
|
1462
|
+
width?: number;
|
|
1463
|
+
height?: number;
|
|
1464
|
+
/** Padding on every side of the text, in px (default 0). */
|
|
1465
|
+
padding?: number;
|
|
1466
|
+
/** Shown in place of an empty label when not editing. */
|
|
1467
|
+
placeholder?: string;
|
|
1468
|
+
/** Single-line labels commit on Enter and never contain newlines (frame names). */
|
|
1469
|
+
singleLine?: boolean;
|
|
1470
|
+
/** Called with the new text; at most once per animation frame while typing. */
|
|
1471
|
+
onChange(text: string): void;
|
|
1472
|
+
}
|
|
1473
|
+
declare function alignToJustify(align: DefaultHorizontalAlignStyle): CSSProperties["justifyContent"];
|
|
1474
|
+
declare function alignToTextAlign(align: DefaultHorizontalAlignStyle): CSSProperties["textAlign"];
|
|
1475
|
+
declare function verticalAlignToAlignItems(align: DefaultVerticalAlignStyle): CSSProperties["alignItems"];
|
|
1476
|
+
/**
|
|
1477
|
+
* A shape's text label. Static text when idle; while editing, a plain-text
|
|
1478
|
+
* `<textarea>` overlays an invisible copy of the text so the box keeps the
|
|
1479
|
+
* exact size the static label would have.
|
|
1480
|
+
*/
|
|
1481
|
+
declare function TextLabel(props: TextLabelProps): ReactNode;
|
|
1482
|
+
|
|
1483
|
+
/** Side of the drawing grid. */
|
|
1484
|
+
declare const ICON_GRID = 24;
|
|
1485
|
+
/** Longest side of the box a geo outline is fitted into, centred in the grid. */
|
|
1486
|
+
declare const GEO_BOX = 16;
|
|
1487
|
+
/** The box a geo icon's outline is fitted into, in grid units. */
|
|
1488
|
+
declare function getGeoIconBox(kind: GeoShapeKind): readonly [number, number];
|
|
1489
|
+
/** `d` attribute for each geo kind, fitted to its icon box. */
|
|
1490
|
+
declare const GEO_ICON_PATHS: Record<GeoShapeKind, string>;
|
|
1491
|
+
/** Every icon in the set, keyed by name. */
|
|
1492
|
+
declare const ICONS: {
|
|
1493
|
+
"geo-rectangle": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1494
|
+
"geo-ellipse": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1495
|
+
"geo-triangle": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1496
|
+
"geo-diamond": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1497
|
+
"geo-pentagon": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1498
|
+
"geo-hexagon": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1499
|
+
"geo-octagon": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1500
|
+
"geo-star": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1501
|
+
"geo-rhombus": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1502
|
+
"geo-rhombus-2": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1503
|
+
"geo-oval": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1504
|
+
"geo-trapezoid": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1505
|
+
"geo-arrow-right": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1506
|
+
"geo-arrow-left": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1507
|
+
"geo-arrow-up": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1508
|
+
"geo-arrow-down": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1509
|
+
"geo-x-box": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1510
|
+
"geo-check-box": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1511
|
+
"geo-cloud": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1512
|
+
"geo-heart": ReactElement<unknown, string | react.JSXElementConstructor<any>>;
|
|
1513
|
+
"fill-none": react.JSX.Element;
|
|
1514
|
+
"fill-semi": react.JSX.Element;
|
|
1515
|
+
"fill-solid": react.JSX.Element;
|
|
1516
|
+
"fill-pattern": react.JSX.Element;
|
|
1517
|
+
"dash-draw": react.JSX.Element;
|
|
1518
|
+
"dash-solid": react.JSX.Element;
|
|
1519
|
+
"dash-dashed": react.JSX.Element;
|
|
1520
|
+
"dash-dotted": react.JSX.Element;
|
|
1521
|
+
"size-s": react.JSX.Element;
|
|
1522
|
+
"size-m": react.JSX.Element;
|
|
1523
|
+
"size-l": react.JSX.Element;
|
|
1524
|
+
"size-xl": react.JSX.Element;
|
|
1525
|
+
"align-left": react.JSX.Element;
|
|
1526
|
+
"align-center": react.JSX.Element;
|
|
1527
|
+
"align-right": react.JSX.Element;
|
|
1528
|
+
"valign-top": react.JSX.Element;
|
|
1529
|
+
"valign-middle": react.JSX.Element;
|
|
1530
|
+
"valign-bottom": react.JSX.Element;
|
|
1531
|
+
"font-draw": react.JSX.Element;
|
|
1532
|
+
"font-sans": react.JSX.Element;
|
|
1533
|
+
"font-serif": react.JSX.Element;
|
|
1534
|
+
"font-mono": react.JSX.Element;
|
|
1535
|
+
"zoom-in": react.JSX.Element;
|
|
1536
|
+
"zoom-out": react.JSX.Element;
|
|
1537
|
+
"zoom-fit": react.JSX.Element;
|
|
1538
|
+
undo: react.JSX.Element;
|
|
1539
|
+
redo: react.JSX.Element;
|
|
1540
|
+
lock: react.JSX.Element;
|
|
1541
|
+
unlock: react.JSX.Element;
|
|
1542
|
+
duplicate: react.JSX.Element;
|
|
1543
|
+
trash: react.JSX.Element;
|
|
1544
|
+
group: react.JSX.Element;
|
|
1545
|
+
ungroup: react.JSX.Element;
|
|
1546
|
+
"bring-forward": react.JSX.Element;
|
|
1547
|
+
"send-backward": react.JSX.Element;
|
|
1548
|
+
"chevron-down": react.JSX.Element;
|
|
1549
|
+
"chevron-up": react.JSX.Element;
|
|
1550
|
+
check: react.JSX.Element;
|
|
1551
|
+
close: react.JSX.Element;
|
|
1552
|
+
mixed: react.JSX.Element;
|
|
1553
|
+
select: react.JSX.Element;
|
|
1554
|
+
hand: react.JSX.Element;
|
|
1555
|
+
draw: react.JSX.Element;
|
|
1556
|
+
eraser: react.JSX.Element;
|
|
1557
|
+
text: react.JSX.Element;
|
|
1558
|
+
note: react.JSX.Element;
|
|
1559
|
+
frame: react.JSX.Element;
|
|
1560
|
+
arrow: react.JSX.Element;
|
|
1561
|
+
line: react.JSX.Element;
|
|
1562
|
+
image: react.JSX.Element;
|
|
1563
|
+
};
|
|
1564
|
+
type IconName = keyof typeof ICONS;
|
|
1565
|
+
/** All icon names, sorted — handy for tests and galleries. */
|
|
1566
|
+
declare const ICON_NAMES: IconName[];
|
|
1567
|
+
interface IconProps {
|
|
1568
|
+
name: IconName;
|
|
1569
|
+
/** Rendered size in px; the artwork is scaled from the 24×24 grid. */
|
|
1570
|
+
size?: number;
|
|
1571
|
+
className?: string;
|
|
1572
|
+
}
|
|
1573
|
+
/** Renders one icon from the set. Decorative by default: label the button instead. */
|
|
1574
|
+
declare function Icon({ name, size, className }: IconProps): react.JSX.Element;
|
|
1575
|
+
|
|
1576
|
+
/** Accelerator prefix for tooltips: `⌘` on Apple platforms, `Ctrl+` elsewhere. */
|
|
1577
|
+
declare const MOD_KEY: string;
|
|
1578
|
+
interface ToolbarItem {
|
|
1579
|
+
/** Unique key; also the geo kind for geo entries. */
|
|
1580
|
+
id: string;
|
|
1581
|
+
/** Tool id that must be registered for this entry to appear. */
|
|
1582
|
+
tool: string;
|
|
1583
|
+
icon: IconName;
|
|
1584
|
+
label: string;
|
|
1585
|
+
/** Displayed in the tooltip, next to the label. */
|
|
1586
|
+
shortcut?: string;
|
|
1587
|
+
/** Geo kind the entry selects, for entries driving the `geo` tool. */
|
|
1588
|
+
geo?: GeoShapeKind;
|
|
1589
|
+
/** Tools outside the default set: shown only when the app registers them. */
|
|
1590
|
+
optional?: boolean;
|
|
1591
|
+
}
|
|
1592
|
+
/** Toolbar entries, grouped; groups are separated by a divider. */
|
|
1593
|
+
declare const TOOLBAR_GROUPS: readonly (readonly ToolbarItem[])[];
|
|
1594
|
+
/** Geo kinds that live behind the "more shapes" popover. */
|
|
1595
|
+
declare const PRIMARY_GEO_KINDS: readonly GeoShapeKind[];
|
|
1596
|
+
declare const MORE_GEO_KINDS: readonly GeoShapeKind[];
|
|
1597
|
+
/** Bottom toolbar: tools, grouped, plus a popover with the rest of the shapes. */
|
|
1598
|
+
declare const Toolbar: () => react.JSX.Element;
|
|
1599
|
+
/** Zoom controls and undo/redo. */
|
|
1600
|
+
declare const ZoomBar: () => react.JSX.Element;
|
|
1601
|
+
/** Compact frame-statistics chip. Toggled with ⌥D. */
|
|
1602
|
+
declare const DebugStats: ({ editor }: {
|
|
1603
|
+
editor: Editor;
|
|
1604
|
+
}) => react.JSX.Element;
|
|
1605
|
+
declare function DefaultUi({ editor, showStats }: {
|
|
1606
|
+
editor: Editor;
|
|
1607
|
+
showStats?: boolean;
|
|
1608
|
+
}): react.JSX.Element;
|
|
1609
|
+
|
|
1610
|
+
/**
|
|
1611
|
+
* The style rows the panel renders, in order, grouped as the panel groups them.
|
|
1612
|
+
*
|
|
1613
|
+
* With a selection this is exactly what the selected shapes declare, so a line
|
|
1614
|
+
* (colour, dash, size) never offers Font or Align; with nothing selected it is
|
|
1615
|
+
* what the active drawing tool is about to create. Exported so the rule can be
|
|
1616
|
+
* tested without a DOM.
|
|
1617
|
+
*/
|
|
1618
|
+
interface StylePanelSections {
|
|
1619
|
+
/** The geo picker. */
|
|
1620
|
+
shape: boolean;
|
|
1621
|
+
/** Colour swatch grids. */
|
|
1622
|
+
color: boolean;
|
|
1623
|
+
labelColor: boolean;
|
|
1624
|
+
/** Stroke and fill. */
|
|
1625
|
+
fill: boolean;
|
|
1626
|
+
dash: boolean;
|
|
1627
|
+
size: boolean;
|
|
1628
|
+
/** Text. */
|
|
1629
|
+
font: boolean;
|
|
1630
|
+
align: boolean;
|
|
1631
|
+
verticalAlign: boolean;
|
|
1632
|
+
/** Shape-level, not a style: only with a selection. */
|
|
1633
|
+
opacity: boolean;
|
|
1634
|
+
}
|
|
1635
|
+
/** Decide which rows the style panel shows for the editor's current state. */
|
|
1636
|
+
declare function getStylePanelSections(editor: Editor): StylePanelSections;
|
|
1637
|
+
/** Whether any row at all is on. */
|
|
1638
|
+
declare function hasAnyStyleSection(s: StylePanelSections): boolean;
|
|
1639
|
+
/**
|
|
1640
|
+
* Style controls for the selection (or, with nothing selected, for the next
|
|
1641
|
+
* shape the active drawing tool creates). Sits in the top-right corner.
|
|
1642
|
+
*
|
|
1643
|
+
* Rows are grouped — shape, colour, stroke and fill, text, opacity — and every
|
|
1644
|
+
* group is omitted when the selection cannot carry it.
|
|
1645
|
+
*/
|
|
1646
|
+
declare const StylePanel: () => react.JSX.Element | null;
|
|
1647
|
+
|
|
1648
|
+
interface Placement {
|
|
1649
|
+
left: number;
|
|
1650
|
+
top: number;
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Centre `box` on `anchor`, on the preferred side if it fits there and on the
|
|
1654
|
+
* other side if it does not, then clamp the result inside the viewport.
|
|
1655
|
+
*/
|
|
1656
|
+
declare function placeNear(anchor: DOMRect, box: {
|
|
1657
|
+
width: number;
|
|
1658
|
+
height: number;
|
|
1659
|
+
}, prefer: "above" | "below", vw: number, vh: number): Placement;
|
|
1660
|
+
/**
|
|
1661
|
+
* The single tooltip for the whole UI. Mount it once; it labels any element
|
|
1662
|
+
* carrying `data-tooltip` (and, optionally, `data-shortcut`) by delegation, so
|
|
1663
|
+
* buttons stay plain buttons.
|
|
1664
|
+
*/
|
|
1665
|
+
declare function UiTooltip(): react.JSX.Element | null;
|
|
1666
|
+
interface PopoverProps {
|
|
1667
|
+
/** The control the popover belongs to; also the click target that keeps it open. */
|
|
1668
|
+
anchorRef: RefObject<HTMLElement | null>;
|
|
1669
|
+
open: boolean;
|
|
1670
|
+
onClose: () => void;
|
|
1671
|
+
label: string;
|
|
1672
|
+
/** Columns in the popover grid. */
|
|
1673
|
+
cols?: number;
|
|
1674
|
+
prefer?: "above" | "below";
|
|
1675
|
+
children: ReactNode;
|
|
1676
|
+
}
|
|
1677
|
+
/** A dismissable floating grid anchored to a button. */
|
|
1678
|
+
declare function Popover({ anchorRef, open, onClose, label, cols, prefer, children }: PopoverProps): react.JSX.Element | null;
|
|
1679
|
+
|
|
1680
|
+
/** Whether the frame-statistics chip is visible. Toggled with ⌥D. */
|
|
1681
|
+
declare const debugStatsOpen: _mocanvas_state.Atom<boolean>;
|
|
1682
|
+
/** Default keyboard shortcuts: tool switching, undo/redo, select all, zoom, clipboard. */
|
|
1683
|
+
declare function useKeyboardShortcuts(editor: Editor | null): void;
|
|
1684
|
+
|
|
1685
|
+
/** Serialize the editor's document to `.tldr` JSON text. */
|
|
1686
|
+
declare function serializeMocanvasFile(editor: Editor): string;
|
|
1687
|
+
/**
|
|
1688
|
+
* The parse result plus whatever the load could not read cleanly.
|
|
1689
|
+
*
|
|
1690
|
+
* `warnings` is always present (empty when the file needed nothing unusual)
|
|
1691
|
+
* and is worth surfacing: it names shapes whose props were repaired, segments
|
|
1692
|
+
* that had to be dropped, and shape types with no registered util.
|
|
1693
|
+
*/
|
|
1694
|
+
type LoadMocanvasFileResult = ParseTldrFileResult & {
|
|
1695
|
+
warnings: string[];
|
|
1696
|
+
};
|
|
1697
|
+
/**
|
|
1698
|
+
* Load a `.tldr` file (text or parsed JSON) into the editor, replacing the
|
|
1699
|
+
* document.
|
|
1700
|
+
*
|
|
1701
|
+
* Records are normalized before they reach the store, so shapes written by
|
|
1702
|
+
* another generation of the format — a rich-text label, a packed freehand
|
|
1703
|
+
* path, a prop this build did not exist for — arrive in the form the shape
|
|
1704
|
+
* utils expect. `records` in the result are the normalized ones.
|
|
1705
|
+
*/
|
|
1706
|
+
declare function loadMocanvasFile(editor: Editor, json: unknown): LoadMocanvasFileResult;
|
|
1707
|
+
|
|
1708
|
+
interface MocanvasProps {
|
|
1709
|
+
/** Reuse a store (e.g. for persistence or collaboration). */
|
|
1710
|
+
store?: EditorStore;
|
|
1711
|
+
/** Extra shape utils beyond the defaults. */
|
|
1712
|
+
shapeUtils?: readonly ShapeUtilConstructor[];
|
|
1713
|
+
/** Extra binding utils beyond the defaults. */
|
|
1714
|
+
bindingUtils?: readonly BindingUtilConstructor[];
|
|
1715
|
+
/** Extra tools beyond the defaults. */
|
|
1716
|
+
tools?: readonly StateNodeConstructor[];
|
|
1717
|
+
initialState?: string;
|
|
1718
|
+
onMount?: (editor: Editor) => void | (() => void);
|
|
1719
|
+
/** Hide the default toolbar and zoom bar. */
|
|
1720
|
+
hideUi?: boolean;
|
|
1721
|
+
showStats?: boolean;
|
|
1722
|
+
className?: string;
|
|
1723
|
+
style?: CSSProperties;
|
|
1724
|
+
children?: ReactNode;
|
|
1725
|
+
components?: CanvasProps["components"];
|
|
1726
|
+
/** Editor config overrides. */
|
|
1727
|
+
options?: ConstructorParameters<typeof Editor>[0]["options"];
|
|
1728
|
+
}
|
|
1729
|
+
/** Batteries-included canvas: default shapes, tools, shortcuts and UI. */
|
|
1730
|
+
declare function Mocanvas(props: MocanvasProps): react.JSX.Element;
|
|
1731
|
+
|
|
1732
|
+
/**
|
|
1733
|
+
* Export shapes to a standalone SVG document string.
|
|
1734
|
+
*/
|
|
1735
|
+
|
|
1736
|
+
interface SvgExportOptions {
|
|
1737
|
+
/** Page units added around the shapes' bounds. Default 32. */
|
|
1738
|
+
padding?: number;
|
|
1739
|
+
/** Paint a full-size background rectangle. Default false. */
|
|
1740
|
+
background?: boolean;
|
|
1741
|
+
/** Multiplier applied to the output `width`/`height`. Default 1. */
|
|
1742
|
+
scale?: number;
|
|
1743
|
+
/** Use the dark background colour. Default false. */
|
|
1744
|
+
darkMode?: boolean;
|
|
1745
|
+
}
|
|
1746
|
+
interface SvgExportResult {
|
|
1747
|
+
svg: string;
|
|
1748
|
+
/** Output size in CSS pixels (bounds × scale). */
|
|
1749
|
+
width: number;
|
|
1750
|
+
height: number;
|
|
1751
|
+
}
|
|
1752
|
+
declare const SVG_EXPORT_DEFAULT_PADDING = 32;
|
|
1753
|
+
declare const SVG_LIGHT_BACKGROUND = "#f9fafb";
|
|
1754
|
+
declare const SVG_DARK_BACKGROUND = "#101011";
|
|
1755
|
+
/**
|
|
1756
|
+
* The shapes an export covers: the given ids (or the selection, or the whole
|
|
1757
|
+
* page when nothing is selected) together with all of their descendants, in
|
|
1758
|
+
* page draw order.
|
|
1759
|
+
*/
|
|
1760
|
+
declare function getExportShapes(editor: Editor, ids?: readonly ShapeId[]): UnknownShape[];
|
|
1761
|
+
/** Common page bounds of the shapes, or `undefined` when there are none. */
|
|
1762
|
+
declare function getExportBounds(editor: Editor, shapes: readonly UnknownShape[]): Box | undefined;
|
|
1763
|
+
/**
|
|
1764
|
+
* Serialize shapes to an SVG string. Returns `undefined` when there is
|
|
1765
|
+
* nothing to export.
|
|
1766
|
+
*/
|
|
1767
|
+
declare function getSvgString(editor: Editor, ids?: readonly ShapeId[], opts?: SvgExportOptions): SvgExportResult | undefined;
|
|
1768
|
+
|
|
1769
|
+
/**
|
|
1770
|
+
* Export-wide settings a renderer may consult. Same shape as the context a
|
|
1771
|
+
* `ShapeUtil.toSvg` receives, so the two paths cannot drift apart.
|
|
1772
|
+
*/
|
|
1773
|
+
interface SvgExportContext extends ShapeSvgContext {
|
|
1774
|
+
}
|
|
1775
|
+
/** Renders one shape to SVG markup in shape-local coordinates. */
|
|
1776
|
+
type ShapeSvgRenderer<T extends UnknownShape = UnknownShape> = (editor: Editor, shape: T, ctx: SvgExportContext) => string;
|
|
1777
|
+
/** Outline drawn with the render style, or a hairline in the theme text colour when the shape has none. */
|
|
1778
|
+
declare function geometryFallbackSvg(editor: Editor, shape: UnknownShape): string;
|
|
1779
|
+
/** Fallback renderer applied to shape types without a registry entry. */
|
|
1780
|
+
declare const defaultShapeSvgRenderer: ShapeSvgRenderer;
|
|
1781
|
+
/**
|
|
1782
|
+
* Registry of per-type renderers. Custom shapes can register their own entry;
|
|
1783
|
+
* anything missing falls back to `defaultShapeSvgRenderer`.
|
|
1784
|
+
*/
|
|
1785
|
+
declare const shapeSvgRenderers: Map<string, ShapeSvgRenderer<UnknownShape>>;
|
|
1786
|
+
/** Register (or replace) the SVG renderer for a shape type. */
|
|
1787
|
+
declare function registerShapeSvgRenderer<T extends UnknownShape>(type: T["type"], renderer: ShapeSvgRenderer<T>): void;
|
|
1788
|
+
/**
|
|
1789
|
+
* Markup for a value returned by `toSvg` / `toBackgroundSvg`: a string is used
|
|
1790
|
+
* verbatim, a React node is serialized, and React's empty values become
|
|
1791
|
+
* `undefined` so the caller can fall through to the next renderer.
|
|
1792
|
+
*/
|
|
1793
|
+
declare function svgResultToMarkup(value: ShapeSvgResult): string | undefined;
|
|
1794
|
+
/**
|
|
1795
|
+
* Render a shape to SVG markup in shape-local space.
|
|
1796
|
+
*
|
|
1797
|
+
* Precedence, highest first:
|
|
1798
|
+
*
|
|
1799
|
+
* 1. `ShapeUtil.toSvg` — the shape's own opinion about itself. A custom shape
|
|
1800
|
+
* is exportable by implementing one method, with nothing to register.
|
|
1801
|
+
* 2. `shapeSvgRenderers` — the registry, which carries the built-in shapes and
|
|
1802
|
+
* lets an app override a type whose util it does not own (see
|
|
1803
|
+
* `registerShapeSvgRenderer`).
|
|
1804
|
+
* 3. The shape's geometry stroked with its render style — the last resort, so
|
|
1805
|
+
* an unknown type still exports as something rather than as nothing.
|
|
1806
|
+
*/
|
|
1807
|
+
declare function shapeToSvg(editor: Editor, shape: UnknownShape, ctx: SvgExportContext): string;
|
|
1808
|
+
/**
|
|
1809
|
+
* A shape's backdrop in shape-local space, drawn behind *every* exported
|
|
1810
|
+
* shape. Only `ShapeUtil.toBackgroundSvg` produces one; there is no registry
|
|
1811
|
+
* behind it and no geometry fallback, because a shape that says nothing here
|
|
1812
|
+
* wants nothing drawn.
|
|
1813
|
+
*/
|
|
1814
|
+
declare function shapeToBackgroundSvg(editor: Editor, shape: UnknownShape, ctx: SvgExportContext): string | undefined;
|
|
1815
|
+
|
|
1816
|
+
/**
|
|
1817
|
+
* Export shapes to a `Blob` (SVG or raster), plus small browser helpers to
|
|
1818
|
+
* download or copy the result. Raster export needs a DOM; the functions guard
|
|
1819
|
+
* for Node and fail with a clear error instead of a reference error.
|
|
1820
|
+
*/
|
|
1821
|
+
|
|
1822
|
+
type ExportFormat = "svg" | "png" | "jpeg" | "webp";
|
|
1823
|
+
interface ExportToBlobOptions extends SvgExportOptions {
|
|
1824
|
+
ids?: readonly ShapeId[];
|
|
1825
|
+
format: ExportFormat;
|
|
1826
|
+
/** Encoder quality for lossy formats (0..1). */
|
|
1827
|
+
quality?: number;
|
|
1828
|
+
/** Device pixel ratio multiplier for raster output. Defaults to `window.devicePixelRatio`. */
|
|
1829
|
+
pixelRatio?: number;
|
|
1830
|
+
}
|
|
1831
|
+
/** Render the export to a `Blob`. SVG works anywhere; raster formats need a browser. */
|
|
1832
|
+
declare function exportToBlob(editor: Editor, opts: ExportToBlobOptions): Promise<Blob>;
|
|
1833
|
+
/** Trigger a download of `blob` as `filename`. No-op outside a browser. */
|
|
1834
|
+
declare function downloadBlob(blob: Blob, filename: string): void;
|
|
1835
|
+
/**
|
|
1836
|
+
* Copy `blob` to the system clipboard. Images go through `ClipboardItem`;
|
|
1837
|
+
* SVG (which clipboards do not accept as an image) is copied as text.
|
|
1838
|
+
*/
|
|
1839
|
+
declare function copyBlobToClipboard(blob: Blob): Promise<void>;
|
|
1840
|
+
|
|
1841
|
+
/**
|
|
1842
|
+
* Text labels as `<text>` elements. Layout mirrors the DOM label: paragraphs
|
|
1843
|
+
* split on newlines, soft-wrapped at an average glyph advance (the same
|
|
1844
|
+
* estimate the Node-side text measurer uses), one `<tspan>` per line.
|
|
1845
|
+
*/
|
|
1846
|
+
|
|
1847
|
+
interface SvgTextBox {
|
|
1848
|
+
x: number;
|
|
1849
|
+
y: number;
|
|
1850
|
+
w: number;
|
|
1851
|
+
h: number;
|
|
1852
|
+
}
|
|
1853
|
+
interface SvgTextOptions {
|
|
1854
|
+
fontFamily: string;
|
|
1855
|
+
fontSize: number;
|
|
1856
|
+
/** CSS colour for the glyphs. */
|
|
1857
|
+
color: string;
|
|
1858
|
+
align: DefaultHorizontalAlignStyle | "start" | "middle" | "end";
|
|
1859
|
+
verticalAlign: DefaultVerticalAlignStyle;
|
|
1860
|
+
/** Inset from the box edges on every side. */
|
|
1861
|
+
padding?: number;
|
|
1862
|
+
/** Soft-wrap paragraphs at the box width (minus padding). */
|
|
1863
|
+
wrap?: boolean;
|
|
1864
|
+
fontWeight?: string | number;
|
|
1865
|
+
}
|
|
1866
|
+
/** Split `text` into rendered lines: hard breaks on `\n`, soft wraps at `maxWidth` when given. */
|
|
1867
|
+
declare function wrapTextLines(text: string, fontSize: number, maxWidth?: number): string[];
|
|
1868
|
+
/**
|
|
1869
|
+
* Render `text` inside `box`. Returns an empty string for empty text. Lines
|
|
1870
|
+
* are positioned with `dominant-baseline="central"` so vertical centring does
|
|
1871
|
+
* not depend on font metrics.
|
|
1872
|
+
*/
|
|
1873
|
+
declare function textToSvg(text: string, box: SvgTextBox, opts: SvgTextOptions): string;
|
|
1874
|
+
|
|
1875
|
+
/**
|
|
1876
|
+
* Small, DOM-free helpers shared by the SVG exporter: number formatting,
|
|
1877
|
+
* colour conversion from the engine's `0xRRGGBBAA` words, XML escaping and
|
|
1878
|
+
* dash patterns.
|
|
1879
|
+
*/
|
|
1880
|
+
|
|
1881
|
+
/** Affine transform components as returned by `Editor.getShapePageTransform`. */
|
|
1882
|
+
interface SvgTransform {
|
|
1883
|
+
a: number;
|
|
1884
|
+
b: number;
|
|
1885
|
+
c: number;
|
|
1886
|
+
d: number;
|
|
1887
|
+
e: number;
|
|
1888
|
+
f: number;
|
|
1889
|
+
}
|
|
1890
|
+
/** Escape text for use inside an element or a double-quoted attribute. */
|
|
1891
|
+
declare function escapeXml(text: string): string;
|
|
1892
|
+
/**
|
|
1893
|
+
* Convert an engine `0xRRGGBBAA` word to `#rrggbb` (opaque) or `#rrggbbaa`.
|
|
1894
|
+
* Returns `undefined` when the colour is fully transparent ("none").
|
|
1895
|
+
*/
|
|
1896
|
+
declare function rgbaToHex(word: number): string | undefined;
|
|
1897
|
+
/** `matrix(a b c d e f)` for a `transform` attribute. */
|
|
1898
|
+
declare function matrixAttr(m: SvgTransform): string;
|
|
1899
|
+
/**
|
|
1900
|
+
* `stroke-dasharray` for an engine dash id (0 solid, 1 dashed, 2 dotted,
|
|
1901
|
+
* 3 draw). Solid and draw strokes have no dash array.
|
|
1902
|
+
*/
|
|
1903
|
+
declare function dashArray(dash: number, strokeWidth: number): string | undefined;
|
|
1904
|
+
/**
|
|
1905
|
+
* One `<path>` per leaf geometry, filled only where the leaf is a closed,
|
|
1906
|
+
* filled outline and stroked with the shape's stroke. Empty leaves are skipped.
|
|
1907
|
+
*/
|
|
1908
|
+
declare function geometryToSvgPaths(geometry: Geometry2d, style: StyleWords): string;
|
|
1909
|
+
|
|
1910
|
+
interface ImageSize {
|
|
1911
|
+
w: number;
|
|
1912
|
+
h: number;
|
|
1913
|
+
}
|
|
1914
|
+
/** Resolves an image's pixel size from its source; injectable so tests can run without a DOM. */
|
|
1915
|
+
type ImageSizeLoader = (src: string, file?: File) => Promise<ImageSize>;
|
|
1916
|
+
interface ExternalContentOptions {
|
|
1917
|
+
/** Longest side an inserted image may have, in page units. */
|
|
1918
|
+
maxImageDimension?: number;
|
|
1919
|
+
/** Fallback size when an image's dimensions cannot be read. */
|
|
1920
|
+
fallbackImageSize?: ImageSize;
|
|
1921
|
+
loadImageSize?: ImageSizeLoader;
|
|
1922
|
+
readFileAsDataUrl?: (file: File) => Promise<string>;
|
|
1923
|
+
/** Offset between shapes when several are inserted at the same point. */
|
|
1924
|
+
stackOffset?: number;
|
|
1925
|
+
}
|
|
1926
|
+
declare const DEFAULT_MAX_IMAGE_DIMENSION = 1000;
|
|
1927
|
+
/** Scale `size` down (never up) so both sides fit within `max`, keeping the aspect ratio. */
|
|
1928
|
+
declare function fitImageSize(size: ImageSize, max?: number): ImageSize;
|
|
1929
|
+
declare function isImageFile(file: File): boolean;
|
|
1930
|
+
declare function isSvgFile(file: File): boolean;
|
|
1931
|
+
declare function isAnimatedImageType(mimeType: string): boolean;
|
|
1932
|
+
declare function looksLikeUrl(text: string): boolean;
|
|
1933
|
+
declare function looksLikeSvg(text: string): boolean;
|
|
1934
|
+
/** Read a file into a `data:` URL, via `FileReader` where present and `arrayBuffer()` elsewhere. */
|
|
1935
|
+
declare function readFileAsDataUrl(file: File): Promise<string>;
|
|
1936
|
+
/** A `data:` URL for an SVG document string. */
|
|
1937
|
+
declare function svgTextToDataUrl(text: string): string;
|
|
1938
|
+
/** Pixel size declared by an SVG's root `width`/`height` or `viewBox`, or `null` when neither is usable. */
|
|
1939
|
+
declare function getSvgTextSize(text: string): ImageSize | null;
|
|
1940
|
+
/** Default size loader: `createImageBitmap` when available, else an `Image` element. */
|
|
1941
|
+
declare const loadImageSizeInBrowser: ImageSizeLoader;
|
|
1942
|
+
/** Build (without storing) an image asset for a file; `undefined` for non-image files. */
|
|
1943
|
+
declare function createImageAssetFromFile(file: File, opts?: ExternalContentOptions): Promise<ImageAsset | undefined>;
|
|
1944
|
+
/** Build (without storing) an image asset from SVG markup. */
|
|
1945
|
+
declare function createImageAssetFromSvgText(text: string, opts?: ExternalContentOptions): ImageAsset;
|
|
1946
|
+
/** Store `assets` and create one image shape per asset, centered on `point` and stacked by `stackOffset`. */
|
|
1947
|
+
declare function createImageShapesForAssets(editor: Editor, assets: readonly ImageAsset[], point: VecLike | undefined, stackOffset?: number): ShapeId[];
|
|
1948
|
+
/** Create a text shape whose bounds are centered on `point`. */
|
|
1949
|
+
declare function createTextShapeAt(editor: Editor, text: string, point: VecLike | undefined): ShapeId;
|
|
1950
|
+
/**
|
|
1951
|
+
* Install the default drop/paste behaviour: image files become image assets +
|
|
1952
|
+
* image shapes, SVG markup becomes an image asset, text and urls become text
|
|
1953
|
+
* shapes. Types that already have a handler are left alone. Returns a function
|
|
1954
|
+
* that removes the handlers this call installed.
|
|
1955
|
+
*/
|
|
1956
|
+
declare function registerDefaultExternalContentHandlers(editor: Editor, opts?: ExternalContentOptions): () => void;
|
|
1957
|
+
/** Classify pasted or dropped plain text as our own clipboard JSON (`null`), a url, svg markup, or text. */
|
|
1958
|
+
declare function classifyExternalText(text: string, point?: VecLike): ExternalContent | null;
|
|
1959
|
+
|
|
1960
|
+
/**
|
|
1961
|
+
* Drop and paste support for the canvas container: files, images, svg markup,
|
|
1962
|
+
* urls and plain text are routed to `editor.putExternalContent`. Plain text is
|
|
1963
|
+
* only taken when nothing is being edited and it is not our own
|
|
1964
|
+
* `application/mocanvas` clipboard JSON (the keyboard shortcuts handle that).
|
|
1965
|
+
* Also installs the default handlers for any content type without one.
|
|
1966
|
+
*/
|
|
1967
|
+
declare function useExternalContent(editor: Editor | null, containerRef: RefObject<HTMLElement | null>, options?: ExternalContentOptions): void;
|
|
1968
|
+
|
|
1969
|
+
export { ARROW_KINDS, ARROW_LABEL_PADDING, ARROW_TERMINAL_GAP_STROKES, AVG_CHAR_WIDTH, type ArcBody, type ArrowBinding, type ArrowBindingProps, ArrowBindingUtil, type ArrowBindings, type ArrowBody, type ArrowKind, type ArrowShape, type ArrowShapeProps, ArrowShapeUtil, type ArrowTerminal, type ArrowTerminals, ArrowTool, type ArrowheadKind, BOOKMARK_BANNER_FILL, BOOKMARK_BANNER_HEIGHT, BOOKMARK_FAVICON_SIZE, BOOKMARK_FILL, BOOKMARK_GAP, BOOKMARK_HEIGHT, BOOKMARK_META_COLOR, BOOKMARK_META_FONT_SIZE, BOOKMARK_META_HEIGHT, BOOKMARK_MIN_BODY_HEIGHT, BOOKMARK_PADDING, BOOKMARK_RADIUS, BOOKMARK_STROKE, BOOKMARK_STROKE_WIDTH, BOOKMARK_TEXT_COLOR, BOOKMARK_TEXT_FONT_SIZE, BOOKMARK_TITLE_COLOR, BOOKMARK_TITLE_FONT_SIZE, BOOKMARK_TITLE_HEIGHT, BOOKMARK_WIDTH, type BookmarkCard, type BookmarkLayout, type BookmarkRect, type BookmarkShape, type BookmarkShapeProps, BookmarkShapeUtil, type CubicSegment, DEFAULT_EMBED_DEFINITIONS, DEFAULT_MAX_IMAGE_DIMENSION, DebugStats, DefaultUi, type DrawPoint, type DrawSegment, type DrawShape, type DrawShapeProps, DrawShapeUtil, DrawTool, ELBOW_CORNER_STROKES, EMBED_HEIGHT, EMBED_PLACEHOLDER_FILL, EMBED_PLACEHOLDER_FONT_SIZE, EMBED_PLACEHOLDER_PADDING, EMBED_PLACEHOLDER_STROKE, EMBED_PLACEHOLDER_TEXT, EMBED_RADIUS, EMBED_SANDBOX, EMBED_WIDTH, type ElbowAxis, type ElbowBody, type ElbowRoute, type ElbowRouteOptions, type ElbowTerminalAxes, type EmbedDefinition, type EmbedMatch, type EmbedShape, type EmbedShapeProps, EmbedShapeUtil, EraserTool, type ExportFormat, type ExportToBlobOptions, type ExternalContentOptions, FRAME_FILL, FRAME_NAME_COLOR, FRAME_NAME_FONT_SIZE, FRAME_NAME_GAP, FRAME_NAME_HEIGHT, FRAME_NAME_OFFSET, FRAME_STROKE, FRAME_STROKE_WIDTH, type FrameShape, type FrameShapeProps, FrameShapeUtil, FrameTool, GEO_BOX, GEO_ICON_PATHS, GEO_LABEL_PADDING, type GeoShape, type GeoShapeProps, GeoShapeUtil, GeoTool, type GroupShape, GroupShapeUtil, HEXAGON_FLAT_SIDE_SPAN, HandTool, ICONS, ICON_GRID, ICON_NAMES, IMAGE_PLACEHOLDER_FILL, IMAGE_PLACEHOLDER_STROKE, Icon, type IconName, type IconProps, type ImageCrop, type ImageShape, type ImageShapeProps, ImageShapeUtil, type ImageSize, type ImageSizeLoader, LINE_HEIGHT, type LabelMeasureOptions, type LinePoint, type LineShape, type LineShapeProps, LineShapeUtil, LineTool, type LoadMocanvasFileResult, MAX_TEXT_TEXTURE_PX, MOD_KEY, MORE_GEO_KINDS, Mocanvas, type MocanvasProps, NOTE_GRADIENT_TOP_SCALE, NOTE_PADDING, NOTE_SHADOW_BLUR, NOTE_SHADOW_COLOR, NOTE_SHADOW_OFFSET_Y, NOTE_SHADOW_OPACITY, NOTE_SHADOW_SPREAD, NOTE_SIZE, type NoteShape, type NoteShapeProps, NoteShapeUtil, NoteTool, PRIMARY_GEO_KINDS, type Placement, Popover, type PopoverProps, STAR_INNER_RATIO, SVG_DARK_BACKGROUND, SVG_EXPORT_DEFAULT_PADDING, SVG_LIGHT_BACKGROUND, SelectTool, type ShapeSvgRenderer, type StraightBody, StylePanel, type StylePanelSections, type SvgExportContext, type SvgExportOptions, type SvgExportResult, type SvgTextBox, type SvgTextOptions, type SvgTransform, TEXT_SHAPE_MIN_WIDTH, TOOLBAR_GROUPS, TextLabel, type TextLabelProps, TextMeasure, type TextMeasureOptions, type TextMeasurement, type TextShape, type TextShapeProps, type TextShapeSizeInput, TextShapeUtil, type TextSizeEstimate, type TextTextureAlign, type TextTextureSpec, TextTool, Toolbar, type ToolbarItem, type TransformLike, UiTooltip, VIDEO_HEIGHT, VIDEO_PLACEHOLDER_FILL, VIDEO_PLACEHOLDER_STROKE, VIDEO_PLAY_COLOR, VIDEO_PLAY_SIZE, VIDEO_TIME_EPSILON, VIDEO_WIDTH, type VideoShape, type VideoShapeProps, VideoShapeUtil, ZoomBar, alignToJustify, alignToTextAlign, applyTransform, arcToCubicSegments, bodyToGeometry, canUploadImageTextures, catmullRomToBezier, classifyExternalText, computeGrowY, copyBlobToClipboard, createImageAssetFromFile, createImageAssetFromSvgText, createImageShapesForAssets, createTextShapeAt, dashArray, debugStatsOpen, defaultBindingUtils, defaultShapeSvgRenderer, defaultShapeUtils, defaultTools, downloadBlob, embedDefinitions, escapeXml, estimateTextSize, exportToBlob, fitImageSize, fitPointsToBox, geometryFallbackSvg, geometryToSvgPaths, getAnchorInShapeSpace, getArrowBindingTargetAtPoint, getArrowBindings, getArrowBody, getArrowTerminalGap, getArrowTerminalsInArrowSpace, getArrowheadGeometry, getArrowheadInset, getArrowheadLength, getBendFromPoint, getBodyLength, getBookmarkAsset, getBookmarkCard, getBookmarkHostname, getBookmarkLayout, getBoundElbowAxes, getCloudSegments, getDashId, getDominantAxis, getDrawOutlinePoints, getElbowBody, getElbowMidPointFromPoint, getElbowRoute, getEmbedDefinition, getExportBounds, getExportShapes, getFillRgba, getFontFamily, getGeoDecorations, getGeoGeometry, getGeoGrowY, getGeoIconBox, getGeoPolygonPoints, getHeartSegments, getImageCropStyle, getImageTextureKey, getImageTextureSource, getLinePoints, getNormalizedAnchor, getNoteBodyGradientCss, getNoteFillCssColor, getNoteFillRgba, getNoteFontSize, getNoteGradientTopCssColor, getNoteGradientTopFrom, getNoteGrowY, getNoteShadowCss, getNoteShadowSvgRect, getNoteTextCssColor, getOutlineSegments, getPointOnBody, getStadiumSegments, getStrokeRgba, getStylePanelSections, getSvgString, getSvgTextSize, getTangentOnBody, getTextCssColor, getTextMeasure, getTextShapeBox, getTextShapeHeight, getTextShapeSize, getTextShapeSizeFor, getTextShapeTextureSpec, getTextTextureKey, getTextTextureScale, getVideoPlayTriangle, getVideoSource, hasAnyStyleSection, hexToCssRgba, horizontalAlignToFlex, horizontalAlignToTextAlign, intersectSegments, isAnimatedImageType, isImageFile, isSvgFile, lineSegment, loadImageSizeInBrowser, loadImageTextureSource, loadMocanvasFile, looksLikeSvg, looksLikeUrl, matrixAttr, measureGeoLabel, measureLabel, pathWordsToSvgD, placeNear, pointOnCubic, propsOf, readArray, readArrowProps, readArrowShape, readBoolean, readDrawSegments, readEnum, readFileAsDataUrl, readGeoProps, readNoteProps, readNumber, readPoint, readRecord, readString, readStyle, readText, readTextProps, registerDefaultExternalContentHandlers, registerShapeSvgRenderer, renderTextToCanvas, rgbaToHex, roundElbowCorners, serializeMocanvasFile, shapeSvgRenderers, shapeToBackgroundSvg, shapeToSvg, shortenBody, smoothPoints, svgResultToMarkup, svgTextToDataUrl, textToSvg, toDisplayText, trimTrailingWhitespace, useExternalContent, useKeyboardShortcuts, verticalAlignToAlignItems, verticalAlignToFlex, wrapTextLines, wrapTextTextureLines };
|