@scrawl-board/board 0.1.0-beta.5 → 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/browser.d.ts +261 -2
- package/dist/browser.js +7516 -961
- package/dist/core.d.ts +618 -8
- package/dist/core.js +1848 -20
- package/dist/index.d.ts +740 -14
- package/dist/index.js +7674 -835
- package/dist/react.d.ts +274 -6
- package/dist/react.js +7622 -852
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,8 +27,10 @@ undo and redo, zoom and fit, search, JSON import, and PNG, SVG, or JSON export.
|
|
|
27
27
|
Every control calls a public grouped controller capability. The default keyboard
|
|
28
28
|
surface is scoped to the focused Board: `P` selects marker, `H` selects
|
|
29
29
|
highlighter, `V` selects selection, `E` selects eraser, `N` selects note, `T`
|
|
30
|
-
selects text, `F` fits content, `Mod+Z` undoes, `Mod+Shift+Z` redoes,
|
|
31
|
-
opens Board search
|
|
30
|
+
selects text, `F` fits content, `Mod+Z` undoes, `Mod+Shift+Z` redoes, `Mod+F`
|
|
31
|
+
opens Board search, and the arrow keys nudge the current selection by a small
|
|
32
|
+
step (`Shift` for a bigger one) across any mix of selected object types, as one
|
|
33
|
+
undo step. Dialogs trap focus, close with Escape, and restore focus to
|
|
32
34
|
their opener. Hosts that replace the default UI can remove it independently while
|
|
33
35
|
retaining `ScrawlProvider` and `ScrawlCanvas`, or disable individual `tools`,
|
|
34
36
|
`history`, `view`, `style`, `search`, `import`, and `export` regions with the
|
package/dist/browser.d.ts
CHANGED
|
@@ -355,6 +355,133 @@ interface KitchenTimer extends Lockable {
|
|
|
355
355
|
runningSince?: number;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
+
interface RectangleObject extends Lockable {
|
|
359
|
+
id: string;
|
|
360
|
+
x: number;
|
|
361
|
+
y: number;
|
|
362
|
+
width: number;
|
|
363
|
+
height: number;
|
|
364
|
+
fill?: string;
|
|
365
|
+
stroke?: string;
|
|
366
|
+
strokeWidth?: number;
|
|
367
|
+
/** Corner radius in board units; clamped to at most half the shorter side at render time. */
|
|
368
|
+
cornerRadius?: number;
|
|
369
|
+
/** `[0, 1]`; undefined means fully opaque (Phase 4). */
|
|
370
|
+
opacity?: number;
|
|
371
|
+
/**
|
|
372
|
+
* Radians, about the shape's own center `(x + width/2, y - height/2)`.
|
|
373
|
+
* Undefined means 0 (Phase 3). `x`/`y`/`width`/`height` stay in the
|
|
374
|
+
* shape's own unrotated local frame — rotation is a separate, applied-last
|
|
375
|
+
* transform, not baked into them, matching how Stroke/CustomBoardObject
|
|
376
|
+
* keep geometry and placement independent via their own `matrix`.
|
|
377
|
+
*/
|
|
378
|
+
rotation?: number;
|
|
379
|
+
}
|
|
380
|
+
interface EllipseObject extends Lockable {
|
|
381
|
+
id: string;
|
|
382
|
+
x: number;
|
|
383
|
+
y: number;
|
|
384
|
+
width: number;
|
|
385
|
+
height: number;
|
|
386
|
+
fill?: string;
|
|
387
|
+
stroke?: string;
|
|
388
|
+
strokeWidth?: number;
|
|
389
|
+
/** `[0, 1]`; undefined means fully opaque (Phase 4). */
|
|
390
|
+
opacity?: number;
|
|
391
|
+
/** Radians, about the shape's own center — see RectangleObject's `rotation` doc. */
|
|
392
|
+
rotation?: number;
|
|
393
|
+
}
|
|
394
|
+
/** `"none"` is a plain line with no arrowhead; today's only real head shape is `"triangle"`. New head shapes extend this union without touching `ArrowObject`'s own fields. */
|
|
395
|
+
type ArrowHeadStyle = "triangle" | "none";
|
|
396
|
+
interface LineObject extends Lockable {
|
|
397
|
+
id: string;
|
|
398
|
+
start: BoardPoint;
|
|
399
|
+
end: BoardPoint;
|
|
400
|
+
stroke?: string;
|
|
401
|
+
strokeWidth?: number;
|
|
402
|
+
opacity?: number;
|
|
403
|
+
}
|
|
404
|
+
interface ArrowObject extends Lockable {
|
|
405
|
+
id: string;
|
|
406
|
+
start: BoardPoint;
|
|
407
|
+
end: BoardPoint;
|
|
408
|
+
head?: ArrowHeadStyle;
|
|
409
|
+
stroke?: string;
|
|
410
|
+
strokeWidth?: number;
|
|
411
|
+
opacity?: number;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Triangle(3)/Diamond(4)/Pentagon(5)/Hexagon(6)/Octagon(8) as one shared
|
|
415
|
+
* type instead of five near-duplicate interfaces — a regular N-gon
|
|
416
|
+
* inscribed in the same `x`/`y`/`width`/`height`/`rotation` bounding box
|
|
417
|
+
* Rectangle already uses, parameterized by `sides`. Diamond is exactly a
|
|
418
|
+
* 4-sided regular polygon with vertex 0 pointing right (not up, like
|
|
419
|
+
* Triangle/Pentagon/Hexagon) — see `polygonGeometry.ts`'s
|
|
420
|
+
* `polygonStartAngle`, which encodes each side count's own vertex
|
|
421
|
+
* orientation so the outline always matches the legacy drag-preview shape.
|
|
422
|
+
*/
|
|
423
|
+
interface PolygonObject extends Lockable {
|
|
424
|
+
id: string;
|
|
425
|
+
x: number;
|
|
426
|
+
y: number;
|
|
427
|
+
width: number;
|
|
428
|
+
height: number;
|
|
429
|
+
sides: 3 | 4 | 5 | 6 | 8;
|
|
430
|
+
fill?: string;
|
|
431
|
+
stroke?: string;
|
|
432
|
+
strokeWidth?: number;
|
|
433
|
+
opacity?: number;
|
|
434
|
+
/** Radians, about the shape's own center — see RectangleObject's `rotation` doc. */
|
|
435
|
+
rotation?: number;
|
|
436
|
+
}
|
|
437
|
+
/** Same bounding-box/rotation convention as Rectangle; a 5-pointed star with a tuned inner-radius ratio, matching the legacy tool's own default (see `polygonGeometry.ts`'s `starPoints`). */
|
|
438
|
+
interface StarObject extends Lockable {
|
|
439
|
+
id: string;
|
|
440
|
+
x: number;
|
|
441
|
+
y: number;
|
|
442
|
+
width: number;
|
|
443
|
+
height: number;
|
|
444
|
+
/** Vertex count; today's only shipped preset is 5, matching the legacy tool. */
|
|
445
|
+
points: number;
|
|
446
|
+
/** `(0, 1)` — inner vertex radius as a fraction of the outer radius. */
|
|
447
|
+
innerRadiusRatio: number;
|
|
448
|
+
fill?: string;
|
|
449
|
+
stroke?: string;
|
|
450
|
+
strokeWidth?: number;
|
|
451
|
+
opacity?: number;
|
|
452
|
+
rotation?: number;
|
|
453
|
+
}
|
|
454
|
+
/** Same bounding-box/rotation convention as Rectangle; the standard parametric heart curve (see `polygonGeometry.ts`'s `heartPoints`), no extra parameters beyond the shared shape fields. */
|
|
455
|
+
interface HeartObject extends Lockable {
|
|
456
|
+
id: string;
|
|
457
|
+
x: number;
|
|
458
|
+
y: number;
|
|
459
|
+
width: number;
|
|
460
|
+
height: number;
|
|
461
|
+
fill?: string;
|
|
462
|
+
stroke?: string;
|
|
463
|
+
strokeWidth?: number;
|
|
464
|
+
opacity?: number;
|
|
465
|
+
rotation?: number;
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* A logical grouping of other board objects (Phase 3 — Selection,
|
|
469
|
+
* Transformation & Grouping). Deliberately has no `x`/`y`/`transform` of its
|
|
470
|
+
* own — a group's bounds are always derived on demand from its (recursively
|
|
471
|
+
* resolved) children, and "moving/rotating/scaling the group" is exactly a
|
|
472
|
+
* multi-object transform applied to those children, nothing more. A group
|
|
473
|
+
* has no renderer/mesh of its own; its only visual presence is the
|
|
474
|
+
* selection gizmo's bounding box while it's the current selection.
|
|
475
|
+
*
|
|
476
|
+
* `children` may itself contain other group ids (nested groups) — expanding
|
|
477
|
+
* a group into its leaf members is always done by the caller (recursively,
|
|
478
|
+
* with cycle protection), never assumed here.
|
|
479
|
+
*/
|
|
480
|
+
interface GroupObject extends Lockable {
|
|
481
|
+
id: string;
|
|
482
|
+
children: string[];
|
|
483
|
+
}
|
|
484
|
+
|
|
358
485
|
interface BoardPoint {
|
|
359
486
|
x: number;
|
|
360
487
|
y: number;
|
|
@@ -417,6 +544,31 @@ interface SerializedDocument {
|
|
|
417
544
|
timers?: KitchenTimer[];
|
|
418
545
|
/** Absent in documents saved before Custom board objects existed (ticket #22). */
|
|
419
546
|
customObjects?: CustomBoardObject[];
|
|
547
|
+
/** Absent in documents saved before semantic Rectangle objects existed (Phase 2). */
|
|
548
|
+
rectangles?: RectangleObject[];
|
|
549
|
+
/** Absent in documents saved before semantic Ellipse objects existed (Phase 2). */
|
|
550
|
+
ellipses?: EllipseObject[];
|
|
551
|
+
/** Absent in documents saved before Groups existed (Phase 3). */
|
|
552
|
+
groups?: GroupObject[];
|
|
553
|
+
/** Absent in documents saved before semantic Line objects existed (Phase 4). */
|
|
554
|
+
lines?: LineObject[];
|
|
555
|
+
/** Absent in documents saved before semantic Arrow objects existed (Phase 4). */
|
|
556
|
+
arrows?: ArrowObject[];
|
|
557
|
+
/** Absent in documents saved before semantic Polygon objects existed (Phase 4). */
|
|
558
|
+
polygons?: PolygonObject[];
|
|
559
|
+
/** Absent in documents saved before semantic Star objects existed (Phase 4). */
|
|
560
|
+
stars?: StarObject[];
|
|
561
|
+
/** Absent in documents saved before semantic Heart objects existed (Phase 4). */
|
|
562
|
+
hearts?: HeartObject[];
|
|
563
|
+
/**
|
|
564
|
+
* Every content-object id (every type above except comments, which are
|
|
565
|
+
* host-synced and never enter this schema) in paint order, back to front.
|
|
566
|
+
* Absent in documents saved before per-object z-order existed (Phase 3) —
|
|
567
|
+
* migration synthesizes a default order preserving the old fixed-Z-band
|
|
568
|
+
* visual stacking exactly, so an existing document never visibly changes
|
|
569
|
+
* on load; only an explicit reorder action touches this from then on.
|
|
570
|
+
*/
|
|
571
|
+
objectOrder?: string[];
|
|
420
572
|
}
|
|
421
573
|
/**
|
|
422
574
|
* One collaborator's vote on a note. One per person; toggling removes it.
|
|
@@ -616,7 +768,7 @@ interface BoardSnapshot {
|
|
|
616
768
|
* here, matching the engine's own internal selection-badge behavior.
|
|
617
769
|
*/
|
|
618
770
|
interface FocusedItem {
|
|
619
|
-
readonly type: "stroke" | "note" | "text" | "table" | "image" | "timer" | "custom";
|
|
771
|
+
readonly type: "stroke" | "note" | "text" | "table" | "image" | "timer" | "rectangle" | "ellipse" | "group" | "line" | "arrow" | "polygon" | "star" | "heart" | "custom";
|
|
620
772
|
readonly id: string;
|
|
621
773
|
readonly locked: boolean;
|
|
622
774
|
readonly lockedBy?: string;
|
|
@@ -798,6 +950,22 @@ type BoardObject = ({
|
|
|
798
950
|
} & ImageBlock) | ({
|
|
799
951
|
type: "timer";
|
|
800
952
|
} & KitchenTimer) | ({
|
|
953
|
+
type: "rectangle";
|
|
954
|
+
} & RectangleObject) | ({
|
|
955
|
+
type: "ellipse";
|
|
956
|
+
} & EllipseObject) | ({
|
|
957
|
+
type: "group";
|
|
958
|
+
} & GroupObject) | ({
|
|
959
|
+
type: "line";
|
|
960
|
+
} & LineObject) | ({
|
|
961
|
+
type: "arrow";
|
|
962
|
+
} & ArrowObject) | ({
|
|
963
|
+
type: "polygon";
|
|
964
|
+
} & PolygonObject) | ({
|
|
965
|
+
type: "star";
|
|
966
|
+
} & StarObject) | ({
|
|
967
|
+
type: "heart";
|
|
968
|
+
} & HeartObject) | ({
|
|
801
969
|
type: "custom";
|
|
802
970
|
customType: ObjectType;
|
|
803
971
|
} & Omit<CustomBoardObject, "type">);
|
|
@@ -826,6 +994,30 @@ type BoardObjectInput = {
|
|
|
826
994
|
type: "timer";
|
|
827
995
|
id?: string;
|
|
828
996
|
} & Omit<KitchenTimer, "id">) | ({
|
|
997
|
+
type: "rectangle";
|
|
998
|
+
id?: string;
|
|
999
|
+
} & Omit<RectangleObject, "id">) | ({
|
|
1000
|
+
type: "ellipse";
|
|
1001
|
+
id?: string;
|
|
1002
|
+
} & Omit<EllipseObject, "id">) | ({
|
|
1003
|
+
type: "group";
|
|
1004
|
+
id?: string;
|
|
1005
|
+
} & Omit<GroupObject, "id">) | ({
|
|
1006
|
+
type: "line";
|
|
1007
|
+
id?: string;
|
|
1008
|
+
} & Omit<LineObject, "id">) | ({
|
|
1009
|
+
type: "arrow";
|
|
1010
|
+
id?: string;
|
|
1011
|
+
} & Omit<ArrowObject, "id">) | ({
|
|
1012
|
+
type: "polygon";
|
|
1013
|
+
id?: string;
|
|
1014
|
+
} & Omit<PolygonObject, "id">) | ({
|
|
1015
|
+
type: "star";
|
|
1016
|
+
id?: string;
|
|
1017
|
+
} & Omit<StarObject, "id">) | ({
|
|
1018
|
+
type: "heart";
|
|
1019
|
+
id?: string;
|
|
1020
|
+
} & Omit<HeartObject, "id">) | ({
|
|
829
1021
|
type: "custom";
|
|
830
1022
|
id?: string;
|
|
831
1023
|
customType: ObjectType;
|
|
@@ -887,7 +1079,16 @@ interface ControllerOp {
|
|
|
887
1079
|
id: string;
|
|
888
1080
|
schemaVersion: 1;
|
|
889
1081
|
kind: "upsert" | "restore" | "remove";
|
|
890
|
-
objectType: "stroke" | "note" | "text" | "table" | "image" | "timer" | "custom"
|
|
1082
|
+
objectType: "stroke" | "note" | "text" | "table" | "image" | "timer" | "rectangle" | "ellipse" | "group" | "line" | "arrow" | "polygon" | "star" | "heart" | "custom"
|
|
1083
|
+
/**
|
|
1084
|
+
* A whole-document paint-order sync (Phase 3), not a per-object type —
|
|
1085
|
+
* `objectId` is always the fixed sentinel `"order"` and `payload` is
|
|
1086
|
+
* `{ order: string[] }`. The only `objectType` with no matching
|
|
1087
|
+
* `BoardObject`/document collection; kept in this same union (rather
|
|
1088
|
+
* than a separate wire message) so it flows through the existing
|
|
1089
|
+
* `PersistenceAdapter`/`CollaborationAdapter` opaquely, unchanged.
|
|
1090
|
+
*/
|
|
1091
|
+
| "order";
|
|
891
1092
|
objectId: string;
|
|
892
1093
|
payload?: unknown;
|
|
893
1094
|
}
|
|
@@ -1000,6 +1201,56 @@ interface BoardController {
|
|
|
1000
1201
|
* Unknown ids are silently skipped, matching `remove`'s convention.
|
|
1001
1202
|
*/
|
|
1002
1203
|
duplicate(ids: readonly string[]): readonly string[];
|
|
1204
|
+
/**
|
|
1205
|
+
* Creates a new Group referencing `ids` as its children and returns its
|
|
1206
|
+
* id, as one undoable step. Unknown ids are silently skipped, matching
|
|
1207
|
+
* `duplicate`/`remove`'s convention. A child id that's itself a group
|
|
1208
|
+
* makes a nested group — expanding nested groups into their leaf
|
|
1209
|
+
* members is always the caller's job, never assumed here (matches the
|
|
1210
|
+
* document-model `GroupObject` itself).
|
|
1211
|
+
*/
|
|
1212
|
+
group(ids: readonly string[]): string;
|
|
1213
|
+
/**
|
|
1214
|
+
* Dissolves one group, returning its immediate children's ids (a nested
|
|
1215
|
+
* subgroup among them stays intact, itself still a group) — the group
|
|
1216
|
+
* record itself is removed, the children are untouched. A no-op
|
|
1217
|
+
* (returns `[]`) if `groupId` isn't a group.
|
|
1218
|
+
*/
|
|
1219
|
+
ungroup(groupId: string): readonly string[];
|
|
1220
|
+
/**
|
|
1221
|
+
* Aligns every given object's matching edge/center to the corresponding
|
|
1222
|
+
* edge/center of their combined bounding box, as one undoable step.
|
|
1223
|
+
* `"top"`/`"bottom"` follow board space's Y-up convention (`"top"` is
|
|
1224
|
+
* the larger Y). Ids that don't resolve, or resolve to a Group (which
|
|
1225
|
+
* has no position of its own), are skipped. A no-op under 2 resolvable
|
|
1226
|
+
* ids — there's nothing to align relative to.
|
|
1227
|
+
*/
|
|
1228
|
+
align(ids: readonly string[], edge: "left" | "right" | "top" | "bottom" | "centerX" | "centerY"): void;
|
|
1229
|
+
/**
|
|
1230
|
+
* Spaces the middle objects' centers evenly between the first and last
|
|
1231
|
+
* (sorted along `axis`), as one undoable step — the two endpoints don't
|
|
1232
|
+
* move. Ids that don't resolve, or resolve to a Group, are skipped. A
|
|
1233
|
+
* no-op under 3 resolvable ids — there's no "middle" to distribute.
|
|
1234
|
+
*/
|
|
1235
|
+
distribute(ids: readonly string[], axis: "x" | "y"): void;
|
|
1236
|
+
/**
|
|
1237
|
+
* Snapshots `ids` (recursively expanded through any group, same as
|
|
1238
|
+
* `duplicate`) into an internal in-memory clipboard — never
|
|
1239
|
+
* `navigator.clipboard`, scoped to this one controller instance and
|
|
1240
|
+
* replaced wholesale by the next `copy`/`cut`. Read-only; works even
|
|
1241
|
+
* on a read-only board.
|
|
1242
|
+
*/
|
|
1243
|
+
copy(ids: readonly string[]): void;
|
|
1244
|
+
/** `copy`, then removes every resolved object (recursively through any group) as one undoable step. */
|
|
1245
|
+
cut(ids: readonly string[]): void;
|
|
1246
|
+
/**
|
|
1247
|
+
* Clones the current clipboard contents onto the board as one undoable
|
|
1248
|
+
* step, offset the same small cascade `duplicate` uses (no cursor
|
|
1249
|
+
* position to paste relative to yet). Returns the new top-level ids —
|
|
1250
|
+
* a pasted group's own id stands for its (also-pasted) children, which
|
|
1251
|
+
* aren't listed separately. `[]` when the clipboard is empty.
|
|
1252
|
+
*/
|
|
1253
|
+
paste(): readonly string[];
|
|
1003
1254
|
table: {
|
|
1004
1255
|
addRow(tableId: string): void;
|
|
1005
1256
|
addCol(tableId: string): void;
|
|
@@ -1010,6 +1261,14 @@ interface BoardController {
|
|
|
1010
1261
|
};
|
|
1011
1262
|
select(ids: readonly string[]): void;
|
|
1012
1263
|
import(document: SerializedBoardDocument): readonly string[];
|
|
1264
|
+
/**
|
|
1265
|
+
* Toggle lock state for the current selection (or focused note/text/
|
|
1266
|
+
* table/image/timer), matching whatever a single Host lock/unlock
|
|
1267
|
+
* control already does per object type. A no-op with nothing selected,
|
|
1268
|
+
* on a headless board, or when every actionable target is locked by
|
|
1269
|
+
* another collaborator who isn't the current lock holder.
|
|
1270
|
+
*/
|
|
1271
|
+
toggleSelectionLock(): void;
|
|
1013
1272
|
};
|
|
1014
1273
|
readonly query: {
|
|
1015
1274
|
get(id: string): DeepReadonly<BoardObject> | undefined;
|