@scrawl-board/board 0.1.0-beta.1 → 0.1.0-beta.3

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
@@ -425,8 +425,13 @@ interface StrokePoint extends BoardPoint {
425
425
  */
426
426
  erase?: number;
427
427
  }
428
- /** Which drawing tool made a stroke; undefined means marker (back-compat). */
429
- type StrokeTool = "marker" | "highlighter";
428
+ /**
429
+ * Which drawing tool made a stroke; undefined means marker (back-compat).
430
+ * `"shape"` (rect/ellipse/line/arrow/polygon/star/heart) renders at its
431
+ * exact configured width with no pressure variance or end taper — a
432
+ * geometric outline, not an expressive ink mark.
433
+ */
434
+ type StrokeTool = "marker" | "highlighter" | "shape";
430
435
  interface Stroke extends Lockable {
431
436
  id: string;
432
437
  color: string;
@@ -1061,7 +1066,15 @@ interface RibbonEdgePoint {
1061
1066
  /** 1 = intact ink, 0 = fully erased (from the erasure channel). */
1062
1067
  alpha: number;
1063
1068
  }
1064
- declare function ribbonEdges(points: StrokePoint[], baseWidth: number): RibbonEdgePoint[];
1069
+ /**
1070
+ * @param handDrawn Default `true`: ink's organic feel — width follows
1071
+ * per-point pressure, and both ends taper. Pass `false` for a geometric
1072
+ * shape outline, which has no real pressure signal and isn't a pen stroke
1073
+ * with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
1074
+ * taper at its start/end (which, for a closed shape, is the same point —
1075
+ * tapering it would visibly pinch just that one corner).
1076
+ */
1077
+ declare function ribbonEdges(points: StrokePoint[], baseWidth: number, handDrawn?: boolean): RibbonEdgePoint[];
1065
1078
 
1066
1079
  declare class SpatialIndex {
1067
1080
  private readonly doc;
package/dist/core.js CHANGED
@@ -1,4 +1,4 @@
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
3
  /** Hard ceiling on a reference's own wire length — independent of any resource limit below. */
4
4
  var ASSET_REF_MAX_BYTES = 512;
@@ -38,7 +38,7 @@ function clampAssetCacheBytes(value) {
38
38
  return Math.min(ASSET_CACHE_BYTES_MAX, Math.max(ASSET_CACHE_BYTES_MIN, Math.floor(value)));
39
39
  }
40
40
  //#endregion
41
- //#region src/core-internal/identifiers.ts
41
+ //#region src/core/document/identifiers.ts
42
42
  function documentId(value) {
43
43
  if (!value) throw new TypeError("DocumentId cannot be empty");
44
44
  return value;
@@ -48,7 +48,7 @@ function strokeId(value) {
48
48
  return value;
49
49
  }
50
50
  //#endregion
51
- //#region src/document-schema.ts
51
+ //#region src/core/document/document-schema.ts
52
52
  var CURRENT_DOCUMENT_SCHEMA_VERSION = 1;
53
53
  var DocumentRecoveryError = class extends Error {
54
54
  constructor(code, message, originalBytes, path) {
@@ -256,7 +256,7 @@ function validation(message, path) {
256
256
  return new DocumentRecoveryError("DOCUMENT_VALIDATION_FAILED", message, void 0, path);
257
257
  }
258
258
  //#endregion
259
- //#region src/core-internal/affine2d.ts
259
+ //#region src/core/shapes/affine2d.ts
260
260
  var IDENTITY = [
261
261
  1,
262
262
  0,
@@ -340,7 +340,7 @@ function avgScale(m) {
340
340
  return (Math.hypot(m[0], m[1]) + Math.hypot(m[2], m[3])) / 2;
341
341
  }
342
342
  //#endregion
343
- //#region src/core-internal/boardSearch.ts
343
+ //#region src/core/document/boardSearch.ts
344
344
  function hay(s) {
345
345
  return s.toLowerCase();
346
346
  }
@@ -405,7 +405,7 @@ function searchBoard(query, board) {
405
405
  return hits;
406
406
  }
407
407
  //#endregion
408
- //#region src/core-internal/types.ts
408
+ //#region src/core/shapes/types.ts
409
409
  /** Erasure level at which a point counts as fully erased. */
410
410
  var ERASE_THRESHOLD = .95;
411
411
  function cloneStroke(stroke) {
@@ -481,7 +481,7 @@ function cloneImage(img) {
481
481
  return { ...img };
482
482
  }
483
483
  //#endregion
484
- //#region src/core-internal/clusterStore.ts
484
+ //#region src/core/document/clusterStore.ts
485
485
  /** A stroke joins a cluster last written to within this window at full reach. */
486
486
  var RECENT_MS = 8e3;
487
487
  /** Older clusters are still joinable, at half the gap threshold. */
@@ -652,7 +652,7 @@ function clamp$1(v, lo, hi) {
652
652
  return Math.min(hi, Math.max(lo, v));
653
653
  }
654
654
  //#endregion
655
- //#region src/core-internal/extensionTypes.ts
655
+ //#region src/core/shapes/extensionTypes.ts
656
656
  function cloneCustomObject(object) {
657
657
  return {
658
658
  ...object,
@@ -662,7 +662,7 @@ function cloneCustomObject(object) {
662
662
  };
663
663
  }
664
664
  //#endregion
665
- //#region src/core-internal/kitchenTimer.ts
665
+ //#region src/core/shapes/kitchenTimer.ts
666
666
  var TIMER_DEFAULT_SIZE = 12;
667
667
  var TIMER_DEFAULT_DURATION_MS = 300 * 1e3;
668
668
  var TIMER_PRESETS_MS = [
@@ -719,7 +719,7 @@ function formatTimer(ms) {
719
719
  return `${Math.floor(total / 60)}:${(total % 60).toString().padStart(2, "0")}`;
720
720
  }
721
721
  //#endregion
722
- //#region src/core-internal/itemLock.ts
722
+ //#region src/core/shapes/itemLock.ts
723
723
  /**
724
724
  * Apply or clear a lock in place. Unlock always drops the holder so a stale
725
725
  * name cannot linger on an unlocked item.
@@ -756,7 +756,7 @@ function serializeLock(item) {
756
756
  };
757
757
  }
758
758
  //#endregion
759
- //#region src/core-internal/document.ts
759
+ //#region src/core/document/document.ts
760
760
  /**
761
761
  * The one place a live Stroke becomes its wire representation — in
762
762
  * particular, points collapse from {x,y,pressure,erase?} objects into
@@ -1315,7 +1315,7 @@ function computeBBox(stroke) {
1315
1315
  return world;
1316
1316
  }
1317
1317
  //#endregion
1318
- //#region src/core-internal/commands.ts
1318
+ //#region src/core/commands/commands.ts
1319
1319
  /** One drawing action — a marker stroke, or a shape's strokes as one unit. */
1320
1320
  var AddStrokesCommand = class {
1321
1321
  constructor(strokes) {
@@ -1614,7 +1614,7 @@ var LockItemsCommand = class {
1614
1614
  }
1615
1615
  };
1616
1616
  //#endregion
1617
- //#region src/core-internal/history.ts
1617
+ //#region src/core/history/history.ts
1618
1618
  var LIMIT = 200;
1619
1619
  var History = class {
1620
1620
  constructor(doc) {
@@ -1675,7 +1675,7 @@ var History = class {
1675
1675
  }
1676
1676
  };
1677
1677
  //#endregion
1678
- //#region src/core-internal/ops.ts
1678
+ //#region src/core/events/ops.ts
1679
1679
  var ADDED = [
1680
1680
  ["added", "strokes"],
1681
1681
  ["notesAdded", "notes"],
@@ -1729,10 +1729,18 @@ function changeToOps(change, restoring = false) {
1729
1729
  return ops;
1730
1730
  }
1731
1731
  //#endregion
1732
- //#region src/core-internal/ribbonEdges.ts
1732
+ //#region src/core/shapes/ribbonEdges.ts
1733
1733
  var MIN_WIDTH_FACTOR = .35;
1734
1734
  var END_TAPER = .55;
1735
- function ribbonEdges(points, baseWidth) {
1735
+ /**
1736
+ * @param handDrawn Default `true`: ink's organic feel — width follows
1737
+ * per-point pressure, and both ends taper. Pass `false` for a geometric
1738
+ * shape outline, which has no real pressure signal and isn't a pen stroke
1739
+ * with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
1740
+ * taper at its start/end (which, for a closed shape, is the same point —
1741
+ * tapering it would visibly pinch just that one corner).
1742
+ */
1743
+ function ribbonEdges(points, baseWidth, handDrawn = true) {
1736
1744
  const pts = points.length === 1 ? [points[0], {
1737
1745
  ...points[0],
1738
1746
  x: points[0].x + baseWidth * .05
@@ -1747,8 +1755,8 @@ function ribbonEdges(points, baseWidth) {
1747
1755
  const len = Math.hypot(dx, dy) || 1;
1748
1756
  dx /= len;
1749
1757
  dy /= len;
1750
- let width = baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure);
1751
- if (i === 0 || i === n - 1) width *= END_TAPER;
1758
+ let width = handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure) : baseWidth;
1759
+ if (handDrawn && (i === 0 || i === n - 1)) width *= END_TAPER;
1752
1760
  const hw = width / 2;
1753
1761
  out[i] = {
1754
1762
  lx: pts[i].x - dy * hw,
@@ -1761,7 +1769,7 @@ function ribbonEdges(points, baseWidth) {
1761
1769
  return out;
1762
1770
  }
1763
1771
  //#endregion
1764
- //#region src/core-internal/spatialIndex.ts
1772
+ //#region src/core/document/spatialIndex.ts
1765
1773
  var CELL = 8;
1766
1774
  var SpatialIndex = class {
1767
1775
  constructor(doc) {
@@ -1834,7 +1842,7 @@ function cellsOf(box) {
1834
1842
  return keys;
1835
1843
  }
1836
1844
  //#endregion
1837
- //#region src/core-internal/stamps.ts
1845
+ //#region src/core/shapes/stamps.ts
1838
1846
  var STAMP_SIZE = 6;
1839
1847
  var STAMPS = [
1840
1848
  {
@@ -1883,7 +1891,7 @@ function isStampKind(value) {
1883
1891
  return typeof value === "string" && STAMPS.some((s) => s.kind === value);
1884
1892
  }
1885
1893
  //#endregion
1886
- //#region src/core-internal/presence.ts
1894
+ //#region src/collaboration/presence/presence.ts
1887
1895
  /** Keep beacons clear of typical chrome at the top and bottom of the Board. */
1888
1896
  var BEACON_INSET = {
1889
1897
  top: 72,
@@ -1919,7 +1927,7 @@ function clamp(value, min, max) {
1919
1927
  return Math.min(max, Math.max(min, value));
1920
1928
  }
1921
1929
  //#endregion
1922
- //#region src/core-internal/svg.ts
1930
+ //#region src/persistence/serialization/svg.ts
1923
1931
  /** Matches the key light used by the board shader and note shadows. */
1924
1932
  var LIGHT = {
1925
1933
  x: -.25,
@@ -2061,7 +2069,7 @@ function strokeToPaths(stroke) {
2061
2069
  y,
2062
2070
  pressure,
2063
2071
  ...erase > 0 ? { erase } : {}
2064
- })), stroke.baseWidth);
2072
+ })), stroke.baseWidth, stroke.tool !== "shape");
2065
2073
  const transform = stroke.matrix ? ` transform="matrix(${fmt(stroke.matrix[0])} ${fmt(-stroke.matrix[1])} ${fmt(-stroke.matrix[2])} ${fmt(stroke.matrix[3])} ${fmt(stroke.matrix[4])} ${fmt(-stroke.matrix[5])})"` : "";
2066
2074
  const baseOpacity = stroke.tool === "highlighter" ? .4 : 1;
2067
2075
  const paths = [];
package/dist/index.d.ts CHANGED
@@ -428,8 +428,13 @@ interface StrokePoint extends BoardPoint {
428
428
  */
429
429
  erase?: number;
430
430
  }
431
- /** Which drawing tool made a stroke; undefined means marker (back-compat). */
432
- type StrokeTool = "marker" | "highlighter";
431
+ /**
432
+ * Which drawing tool made a stroke; undefined means marker (back-compat).
433
+ * `"shape"` (rect/ellipse/line/arrow/polygon/star/heart) renders at its
434
+ * exact configured width with no pressure variance or end taper — a
435
+ * geometric outline, not an expressive ink mark.
436
+ */
437
+ type StrokeTool = "marker" | "highlighter" | "shape";
433
438
  interface Stroke extends Lockable {
434
439
  id: string;
435
440
  color: string;
@@ -1064,7 +1069,15 @@ interface RibbonEdgePoint {
1064
1069
  /** 1 = intact ink, 0 = fully erased (from the erasure channel). */
1065
1070
  alpha: number;
1066
1071
  }
1067
- declare function ribbonEdges(points: StrokePoint[], baseWidth: number): RibbonEdgePoint[];
1072
+ /**
1073
+ * @param handDrawn Default `true`: ink's organic feel — width follows
1074
+ * per-point pressure, and both ends taper. Pass `false` for a geometric
1075
+ * shape outline, which has no real pressure signal and isn't a pen stroke
1076
+ * with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
1077
+ * taper at its start/end (which, for a closed shape, is the same point —
1078
+ * tapering it would visibly pinch just that one corner).
1079
+ */
1080
+ declare function ribbonEdges(points: StrokePoint[], baseWidth: number, handDrawn?: boolean): RibbonEdgePoint[];
1068
1081
 
1069
1082
  declare class SpatialIndex {
1070
1083
  private readonly doc;
@@ -1305,6 +1318,27 @@ interface BoardView {
1305
1318
  y: number;
1306
1319
  zoom: number;
1307
1320
  }
1321
+ /**
1322
+ * The rendered board surface's color, reference grid, and shape stroke
1323
+ * width — the subset of {@link ScrawlTheme} that reaches the rendering
1324
+ * engine directly (everything else is UI-chrome-only, applied as CSS). Any
1325
+ * field left unset keeps its current value.
1326
+ */
1327
+ interface BoardThemeOptions {
1328
+ /** The board/canvas background color — distinct from UI chrome panels. */
1329
+ surface?: string;
1330
+ /** `"flat"` (default): a plain, uniform board surface. `"textured"`: a subtle "melamine" micro-noise + satin sheen, like a physical whiteboard. */
1331
+ surfaceTexture?: "flat" | "textured";
1332
+ /** `"none"` (default) keeps the board a plain surface; `"line"`/`"dot"` draw a zoom-adaptive reference grid. */
1333
+ gridMode?: "none" | "line" | "dot";
1334
+ gridColor?: string;
1335
+ /** Grid spacing in board units at 100% zoom. Ignored when `gridMode` is `"none"`. */
1336
+ gridSpacing?: number;
1337
+ /** On-screen grid line/dot width in CSS pixels — stays this width at any zoom, since the grid is a reference aid, not content. */
1338
+ gridLineWidth?: number;
1339
+ /** Shape (rectangle, ellipse, arrow, ...) border width, in board units — scales with zoom like ink, since it's part of the drawn content. */
1340
+ shapeStrokeWidth?: number;
1341
+ }
1308
1342
  /**
1309
1343
  * A Host-owned comment, summarized for Board-side search and marker
1310
1344
  * rendering. Comments are not Document content — they carry no undo
@@ -1515,12 +1549,7 @@ interface CreateBoardControllerOptions {
1515
1549
  * changes via `boardTheme.set` below — a headless/browser-tier Host that
1516
1550
  * doesn't use the React theme system can set this directly instead.
1517
1551
  */
1518
- boardTheme?: {
1519
- surface?: string;
1520
- gridMode?: "none" | "line" | "dot";
1521
- gridColor?: string;
1522
- gridSpacing?: number;
1523
- };
1552
+ boardTheme?: BoardThemeOptions;
1524
1553
  }
1525
1554
  interface BoardController {
1526
1555
  readonly document: ReadonlyBoardDocument;
@@ -1547,13 +1576,8 @@ interface BoardController {
1547
1576
  redo(): void;
1548
1577
  };
1549
1578
  readonly boardTheme: {
1550
- /** Live update of the board surface color/grid — the controller's identity stays fixed across theme changes. */
1551
- set(theme: {
1552
- surface?: string;
1553
- gridMode?: "none" | "line" | "dot";
1554
- gridColor?: string;
1555
- gridSpacing?: number;
1556
- }): void;
1579
+ /** Live update of the board surface color/grid/shape-stroke-width — the controller's identity stays fixed across theme changes. */
1580
+ set(theme: BoardThemeOptions): void;
1557
1581
  };
1558
1582
  readonly view: {
1559
1583
  fit(): void;
@@ -1658,37 +1682,82 @@ declare function createLocalBoard(options: LocalBoardOptions): LocalBoard;
1658
1682
  type ScrawlThemePreset = "light" | "dark";
1659
1683
  type ScrawlDensity = "comfortable" | "compact";
1660
1684
  type ScrawlGridMode = "none" | "line" | "dot";
1685
+ type ScrawlSurfaceTexture = "flat" | "textured";
1686
+ /**
1687
+ * Every field is optional — anything you don't set falls back to the
1688
+ * chosen `preset` ("light" or "dark", see {@link resolveScrawlTheme}).
1689
+ * Overrides are semantic, board-local runtime configuration: they never
1690
+ * get written into the Document or into exports, so switching themes is
1691
+ * always non-destructive.
1692
+ */
1661
1693
  interface ScrawlTheme {
1694
+ /** UI chrome background — toolbar/panel base surface. Distinct from `boardSurface` (the canvas itself). */
1662
1695
  surface?: string;
1696
+ /** UI chrome background, one step up from `surface` — popovers, dropdowns, elevated panels. */
1663
1697
  surfaceRaised?: string;
1698
+ /** UI chrome background, one step down from `surface` — subtle fills, hover states. */
1664
1699
  surfaceMuted?: string;
1700
+ /** Primary UI text color. Checked for contrast against `surface`. */
1665
1701
  text?: string;
1702
+ /** Secondary/de-emphasized UI text color. Checked for contrast against `surface`. */
1666
1703
  textMuted?: string;
1704
+ /** Borders and dividers between UI chrome elements. */
1667
1705
  edge?: string;
1706
+ /**
1707
+ * Brand accent color. Drives the active/selected state of toolbar
1708
+ * controls (e.g. the active tool button): its icon/text render in this
1709
+ * color, and its background is automatically derived as a light tint of
1710
+ * it (via `color-mix`) — set this one token and both follow.
1711
+ */
1712
+ primary?: string;
1713
+ /** Focus ring color. Checked for contrast against `surface`. */
1668
1714
  focus?: string;
1715
+ /** Selection highlight color (e.g. selected list items, not board object selection). */
1669
1716
  selection?: string;
1717
+ /** Destructive/error state color (delete confirmations, error text). */
1670
1718
  danger?: string;
1719
+ /** Warning state color. */
1671
1720
  warning?: string;
1721
+ /** Success/confirmation state color. */
1672
1722
  success?: string;
1723
+ /** Font stack for UI chrome (toolbar labels, menus, dialogs). */
1673
1724
  uiFontFamily?: string;
1725
+ /** Font stack for board content and data (e.g. table cell text). */
1674
1726
  dataFontFamily?: string;
1727
+ /** Base UI font size in px. Range: 12–24. */
1675
1728
  baseFontSize?: number;
1729
+ /** Regular UI font weight. Range: 300–900. */
1676
1730
  regularWeight?: number;
1731
+ /** Emphasized UI font weight (headings, active states). Range: 300–900. */
1677
1732
  strongWeight?: number;
1733
+ /** Corner radius for small controls (buttons, inputs) in px. Range: 0–32. */
1678
1734
  controlRadius?: number;
1735
+ /** Corner radius for panels/dialogs in px. Range: 0–32. */
1679
1736
  panelRadius?: number;
1737
+ /** CSS `box-shadow` value for subtle elevation (e.g. toolbar). */
1680
1738
  elevationLow?: string;
1739
+ /** CSS `box-shadow` value for prominent elevation (e.g. modals). */
1681
1740
  elevationHigh?: string;
1741
+ /** UI transition duration in ms. Range: 0–500. */
1682
1742
  motionDuration?: number;
1743
+ /** CSS easing function for UI transitions. */
1683
1744
  motionEasing?: string;
1745
+ /** UI chrome spacing/sizing scale. */
1684
1746
  density?: ScrawlDensity;
1685
1747
  /** The rendered board/canvas surface color — distinct from `surface` (UI chrome panels). */
1686
1748
  boardSurface?: string;
1749
+ /** `"flat"` (default): a plain, uniform board surface. `"textured"`: a subtle "melamine" micro-noise + satin sheen, like a physical whiteboard. */
1750
+ boardSurfaceTexture?: ScrawlSurfaceTexture;
1687
1751
  /** `"none"` (default) keeps the board a plain surface; `"line"`/`"dot"` draw a zoom-adaptive reference grid. */
1688
1752
  gridMode?: ScrawlGridMode;
1753
+ /** Grid line/dot color. Ignored when `gridMode` is `"none"`. */
1689
1754
  gridColor?: string;
1690
1755
  /** Grid spacing in board units at 100% zoom. Ignored when `gridMode` is `"none"`. */
1691
1756
  gridSpacing?: number;
1757
+ /** On-screen grid line/dot width in CSS pixels. Range: 0.5–8. A reference aid, so unlike shape/ink strokes it stays this width at any zoom. */
1758
+ gridLineWidth?: number;
1759
+ /** Shape (rectangle, ellipse, arrow, ...) border width, in board units. Range: 0.01–5. Scales with zoom like ink, since it's part of the drawn content. */
1760
+ shapeStrokeWidth?: number;
1692
1761
  }
1693
1762
  type ResolvedScrawlTheme = Required<ScrawlTheme>;
1694
1763
  interface ScrawlThemeDiagnostic {
@@ -1713,6 +1782,19 @@ interface DefaultBoardChromeProps {
1713
1782
  style?: React.CSSProperties;
1714
1783
  regions?: Partial<Record<DefaultUIRegion, boolean>>;
1715
1784
  slots?: DefaultUISlots;
1785
+ /**
1786
+ * Swap in your own icon for a tool's primary toolbar button — pass any
1787
+ * icon from any library (`<Pencil />`, `<Icon icon="..." />`, an inline
1788
+ * `<svg>`, whatever). A tool with no entry here keeps its default text
1789
+ * label, so this is opt-in per tool, not all-or-nothing. Aria labeling
1790
+ * and the keyboard-shortcut tooltip stay text-based regardless, so the
1791
+ * board remains fully accessible even with icon-only buttons.
1792
+ *
1793
+ * Only affects the six primary toolbar buttons (select/marker/
1794
+ * highlighter/eraser/note/text) — the "more tools" overflow menu is a
1795
+ * native `<select>`, which can only render plain text `<option>`s.
1796
+ */
1797
+ icons?: Partial<Record<BuiltInTool, ReactNode>>;
1716
1798
  }
1717
1799
  type DefaultUIRegion = "tools" | "history" | "view" | "style" | "search" | "import" | "export" | "inlineEditing" | "styleShelf";
1718
1800
  /** Props for the toolbar/topBar/stylePanel/contextMenu slots. */
@@ -1742,7 +1824,7 @@ interface DefaultUISlots {
1742
1824
  */
1743
1825
  contextMenu?: ComponentType<BoardSlotProps> | null;
1744
1826
  }
1745
- declare function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots }: DefaultBoardChromeProps): react.JSX.Element;
1827
+ declare function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots, icons }: DefaultBoardChromeProps): react.JSX.Element;
1746
1828
 
1747
1829
  interface InlineEditorsProps {
1748
1830
  controller: BoardController;
@@ -1799,8 +1881,14 @@ interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
1799
1881
  onThemeDiagnostic?: (diagnostic: ScrawlThemeDiagnostic) => void;
1800
1882
  /** Provide null for a headless Board, or an existing canvas to control its identity. */
1801
1883
  canvas?: HTMLCanvasElement | null;
1884
+ /**
1885
+ * Per-tool icon override for the default toolbar's primary buttons —
1886
+ * only applies when you don't supply `children` (i.e. you're using the
1887
+ * SDK's default UI). See DefaultBoardChromeProps.icons.
1888
+ */
1889
+ icons?: Partial<Record<BuiltInTool, ReactNode>>;
1802
1890
  }
1803
- declare function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, ...options }: ScrawlProps): react.JSX.Element;
1891
+ declare function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, icons, ...options }: ScrawlProps): react.JSX.Element;
1804
1892
  interface ScrawlCanvasProps {
1805
1893
  element?: HTMLCanvasElement;
1806
1894
  className?: string;
@@ -1815,8 +1903,10 @@ interface ScrawlDefaultUIProps {
1815
1903
  regions?: Partial<Record<DefaultUIRegion, boolean>>;
1816
1904
  /** Replace (component) or hide (null) a coarse region; omit for the SDK default. */
1817
1905
  slots?: DefaultUISlots;
1906
+ /** Per-tool icon override for the primary toolbar buttons — see DefaultBoardChromeProps.icons. */
1907
+ icons?: Partial<Record<BuiltInTool, ReactNode>>;
1818
1908
  }
1819
- declare function ScrawlDefaultUI({ className, style, regions, slots }: ScrawlDefaultUIProps): react.JSX.Element;
1909
+ declare function ScrawlDefaultUI({ className, style, regions, slots, icons }: ScrawlDefaultUIProps): react.JSX.Element;
1820
1910
  declare function ScrawlPortal({ children }: {
1821
1911
  children: ReactNode;
1822
1912
  }): react.ReactPortal | null;
@@ -1833,4 +1923,4 @@ type ScrawlBoardProps = {
1833
1923
  declare function ScrawlBoard({ documentId, initialDocument, onReady, className, style }: ScrawlBoardProps): react.JSX.Element;
1834
1924
 
1835
1925
  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, DefaultBoardChrome, DeleteImageCommand, DeleteNoteCommand, DeleteStrokesCommand, DeleteTableCommand, DeleteTextCommand, DeleteTimerCommand, DocumentRecoveryError, END_TAPER, ERASE_THRESHOLD, EraseCommand, FOG_COLOR, 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, SDK_DEVELOPMENT_VERSION, SDK_PACKAGE_NAME, 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, UpdateImageCommand, UpdateNoteCommand, UpdateTableCommand, UpdateTextCommand, UpdateTimerCommand, apply, applyItemLock, assetRef, avgScale, canUnlockItem, changeToOps, clampAssetCacheBytes, cloneCustomObject, cloneImage, cloneNote, 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 };
1836
- export type { ApplyOpsResult, 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, 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, ExtensionCommand, ExtensionDiagnostic, ExtensionHitResult, ExtensionId, ExtensionRequirement, ImageBlock, InlineEditorsProps, InputModifiers, JsonObject, JsonValue, KitchenTimer, LoadResult, LocalBoard, LocalBoardOptions, LocalBoardSnapshot, LockHolder, LockTarget, Lockable, Mat2x3, MultiplayerCursorsProps, NoteVote, ObjectDescribeContext, ObjectIntent, ObjectType, Op, OpCollection, PersistenceAdapter, PersistenceDiagnostic, PersistenceSnapshot, PresenceCursor, PresencePlacement, PresenceUser, PresenceView, QueryableBoardObject, ReadonlyBoardDocument, ReadonlyCustomObject, ReadonlyDocumentChange, RibbonEdgePoint, SceneEllipse, SceneGroup, SceneImage, ScenePath, SceneRect, SceneText, ScrawlBoardProps, ScrawlCanvasProps, ScrawlDefaultUIProps, ScrawlDensity, ScrawlExtension, ScrawlGridMode, ScrawlProps, ScrawlProviderProps, ScrawlResolvedTheme, ScrawlTheme, ScrawlThemeDiagnostic, ScrawlThemePreset, ScreenPoint, ScreenRect, SearchHit, SearchHitKind, SearchableBoard, SearchableComment, SerializedBoardDocument, SerializedBoardStroke, SerializedPoint, SerializedStroke, StampKind, StickyNote, Stroke, StrokeId, StrokePoint, StrokeTool, StyleShelfProps, SupportedAssetMediaType, TableBlock, TextBlock, ToolCancelReason, ToolCapabilities, ToolCursor, ToolId, ViewportInset };
1926
+ export type { ApplyOpsResult, 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, ExtensionCommand, ExtensionDiagnostic, ExtensionHitResult, ExtensionId, ExtensionRequirement, ImageBlock, InlineEditorsProps, InputModifiers, JsonObject, JsonValue, KitchenTimer, LoadResult, LocalBoard, LocalBoardOptions, LocalBoardSnapshot, LockHolder, LockTarget, Lockable, Mat2x3, MultiplayerCursorsProps, NoteVote, ObjectDescribeContext, ObjectIntent, ObjectType, Op, OpCollection, PersistenceAdapter, PersistenceDiagnostic, PersistenceSnapshot, PresenceCursor, PresencePlacement, PresenceUser, PresenceView, QueryableBoardObject, ReadonlyBoardDocument, ReadonlyCustomObject, ReadonlyDocumentChange, 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, StickyNote, Stroke, StrokeId, StrokePoint, StrokeTool, StyleShelfProps, SupportedAssetMediaType, TableBlock, TextBlock, ToolCancelReason, ToolCapabilities, ToolCursor, ToolId, ViewportInset };