@scrawl-board/board 0.1.0-beta.7 → 0.1.0-beta.8
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 +11 -7
- package/dist/browser.d.ts +340 -21
- package/dist/browser.js +1536 -35811
- package/dist/core.d.ts +115 -30
- package/dist/core.js +260 -124
- package/dist/index.d.ts +454 -35
- package/dist/index.js +1627 -35810
- package/dist/local.d.ts +469 -0
- package/dist/local.js +234 -0
- package/dist/react.d.ts +369 -21
- package/dist/react.js +1377 -35736
- package/package.json +17 -3
package/dist/index.d.ts
CHANGED
|
@@ -184,6 +184,21 @@ interface CustomObjectDefinition<Props extends JsonValue = JsonValue> {
|
|
|
184
184
|
/** One pure, synchronous step per consecutive schema version. */
|
|
185
185
|
migrate?: Readonly<Record<number, (oldProps: JsonValue) => JsonValue>>;
|
|
186
186
|
describe(object: ReadonlyCustomObject<Props>, context: ObjectDescribeContext): BoardScene;
|
|
187
|
+
/**
|
|
188
|
+
* Optional point-level hit-test precision (Phase 8). Every custom object
|
|
189
|
+
* hit-tests against its bounding box (`fallback.bounds`) by default — this
|
|
190
|
+
* lets a non-rectangular shape (e.g. a circular card, an L-shaped region)
|
|
191
|
+
* reject a point that's inside that box but outside its actual visible
|
|
192
|
+
* silhouette, tightening a click/marquee/raycast hit to the shape's real
|
|
193
|
+
* outline. `point` is in this object's own local space — the same
|
|
194
|
+
* untransformed space `describe`'s returned geometry already lives in
|
|
195
|
+
* (the caller inverse-transforms the pointer's board point through
|
|
196
|
+
* `object.transform` before calling this). Absent means every point
|
|
197
|
+
* inside the bounding box hits, matching pre-Phase-8 behavior exactly.
|
|
198
|
+
* Rejecting a point here does not fall through to whatever's underneath —
|
|
199
|
+
* the gesture simply misses this object, same as clicking empty space.
|
|
200
|
+
*/
|
|
201
|
+
hitTest?(object: ReadonlyCustomObject<Props>, point: BoardPoint): boolean;
|
|
187
202
|
}
|
|
188
203
|
interface SceneNodeBase {
|
|
189
204
|
key: string;
|
|
@@ -217,6 +232,15 @@ interface SceneGroup extends SceneNodeBase {
|
|
|
217
232
|
kind: "group";
|
|
218
233
|
children: readonly BoardScene[];
|
|
219
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* **No renderer or SVG-export interpreter exists for this node kind yet**
|
|
237
|
+
* (tracked as deferred work — see `renderer/shapes/customObjects.ts`'s
|
|
238
|
+
* `"path"` case). Returning a `ScenePath` from `describe()` renders nothing,
|
|
239
|
+
* exports nothing, and contributes no hit-test bounds — it neither errors
|
|
240
|
+
* nor emits a diagnostic. Until an interpreter ships, build custom shapes
|
|
241
|
+
* from `SceneRect`/`SceneEllipse`/`SceneGroup`/`SceneText`/`SceneImage`
|
|
242
|
+
* instead.
|
|
243
|
+
*/
|
|
220
244
|
interface ScenePath extends SceneNodeBase {
|
|
221
245
|
kind: "path";
|
|
222
246
|
/** SVG-style path data, board-local coordinates. */
|
|
@@ -389,8 +413,23 @@ declare function canUnlockItem(item: Lockable | undefined, userId: string | unde
|
|
|
389
413
|
/** Wire fields for a locked item; omitted entirely when unlocked. */
|
|
390
414
|
declare function serializeLock(item: Lockable): Lockable;
|
|
391
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Per-object visibility (Phase 8) — mirrors `itemLock.ts`'s `Lockable`
|
|
418
|
+
* pattern exactly, but simpler: unlike a lock, hidden state carries no
|
|
419
|
+
* holder/ownership concept, so there's no analogue to `LockHolder`/
|
|
420
|
+
* `canUnlockItem`. A hidden object stays fully present in the Document
|
|
421
|
+
* (still serializes, persists, syncs, undoes/redoes) — it just skips
|
|
422
|
+
* rendering and hit-testing/selection candidacy. `hidden` absent or
|
|
423
|
+
* `false` means visible; this keeps every pre-Phase-8 document (which has
|
|
424
|
+
* no `hidden` field on any object at all) implicitly fully visible with
|
|
425
|
+
* zero migration needed.
|
|
426
|
+
*/
|
|
427
|
+
interface Hideable {
|
|
428
|
+
hidden?: boolean;
|
|
429
|
+
}
|
|
430
|
+
|
|
392
431
|
/** A kitchen timer sitting on the board. Remaining time is derived, not ticked. */
|
|
393
|
-
interface KitchenTimer extends Lockable {
|
|
432
|
+
interface KitchenTimer extends Lockable, Hideable {
|
|
394
433
|
id: string;
|
|
395
434
|
x: number;
|
|
396
435
|
y: number;
|
|
@@ -416,9 +455,25 @@ declare function setTimerDuration(timer: KitchenTimer, durationMs: number): Kitc
|
|
|
416
455
|
declare function formatTimer(ms: number): string;
|
|
417
456
|
|
|
418
457
|
declare const SHAPE_MIN_SIZE = 0.5;
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
458
|
+
/**
|
|
459
|
+
* Centralized shape style defaults (Phase 8) — one object a Host can read
|
|
460
|
+
* to know (or, by not relying on the standalone constants below, override
|
|
461
|
+
* via its own UI state) what a newly drawn Rectangle/Ellipse/Line/Arrow/
|
|
462
|
+
* Polygon/Star/Heart starts with when the user hasn't picked a stroke/width
|
|
463
|
+
* yet. `shapeTool.ts` (commit-time), `renderer/shapes/lines.ts` (render-time
|
|
464
|
+
* fallback for an object missing these fields), and
|
|
465
|
+
* `persistence/serialization/svg.ts` (export-time fallback) all read from
|
|
466
|
+
* here — the two standalone constants below are kept for source
|
|
467
|
+
* compatibility and simply mirror this object's values, not a second
|
|
468
|
+
* source of truth.
|
|
469
|
+
*/
|
|
470
|
+
declare const SHAPE_STYLE_DEFAULTS: {
|
|
471
|
+
readonly stroke: "#1C1C1E";
|
|
472
|
+
readonly strokeWidth: 0.12;
|
|
473
|
+
};
|
|
474
|
+
declare const SHAPE_DEFAULT_STROKE: "#1C1C1E";
|
|
475
|
+
declare const SHAPE_DEFAULT_STROKE_WIDTH: 0.12;
|
|
476
|
+
interface RectangleObject extends Lockable, Hideable {
|
|
422
477
|
id: string;
|
|
423
478
|
x: number;
|
|
424
479
|
y: number;
|
|
@@ -440,7 +495,7 @@ interface RectangleObject extends Lockable {
|
|
|
440
495
|
*/
|
|
441
496
|
rotation?: number;
|
|
442
497
|
}
|
|
443
|
-
interface EllipseObject extends Lockable {
|
|
498
|
+
interface EllipseObject extends Lockable, Hideable {
|
|
444
499
|
id: string;
|
|
445
500
|
x: number;
|
|
446
501
|
y: number;
|
|
@@ -458,7 +513,7 @@ declare function cloneRectangle(rect: RectangleObject): RectangleObject;
|
|
|
458
513
|
declare function cloneEllipse(ellipse: EllipseObject): EllipseObject;
|
|
459
514
|
/** `"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. */
|
|
460
515
|
type ArrowHeadStyle = "triangle" | "none";
|
|
461
|
-
interface LineObject extends Lockable {
|
|
516
|
+
interface LineObject extends Lockable, Hideable {
|
|
462
517
|
id: string;
|
|
463
518
|
start: BoardPoint;
|
|
464
519
|
end: BoardPoint;
|
|
@@ -466,7 +521,7 @@ interface LineObject extends Lockable {
|
|
|
466
521
|
strokeWidth?: number;
|
|
467
522
|
opacity?: number;
|
|
468
523
|
}
|
|
469
|
-
interface ArrowObject extends Lockable {
|
|
524
|
+
interface ArrowObject extends Lockable, Hideable {
|
|
470
525
|
id: string;
|
|
471
526
|
start: BoardPoint;
|
|
472
527
|
end: BoardPoint;
|
|
@@ -485,7 +540,7 @@ interface ArrowObject extends Lockable {
|
|
|
485
540
|
* `polygonStartAngle`, which encodes each side count's own vertex
|
|
486
541
|
* orientation so the outline always matches the legacy drag-preview shape.
|
|
487
542
|
*/
|
|
488
|
-
interface PolygonObject extends Lockable {
|
|
543
|
+
interface PolygonObject extends Lockable, Hideable {
|
|
489
544
|
id: string;
|
|
490
545
|
x: number;
|
|
491
546
|
y: number;
|
|
@@ -501,7 +556,7 @@ interface PolygonObject extends Lockable {
|
|
|
501
556
|
}
|
|
502
557
|
declare function clonePolygon(polygon: PolygonObject): PolygonObject;
|
|
503
558
|
/** 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`). */
|
|
504
|
-
interface StarObject extends Lockable {
|
|
559
|
+
interface StarObject extends Lockable, Hideable {
|
|
505
560
|
id: string;
|
|
506
561
|
x: number;
|
|
507
562
|
y: number;
|
|
@@ -519,7 +574,7 @@ interface StarObject extends Lockable {
|
|
|
519
574
|
}
|
|
520
575
|
declare function cloneStar(star: StarObject): StarObject;
|
|
521
576
|
/** 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. */
|
|
522
|
-
interface HeartObject extends Lockable {
|
|
577
|
+
interface HeartObject extends Lockable, Hideable {
|
|
523
578
|
id: string;
|
|
524
579
|
x: number;
|
|
525
580
|
y: number;
|
|
@@ -547,7 +602,7 @@ declare function cloneArrow(arrow: ArrowObject): ArrowObject;
|
|
|
547
602
|
* a group into its leaf members is always done by the caller (recursively,
|
|
548
603
|
* with cycle protection), never assumed here.
|
|
549
604
|
*/
|
|
550
|
-
interface GroupObject extends Lockable {
|
|
605
|
+
interface GroupObject extends Lockable, Hideable {
|
|
551
606
|
id: string;
|
|
552
607
|
children: string[];
|
|
553
608
|
}
|
|
@@ -573,7 +628,7 @@ interface StrokePoint extends BoardPoint {
|
|
|
573
628
|
* geometric outline, not an expressive ink mark.
|
|
574
629
|
*/
|
|
575
630
|
type StrokeTool = "marker" | "highlighter" | "shape";
|
|
576
|
-
interface Stroke extends Lockable {
|
|
631
|
+
interface Stroke extends Lockable, Hideable {
|
|
577
632
|
id: string;
|
|
578
633
|
color: string;
|
|
579
634
|
baseWidth: number;
|
|
@@ -592,7 +647,7 @@ interface Stroke extends Lockable {
|
|
|
592
647
|
declare const ERASE_THRESHOLD = 0.95;
|
|
593
648
|
declare function cloneStroke(stroke: Stroke): Stroke;
|
|
594
649
|
type SerializedPoint = [number, number, number, number];
|
|
595
|
-
interface SerializedStroke extends Lockable {
|
|
650
|
+
interface SerializedStroke extends Lockable, Hideable {
|
|
596
651
|
id: string;
|
|
597
652
|
color: string;
|
|
598
653
|
baseWidth: number;
|
|
@@ -686,7 +741,7 @@ interface NoteVote {
|
|
|
686
741
|
* A sticky note: content floating above the board at a z-offset (pillar 3 —
|
|
687
742
|
* depth as an organizational axis). Center position in board space.
|
|
688
743
|
*/
|
|
689
|
-
interface StickyNote extends Lockable {
|
|
744
|
+
interface StickyNote extends Lockable, Hideable {
|
|
690
745
|
id: string;
|
|
691
746
|
x: number;
|
|
692
747
|
y: number;
|
|
@@ -704,7 +759,7 @@ interface StickyNote extends Lockable {
|
|
|
704
759
|
* top-left corner; lines flow downward (-y). Text joins the clustering
|
|
705
760
|
* system like handwriting (build prompt §6.4).
|
|
706
761
|
*/
|
|
707
|
-
interface TextBlock extends Lockable {
|
|
762
|
+
interface TextBlock extends Lockable, Hideable {
|
|
708
763
|
id: string;
|
|
709
764
|
x: number;
|
|
710
765
|
y: number;
|
|
@@ -734,7 +789,7 @@ declare function cloneNote(note: StickyNote): StickyNote;
|
|
|
734
789
|
* Interactive structured table on the board. Position (x, y) is top-left in board units.
|
|
735
790
|
* Cells are indexed as `${row},${col}` keys mapping to cell text content.
|
|
736
791
|
*/
|
|
737
|
-
interface TableBlock extends Lockable {
|
|
792
|
+
interface TableBlock extends Lockable, Hideable {
|
|
738
793
|
id: string;
|
|
739
794
|
x: number;
|
|
740
795
|
y: number;
|
|
@@ -760,7 +815,7 @@ declare const FOG_COLOR = "#FFFFFF";
|
|
|
760
815
|
* An imported image block on the board plane.
|
|
761
816
|
* Coordinates (x, y) represent the center of the image in board space.
|
|
762
817
|
*/
|
|
763
|
-
interface ImageBlock extends Lockable {
|
|
818
|
+
interface ImageBlock extends Lockable, Hideable {
|
|
764
819
|
id: string;
|
|
765
820
|
/**
|
|
766
821
|
* A legacy, read-only data URL (or, historically, an arbitrary string) —
|
|
@@ -810,6 +865,15 @@ type DocumentLoadResult = {
|
|
|
810
865
|
declare function loadDocumentBytes(originalBytes: string): DocumentLoadResult;
|
|
811
866
|
declare function migrateDocument(raw: unknown): DocumentLoadResult;
|
|
812
867
|
declare function serializeDocument(document: unknown): string;
|
|
868
|
+
/**
|
|
869
|
+
* A Document's serialized size in bytes (Phase 5) — UTF-8, not UTF-16
|
|
870
|
+
* `string.length`, since a Document with non-ASCII note/text content (most
|
|
871
|
+
* of them, eventually) would otherwise under-report. Useful for a Host
|
|
872
|
+
* deciding when to warn about an unusually large board, or for logging/
|
|
873
|
+
* telemetry around save size — not consulted by anything inside this
|
|
874
|
+
* package itself, which has no size limit of its own.
|
|
875
|
+
*/
|
|
876
|
+
declare function documentSize(document: CurrentSerializedDocument): number;
|
|
813
877
|
|
|
814
878
|
interface SearchableComment {
|
|
815
879
|
id: string;
|
|
@@ -1019,6 +1083,16 @@ declare class BoardDocument {
|
|
|
1019
1083
|
* `getSelectedItemInfo`'s custom branch hardcodes `isLocked: false`).
|
|
1020
1084
|
*/
|
|
1021
1085
|
isLocked(id: string): boolean;
|
|
1086
|
+
/**
|
|
1087
|
+
* True if `id` exists and is hidden, for any type — the same per-type
|
|
1088
|
+
* probe pattern as {@link isLocked} (Phase 8). Custom objects are
|
|
1089
|
+
* excluded for the same reason `isLocked` excludes them: they have no
|
|
1090
|
+
* `Hideable` field at all, so "hidden" isn't a concept that applies to
|
|
1091
|
+
* them yet. Used by marquee selection (`selectTool.ts`) to keep a hidden
|
|
1092
|
+
* object out of a rubber-band selection even for object types whose own
|
|
1093
|
+
* renderer doesn't yet suppress click-based hit-testing.
|
|
1094
|
+
*/
|
|
1095
|
+
isHidden(id: string): boolean;
|
|
1022
1096
|
subscribe(listener: Listener): () => void;
|
|
1023
1097
|
addStrokes(strokes: Stroke[]): void;
|
|
1024
1098
|
removeStrokes(ids: string[]): void;
|
|
@@ -1145,7 +1219,17 @@ declare class BoardDocument {
|
|
|
1145
1219
|
static deserializeNotes(data: SerializedDocument): StickyNote[];
|
|
1146
1220
|
static deserializeTexts(data: SerializedDocument): TextBlock[];
|
|
1147
1221
|
static deserializeTables(data: SerializedDocument): TableBlock[];
|
|
1148
|
-
|
|
1222
|
+
/**
|
|
1223
|
+
* `onSkip` (Phase 9) replaces an unconditional `console.error` — `core`
|
|
1224
|
+
* must never do raw console I/O (no dev-gate, no way for a Host to
|
|
1225
|
+
* suppress or redirect it), so a skipped stroke is now reported only if
|
|
1226
|
+
* the caller asks for it, via whatever diagnostic channel it already
|
|
1227
|
+
* has (e.g. `controller-internal.ts` routes this into the same typed
|
|
1228
|
+
* `"error"` event every other diagnostic already uses). Silent by
|
|
1229
|
+
* default, matching how every other `deserialize*` method here already
|
|
1230
|
+
* behaves (no diagnostics at all).
|
|
1231
|
+
*/
|
|
1232
|
+
static deserializeStrokes(data: SerializedDocument, onSkip?: (id: string, cause: unknown) => void): Stroke[];
|
|
1149
1233
|
/**
|
|
1150
1234
|
* Every mutation funnels through here, so paint-order tracking lives in
|
|
1151
1235
|
* exactly one place rather than at every individual add/remove call site
|
|
@@ -1156,17 +1240,18 @@ declare class BoardDocument {
|
|
|
1156
1240
|
* ids already tracked (idempotent by construction: `orderIndex.has` gates
|
|
1157
1241
|
* every append).
|
|
1158
1242
|
*
|
|
1159
|
-
* `orderChanged`
|
|
1160
|
-
* changed
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1169
|
-
*
|
|
1243
|
+
* `orderChanged` (Phase 9) reports exactly the ids whose rank actually
|
|
1244
|
+
* changed, using `orderIndex` throughout instead of `indexOf` — a pure
|
|
1245
|
+
* append never shifts any existing id's rank (new ids land at the tail,
|
|
1246
|
+
* already covered by this same change's own `Added` field, so
|
|
1247
|
+
* `orderChanged` stays unset), while a removal shifts every id at-or-
|
|
1248
|
+
* after the lowest removed rank down by one, computed in a single O(n)
|
|
1249
|
+
* filter pass (not one `indexOf`+`splice` per removed id) regardless of
|
|
1250
|
+
* how many ids this one change removes. Every renderer's `onChange` now
|
|
1251
|
+
* looks up only the ids actually in `orderChanged` instead of walking
|
|
1252
|
+
* its entire mesh map on any order-touching change — a broad, unfiltered
|
|
1253
|
+
* `orderChanged` here would silently defeat that fix, not just waste
|
|
1254
|
+
* cycles here.
|
|
1170
1255
|
*/
|
|
1171
1256
|
private emit;
|
|
1172
1257
|
}
|
|
@@ -1943,6 +2028,8 @@ interface BoardEventMap {
|
|
|
1943
2028
|
"asset-diagnostic": AssetDiagnostic;
|
|
1944
2029
|
/** A batch of Ops was reconciled (not applied as-sent) by the persistence adapter (ticket #24). */
|
|
1945
2030
|
"persistence-diagnostic": PersistenceDiagnostic;
|
|
2031
|
+
/** The collaboration server has confirmed receipt of these op ids (Phase 7) — observability only; the Document was already correct via optimistic local apply before this ever fires. */
|
|
2032
|
+
"collaboration-ops-acknowledged": CollaborationAckDiagnostic;
|
|
1946
2033
|
audit: unknown;
|
|
1947
2034
|
error: BoardControllerError;
|
|
1948
2035
|
disposed: undefined;
|
|
@@ -2006,9 +2093,12 @@ interface PresenceView {
|
|
|
2006
2093
|
readonly height: number;
|
|
2007
2094
|
}
|
|
2008
2095
|
/**
|
|
2009
|
-
* A
|
|
2010
|
-
*
|
|
2011
|
-
*
|
|
2096
|
+
* A collaborator, synced in for cursor/roster rendering only. Presence is
|
|
2097
|
+
* ephemeral — it never touches the Document, Ops, undo/redo, or persistence
|
|
2098
|
+
* (ADR 0006/0007). Two ways a roster gets populated (`presence.sync`
|
|
2099
|
+
* directly, or a `CollaborationAdapter`'s optional presence channel —
|
|
2100
|
+
* Phase 6, ADR 0015) both feed the exact same read/query capability below;
|
|
2101
|
+
* a Host picks one, not both, for a given controller.
|
|
2012
2102
|
*/
|
|
2013
2103
|
interface PresenceUser {
|
|
2014
2104
|
readonly id: string;
|
|
@@ -2017,6 +2107,14 @@ interface PresenceUser {
|
|
|
2017
2107
|
readonly tool?: string;
|
|
2018
2108
|
readonly cursor?: PresenceCursor;
|
|
2019
2109
|
readonly view?: PresenceView;
|
|
2110
|
+
/** Host-supplied extras (avatar URL, role, etc.) — opaque to Scrawl, never interpreted. */
|
|
2111
|
+
readonly metadata?: Record<string, unknown>;
|
|
2112
|
+
}
|
|
2113
|
+
/** This client's own local presence, published via `presence.broadcast()` (Phase 6). */
|
|
2114
|
+
interface LocalPresence {
|
|
2115
|
+
readonly cursor?: PresenceCursor | null;
|
|
2116
|
+
readonly view?: PresenceView | null;
|
|
2117
|
+
readonly tool?: string;
|
|
2020
2118
|
}
|
|
2021
2119
|
/**
|
|
2022
2120
|
* The Custom arm wraps `CustomBoardObject` under the same `type` discriminant
|
|
@@ -2142,10 +2240,34 @@ type LoadResult = {
|
|
|
2142
2240
|
} | {
|
|
2143
2241
|
state: "missing";
|
|
2144
2242
|
};
|
|
2243
|
+
/**
|
|
2244
|
+
* Result of a whole-document `PersistenceAdapter.replace()` call (ADR 0006:
|
|
2245
|
+
* "Whole-document writes survive only for create, clear-board and import,
|
|
2246
|
+
* where replacing everything is the actual intent"). Revision-gated, unlike
|
|
2247
|
+
* `applyOps` — `conflict` means `baseRevision` was stale (someone else's
|
|
2248
|
+
* write landed first); the caller must reload and never overwrites blind.
|
|
2249
|
+
*/
|
|
2250
|
+
type ReplaceResult = {
|
|
2251
|
+
state: "applied";
|
|
2252
|
+
revision: string;
|
|
2253
|
+
} | {
|
|
2254
|
+
state: "conflict";
|
|
2255
|
+
currentRevision: string;
|
|
2256
|
+
};
|
|
2257
|
+
/**
|
|
2258
|
+
* The one sanctioned seam for persisting a Board's Document to a Host's own
|
|
2259
|
+
* storage — implement this against a database, an HTTP API, IndexedDB
|
|
2260
|
+
* (see `@scrawl-board/board/local`'s `createIndexedDBPersistence`), or
|
|
2261
|
+
* anything else. `load()` fetches the current state on connect; `applyOps()`
|
|
2262
|
+
* streams incremental Ops as edits happen; `replace()` is only for
|
|
2263
|
+
* whole-document writes (create, clear-board, import — see ADR 0006) and is
|
|
2264
|
+
* revision-gated so a stale write never silently clobbers a newer one.
|
|
2265
|
+
* Passed via `createBoardController({ adapters: { persistence } })`.
|
|
2266
|
+
*/
|
|
2145
2267
|
interface PersistenceAdapter {
|
|
2146
2268
|
load(context: DocumentContext): Promise<LoadResult>;
|
|
2147
2269
|
applyOps(context: DocumentContext, ops: readonly ControllerOp[]): Promise<ApplyOpsResult>;
|
|
2148
|
-
replace(context: DocumentContext, document: CurrentSerializedDocument, baseRevision: string): Promise<
|
|
2270
|
+
replace(context: DocumentContext, document: CurrentSerializedDocument, baseRevision: string): Promise<ReplaceResult>;
|
|
2149
2271
|
}
|
|
2150
2272
|
/**
|
|
2151
2273
|
* `"reconcile"` (ticket #24) means the server authoritatively resolved the
|
|
@@ -2169,6 +2291,10 @@ interface PersistenceDiagnostic {
|
|
|
2169
2291
|
rejectedOpIds: readonly string[];
|
|
2170
2292
|
revision: string;
|
|
2171
2293
|
}
|
|
2294
|
+
/** Emitted as `"collaboration-ops-acknowledged"` (Phase 7) — the server has confirmed receipt of these op ids on the live pipe. */
|
|
2295
|
+
interface CollaborationAckDiagnostic {
|
|
2296
|
+
opIds: readonly string[];
|
|
2297
|
+
}
|
|
2172
2298
|
interface ControllerOp {
|
|
2173
2299
|
id: string;
|
|
2174
2300
|
schemaVersion: 1;
|
|
@@ -2185,7 +2311,39 @@ interface ControllerOp {
|
|
|
2185
2311
|
| "order";
|
|
2186
2312
|
objectId: string;
|
|
2187
2313
|
payload?: unknown;
|
|
2314
|
+
/**
|
|
2315
|
+
* This op's position in its own originating client's local sequence
|
|
2316
|
+
* (Phase 7) — 1, 2, 3, ... per controller instance, distinct from `id`
|
|
2317
|
+
* (an opaque, globally-unique identifier used for dedup/ack, not
|
|
2318
|
+
* ordering) and from a server's own authoritative ordering (e.g.
|
|
2319
|
+
* `referenceCollaborationServer.ts`'s per-room `version` counter).
|
|
2320
|
+
* Present on every op this SDK originates locally; a remote peer's op
|
|
2321
|
+
* carries whatever its own origin set, unchanged — never renumbered in
|
|
2322
|
+
* transit. Absent on an op minted by decoding the legacy wire envelope
|
|
2323
|
+
* (`scrawlOpEnvelope.ts`), which predates this field and has no
|
|
2324
|
+
* per-client sequence concept of its own.
|
|
2325
|
+
*/
|
|
2326
|
+
clientSequence?: number;
|
|
2327
|
+
/**
|
|
2328
|
+
* The `CollaboratorIdentity.id` of this op's originating client (Phase
|
|
2329
|
+
* 7) — set for every op this SDK originates locally when `identity` is
|
|
2330
|
+
* configured, omitted entirely otherwise (never sent as `undefined`).
|
|
2331
|
+
* The explicit foundation for a future per-author undo filter (a local
|
|
2332
|
+
* user's own undo should only ever touch their own ops) — no undo-stack
|
|
2333
|
+
* behavior itself changes this phase.
|
|
2334
|
+
*/
|
|
2335
|
+
clientId?: string;
|
|
2188
2336
|
}
|
|
2337
|
+
/**
|
|
2338
|
+
* The one sanctioned seam for real-time multiplayer — implement this against
|
|
2339
|
+
* a Host's own collaboration backend (WebSocket relay, CRDT server, etc.).
|
|
2340
|
+
* `connect()` is called once per controller with the local user's
|
|
2341
|
+
* `identity` and a `receive` callback the adapter invokes with incoming
|
|
2342
|
+
* Ops, presence updates, acks, and connection status; it resolves with a
|
|
2343
|
+
* `CollaborationSession` the controller uses to send local Ops and presence
|
|
2344
|
+
* back out. Passed via `createBoardController({ adapters: { collaboration } })`;
|
|
2345
|
+
* omit it entirely to run single-player.
|
|
2346
|
+
*/
|
|
2189
2347
|
interface CollaborationAdapter {
|
|
2190
2348
|
connect(options: DocumentContext & {
|
|
2191
2349
|
identity: CollaboratorIdentity;
|
|
@@ -2196,14 +2354,72 @@ interface CollaboratorIdentity {
|
|
|
2196
2354
|
id: string;
|
|
2197
2355
|
name: string;
|
|
2198
2356
|
color?: string;
|
|
2357
|
+
/** Host-supplied extras (avatar URL, role, etc.) — opaque to Scrawl, forwarded into any resulting `PresenceUser` unread and never interpreted. */
|
|
2358
|
+
metadata?: Record<string, unknown>;
|
|
2199
2359
|
}
|
|
2200
2360
|
interface CollaborationReceiver {
|
|
2201
2361
|
ops(ops: readonly ControllerOp[]): void;
|
|
2362
|
+
/**
|
|
2363
|
+
* The current presence roster (Phase 6, ADR 0015) — always a full
|
|
2364
|
+
* replacement, never a delta, matching `presence.sync`'s existing
|
|
2365
|
+
* semantics exactly (an adapter that aggregates wire deltas into a full
|
|
2366
|
+
* roster before calling this is the adapter's own job, not the
|
|
2367
|
+
* controller's). Required on this interface (not optional) because a
|
|
2368
|
+
* Host only ever *consumes* `CollaborationReceiver` — never implements
|
|
2369
|
+
* it — so adding a required method here cannot break an existing custom
|
|
2370
|
+
* `CollaborationAdapter`. An adapter with no presence support simply
|
|
2371
|
+
* never calls it.
|
|
2372
|
+
*/
|
|
2373
|
+
presence(users: readonly PresenceUser[]): void;
|
|
2374
|
+
/**
|
|
2375
|
+
* The server has confirmed receipt of these op ids (Phase 7) —
|
|
2376
|
+
* distinguishes "sent" from "server accepted," which `sendOps` alone
|
|
2377
|
+
* (fire-and-forget) cannot. Required for the same reason `presence` is:
|
|
2378
|
+
* Hosts only ever consume this interface, never implement it, so this
|
|
2379
|
+
* cannot break an existing custom `CollaborationAdapter`. An adapter with
|
|
2380
|
+
* no ack support simply never calls it — the collaboration pipe still
|
|
2381
|
+
* works exactly as it did before this existed, just without the
|
|
2382
|
+
* bookkeeping/observability this enables.
|
|
2383
|
+
*/
|
|
2384
|
+
acknowledged(opIds: readonly string[]): void;
|
|
2202
2385
|
status(state: "online" | "reconnecting" | "offline"): void;
|
|
2203
2386
|
error(cause: unknown): void;
|
|
2204
2387
|
}
|
|
2388
|
+
/**
|
|
2389
|
+
* Result of `CollaborationSession.requestSync()` (Phase 7). `"ops"` means
|
|
2390
|
+
* the adapter's own live-pipe cache fully covered the gap since the
|
|
2391
|
+
* caller's last known revision — apply `ops` and the client is caught up,
|
|
2392
|
+
* no persistence reload needed. `"unavailable"` means it couldn't (gap too
|
|
2393
|
+
* large, server restarted, or the adapter has no retained history at all)
|
|
2394
|
+
* — the caller must fall back to a persistence-backed reload. This is a
|
|
2395
|
+
* best-effort *liveness* cache, deliberately never a durable source of
|
|
2396
|
+
* truth (ADR 0006's "collaboration is never a second source of document
|
|
2397
|
+
* truth" — see ADR 0015's own extension of that principle to presence,
|
|
2398
|
+
* now extended once more, the same way, to this).
|
|
2399
|
+
*/
|
|
2400
|
+
type CollaborationSyncResult = {
|
|
2401
|
+
state: "ops";
|
|
2402
|
+
ops: readonly ControllerOp[];
|
|
2403
|
+
serverRevision: string;
|
|
2404
|
+
} | {
|
|
2405
|
+
state: "unavailable";
|
|
2406
|
+
};
|
|
2205
2407
|
interface CollaborationSession {
|
|
2206
2408
|
sendOps(ops: readonly ControllerOp[]): void;
|
|
2409
|
+
/**
|
|
2410
|
+
* Publishes this client's own local presence (Phase 6, ADR 0015) —
|
|
2411
|
+
* best-effort, unordered, never persisted, never an Op. Optional: an
|
|
2412
|
+
* adapter that doesn't support presence simply omits this method, and
|
|
2413
|
+
* `presence.broadcast()` becomes a silent no-op.
|
|
2414
|
+
*/
|
|
2415
|
+
updatePresence?(presence: LocalPresence): void;
|
|
2416
|
+
/**
|
|
2417
|
+
* Requests an incremental catch-up after a reconnect (Phase 7) — optional;
|
|
2418
|
+
* an adapter that doesn't support this simply omits the method, and the
|
|
2419
|
+
* caller (`resyncAfterReconnect`) goes straight to its existing
|
|
2420
|
+
* persistence-backed full reload, unchanged from Phase 6.
|
|
2421
|
+
*/
|
|
2422
|
+
requestSync?(): Promise<CollaborationSyncResult>;
|
|
2207
2423
|
close(): Promise<void>;
|
|
2208
2424
|
}
|
|
2209
2425
|
interface CreateBoardControllerOptions {
|
|
@@ -2243,6 +2459,87 @@ interface CreateBoardControllerOptions {
|
|
|
2243
2459
|
* doesn't use the React theme system can set this directly instead.
|
|
2244
2460
|
*/
|
|
2245
2461
|
boardTheme?: BoardThemeOptions;
|
|
2462
|
+
/**
|
|
2463
|
+
* Debounced auto-flush of pending persistence Ops after document changes
|
|
2464
|
+
* settle (Phase 5). Enabled by default (1000ms debounce) whenever
|
|
2465
|
+
* `adapters.persistence` is configured — today, without this, a Host must
|
|
2466
|
+
* call `flush()` manually after every edit for anything to persist. Pass
|
|
2467
|
+
* `false` to opt out entirely and drive `flush()` yourself, preserving
|
|
2468
|
+
* prior behavior exactly. Never fires on a per-change basis — rapid edits
|
|
2469
|
+
* coalesce into one flush of their final state (ADR 0006).
|
|
2470
|
+
*/
|
|
2471
|
+
autosave?: boolean | {
|
|
2472
|
+
debounceMs?: number;
|
|
2473
|
+
};
|
|
2474
|
+
/**
|
|
2475
|
+
* Throttle for `presence.broadcast()` (Phase 6, ADR 0015) — the minimum
|
|
2476
|
+
* interval between outgoing presence updates sent via the configured
|
|
2477
|
+
* `CollaborationAdapter`. Defaults to 50ms. A trailing throttle: the
|
|
2478
|
+
* latest value passed to `broadcast()` always eventually sends, even if
|
|
2479
|
+
* calls arrive faster than this interval.
|
|
2480
|
+
*/
|
|
2481
|
+
presenceThrottleMs?: number;
|
|
2482
|
+
/**
|
|
2483
|
+
* Caps how many `ControllerOp`s can sit queued, unsent, for the
|
|
2484
|
+
* persistence pipe (`pendingOps`) or the collaboration pipe
|
|
2485
|
+
* (`pendingCollaborationOps`) at once (Phase 7) — each pipe is capped
|
|
2486
|
+
* independently. Prevents unbounded memory growth from a long-lived
|
|
2487
|
+
* offline session or a stuck adapter. Exceeding it never fails or drops
|
|
2488
|
+
* the local edit itself (the Document already applied it optimistically)
|
|
2489
|
+
* — only queueing for that one pipe is skipped, and a
|
|
2490
|
+
* `{code:"queue-overflow", retryable:false}` error is emitted so a Host
|
|
2491
|
+
* can react. Defaults to 1000 — the Phase 9 collaboration coalescing
|
|
2492
|
+
* above (`collaborationCoalesceMs`) already keeps a busy drag from
|
|
2493
|
+
* approaching this on its own, so hitting it in practice means a pipe
|
|
2494
|
+
* has been offline/stuck for a genuinely long editing session.
|
|
2495
|
+
*
|
|
2496
|
+
* **Recovery** (Phase 9): the dropped op itself is gone from that one
|
|
2497
|
+
* pipe's queue — there is no automatic backfill, and the live
|
|
2498
|
+
* controller keeps running with that pipe now silently missing one
|
|
2499
|
+
* edit. Two things stay true regardless: (1) the in-memory Document is
|
|
2500
|
+
* never affected — a queue-overflow can never corrupt or roll back a
|
|
2501
|
+
* local edit, only skip sending it; (2) staleness is per-object, not
|
|
2502
|
+
* permanent — any *later* edit to that same object produces a brand
|
|
2503
|
+
* new, undropped Op carrying its full current state, which naturally
|
|
2504
|
+
* supersedes the gap (the Op model is already last-write-wins/
|
|
2505
|
+
* idempotent, so a superseding Op doesn't need the earlier one to have
|
|
2506
|
+
* arrived). The real risk is an object that's dropped and never edited
|
|
2507
|
+
* again before the controller is disposed or the page reloads — a Host
|
|
2508
|
+
* that needs strict durability should treat `queue-overflow` as a
|
|
2509
|
+
* signal to check `persistence.state`/`pendingOps` pressure (via
|
|
2510
|
+
* `usePersistenceStatus`/`getSnapshot().connection.persistence`)
|
|
2511
|
+
* before disposing, not assume disposing and reconnecting alone
|
|
2512
|
+
* repairs the gap (a fresh `load()` only returns what the backend
|
|
2513
|
+
* already has, which is exactly what's missing the dropped edit).
|
|
2514
|
+
*/
|
|
2515
|
+
maxPendingOps?: number;
|
|
2516
|
+
/**
|
|
2517
|
+
* Coalescing window (ms) for outgoing collaboration Ops (Phase 9) — same
|
|
2518
|
+
* trailing-throttle shape as `presenceThrottleMs`: the first Op after an
|
|
2519
|
+
* idle period sends immediately, and subsequent Ops for the *same*
|
|
2520
|
+
* object within this window replace each other (latest value wins,
|
|
2521
|
+
* matching the already-idempotent Op model) rather than each triggering
|
|
2522
|
+
* its own send. A multi-second drag that previously sent one full Op per
|
|
2523
|
+
* pointer-move now sends at most one per window per touched object.
|
|
2524
|
+
* Persistence (`pendingOps`) is unaffected — it already debounces via
|
|
2525
|
+
* `autosave`, so this option only changes live collaboration traffic.
|
|
2526
|
+
* Defaults to 50ms.
|
|
2527
|
+
*/
|
|
2528
|
+
collaborationCoalesceMs?: number;
|
|
2529
|
+
/**
|
|
2530
|
+
* Caps how many resolved objects a single `content.copy`/`content.cut`
|
|
2531
|
+
* (or their Cmd/Ctrl+C/X keyboard equivalents) will hold in the
|
|
2532
|
+
* in-memory clipboard at once (Phase 9) — `expandSelection` recursively
|
|
2533
|
+
* expands groups, so an unbounded selection (a huge group, or thousands
|
|
2534
|
+
* of individually selected strokes) could otherwise clone and retain an
|
|
2535
|
+
* arbitrarily large snapshot indefinitely, until the next copy/cut
|
|
2536
|
+
* replaces it. Exceeding it rejects the whole copy/cut (nothing is
|
|
2537
|
+
* cloned, and — for cut — nothing is removed from the Document either,
|
|
2538
|
+
* never a partial copy of an arbitrary subset) and emits a
|
|
2539
|
+
* `{code:"clipboard-overflow", retryable:false}` error. Defaults to
|
|
2540
|
+
* 5000.
|
|
2541
|
+
*/
|
|
2542
|
+
maxClipboardItems?: number;
|
|
2246
2543
|
}
|
|
2247
2544
|
interface BoardController {
|
|
2248
2545
|
readonly document: ReadonlyBoardDocument;
|
|
@@ -2274,6 +2571,15 @@ interface BoardController {
|
|
|
2274
2571
|
};
|
|
2275
2572
|
readonly view: {
|
|
2276
2573
|
fit(): void;
|
|
2574
|
+
/**
|
|
2575
|
+
* Frame the current selection (Phase 8), the same way `fit()` frames the
|
|
2576
|
+
* whole board. A no-op with nothing selected — deliberately doesn't fall
|
|
2577
|
+
* back to `fit()`'s "frame everything," which would be a surprising
|
|
2578
|
+
* result for an empty selection. On a headless board this can only
|
|
2579
|
+
* re-center the view (no viewport to compute a real zoom-to-fit from),
|
|
2580
|
+
* matching `fit()`'s own headless limitation exactly.
|
|
2581
|
+
*/
|
|
2582
|
+
zoomToSelection(): void;
|
|
2277
2583
|
zoomTo(value: number): void;
|
|
2278
2584
|
centerOn(point: BoardPoint): void;
|
|
2279
2585
|
get(): BoardView;
|
|
@@ -2345,6 +2651,14 @@ interface BoardController {
|
|
|
2345
2651
|
* aren't listed separately. `[]` when the clipboard is empty.
|
|
2346
2652
|
*/
|
|
2347
2653
|
paste(): readonly string[];
|
|
2654
|
+
/**
|
|
2655
|
+
* Select every top-level object (Phase 8) — a group's own id stands for
|
|
2656
|
+
* its children, which aren't selected separately, matching `paste`'s own
|
|
2657
|
+
* "what the user sees" id list. Hidden objects are excluded, consistent
|
|
2658
|
+
* with them already being excluded from marquee selection. Works with
|
|
2659
|
+
* no canvas/engine, same as {@link toggleSelectionVisibility}.
|
|
2660
|
+
*/
|
|
2661
|
+
selectAll(): void;
|
|
2348
2662
|
table: {
|
|
2349
2663
|
addRow(tableId: string): void;
|
|
2350
2664
|
addCol(tableId: string): void;
|
|
@@ -2363,6 +2677,21 @@ interface BoardController {
|
|
|
2363
2677
|
* another collaborator who isn't the current lock holder.
|
|
2364
2678
|
*/
|
|
2365
2679
|
toggleSelectionLock(): void;
|
|
2680
|
+
/**
|
|
2681
|
+
* Toggle hidden state for the current selection, as one undo entry
|
|
2682
|
+
* (Phase 8). If any selected object is hidden, shows every selected
|
|
2683
|
+
* object; otherwise hides them all — same "any wins" semantics as
|
|
2684
|
+
* {@link toggleSelectionLock}. Hidden objects stay fully present in the
|
|
2685
|
+
* document (they still serialize, persist, sync, undo/redo) — they just
|
|
2686
|
+
* stop rendering and stop being hit-testable/selectable via pointer
|
|
2687
|
+
* interaction. Unlike `toggleSelectionLock`, this works on a headless
|
|
2688
|
+
* board too: it only touches `selection`/the document, no canvas or
|
|
2689
|
+
* engine involved. Custom objects have no visibility concept (no
|
|
2690
|
+
* `Hideable` field) and are silently skipped, matching how
|
|
2691
|
+
* `toggleSelectionLock` already excludes them. A no-op with nothing
|
|
2692
|
+
* selected or when the selection is only custom objects.
|
|
2693
|
+
*/
|
|
2694
|
+
toggleSelectionVisibility(): void;
|
|
2366
2695
|
};
|
|
2367
2696
|
readonly query: {
|
|
2368
2697
|
get(id: string): DeepReadonly<BoardObject> | undefined;
|
|
@@ -2391,6 +2720,15 @@ interface BoardController {
|
|
|
2391
2720
|
follow(view: PresenceView): void;
|
|
2392
2721
|
/** Ease the camera to a peer's view; returns false (no-op) while mid-stroke. */
|
|
2393
2722
|
gather(view: PresenceView): boolean;
|
|
2723
|
+
/**
|
|
2724
|
+
* Publishes this client's own cursor/tool/view for other collaborators
|
|
2725
|
+
* (Phase 6, ADR 0015), via the configured `CollaborationAdapter` —
|
|
2726
|
+
* throttled internally (`presenceThrottleMs` option, default 50ms) so
|
|
2727
|
+
* a raw pointermove stream never becomes a message-per-event flood. A
|
|
2728
|
+
* no-op if no collaboration adapter is configured, or if the
|
|
2729
|
+
* configured one doesn't implement `updatePresence`.
|
|
2730
|
+
*/
|
|
2731
|
+
broadcast(local: LocalPresence): void;
|
|
2394
2732
|
};
|
|
2395
2733
|
readonly export: {
|
|
2396
2734
|
svg(): string;
|
|
@@ -2416,7 +2754,19 @@ interface BoardController {
|
|
|
2416
2754
|
}>;
|
|
2417
2755
|
dispose(): Promise<void>;
|
|
2418
2756
|
}
|
|
2757
|
+
/**
|
|
2758
|
+
* Creates a {@link BoardController} — the SDK's canonical, capability-grouped
|
|
2759
|
+
* entry point (`content`, `tools`, `style`, `history`, `view`, `query`,
|
|
2760
|
+
* `comments`, `presence`, `export`, `assets`, plus top-level `getSnapshot`/
|
|
2761
|
+
* `subscribe`/`on`/`setReadOnly`/`flush`/`dispose`) for driving a Board
|
|
2762
|
+
* imperatively from any JS/TS runtime. Supply a `canvas` to render, or omit
|
|
2763
|
+
* it to run headless (SSR, tests, or a document/history-only integration).
|
|
2764
|
+
* Persistence and Collaboration are opt-in via `options.adapters` — without
|
|
2765
|
+
* them the controller runs entirely in memory. Call `dispose()` when done to
|
|
2766
|
+
* release the renderer, adapters, and any pending timers.
|
|
2767
|
+
*/
|
|
2419
2768
|
declare function createBoardController(options: CreateBoardControllerOptions): BoardController;
|
|
2769
|
+
/** @deprecated Use `BoardController.getSnapshot()`'s return type instead. */
|
|
2420
2770
|
type LocalBoardSnapshot = {
|
|
2421
2771
|
documentId: string;
|
|
2422
2772
|
selectedStrokeId: string | null;
|
|
@@ -2425,10 +2775,12 @@ type LocalBoardSnapshot = {
|
|
|
2425
2775
|
canRedo: boolean;
|
|
2426
2776
|
disposed: boolean;
|
|
2427
2777
|
};
|
|
2778
|
+
/** @deprecated Use `CreateBoardControllerOptions` with `createBoardController` instead. */
|
|
2428
2779
|
type LocalBoardOptions = {
|
|
2429
2780
|
documentId: string;
|
|
2430
2781
|
initialDocument?: SerializedBoardDocument;
|
|
2431
2782
|
};
|
|
2783
|
+
/** @deprecated Use `BoardController` (from `createBoardController`) instead — this stroke-only, single-tool surface predates the full capability-grouped controller. */
|
|
2432
2784
|
type LocalBoard = {
|
|
2433
2785
|
drawStroke(stroke: Stroke): void;
|
|
2434
2786
|
selectAt(point: BoardPoint): string | null;
|
|
@@ -2439,8 +2791,21 @@ type LocalBoard = {
|
|
|
2439
2791
|
subscribe(listener: () => void): () => void;
|
|
2440
2792
|
dispose(): Promise<void>;
|
|
2441
2793
|
};
|
|
2794
|
+
/** @deprecated Use `createBoardController` instead — this is a thin, stroke-only wrapper kept for the original Phase 1 tracer's compatibility. */
|
|
2442
2795
|
declare function createLocalBoard(options: LocalBoardOptions): LocalBoard;
|
|
2443
2796
|
|
|
2797
|
+
interface CreateMemoryPersistenceOptions {
|
|
2798
|
+
/** Pre-seeded documents, keyed by document id — as if a prior session had already saved them. Seeded documents start at revision "1". */
|
|
2799
|
+
seed?: Record<string, CurrentSerializedDocument>;
|
|
2800
|
+
}
|
|
2801
|
+
/**
|
|
2802
|
+
* Creates a real, in-memory `PersistenceAdapter`. One instance can back
|
|
2803
|
+
* multiple documents (keyed by `DocumentContext.documentId`, like every
|
|
2804
|
+
* other adapter in this package). State lives only in this instance —
|
|
2805
|
+
* discarded on garbage collection, never written to disk.
|
|
2806
|
+
*/
|
|
2807
|
+
declare function createMemoryPersistence(options?: CreateMemoryPersistenceOptions): PersistenceAdapter;
|
|
2808
|
+
|
|
2444
2809
|
type ScrawlThemePreset = "light" | "dark";
|
|
2445
2810
|
type ScrawlDensity = "comfortable" | "compact";
|
|
2446
2811
|
type ScrawlGridMode = "none" | "line" | "dot";
|
|
@@ -2683,6 +3048,16 @@ interface ScrawlProviderProps {
|
|
|
2683
3048
|
className?: string;
|
|
2684
3049
|
style?: ThemeStyle;
|
|
2685
3050
|
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Wraps a Board `controller` you created yourself (via `createBoardController`)
|
|
3053
|
+
* so its descendants can use `useScrawlController`/`useScrawlSnapshot`/etc.
|
|
3054
|
+
* and render its default UI pieces (`DefaultBoardChrome`, `StyleShelf`, ...).
|
|
3055
|
+
* Prefer `Scrawl` unless you need to construct or own the controller's
|
|
3056
|
+
* lifecycle yourself (e.g. you create it outside React, or need it before
|
|
3057
|
+
* first render). Set `disposeOnUnmount` to have this provider call
|
|
3058
|
+
* `controller.dispose()` on unmount; otherwise disposal remains your own
|
|
3059
|
+
* responsibility.
|
|
3060
|
+
*/
|
|
2686
3061
|
declare function ScrawlProvider({ controller, children, preset, theme, portalContainer: customPortal, disposeOnUnmount, onThemeDiagnostic, className, style }: ScrawlProviderProps): react.JSX.Element;
|
|
2687
3062
|
interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
|
|
2688
3063
|
children?: ReactNode;
|
|
@@ -2703,6 +3078,17 @@ interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
|
|
|
2703
3078
|
*/
|
|
2704
3079
|
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
2705
3080
|
}
|
|
3081
|
+
/**
|
|
3082
|
+
* The fastest path to an embedded Board: creates and owns a
|
|
3083
|
+
* `BoardController` for you (constructed once, disposed on unmount) and
|
|
3084
|
+
* renders it into a canvas. Render with no `children` to get the SDK's
|
|
3085
|
+
* default toolbar/UI chrome, or supply your own `children` (using the
|
|
3086
|
+
* `useScrawlController`/`useScrawlSnapshot` hooks, or the exported
|
|
3087
|
+
* `DefaultBoardChrome`/`StyleShelf`/etc. pieces) to build a custom UI on
|
|
3088
|
+
* top of the same controller. Accepts every `CreateBoardControllerOptions`
|
|
3089
|
+
* field except `canvas` (pass `canvas={null}` for a headless board, or an
|
|
3090
|
+
* existing `<canvas>` element to control its identity yourself).
|
|
3091
|
+
*/
|
|
2706
3092
|
declare function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, icons, ...options }: ScrawlProps): react.JSX.Element;
|
|
2707
3093
|
interface ScrawlCanvasProps {
|
|
2708
3094
|
element?: HTMLCanvasElement;
|
|
@@ -2728,6 +3114,38 @@ declare function ScrawlPortal({ children }: {
|
|
|
2728
3114
|
declare function useScrawlController(): BoardController;
|
|
2729
3115
|
declare function useScrawlTheme(): ScrawlResolvedTheme;
|
|
2730
3116
|
declare function useScrawlSnapshot(): BoardSnapshot;
|
|
3117
|
+
/**
|
|
3118
|
+
* A thin, purely-derived convenience over
|
|
3119
|
+
* `useScrawlSnapshot().connection.persistence` (Phase 5) — for a Host
|
|
3120
|
+
* component that only cares about save status (e.g. a
|
|
3121
|
+
* "Saving…"/"Saved"/"Offline" indicator) and would otherwise re-derive
|
|
3122
|
+
* this same field access itself. Adds no state and no behavior of its
|
|
3123
|
+
* own: React still owns none of persistence, exactly as before — this
|
|
3124
|
+
* hook only re-reads what the controller already tracks.
|
|
3125
|
+
*
|
|
3126
|
+
* Deliberately does *not* go through `useScrawlSnapshot()` (Phase 9):
|
|
3127
|
+
* `changed()` rebuilds the whole `BoardSnapshot` — including a fresh
|
|
3128
|
+
* `connection.persistence` object — on every document mutation, even ones
|
|
3129
|
+
* that never touch persistence at all, so a component using only this
|
|
3130
|
+
* hook would otherwise re-render on every stroke draw. `useSyncStatus`
|
|
3131
|
+
* below memoizes on the value (`state`/`error`), not just the object
|
|
3132
|
+
* reference, and returns the same cached value across renders where
|
|
3133
|
+
* nothing relevant changed — the same shape `usePresence` already uses
|
|
3134
|
+
* for its own independently-scoped store.
|
|
3135
|
+
*/
|
|
3136
|
+
declare function usePersistenceStatus(): PersistenceSnapshot;
|
|
3137
|
+
/** The `CollaborationSnapshot` equivalent of `usePersistenceStatus` (Phase 6) — same value-memoized convenience over `useScrawlSnapshot().connection.collaboration` (Phase 9: see `usePersistenceStatus`'s doc comment for why it doesn't derive from the full snapshot). */
|
|
3138
|
+
declare function useCollaborationStatus(): CollaborationSnapshot;
|
|
3139
|
+
/**
|
|
3140
|
+
* The current presence roster (Phase 6), live-updating — wraps
|
|
3141
|
+
* `controller.presence.list()`/`subscribe` the same way `useScrawlSnapshot`
|
|
3142
|
+
* wraps the controller's main snapshot, via `useSyncExternalStore`. Does
|
|
3143
|
+
* not re-render on every remote pointer move by itself; it re-renders
|
|
3144
|
+
* whenever the roster the controller already tracks changes, at whatever
|
|
3145
|
+
* rate that arrives at (throttled adapter-side, per `presenceThrottleMs`).
|
|
3146
|
+
*/
|
|
3147
|
+
declare function usePresence(): readonly PresenceUser[];
|
|
3148
|
+
/** @deprecated Use `Scrawl` instead — this predates the full capability-grouped `BoardController` and only exposes the stroke-only `LocalBoard` surface. */
|
|
2731
3149
|
type ScrawlBoardProps = {
|
|
2732
3150
|
documentId: string;
|
|
2733
3151
|
initialDocument?: SerializedBoardDocument;
|
|
@@ -2735,7 +3153,8 @@ type ScrawlBoardProps = {
|
|
|
2735
3153
|
className?: string;
|
|
2736
3154
|
style?: CSSProperties;
|
|
2737
3155
|
};
|
|
3156
|
+
/** @deprecated Use `Scrawl` instead — this predates the full capability-grouped `BoardController` and only exposes the stroke-only `LocalBoard` surface. */
|
|
2738
3157
|
declare function ScrawlBoard({ documentId, initialDocument, onReady, className, style }: ScrawlBoardProps): react.JSX.Element;
|
|
2739
3158
|
|
|
2740
|
-
export { ASSET_CACHE_BYTES_DEFAULT, ASSET_CACHE_BYTES_MAX, ASSET_CACHE_BYTES_MIN, ASSET_EXPORT_MAX_DECODED_MEGAPIXELS, ASSET_EXPORT_MAX_ENCODED_BYTES, ASSET_MAX_CONCURRENT_RESOLUTIONS, ASSET_MAX_DECODED_MEGAPIXELS, ASSET_MAX_DIMENSION_PX, ASSET_MAX_ENCODED_BYTES, ASSET_REF_MAX_BYTES, ASSET_REF_PATTERN, AddArrowCommand, AddEllipseCommand, AddGroupCommand, AddHeartCommand, AddImageCommand, AddLineCommand, AddNoteCommand, AddPolygonCommand, AddRectangleCommand, AddStarCommand, AddStrokesCommand, AddTableCommand, AddTextCommand, AddTimerCommand, AssetResolutionError, BEACON_INSET, BoardDocument, CURRENT_DOCUMENT_SCHEMA_VERSION, ClusterStore, CommandBatch, DefaultBoardChrome, DeleteArrowCommand, DeleteEllipseCommand, DeleteGroupCommand, DeleteHeartCommand, DeleteImageCommand, DeleteLineCommand, DeleteNoteCommand, DeletePolygonCommand, DeleteRectangleCommand, DeleteStarCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, FocusedItemToolbar, HIGHLIGHT_COLORS, History, IDENTITY, INK_COLORS, InlineEditors, LockItemsCommand, MIN_WIDTH_FACTOR, MultiplayerCursors, NOTE_COLORS, NOTE_DEFAULT_SIZE, NOTE_DEFAULT_Z, NOTE_MAX_Z, NOTE_MIN_Z, NOTE_PEEL_STEP, ReorderObjectCommand, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, SHAPE_DEFAULT_STROKE, SHAPE_DEFAULT_STROKE_WIDTH, SHAPE_MIN_SIZE, STAMPS, STAMP_SIZE, SUPPORTED_ASSET_MEDIA_TYPES, Scrawl, ScrawlBoard, ScrawlCanvas, ScrawlDefaultUI, ScrawlPortal, ScrawlProvider, SpatialIndex, StyleShelf, TABLE_DEFAULT_CELL_HEIGHT, TABLE_DEFAULT_CELL_WIDTH, TABLE_DEFAULT_FONT_SIZE, TEXT_DEFAULT_SIZE, TIMER_DEFAULT_DURATION_MS, TIMER_DEFAULT_SIZE, TIMER_PRESETS_MS, TransformCommand, TransformObjectsCommand, UpdateArrowCommand, UpdateEllipseCommand, UpdateGroupCommand, UpdateHeartCommand, UpdateImageCommand, UpdateLineCommand, UpdateNoteCommand, UpdatePolygonCommand, UpdateRectangleCommand, UpdateStarCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, assetRef, avgScale, canUnlockItem, changeToOps, clampAssetCacheBytes, cloneArrow, cloneCustomObject, cloneEllipse, cloneGroup, cloneHeart, cloneImage, cloneLine, cloneNote, clonePolygon, cloneRectangle, cloneStar, cloneStroke, cloneTable, cloneText, cloneTimer, createBoardController, createLocalBoard, documentId, documentToSVG, formatTimer, invert, isAssetRef, isIdentity, isStampKind, loadDocumentBytes, measureTable, measureTextBlock, migrateDocument, mul, pauseTimer, placePresenceBeacon, resolveScrawlTheme, ribbonEdges, rotationAbout, scalingAbout, scrawlThemePresets, searchBoard, serializeDocument, serializeLock, serializeStroke, setTimerDuration, stampDataUrl, startTimer, strokeId, timerExpired, timerRemaining, toggleTimer, translation, useScrawlController, useScrawlSnapshot, useScrawlTheme, validateScrawlTheme };
|
|
2741
|
-
export type { ApplyOpsResult, ArrowHeadStyle, ArrowObject, AssetDiagnostic, AssetIngestRequest, AssetIngestResult, AssetIngestor, AssetKind, AssetPurpose, AssetRef, AssetResolutionErrorCode, AssetResolveRequest, AssetResolveResult, AssetResolver, BBox, BoardController, BoardControllerError, BoardEventMap, BoardKeyInput, BoardObject, BoardObjectInput, BoardObjectPatch, BoardPoint, BoardPointerInput, BoardScene, BoardSlotProps, BoardSnapshot, BoardStroke, BoardStyle, BoardThemeOptions, BoardView, BuiltInTool, ClusterIdFactory, CollaborationAdapter, CollaborationReceiver, CollaborationSession, CollaborationSnapshot, CollaboratorIdentity, Command, CommandKind, CommentMarker, ControllerOp, CreateBoardControllerOptions, CurrentSerializedDocument, CurrentSerializedStroke, CustomBoardObject, CustomObjectAddInput, CustomObjectDefinition, CustomTool, CustomToolDefinition, DeepReadonly, DefaultBoardChromeProps, DefaultUIRegion, DefaultUISlot, DefaultUISlots, DialogSlotProps, DocumentChange, DocumentContext, DocumentId, DocumentLoadResult, DocumentRecoveryCode, EllipseObject, ExtensionCommand, ExtensionDiagnostic, ExtensionHitResult, ExtensionId, ExtensionRequirement, FocusedItem, FocusedItemToolbarProps, GroupObject, HeartObject, ImageBlock, InlineEditorsProps, InputModifiers, JsonObject, JsonValue, KitchenTimer, LineObject, LoadResult, LocalBoard, LocalBoardOptions, LocalBoardSnapshot, LockHolder, LockTarget, Lockable, Mat2x3, MultiplayerCursorsProps, NoteVote, ObjectDescribeContext, ObjectIntent, ObjectType, Op, OpCollection, PersistenceAdapter, PersistenceDiagnostic, PersistenceSnapshot, PolygonObject, PresenceCursor, PresencePlacement, PresenceUser, PresenceView, QueryableBoardObject, ReadonlyBoardDocument, ReadonlyCustomObject, ReadonlyDocumentChange, RectangleObject, ReorderDirection, RibbonEdgePoint, SceneEllipse, SceneGroup, SceneImage, ScenePath, SceneRect, SceneText, ScrawlBoardProps, ScrawlCanvasProps, ScrawlDefaultUIProps, ScrawlDensity, ScrawlExtension, ScrawlGridMode, ScrawlProps, ScrawlProviderProps, ScrawlResolvedTheme, ScrawlSurfaceTexture, ScrawlTheme, ScrawlThemeDiagnostic, ScrawlThemePreset, ScreenPoint, ScreenRect, SearchHit, SearchHitKind, SearchableBoard, SearchableComment, SerializedBoardDocument, SerializedBoardStroke, SerializedPoint, SerializedStroke, StampKind, StarObject, StickyNote, Stroke, StrokeId, StrokePoint, StrokeTool, StyleShelfProps, SupportedAssetMediaType, TableBlock, TextBlock, ToolCancelReason, ToolCapabilities, ToolCursor, ToolId, ViewportInset };
|
|
3159
|
+
export { ASSET_CACHE_BYTES_DEFAULT, ASSET_CACHE_BYTES_MAX, ASSET_CACHE_BYTES_MIN, ASSET_EXPORT_MAX_DECODED_MEGAPIXELS, ASSET_EXPORT_MAX_ENCODED_BYTES, ASSET_MAX_CONCURRENT_RESOLUTIONS, ASSET_MAX_DECODED_MEGAPIXELS, ASSET_MAX_DIMENSION_PX, ASSET_MAX_ENCODED_BYTES, ASSET_REF_MAX_BYTES, ASSET_REF_PATTERN, AddArrowCommand, AddEllipseCommand, AddGroupCommand, AddHeartCommand, AddImageCommand, AddLineCommand, AddNoteCommand, AddPolygonCommand, AddRectangleCommand, AddStarCommand, AddStrokesCommand, AddTableCommand, AddTextCommand, AddTimerCommand, AssetResolutionError, BEACON_INSET, BoardDocument, CURRENT_DOCUMENT_SCHEMA_VERSION, ClusterStore, CommandBatch, DefaultBoardChrome, DeleteArrowCommand, DeleteEllipseCommand, DeleteGroupCommand, DeleteHeartCommand, DeleteImageCommand, DeleteLineCommand, DeleteNoteCommand, DeletePolygonCommand, DeleteRectangleCommand, DeleteStarCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, FocusedItemToolbar, HIGHLIGHT_COLORS, History, IDENTITY, INK_COLORS, InlineEditors, LockItemsCommand, MIN_WIDTH_FACTOR, MultiplayerCursors, NOTE_COLORS, NOTE_DEFAULT_SIZE, NOTE_DEFAULT_Z, NOTE_MAX_Z, NOTE_MIN_Z, NOTE_PEEL_STEP, ReorderObjectCommand, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, SHAPE_DEFAULT_STROKE, SHAPE_DEFAULT_STROKE_WIDTH, SHAPE_MIN_SIZE, SHAPE_STYLE_DEFAULTS, STAMPS, STAMP_SIZE, SUPPORTED_ASSET_MEDIA_TYPES, Scrawl, ScrawlBoard, ScrawlCanvas, ScrawlDefaultUI, ScrawlPortal, ScrawlProvider, SpatialIndex, StyleShelf, TABLE_DEFAULT_CELL_HEIGHT, TABLE_DEFAULT_CELL_WIDTH, TABLE_DEFAULT_FONT_SIZE, TEXT_DEFAULT_SIZE, TIMER_DEFAULT_DURATION_MS, TIMER_DEFAULT_SIZE, TIMER_PRESETS_MS, TransformCommand, TransformObjectsCommand, UpdateArrowCommand, UpdateEllipseCommand, UpdateGroupCommand, UpdateHeartCommand, UpdateImageCommand, UpdateLineCommand, UpdateNoteCommand, UpdatePolygonCommand, UpdateRectangleCommand, UpdateStarCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, assetRef, avgScale, canUnlockItem, changeToOps, clampAssetCacheBytes, cloneArrow, cloneCustomObject, cloneEllipse, cloneGroup, cloneHeart, cloneImage, cloneLine, cloneNote, clonePolygon, cloneRectangle, cloneStar, cloneStroke, cloneTable, cloneText, cloneTimer, createBoardController, createLocalBoard, createMemoryPersistence, documentId, documentSize, documentToSVG, formatTimer, invert, isAssetRef, isIdentity, isStampKind, loadDocumentBytes, measureTable, measureTextBlock, migrateDocument, mul, pauseTimer, placePresenceBeacon, resolveScrawlTheme, ribbonEdges, rotationAbout, scalingAbout, scrawlThemePresets, searchBoard, serializeDocument, serializeLock, serializeStroke, setTimerDuration, stampDataUrl, startTimer, strokeId, timerExpired, timerRemaining, toggleTimer, translation, useCollaborationStatus, usePersistenceStatus, usePresence, useScrawlController, useScrawlSnapshot, useScrawlTheme, validateScrawlTheme };
|
|
3160
|
+
export type { ApplyOpsResult, ArrowHeadStyle, ArrowObject, AssetDiagnostic, AssetExportFailure, AssetIngestRequest, AssetIngestResult, AssetIngestor, AssetKind, AssetPurpose, AssetRef, AssetResolutionErrorCode, AssetResolveRequest, AssetResolveResult, AssetResolver, BBox, BoardController, BoardControllerError, BoardEventMap, BoardKeyInput, BoardObject, BoardObjectInput, BoardObjectPatch, BoardPoint, BoardPointerInput, BoardScene, BoardSlotProps, BoardSnapshot, BoardStroke, BoardStyle, BoardThemeOptions, BoardView, BuiltInTool, ClusterIdFactory, CollaborationAckDiagnostic, CollaborationAdapter, CollaborationReceiver, CollaborationSession, CollaborationSnapshot, CollaborationSyncResult, CollaboratorIdentity, Command, CommandKind, CommentMarker, ControllerOp, CreateBoardControllerOptions, CreateMemoryPersistenceOptions, CurrentSerializedDocument, CurrentSerializedStroke, CustomBoardObject, CustomObjectAddInput, CustomObjectDefinition, CustomTool, CustomToolDefinition, DeepReadonly, DefaultBoardChromeProps, DefaultUIRegion, DefaultUISlot, DefaultUISlots, DialogSlotProps, DocumentChange, DocumentContext, DocumentId, DocumentLoadResult, DocumentRecoveryCode, EllipseObject, ExportDocumentSVGOptions, ExportDocumentSVGResult, ExtensionCommand, ExtensionDiagnostic, ExtensionHitResult, ExtensionId, ExtensionRequirement, FocusedItem, FocusedItemToolbarProps, GroupObject, HeartObject, ImageBlock, InlineEditorsProps, InputModifiers, JsonObject, JsonValue, KitchenTimer, LineObject, LoadResult, LocalBoard, LocalBoardOptions, LocalBoardSnapshot, LocalPresence, LockHolder, LockTarget, Lockable, Mat2x3, MultiplayerCursorsProps, NoteVote, ObjectDescribeContext, ObjectIntent, ObjectType, Op, OpCollection, PersistenceAdapter, PersistenceDiagnostic, PersistenceSnapshot, PolygonObject, PresenceCursor, PresencePlacement, PresenceUser, PresenceView, QueryableBoardObject, ReadonlyBoardDocument, ReadonlyCustomObject, ReadonlyDocumentChange, RectangleObject, ReorderDirection, ReplaceResult, RibbonEdgePoint, SceneEllipse, SceneGroup, SceneImage, ScenePath, SceneRect, SceneText, ScrawlBoardProps, ScrawlCanvasProps, ScrawlDefaultUIProps, ScrawlDensity, ScrawlExtension, ScrawlGridMode, ScrawlProps, ScrawlProviderProps, ScrawlResolvedTheme, ScrawlSurfaceTexture, ScrawlTheme, ScrawlThemeDiagnostic, ScrawlThemePreset, ScreenPoint, ScreenRect, SearchHit, SearchHitKind, SearchableBoard, SearchableComment, SerializedBoardDocument, SerializedBoardStroke, SerializedPoint, SerializedStroke, StampKind, StarObject, StickyNote, Stroke, StrokeId, StrokePoint, StrokeTool, StyleShelfProps, SupportedAssetMediaType, TableBlock, TextBlock, ToolCancelReason, ToolCapabilities, ToolCursor, ToolId, ViewportInset };
|