@scrawl-board/board 0.1.0-beta.0 → 0.1.0-beta.2

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/dist/core.d.ts CHANGED
@@ -11,6 +11,79 @@ declare function strokeId(value: string): StrokeId;
11
11
 
12
12
  /** Wire grammar: `asset:<namespace>:<opaque-id>`. Interpreted only by the Host. */
13
13
  type AssetRef = string;
14
+ declare const ASSET_REF_PATTERN: RegExp;
15
+ /** Hard ceiling on a reference's own wire length — independent of any resource limit below. */
16
+ declare const ASSET_REF_MAX_BYTES = 512;
17
+ declare function isAssetRef(value: unknown): value is AssetRef;
18
+ /** Throws on malformed input; use `isAssetRef` where a boolean is wanted instead. */
19
+ declare function assetRef(value: string): AssetRef;
20
+ type AssetKind = "image";
21
+ type AssetPurpose = "render" | "thumbnail" | "export";
22
+ interface AssetResolveRequest {
23
+ ref: AssetRef;
24
+ kind: AssetKind;
25
+ purpose: AssetPurpose;
26
+ signal: AbortSignal;
27
+ }
28
+ interface AssetResolveResult {
29
+ bytes: Uint8Array;
30
+ mediaType: string;
31
+ }
32
+ /**
33
+ * Host-owned lookup. Scrawl passes only the reference, kind, purpose, and
34
+ * signal — never Document contents, an Extension instance, or credentials.
35
+ * The returned bytes are copied into SDK-owned storage and independently
36
+ * validated before use; a resolver's claimed `mediaType` is never trusted
37
+ * on its own (see `assetValidation.ts`).
38
+ */
39
+ interface AssetResolver {
40
+ resolve(request: AssetResolveRequest): Promise<AssetResolveResult>;
41
+ }
42
+ interface AssetIngestRequest {
43
+ bytes: Uint8Array;
44
+ mediaType: string;
45
+ name?: string;
46
+ signal: AbortSignal;
47
+ }
48
+ interface AssetIngestResult {
49
+ ref: AssetRef;
50
+ }
51
+ /**
52
+ * Host-owned upload/creation path. Scrawl validates the candidate against
53
+ * SDK limits before calling this, and validates the returned reference
54
+ * before it can enter any command. Cancellation or failure creates no
55
+ * Document object, Op, or history entry.
56
+ */
57
+ interface AssetIngestor {
58
+ ingest(request: AssetIngestRequest): Promise<AssetIngestResult>;
59
+ }
60
+ type AssetResolutionErrorCode = "resolver-unavailable" | "not-found" | "forbidden" | "offline" | "unsupported-type" | "too-large" | "invalid-content" | "decode-failed" | "budget-exceeded" | "aborted" | "unknown";
61
+ declare class AssetResolutionError extends Error {
62
+ readonly code: AssetResolutionErrorCode;
63
+ readonly retryable: boolean;
64
+ readonly ref?: AssetRef | undefined;
65
+ constructor(code: AssetResolutionErrorCode, retryable: boolean, message: string, ref?: AssetRef | undefined);
66
+ }
67
+ /** Runtime event for a resolution/ingestion failure — never carries credentials or a fetchable location. */
68
+ interface AssetDiagnostic {
69
+ code: AssetResolutionErrorCode;
70
+ ref?: AssetRef;
71
+ objectId: string;
72
+ objectKind: "image" | "custom";
73
+ retryable: boolean;
74
+ }
75
+ declare const ASSET_MAX_ENCODED_BYTES: number;
76
+ declare const ASSET_MAX_DIMENSION_PX = 8192;
77
+ declare const ASSET_MAX_DECODED_MEGAPIXELS = 40;
78
+ declare const ASSET_MAX_CONCURRENT_RESOLUTIONS = 6;
79
+ declare const ASSET_CACHE_BYTES_DEFAULT: number;
80
+ declare const ASSET_CACHE_BYTES_MIN: number;
81
+ declare const ASSET_CACHE_BYTES_MAX: number;
82
+ declare const ASSET_EXPORT_MAX_ENCODED_BYTES: number;
83
+ declare const ASSET_EXPORT_MAX_DECODED_MEGAPIXELS = 100;
84
+ declare const SUPPORTED_ASSET_MEDIA_TYPES: readonly ["image/png", "image/jpeg", "image/webp"];
85
+ type SupportedAssetMediaType = (typeof SUPPORTED_ASSET_MEDIA_TYPES)[number];
86
+ declare function clampAssetCacheBytes(value: number | undefined): number;
14
87
 
15
88
  type Mat2x3 = [number, number, number, number, number, number];
16
89
  declare const IDENTITY: Mat2x3;
@@ -69,6 +142,7 @@ interface CustomBoardObject {
69
142
  };
70
143
  props: JsonValue;
71
144
  }
145
+ declare function cloneCustomObject(object: CustomBoardObject): CustomBoardObject;
72
146
  /**
73
147
  * The read-only view handed to `describe`. Deep-readonly by construction
74
148
  * (not derived via a shallow `Readonly<>`) because `describe` must treat its
@@ -106,7 +180,7 @@ interface CustomObjectDefinition<Props extends JsonValue = JsonValue> {
106
180
  parse(input: unknown, schemaVersion: number): Props;
107
181
  /** One pure, synchronous step per consecutive schema version. */
108
182
  migrate?: Readonly<Record<number, (oldProps: JsonValue) => JsonValue>>;
109
- describe(object: ReadonlyCustomObject<Props>, context: ObjectDescribeContext): BoardScene$1;
183
+ describe(object: ReadonlyCustomObject<Props>, context: ObjectDescribeContext): BoardScene;
110
184
  }
111
185
  interface SceneNodeBase {
112
186
  key: string;
@@ -138,9 +212,9 @@ interface SceneText extends SceneNodeBase {
138
212
  }
139
213
  interface SceneGroup extends SceneNodeBase {
140
214
  kind: "group";
141
- children: readonly BoardScene$1[];
215
+ children: readonly BoardScene[];
142
216
  }
143
- interface ScenePath$1 extends SceneNodeBase {
217
+ interface ScenePath extends SceneNodeBase {
144
218
  kind: "path";
145
219
  /** SVG-style path data, board-local coordinates. */
146
220
  d: string;
@@ -173,7 +247,7 @@ interface SceneEllipse extends SceneNodeBase {
173
247
  stroke?: string;
174
248
  strokeWidth?: number;
175
249
  }
176
- type BoardScene$1 = SceneGroup | ScenePath$1 | SceneText | SceneImage | SceneRect | SceneEllipse;
250
+ type BoardScene = SceneGroup | ScenePath | SceneText | SceneImage | SceneRect | SceneEllipse;
177
251
  type ToolCursor = "default" | "crosshair" | "pointer" | "grab" | "grabbing" | "text";
178
252
  type ToolCancelReason = "escape-key" | "tool-switched" | "pointer-lost" | "error";
179
253
  interface InputModifiers {
@@ -269,7 +343,7 @@ interface ToolCapabilities {
269
343
  };
270
344
  /** Session-only geometry — never enters Document/history/persistence/collaboration. */
271
345
  preview: {
272
- set(scene: BoardScene$1): void;
346
+ set(scene: BoardScene): void;
273
347
  clear(): void;
274
348
  };
275
349
  /** Constructs, validates, and commits one atomic command; controller derives Ops. */
@@ -279,6 +353,15 @@ interface ToolCapabilities {
279
353
  cancel(): void;
280
354
  };
281
355
  }
356
+ interface ExtensionDiagnostic {
357
+ extensionId: ExtensionId;
358
+ phase: "parse" | "migrate" | "describe" | "tool-lifecycle" | "react-companion" | "asset-resolution";
359
+ toolId?: ToolId;
360
+ objectId?: string;
361
+ objectType?: ObjectType;
362
+ recoverable: boolean;
363
+ cause: unknown;
364
+ }
282
365
 
283
366
  /** Who holds a lock. Stored on the item so only they can take it off. */
284
367
  interface LockHolder {
@@ -489,7 +572,6 @@ declare function measureTable(table: TableBlock): {
489
572
  width: number;
490
573
  height: number;
491
574
  };
492
- declare const BOARD_COLOR = "#FFFFFF";
493
575
  declare const FOG_COLOR = "#FFFFFF";
494
576
  /**
495
577
  * An imported image block on the board plane.
@@ -1048,6 +1130,11 @@ interface ExtensionRegistry {
1048
1130
  * isn't in it), Custom objects still export via the standard fallback
1049
1131
  * placeholder (`fallback.bounds`/`label`), never silently dropped.
1050
1132
  *
1133
+ * `backgroundColor` should be the Host's actual resolved `boardTheme.surface`
1134
+ * so an export matches what was on screen; defaults to the theme system's
1135
+ * own light-preset default when the caller doesn't have one on hand. The
1136
+ * board's reference grid, if any, is a screen-only aid and never exported.
1137
+ *
1051
1138
  * `resolvedAssets` (ticket #23) maps an Asset reference to an already-
1052
1139
  * resolved `data:` URI — see `assetExport.ts`'s `exportDocumentSVGWithAssets`,
1053
1140
  * which is the only intended caller that ever passes one. Without it, every
@@ -1055,30 +1142,14 @@ interface ExtensionRegistry {
1055
1142
  * placeholder instead of guessing at a URL; a legacy `src`-backed image is
1056
1143
  * unaffected either way.
1057
1144
  */
1058
- declare function documentToSVG(doc: SerializedDocument, now?: number, registry?: ExtensionRegistry, resolvedAssets?: ReadonlyMap<AssetRef, string>): string;
1145
+ declare function documentToSVG(doc: SerializedDocument, now?: number, registry?: ExtensionRegistry, backgroundColor?: string, resolvedAssets?: ReadonlyMap<AssetRef, string>): string;
1059
1146
 
1060
1147
  declare const SDK_PACKAGE_NAME = "@scrawl-board/board";
1061
1148
  declare const SDK_DEVELOPMENT_VERSION = "0.0.0-development";
1062
1149
 
1063
- type BoardBounds = {
1064
- minX: number;
1065
- minY: number;
1066
- maxX: number;
1067
- maxY: number;
1068
- };
1069
- type ScenePath = {
1070
- id: string;
1071
- color: string;
1072
- width: number;
1073
- points: readonly BoardPoint[];
1074
- };
1075
- type BoardScene = {
1076
- bounds: BoardBounds;
1077
- paths: readonly ScenePath[];
1078
- };
1079
1150
  type BoardStroke = Stroke;
1080
1151
  type SerializedBoardStroke = SerializedStroke;
1081
1152
  type SerializedBoardDocument = CurrentSerializedDocument;
1082
1153
 
1083
- export { AddImageCommand, AddNoteCommand, AddStrokesCommand, AddTableCommand, AddTextCommand, AddTimerCommand, BEACON_INSET, BOARD_COLOR, BoardDocument, CURRENT_DOCUMENT_SCHEMA_VERSION, ClusterStore, DeleteImageCommand, DeleteNoteCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, HIGHLIGHT_COLORS, History, IDENTITY, INK_COLORS, LockItemsCommand, MIN_WIDTH_FACTOR, NOTE_COLORS, NOTE_DEFAULT_SIZE, NOTE_DEFAULT_Z, NOTE_MAX_Z, NOTE_MIN_Z, NOTE_PEEL_STEP, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, STAMPS, STAMP_SIZE, SpatialIndex, 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, UpdateImageCommand, UpdateNoteCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, avgScale, canUnlockItem, changeToOps, cloneImage, cloneNote, cloneStroke, cloneTable, cloneText, cloneTimer, documentId, documentToSVG, formatTimer, invert, isIdentity, isStampKind, loadDocumentBytes, measureTable, measureTextBlock, migrateDocument, mul, pauseTimer, placePresenceBeacon, ribbonEdges, rotationAbout, scalingAbout, searchBoard, serializeDocument, serializeLock, serializeStroke, setTimerDuration, stampDataUrl, startTimer, strokeId, timerExpired, timerRemaining, toggleTimer, translation };
1084
- export type { BBox, BoardBounds, BoardPoint, BoardScene, BoardStroke, ClusterIdFactory, Command, CommandKind, CurrentSerializedDocument, CurrentSerializedStroke, DocumentChange, DocumentId, DocumentLoadResult, DocumentRecoveryCode, ImageBlock, KitchenTimer, LockHolder, LockTarget, Lockable, Mat2x3, NoteVote, Op, OpCollection, PresencePlacement, RibbonEdgePoint, ScenePath, SearchHit, SearchHitKind, SearchableBoard, SearchableComment, SerializedBoardDocument, SerializedBoardStroke, SerializedPoint, SerializedStroke, StampKind, StickyNote, Stroke, StrokeId, StrokePoint, StrokeTool, TableBlock, TextBlock, ViewportInset };
1154
+ 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, AddImageCommand, AddNoteCommand, AddStrokesCommand, AddTableCommand, AddTextCommand, AddTimerCommand, AssetResolutionError, BEACON_INSET, BoardDocument, CURRENT_DOCUMENT_SCHEMA_VERSION, ClusterStore, DeleteImageCommand, DeleteNoteCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, HIGHLIGHT_COLORS, History, IDENTITY, INK_COLORS, LockItemsCommand, MIN_WIDTH_FACTOR, NOTE_COLORS, NOTE_DEFAULT_SIZE, NOTE_DEFAULT_Z, NOTE_MAX_Z, NOTE_MIN_Z, NOTE_PEEL_STEP, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, STAMPS, STAMP_SIZE, SUPPORTED_ASSET_MEDIA_TYPES, SpatialIndex, 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, UpdateImageCommand, UpdateNoteCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, assetRef, avgScale, canUnlockItem, changeToOps, clampAssetCacheBytes, cloneCustomObject, cloneImage, cloneNote, cloneStroke, cloneTable, cloneText, cloneTimer, documentId, documentToSVG, formatTimer, invert, isAssetRef, isIdentity, isStampKind, loadDocumentBytes, measureTable, measureTextBlock, migrateDocument, mul, pauseTimer, placePresenceBeacon, ribbonEdges, rotationAbout, scalingAbout, searchBoard, serializeDocument, serializeLock, serializeStroke, setTimerDuration, stampDataUrl, startTimer, strokeId, timerExpired, timerRemaining, toggleTimer, translation };
1155
+ export type { AssetDiagnostic, AssetIngestRequest, AssetIngestResult, AssetIngestor, AssetKind, AssetPurpose, AssetRef, AssetResolutionErrorCode, AssetResolveRequest, AssetResolveResult, AssetResolver, BBox, BoardKeyInput, BoardPoint, BoardPointerInput, BoardScene, BoardStroke, ClusterIdFactory, Command, CommandKind, CurrentSerializedDocument, CurrentSerializedStroke, CustomBoardObject, CustomObjectAddInput, CustomObjectDefinition, CustomTool, CustomToolDefinition, DocumentChange, DocumentId, DocumentLoadResult, DocumentRecoveryCode, ExtensionCommand, ExtensionDiagnostic, ExtensionHitResult, ExtensionId, ExtensionRequirement, ImageBlock, InputModifiers, JsonObject, JsonValue, KitchenTimer, LockHolder, LockTarget, Lockable, Mat2x3, NoteVote, ObjectDescribeContext, ObjectIntent, ObjectType, Op, OpCollection, PresencePlacement, QueryableBoardObject, ReadonlyCustomObject, RibbonEdgePoint, SceneEllipse, SceneGroup, SceneImage, ScenePath, SceneRect, SceneText, ScrawlExtension, SearchHit, SearchHitKind, SearchableBoard, SearchableComment, SerializedBoardDocument, SerializedBoardStroke, SerializedPoint, SerializedStroke, StampKind, StickyNote, Stroke, StrokeId, StrokePoint, StrokeTool, SupportedAssetMediaType, TableBlock, TextBlock, ToolCancelReason, ToolCapabilities, ToolCursor, ToolId, ViewportInset };
package/dist/core.js CHANGED
@@ -1,10 +1,44 @@
1
- //#region src/core-internal/assets.ts
1
+ //#region src/core/shapes/assets.ts
2
2
  var ASSET_REF_PATTERN = /^asset:[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?:[A-Za-z0-9._~-]+$/;
3
+ /** Hard ceiling on a reference's own wire length — independent of any resource limit below. */
4
+ var ASSET_REF_MAX_BYTES = 512;
3
5
  function isAssetRef(value) {
4
6
  return typeof value === "string" && ASSET_REF_PATTERN.test(value) && new TextEncoder().encode(value).length <= 512;
5
7
  }
8
+ /** Throws on malformed input; use `isAssetRef` where a boolean is wanted instead. */
9
+ function assetRef(value) {
10
+ if (!isAssetRef(value)) throw new TypeError(`Not a well-formed AssetRef: ${JSON.stringify(value)}`);
11
+ return value;
12
+ }
13
+ var AssetResolutionError = class extends Error {
14
+ constructor(code, retryable, message, ref) {
15
+ super(message);
16
+ this.code = code;
17
+ this.retryable = retryable;
18
+ this.ref = ref;
19
+ this.name = "AssetResolutionError";
20
+ }
21
+ };
22
+ var ASSET_MAX_ENCODED_BYTES = 20 * 1024 * 1024;
23
+ var ASSET_MAX_DIMENSION_PX = 8192;
24
+ var ASSET_MAX_DECODED_MEGAPIXELS = 40;
25
+ var ASSET_MAX_CONCURRENT_RESOLUTIONS = 6;
26
+ var ASSET_CACHE_BYTES_DEFAULT = 256 * 1024 * 1024;
27
+ var ASSET_CACHE_BYTES_MIN = 64 * 1024 * 1024;
28
+ var ASSET_CACHE_BYTES_MAX = 512 * 1024 * 1024;
29
+ var ASSET_EXPORT_MAX_ENCODED_BYTES = 100 * 1024 * 1024;
30
+ var ASSET_EXPORT_MAX_DECODED_MEGAPIXELS = 100;
31
+ var SUPPORTED_ASSET_MEDIA_TYPES = [
32
+ "image/png",
33
+ "image/jpeg",
34
+ "image/webp"
35
+ ];
36
+ function clampAssetCacheBytes(value) {
37
+ if (value === void 0 || !Number.isFinite(value)) return ASSET_CACHE_BYTES_DEFAULT;
38
+ return Math.min(ASSET_CACHE_BYTES_MAX, Math.max(ASSET_CACHE_BYTES_MIN, Math.floor(value)));
39
+ }
6
40
  //#endregion
7
- //#region src/core-internal/identifiers.ts
41
+ //#region src/core/document/identifiers.ts
8
42
  function documentId(value) {
9
43
  if (!value) throw new TypeError("DocumentId cannot be empty");
10
44
  return value;
@@ -14,7 +48,7 @@ function strokeId(value) {
14
48
  return value;
15
49
  }
16
50
  //#endregion
17
- //#region src/document-schema.ts
51
+ //#region src/core/document/document-schema.ts
18
52
  var CURRENT_DOCUMENT_SCHEMA_VERSION = 1;
19
53
  var DocumentRecoveryError = class extends Error {
20
54
  constructor(code, message, originalBytes, path) {
@@ -222,7 +256,7 @@ function validation(message, path) {
222
256
  return new DocumentRecoveryError("DOCUMENT_VALIDATION_FAILED", message, void 0, path);
223
257
  }
224
258
  //#endregion
225
- //#region src/core-internal/affine2d.ts
259
+ //#region src/core/shapes/affine2d.ts
226
260
  var IDENTITY = [
227
261
  1,
228
262
  0,
@@ -306,7 +340,7 @@ function avgScale(m) {
306
340
  return (Math.hypot(m[0], m[1]) + Math.hypot(m[2], m[3])) / 2;
307
341
  }
308
342
  //#endregion
309
- //#region src/core-internal/boardSearch.ts
343
+ //#region src/core/document/boardSearch.ts
310
344
  function hay(s) {
311
345
  return s.toLowerCase();
312
346
  }
@@ -371,7 +405,7 @@ function searchBoard(query, board) {
371
405
  return hits;
372
406
  }
373
407
  //#endregion
374
- //#region src/core-internal/types.ts
408
+ //#region src/core/shapes/types.ts
375
409
  /** Erasure level at which a point counts as fully erased. */
376
410
  var ERASE_THRESHOLD = .95;
377
411
  function cloneStroke(stroke) {
@@ -442,13 +476,12 @@ function measureTable(table) {
442
476
  height: table.rowHeights.reduce((sum, h) => sum + h, 0)
443
477
  };
444
478
  }
445
- var BOARD_COLOR = "#FFFFFF";
446
479
  var FOG_COLOR = "#FFFFFF";
447
480
  function cloneImage(img) {
448
481
  return { ...img };
449
482
  }
450
483
  //#endregion
451
- //#region src/core-internal/clusterStore.ts
484
+ //#region src/core/document/clusterStore.ts
452
485
  /** A stroke joins a cluster last written to within this window at full reach. */
453
486
  var RECENT_MS = 8e3;
454
487
  /** Older clusters are still joinable, at half the gap threshold. */
@@ -619,7 +652,7 @@ function clamp$1(v, lo, hi) {
619
652
  return Math.min(hi, Math.max(lo, v));
620
653
  }
621
654
  //#endregion
622
- //#region src/core-internal/extensionTypes.ts
655
+ //#region src/core/shapes/extensionTypes.ts
623
656
  function cloneCustomObject(object) {
624
657
  return {
625
658
  ...object,
@@ -629,7 +662,7 @@ function cloneCustomObject(object) {
629
662
  };
630
663
  }
631
664
  //#endregion
632
- //#region src/core-internal/kitchenTimer.ts
665
+ //#region src/core/shapes/kitchenTimer.ts
633
666
  var TIMER_DEFAULT_SIZE = 12;
634
667
  var TIMER_DEFAULT_DURATION_MS = 300 * 1e3;
635
668
  var TIMER_PRESETS_MS = [
@@ -686,7 +719,7 @@ function formatTimer(ms) {
686
719
  return `${Math.floor(total / 60)}:${(total % 60).toString().padStart(2, "0")}`;
687
720
  }
688
721
  //#endregion
689
- //#region src/core-internal/itemLock.ts
722
+ //#region src/core/shapes/itemLock.ts
690
723
  /**
691
724
  * Apply or clear a lock in place. Unlock always drops the holder so a stale
692
725
  * name cannot linger on an unlocked item.
@@ -723,7 +756,7 @@ function serializeLock(item) {
723
756
  };
724
757
  }
725
758
  //#endregion
726
- //#region src/core-internal/document.ts
759
+ //#region src/core/document/document.ts
727
760
  /**
728
761
  * The one place a live Stroke becomes its wire representation — in
729
762
  * particular, points collapse from {x,y,pressure,erase?} objects into
@@ -1282,7 +1315,7 @@ function computeBBox(stroke) {
1282
1315
  return world;
1283
1316
  }
1284
1317
  //#endregion
1285
- //#region src/core-internal/commands.ts
1318
+ //#region src/core/commands/commands.ts
1286
1319
  /** One drawing action — a marker stroke, or a shape's strokes as one unit. */
1287
1320
  var AddStrokesCommand = class {
1288
1321
  constructor(strokes) {
@@ -1581,7 +1614,7 @@ var LockItemsCommand = class {
1581
1614
  }
1582
1615
  };
1583
1616
  //#endregion
1584
- //#region src/core-internal/history.ts
1617
+ //#region src/core/history/history.ts
1585
1618
  var LIMIT = 200;
1586
1619
  var History = class {
1587
1620
  constructor(doc) {
@@ -1642,7 +1675,7 @@ var History = class {
1642
1675
  }
1643
1676
  };
1644
1677
  //#endregion
1645
- //#region src/core-internal/ops.ts
1678
+ //#region src/core/events/ops.ts
1646
1679
  var ADDED = [
1647
1680
  ["added", "strokes"],
1648
1681
  ["notesAdded", "notes"],
@@ -1696,7 +1729,7 @@ function changeToOps(change, restoring = false) {
1696
1729
  return ops;
1697
1730
  }
1698
1731
  //#endregion
1699
- //#region src/core-internal/ribbonEdges.ts
1732
+ //#region src/core/shapes/ribbonEdges.ts
1700
1733
  var MIN_WIDTH_FACTOR = .35;
1701
1734
  var END_TAPER = .55;
1702
1735
  function ribbonEdges(points, baseWidth) {
@@ -1728,7 +1761,7 @@ function ribbonEdges(points, baseWidth) {
1728
1761
  return out;
1729
1762
  }
1730
1763
  //#endregion
1731
- //#region src/core-internal/spatialIndex.ts
1764
+ //#region src/core/document/spatialIndex.ts
1732
1765
  var CELL = 8;
1733
1766
  var SpatialIndex = class {
1734
1767
  constructor(doc) {
@@ -1801,7 +1834,7 @@ function cellsOf(box) {
1801
1834
  return keys;
1802
1835
  }
1803
1836
  //#endregion
1804
- //#region src/core-internal/stamps.ts
1837
+ //#region src/core/shapes/stamps.ts
1805
1838
  var STAMP_SIZE = 6;
1806
1839
  var STAMPS = [
1807
1840
  {
@@ -1850,7 +1883,7 @@ function isStampKind(value) {
1850
1883
  return typeof value === "string" && STAMPS.some((s) => s.kind === value);
1851
1884
  }
1852
1885
  //#endregion
1853
- //#region src/core-internal/presence.ts
1886
+ //#region src/collaboration/presence/presence.ts
1854
1887
  /** Keep beacons clear of typical chrome at the top and bottom of the Board. */
1855
1888
  var BEACON_INSET = {
1856
1889
  top: 72,
@@ -1886,7 +1919,7 @@ function clamp(value, min, max) {
1886
1919
  return Math.min(max, Math.max(min, value));
1887
1920
  }
1888
1921
  //#endregion
1889
- //#region src/core-internal/svg.ts
1922
+ //#region src/persistence/serialization/svg.ts
1890
1923
  /** Matches the key light used by the board shader and note shadows. */
1891
1924
  var LIGHT = {
1892
1925
  x: -.25,
@@ -1896,12 +1929,19 @@ var LIGHT = {
1896
1929
  var MARGIN = 4;
1897
1930
  var NOTE_FONT_RATIO = 44 / 512;
1898
1931
  var NOTE_PAD_RATIO = 40 / 512;
1932
+ /** Matches the theme system's light-preset `boardSurface` default (theme.ts) — kept in sync manually, since core-internal stays decoupled from the theme layer. */
1933
+ var DEFAULT_BACKGROUND_COLOR = "#f5f5f3";
1899
1934
  /**
1900
1935
  * `registry` is optional and only enables rendering Custom objects through
1901
1936
  * their own `describe()` — without it (or for an object whose extension
1902
1937
  * isn't in it), Custom objects still export via the standard fallback
1903
1938
  * placeholder (`fallback.bounds`/`label`), never silently dropped.
1904
1939
  *
1940
+ * `backgroundColor` should be the Host's actual resolved `boardTheme.surface`
1941
+ * so an export matches what was on screen; defaults to the theme system's
1942
+ * own light-preset default when the caller doesn't have one on hand. The
1943
+ * board's reference grid, if any, is a screen-only aid and never exported.
1944
+ *
1905
1945
  * `resolvedAssets` (ticket #23) maps an Asset reference to an already-
1906
1946
  * resolved `data:` URI — see `assetExport.ts`'s `exportDocumentSVGWithAssets`,
1907
1947
  * which is the only intended caller that ever passes one. Without it, every
@@ -1909,7 +1949,7 @@ var NOTE_PAD_RATIO = 40 / 512;
1909
1949
  * placeholder instead of guessing at a URL; a legacy `src`-backed image is
1910
1950
  * unaffected either way.
1911
1951
  */
1912
- function documentToSVG(doc, now = 0, registry, resolvedAssets) {
1952
+ function documentToSVG(doc, now = 0, registry, backgroundColor = DEFAULT_BACKGROUND_COLOR, resolvedAssets) {
1913
1953
  const notes = doc.notes ?? [];
1914
1954
  const texts = doc.textBlocks ?? [];
1915
1955
  const tables = doc.tables ?? [];
@@ -1922,7 +1962,7 @@ function documentToSVG(doc, now = 0, registry, resolvedAssets) {
1922
1962
  const highlights = doc.strokes.filter((s) => s.tool === "highlighter");
1923
1963
  const ink = doc.strokes.filter((s) => s.tool !== "highlighter");
1924
1964
  return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${view}">\n<defs>\n${defs}\n</defs>\n${[
1925
- `<rect x="${fmt(bounds.minX)}" y="${fmt(bounds.minY)}" width="${fmt(bounds.width)}" height="${fmt(bounds.height)}" fill="${BOARD_COLOR}"/>`,
1965
+ `<rect x="${fmt(bounds.minX)}" y="${fmt(bounds.minY)}" width="${fmt(bounds.width)}" height="${fmt(bounds.height)}" fill="${backgroundColor}"/>`,
1926
1966
  ...images.map((image) => imageToElement(image, resolvedAssets)),
1927
1967
  ...tables.map(tableToGroup),
1928
1968
  ...highlights.flatMap(strokeToPaths),
@@ -2250,4 +2290,4 @@ function fmt(n) {
2250
2290
  var SDK_PACKAGE_NAME = "@scrawl-board/board";
2251
2291
  var SDK_DEVELOPMENT_VERSION = "0.0.0-development";
2252
2292
  //#endregion
2253
- export { AddImageCommand, AddNoteCommand, AddStrokesCommand, AddTableCommand, AddTextCommand, AddTimerCommand, BEACON_INSET, BOARD_COLOR, BoardDocument, CURRENT_DOCUMENT_SCHEMA_VERSION, ClusterStore, DeleteImageCommand, DeleteNoteCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, HIGHLIGHT_COLORS, History, IDENTITY, INK_COLORS, LockItemsCommand, MIN_WIDTH_FACTOR, NOTE_COLORS, NOTE_DEFAULT_SIZE, NOTE_DEFAULT_Z, NOTE_MAX_Z, NOTE_MIN_Z, NOTE_PEEL_STEP, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, STAMPS, STAMP_SIZE, SpatialIndex, 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, UpdateImageCommand, UpdateNoteCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, avgScale, canUnlockItem, changeToOps, cloneImage, cloneNote, cloneStroke, cloneTable, cloneText, cloneTimer, documentId, documentToSVG, formatTimer, invert, isIdentity, isStampKind, loadDocumentBytes, measureTable, measureTextBlock, migrateDocument, mul, pauseTimer, placePresenceBeacon, ribbonEdges, rotationAbout, scalingAbout, searchBoard, serializeDocument, serializeLock, serializeStroke, setTimerDuration, stampDataUrl, startTimer, strokeId, timerExpired, timerRemaining, toggleTimer, translation };
2293
+ 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, AddImageCommand, AddNoteCommand, AddStrokesCommand, AddTableCommand, AddTextCommand, AddTimerCommand, AssetResolutionError, BEACON_INSET, BoardDocument, CURRENT_DOCUMENT_SCHEMA_VERSION, ClusterStore, DeleteImageCommand, DeleteNoteCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, HIGHLIGHT_COLORS, History, IDENTITY, INK_COLORS, LockItemsCommand, MIN_WIDTH_FACTOR, NOTE_COLORS, NOTE_DEFAULT_SIZE, NOTE_DEFAULT_Z, NOTE_MAX_Z, NOTE_MIN_Z, NOTE_PEEL_STEP, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, STAMPS, STAMP_SIZE, SUPPORTED_ASSET_MEDIA_TYPES, SpatialIndex, 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, UpdateImageCommand, UpdateNoteCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, assetRef, avgScale, canUnlockItem, changeToOps, clampAssetCacheBytes, cloneCustomObject, cloneImage, cloneNote, cloneStroke, cloneTable, cloneText, cloneTimer, documentId, documentToSVG, formatTimer, invert, isAssetRef, isIdentity, isStampKind, loadDocumentBytes, measureTable, measureTextBlock, migrateDocument, mul, pauseTimer, placePresenceBeacon, ribbonEdges, rotationAbout, scalingAbout, searchBoard, serializeDocument, serializeLock, serializeStroke, setTimerDuration, stampDataUrl, startTimer, strokeId, timerExpired, timerRemaining, toggleTimer, translation };