@scrawl-board/board 0.1.0-beta.2 → 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/browser.d.ts +7 -2
- package/dist/browser.js +29 -26
- package/dist/core.d.ts +16 -3
- package/dist/core.js +12 -4
- package/dist/index.d.ts +47 -6
- package/dist/index.js +42 -31
- package/dist/react.d.ts +38 -5
- package/dist/react.js +42 -31
- package/dist/styles.css +2 -1
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
|
@@ -368,8 +368,13 @@ interface StrokePoint extends BoardPoint {
|
|
|
368
368
|
*/
|
|
369
369
|
erase?: number;
|
|
370
370
|
}
|
|
371
|
-
/**
|
|
372
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Which drawing tool made a stroke; undefined means marker (back-compat).
|
|
373
|
+
* `"shape"` (rect/ellipse/line/arrow/polygon/star/heart) renders at its
|
|
374
|
+
* exact configured width with no pressure variance or end taper — a
|
|
375
|
+
* geometric outline, not an expressive ink mark.
|
|
376
|
+
*/
|
|
377
|
+
type StrokeTool = "marker" | "highlighter" | "shape";
|
|
373
378
|
interface Stroke extends Lockable {
|
|
374
379
|
id: string;
|
|
375
380
|
color: string;
|
package/dist/browser.js
CHANGED
|
@@ -1739,7 +1739,15 @@ function changeToOps(change, restoring = false) {
|
|
|
1739
1739
|
//#region src/core/shapes/ribbonEdges.ts
|
|
1740
1740
|
var MIN_WIDTH_FACTOR = .35;
|
|
1741
1741
|
var END_TAPER = .55;
|
|
1742
|
-
|
|
1742
|
+
/**
|
|
1743
|
+
* @param handDrawn Default `true`: ink's organic feel — width follows
|
|
1744
|
+
* per-point pressure, and both ends taper. Pass `false` for a geometric
|
|
1745
|
+
* shape outline, which has no real pressure signal and isn't a pen stroke
|
|
1746
|
+
* with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
|
|
1747
|
+
* taper at its start/end (which, for a closed shape, is the same point —
|
|
1748
|
+
* tapering it would visibly pinch just that one corner).
|
|
1749
|
+
*/
|
|
1750
|
+
function ribbonEdges(points, baseWidth, handDrawn = true) {
|
|
1743
1751
|
const pts = points.length === 1 ? [points[0], {
|
|
1744
1752
|
...points[0],
|
|
1745
1753
|
x: points[0].x + baseWidth * .05
|
|
@@ -1754,8 +1762,8 @@ function ribbonEdges(points, baseWidth) {
|
|
|
1754
1762
|
const len = Math.hypot(dx, dy) || 1;
|
|
1755
1763
|
dx /= len;
|
|
1756
1764
|
dy /= len;
|
|
1757
|
-
let width = baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure);
|
|
1758
|
-
if (i === 0 || i === n - 1) width *= END_TAPER;
|
|
1765
|
+
let width = handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure) : baseWidth;
|
|
1766
|
+
if (handDrawn && (i === 0 || i === n - 1)) width *= END_TAPER;
|
|
1759
1767
|
const hw = width / 2;
|
|
1760
1768
|
out[i] = {
|
|
1761
1769
|
lx: pts[i].x - dy * hw,
|
|
@@ -2031,7 +2039,7 @@ function strokeToPaths(stroke) {
|
|
|
2031
2039
|
y,
|
|
2032
2040
|
pressure,
|
|
2033
2041
|
...erase > 0 ? { erase } : {}
|
|
2034
|
-
})), stroke.baseWidth);
|
|
2042
|
+
})), stroke.baseWidth, stroke.tool !== "shape");
|
|
2035
2043
|
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])})"` : "";
|
|
2036
2044
|
const baseOpacity = stroke.tool === "highlighter" ? .4 : 1;
|
|
2037
2045
|
const paths = [];
|
|
@@ -36308,8 +36316,8 @@ var INK_Z = .02;
|
|
|
36308
36316
|
var HIGHLIGHT_Z = .016;
|
|
36309
36317
|
/** Ghosts sit under everything so live strokes always read on top. */
|
|
36310
36318
|
var GHOST_Z = .012;
|
|
36311
|
-
function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
36312
|
-
const edges = ribbonEdges(points, baseWidth);
|
|
36319
|
+
function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
|
|
36320
|
+
const edges = ribbonEdges(points, baseWidth, handDrawn);
|
|
36313
36321
|
const n = edges.length;
|
|
36314
36322
|
const positions = new Float32Array(n * 2 * 3);
|
|
36315
36323
|
const uvs = new Float32Array(n * 2 * 2);
|
|
@@ -36354,6 +36362,8 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
|
36354
36362
|
//#endregion
|
|
36355
36363
|
//#region src/renderer/shapes/strokeRenderer.ts
|
|
36356
36364
|
var strokeZ = (stroke) => stroke.tool === "highlighter" ? HIGHLIGHT_Z : INK_Z;
|
|
36365
|
+
/** Shapes (rect/ellipse/line/arrow/...) are geometric outlines, not pressure-sensitive ink. */
|
|
36366
|
+
var handDrawn = (stroke) => stroke.tool !== "shape";
|
|
36357
36367
|
/**
|
|
36358
36368
|
* Derives ink meshes from the document by subscription — the document never
|
|
36359
36369
|
* knows the renderer exists. Also hosts session-only ghosts (decision Q7)
|
|
@@ -36378,12 +36388,12 @@ var StrokeRenderer = class {
|
|
|
36378
36388
|
this.ghostGroup.visible = visible;
|
|
36379
36389
|
}
|
|
36380
36390
|
/** Leave a smeared residue of an erased span (never persisted). */
|
|
36381
|
-
addGhost(points, color, baseWidth) {
|
|
36391
|
+
addGhost(points, color, baseWidth, handDrawn = true) {
|
|
36382
36392
|
if (points.length < 2) return;
|
|
36383
36393
|
const mesh = new Mesh(buildRibbonGeometry(points.map((p) => ({
|
|
36384
36394
|
...p,
|
|
36385
36395
|
erase: 0
|
|
36386
|
-
})), baseWidth * 1.7, GHOST_Z), this.materials.ghost(color));
|
|
36396
|
+
})), baseWidth * 1.7, GHOST_Z, handDrawn), this.materials.ghost(color));
|
|
36387
36397
|
this.ghostGroup.add(mesh);
|
|
36388
36398
|
}
|
|
36389
36399
|
/**
|
|
@@ -36395,7 +36405,7 @@ var StrokeRenderer = class {
|
|
|
36395
36405
|
if (this.meshes.has(stroke.id) || this.retiredLive.has(stroke.id) || stroke.points.length < 2) return;
|
|
36396
36406
|
this.liveUser.set(stroke.id, userId);
|
|
36397
36407
|
const existing = this.liveMeshes.get(stroke.id);
|
|
36398
|
-
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36408
|
+
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36399
36409
|
if (existing) {
|
|
36400
36410
|
existing.geometry.dispose();
|
|
36401
36411
|
existing.geometry = geometry;
|
|
@@ -36455,7 +36465,7 @@ var StrokeRenderer = class {
|
|
|
36455
36465
|
const mesh = this.meshes.get(stroke.id);
|
|
36456
36466
|
if (mesh) {
|
|
36457
36467
|
mesh.geometry.dispose();
|
|
36458
|
-
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36468
|
+
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36459
36469
|
}
|
|
36460
36470
|
}
|
|
36461
36471
|
for (const stroke of change.transformed) {
|
|
@@ -36464,7 +36474,7 @@ var StrokeRenderer = class {
|
|
|
36464
36474
|
}
|
|
36465
36475
|
}
|
|
36466
36476
|
create(stroke) {
|
|
36467
|
-
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36477
|
+
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36468
36478
|
mesh.matrixAutoUpdate = false;
|
|
36469
36479
|
syncMatrix(mesh, stroke);
|
|
36470
36480
|
this.meshes.set(stroke.id, mesh);
|
|
@@ -44603,9 +44613,10 @@ function hitStroke(doc, index, p, slop) {
|
|
|
44603
44613
|
const scale = m ? avgScale(m) : 1;
|
|
44604
44614
|
let prev = null;
|
|
44605
44615
|
let prevWidth = 0;
|
|
44616
|
+
const handDrawn = stroke.tool !== "shape";
|
|
44606
44617
|
for (const point of stroke.points) {
|
|
44607
44618
|
const world = m ? apply(m, point) : point;
|
|
44608
|
-
const width = stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) * scale;
|
|
44619
|
+
const width = (handDrawn ? stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : stroke.baseWidth) * scale;
|
|
44609
44620
|
if (prev) {
|
|
44610
44621
|
const reach = Math.max(width, prevWidth) / 2 + slop;
|
|
44611
44622
|
if (distToSegmentSq(p, prev, world) <= reach * reach) {
|
|
@@ -44739,7 +44750,7 @@ var EraserTool = class {
|
|
|
44739
44750
|
...p,
|
|
44740
44751
|
...apply(m, p)
|
|
44741
44752
|
})) : run.points;
|
|
44742
|
-
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1));
|
|
44753
|
+
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1), current.tool !== "shape");
|
|
44743
44754
|
}
|
|
44744
44755
|
}
|
|
44745
44756
|
doc.removeStrokes(before.map((s) => s.id));
|
|
@@ -44972,10 +44983,7 @@ function rect(a, b, step) {
|
|
|
44972
44983
|
a
|
|
44973
44984
|
];
|
|
44974
44985
|
const out = [];
|
|
44975
|
-
for (let i = 0; i < corners.length - 1; i++)
|
|
44976
|
-
const edge = sampleSegment(corners[i], corners[i + 1], step);
|
|
44977
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
44978
|
-
}
|
|
44986
|
+
for (let i = 0; i < corners.length - 1; i++) out.push(...sampleSegment(corners[i], corners[i + 1], step));
|
|
44979
44987
|
return out;
|
|
44980
44988
|
}
|
|
44981
44989
|
function ellipse(a, b, step) {
|
|
@@ -45046,10 +45054,7 @@ function polygon(sides, a, b, step, startAngle = -Math.PI / 2) {
|
|
|
45046
45054
|
}
|
|
45047
45055
|
vertices.push(vertices[0]);
|
|
45048
45056
|
const out = [];
|
|
45049
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45050
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45051
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45052
|
-
}
|
|
45057
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45053
45058
|
return out;
|
|
45054
45059
|
}
|
|
45055
45060
|
function star(points, a, b, step) {
|
|
@@ -45073,10 +45078,7 @@ function star(points, a, b, step) {
|
|
|
45073
45078
|
}
|
|
45074
45079
|
vertices.push(vertices[0]);
|
|
45075
45080
|
const out = [];
|
|
45076
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45077
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45078
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45079
|
-
}
|
|
45081
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45080
45082
|
return out;
|
|
45081
45083
|
}
|
|
45082
45084
|
function heart(a, b, step) {
|
|
@@ -45153,6 +45155,7 @@ var ShapeTool = class {
|
|
|
45153
45155
|
id: crypto.randomUUID(),
|
|
45154
45156
|
color: this.ctx.inkColor(),
|
|
45155
45157
|
baseWidth: this.ctx.shapeStrokeWidth(),
|
|
45158
|
+
tool: "shape",
|
|
45156
45159
|
points
|
|
45157
45160
|
}));
|
|
45158
45161
|
this.ctx.clusters.assign(strokes[0]);
|
|
@@ -45186,7 +45189,7 @@ var ShapeTool = class {
|
|
|
45186
45189
|
this.previews[i].visible = polyline !== void 0;
|
|
45187
45190
|
if (polyline) {
|
|
45188
45191
|
this.previews[i].geometry.dispose();
|
|
45189
|
-
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth());
|
|
45192
|
+
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth(), void 0, false);
|
|
45190
45193
|
}
|
|
45191
45194
|
}
|
|
45192
45195
|
}
|
package/dist/core.d.ts
CHANGED
|
@@ -425,8 +425,13 @@ interface StrokePoint extends BoardPoint {
|
|
|
425
425
|
*/
|
|
426
426
|
erase?: number;
|
|
427
427
|
}
|
|
428
|
-
/**
|
|
429
|
-
|
|
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
|
-
|
|
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
|
@@ -1732,7 +1732,15 @@ function changeToOps(change, restoring = false) {
|
|
|
1732
1732
|
//#region src/core/shapes/ribbonEdges.ts
|
|
1733
1733
|
var MIN_WIDTH_FACTOR = .35;
|
|
1734
1734
|
var END_TAPER = .55;
|
|
1735
|
-
|
|
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,
|
|
@@ -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
|
-
/**
|
|
432
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -1690,6 +1703,13 @@ interface ScrawlTheme {
|
|
|
1690
1703
|
textMuted?: string;
|
|
1691
1704
|
/** Borders and dividers between UI chrome elements. */
|
|
1692
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;
|
|
1693
1713
|
/** Focus ring color. Checked for contrast against `surface`. */
|
|
1694
1714
|
focus?: string;
|
|
1695
1715
|
/** Selection highlight color (e.g. selected list items, not board object selection). */
|
|
@@ -1762,6 +1782,19 @@ interface DefaultBoardChromeProps {
|
|
|
1762
1782
|
style?: React.CSSProperties;
|
|
1763
1783
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
1764
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>>;
|
|
1765
1798
|
}
|
|
1766
1799
|
type DefaultUIRegion = "tools" | "history" | "view" | "style" | "search" | "import" | "export" | "inlineEditing" | "styleShelf";
|
|
1767
1800
|
/** Props for the toolbar/topBar/stylePanel/contextMenu slots. */
|
|
@@ -1791,7 +1824,7 @@ interface DefaultUISlots {
|
|
|
1791
1824
|
*/
|
|
1792
1825
|
contextMenu?: ComponentType<BoardSlotProps> | null;
|
|
1793
1826
|
}
|
|
1794
|
-
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;
|
|
1795
1828
|
|
|
1796
1829
|
interface InlineEditorsProps {
|
|
1797
1830
|
controller: BoardController;
|
|
@@ -1848,8 +1881,14 @@ interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
|
|
|
1848
1881
|
onThemeDiagnostic?: (diagnostic: ScrawlThemeDiagnostic) => void;
|
|
1849
1882
|
/** Provide null for a headless Board, or an existing canvas to control its identity. */
|
|
1850
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>>;
|
|
1851
1890
|
}
|
|
1852
|
-
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;
|
|
1853
1892
|
interface ScrawlCanvasProps {
|
|
1854
1893
|
element?: HTMLCanvasElement;
|
|
1855
1894
|
className?: string;
|
|
@@ -1864,8 +1903,10 @@ interface ScrawlDefaultUIProps {
|
|
|
1864
1903
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
1865
1904
|
/** Replace (component) or hide (null) a coarse region; omit for the SDK default. */
|
|
1866
1905
|
slots?: DefaultUISlots;
|
|
1906
|
+
/** Per-tool icon override for the primary toolbar buttons — see DefaultBoardChromeProps.icons. */
|
|
1907
|
+
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
1867
1908
|
}
|
|
1868
|
-
declare function ScrawlDefaultUI({ className, style, regions, slots }: ScrawlDefaultUIProps): react.JSX.Element;
|
|
1909
|
+
declare function ScrawlDefaultUI({ className, style, regions, slots, icons }: ScrawlDefaultUIProps): react.JSX.Element;
|
|
1869
1910
|
declare function ScrawlPortal({ children }: {
|
|
1870
1911
|
children: ReactNode;
|
|
1871
1912
|
}): react.ReactPortal | null;
|
package/dist/index.js
CHANGED
|
@@ -1778,7 +1778,15 @@ function changeToOps(change, restoring = false) {
|
|
|
1778
1778
|
//#region src/core/shapes/ribbonEdges.ts
|
|
1779
1779
|
var MIN_WIDTH_FACTOR = .35;
|
|
1780
1780
|
var END_TAPER = .55;
|
|
1781
|
-
|
|
1781
|
+
/**
|
|
1782
|
+
* @param handDrawn Default `true`: ink's organic feel — width follows
|
|
1783
|
+
* per-point pressure, and both ends taper. Pass `false` for a geometric
|
|
1784
|
+
* shape outline, which has no real pressure signal and isn't a pen stroke
|
|
1785
|
+
* with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
|
|
1786
|
+
* taper at its start/end (which, for a closed shape, is the same point —
|
|
1787
|
+
* tapering it would visibly pinch just that one corner).
|
|
1788
|
+
*/
|
|
1789
|
+
function ribbonEdges(points, baseWidth, handDrawn = true) {
|
|
1782
1790
|
const pts = points.length === 1 ? [points[0], {
|
|
1783
1791
|
...points[0],
|
|
1784
1792
|
x: points[0].x + baseWidth * .05
|
|
@@ -1793,8 +1801,8 @@ function ribbonEdges(points, baseWidth) {
|
|
|
1793
1801
|
const len = Math.hypot(dx, dy) || 1;
|
|
1794
1802
|
dx /= len;
|
|
1795
1803
|
dy /= len;
|
|
1796
|
-
let width = baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure);
|
|
1797
|
-
if (i === 0 || i === n - 1) width *= END_TAPER;
|
|
1804
|
+
let width = handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure) : baseWidth;
|
|
1805
|
+
if (handDrawn && (i === 0 || i === n - 1)) width *= END_TAPER;
|
|
1798
1806
|
const hw = width / 2;
|
|
1799
1807
|
out[i] = {
|
|
1800
1808
|
lx: pts[i].x - dy * hw,
|
|
@@ -2107,7 +2115,7 @@ function strokeToPaths(stroke) {
|
|
|
2107
2115
|
y,
|
|
2108
2116
|
pressure,
|
|
2109
2117
|
...erase > 0 ? { erase } : {}
|
|
2110
|
-
})), stroke.baseWidth);
|
|
2118
|
+
})), stroke.baseWidth, stroke.tool !== "shape");
|
|
2111
2119
|
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])})"` : "";
|
|
2112
2120
|
const baseOpacity = stroke.tool === "highlighter" ? .4 : 1;
|
|
2113
2121
|
const paths = [];
|
|
@@ -36388,8 +36396,8 @@ var INK_Z = .02;
|
|
|
36388
36396
|
var HIGHLIGHT_Z = .016;
|
|
36389
36397
|
/** Ghosts sit under everything so live strokes always read on top. */
|
|
36390
36398
|
var GHOST_Z = .012;
|
|
36391
|
-
function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
36392
|
-
const edges = ribbonEdges(points, baseWidth);
|
|
36399
|
+
function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
|
|
36400
|
+
const edges = ribbonEdges(points, baseWidth, handDrawn);
|
|
36393
36401
|
const n = edges.length;
|
|
36394
36402
|
const positions = new Float32Array(n * 2 * 3);
|
|
36395
36403
|
const uvs = new Float32Array(n * 2 * 2);
|
|
@@ -36434,6 +36442,8 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
|
36434
36442
|
//#endregion
|
|
36435
36443
|
//#region src/renderer/shapes/strokeRenderer.ts
|
|
36436
36444
|
var strokeZ = (stroke) => stroke.tool === "highlighter" ? HIGHLIGHT_Z : INK_Z;
|
|
36445
|
+
/** Shapes (rect/ellipse/line/arrow/...) are geometric outlines, not pressure-sensitive ink. */
|
|
36446
|
+
var handDrawn = (stroke) => stroke.tool !== "shape";
|
|
36437
36447
|
/**
|
|
36438
36448
|
* Derives ink meshes from the document by subscription — the document never
|
|
36439
36449
|
* knows the renderer exists. Also hosts session-only ghosts (decision Q7)
|
|
@@ -36458,12 +36468,12 @@ var StrokeRenderer = class {
|
|
|
36458
36468
|
this.ghostGroup.visible = visible;
|
|
36459
36469
|
}
|
|
36460
36470
|
/** Leave a smeared residue of an erased span (never persisted). */
|
|
36461
|
-
addGhost(points, color, baseWidth) {
|
|
36471
|
+
addGhost(points, color, baseWidth, handDrawn = true) {
|
|
36462
36472
|
if (points.length < 2) return;
|
|
36463
36473
|
const mesh = new Mesh(buildRibbonGeometry(points.map((p) => ({
|
|
36464
36474
|
...p,
|
|
36465
36475
|
erase: 0
|
|
36466
|
-
})), baseWidth * 1.7, GHOST_Z), this.materials.ghost(color));
|
|
36476
|
+
})), baseWidth * 1.7, GHOST_Z, handDrawn), this.materials.ghost(color));
|
|
36467
36477
|
this.ghostGroup.add(mesh);
|
|
36468
36478
|
}
|
|
36469
36479
|
/**
|
|
@@ -36475,7 +36485,7 @@ var StrokeRenderer = class {
|
|
|
36475
36485
|
if (this.meshes.has(stroke.id) || this.retiredLive.has(stroke.id) || stroke.points.length < 2) return;
|
|
36476
36486
|
this.liveUser.set(stroke.id, userId);
|
|
36477
36487
|
const existing = this.liveMeshes.get(stroke.id);
|
|
36478
|
-
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36488
|
+
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36479
36489
|
if (existing) {
|
|
36480
36490
|
existing.geometry.dispose();
|
|
36481
36491
|
existing.geometry = geometry;
|
|
@@ -36535,7 +36545,7 @@ var StrokeRenderer = class {
|
|
|
36535
36545
|
const mesh = this.meshes.get(stroke.id);
|
|
36536
36546
|
if (mesh) {
|
|
36537
36547
|
mesh.geometry.dispose();
|
|
36538
|
-
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36548
|
+
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36539
36549
|
}
|
|
36540
36550
|
}
|
|
36541
36551
|
for (const stroke of change.transformed) {
|
|
@@ -36544,7 +36554,7 @@ var StrokeRenderer = class {
|
|
|
36544
36554
|
}
|
|
36545
36555
|
}
|
|
36546
36556
|
create(stroke) {
|
|
36547
|
-
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36557
|
+
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36548
36558
|
mesh.matrixAutoUpdate = false;
|
|
36549
36559
|
syncMatrix(mesh, stroke);
|
|
36550
36560
|
this.meshes.set(stroke.id, mesh);
|
|
@@ -44683,9 +44693,10 @@ function hitStroke(doc, index, p, slop) {
|
|
|
44683
44693
|
const scale = m ? avgScale(m) : 1;
|
|
44684
44694
|
let prev = null;
|
|
44685
44695
|
let prevWidth = 0;
|
|
44696
|
+
const handDrawn = stroke.tool !== "shape";
|
|
44686
44697
|
for (const point of stroke.points) {
|
|
44687
44698
|
const world = m ? apply(m, point) : point;
|
|
44688
|
-
const width = stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) * scale;
|
|
44699
|
+
const width = (handDrawn ? stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : stroke.baseWidth) * scale;
|
|
44689
44700
|
if (prev) {
|
|
44690
44701
|
const reach = Math.max(width, prevWidth) / 2 + slop;
|
|
44691
44702
|
if (distToSegmentSq(p, prev, world) <= reach * reach) {
|
|
@@ -44819,7 +44830,7 @@ var EraserTool = class {
|
|
|
44819
44830
|
...p,
|
|
44820
44831
|
...apply(m, p)
|
|
44821
44832
|
})) : run.points;
|
|
44822
|
-
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1));
|
|
44833
|
+
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1), current.tool !== "shape");
|
|
44823
44834
|
}
|
|
44824
44835
|
}
|
|
44825
44836
|
doc.removeStrokes(before.map((s) => s.id));
|
|
@@ -45052,10 +45063,7 @@ function rect(a, b, step) {
|
|
|
45052
45063
|
a
|
|
45053
45064
|
];
|
|
45054
45065
|
const out = [];
|
|
45055
|
-
for (let i = 0; i < corners.length - 1; i++)
|
|
45056
|
-
const edge = sampleSegment(corners[i], corners[i + 1], step);
|
|
45057
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45058
|
-
}
|
|
45066
|
+
for (let i = 0; i < corners.length - 1; i++) out.push(...sampleSegment(corners[i], corners[i + 1], step));
|
|
45059
45067
|
return out;
|
|
45060
45068
|
}
|
|
45061
45069
|
function ellipse(a, b, step) {
|
|
@@ -45126,10 +45134,7 @@ function polygon(sides, a, b, step, startAngle = -Math.PI / 2) {
|
|
|
45126
45134
|
}
|
|
45127
45135
|
vertices.push(vertices[0]);
|
|
45128
45136
|
const out = [];
|
|
45129
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45130
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45131
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45132
|
-
}
|
|
45137
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45133
45138
|
return out;
|
|
45134
45139
|
}
|
|
45135
45140
|
function star(points, a, b, step) {
|
|
@@ -45153,10 +45158,7 @@ function star(points, a, b, step) {
|
|
|
45153
45158
|
}
|
|
45154
45159
|
vertices.push(vertices[0]);
|
|
45155
45160
|
const out = [];
|
|
45156
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45157
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45158
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45159
|
-
}
|
|
45161
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45160
45162
|
return out;
|
|
45161
45163
|
}
|
|
45162
45164
|
function heart(a, b, step) {
|
|
@@ -45233,6 +45235,7 @@ var ShapeTool = class {
|
|
|
45233
45235
|
id: crypto.randomUUID(),
|
|
45234
45236
|
color: this.ctx.inkColor(),
|
|
45235
45237
|
baseWidth: this.ctx.shapeStrokeWidth(),
|
|
45238
|
+
tool: "shape",
|
|
45236
45239
|
points
|
|
45237
45240
|
}));
|
|
45238
45241
|
this.ctx.clusters.assign(strokes[0]);
|
|
@@ -45266,7 +45269,7 @@ var ShapeTool = class {
|
|
|
45266
45269
|
this.previews[i].visible = polyline !== void 0;
|
|
45267
45270
|
if (polyline) {
|
|
45268
45271
|
this.previews[i].geometry.dispose();
|
|
45269
|
-
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth());
|
|
45272
|
+
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth(), void 0, false);
|
|
45270
45273
|
}
|
|
45271
45274
|
}
|
|
45272
45275
|
}
|
|
@@ -49427,6 +49430,7 @@ var scrawlThemePresets = Object.freeze({
|
|
|
49427
49430
|
text: "#1c1c1a",
|
|
49428
49431
|
textMuted: "#62625c",
|
|
49429
49432
|
edge: "#d4d4cc",
|
|
49433
|
+
primary: "#1856a3",
|
|
49430
49434
|
focus: "#1856a3",
|
|
49431
49435
|
selection: "#d9e8f7",
|
|
49432
49436
|
danger: "#a3302b",
|
|
@@ -49459,6 +49463,7 @@ var scrawlThemePresets = Object.freeze({
|
|
|
49459
49463
|
text: "#f4f4ee",
|
|
49460
49464
|
textMuted: "#b7b8ae",
|
|
49461
49465
|
edge: "#484a42",
|
|
49466
|
+
primary: "#9dc8ef",
|
|
49462
49467
|
focus: "#9dc8ef",
|
|
49463
49468
|
selection: "#314b63",
|
|
49464
49469
|
danger: "#ffaaa3",
|
|
@@ -49492,6 +49497,7 @@ var variableNames = {
|
|
|
49492
49497
|
text: "--scrawl-color-text",
|
|
49493
49498
|
textMuted: "--scrawl-color-text-muted",
|
|
49494
49499
|
edge: "--scrawl-color-edge",
|
|
49500
|
+
primary: "--scrawl-color-primary",
|
|
49495
49501
|
focus: "--scrawl-color-focus",
|
|
49496
49502
|
selection: "--scrawl-color-selection",
|
|
49497
49503
|
danger: "--scrawl-color-danger",
|
|
@@ -49524,6 +49530,7 @@ var colorTokens = /* @__PURE__ */ new Set([
|
|
|
49524
49530
|
"text",
|
|
49525
49531
|
"textMuted",
|
|
49526
49532
|
"edge",
|
|
49533
|
+
"primary",
|
|
49527
49534
|
"focus",
|
|
49528
49535
|
"selection",
|
|
49529
49536
|
"danger",
|
|
@@ -50165,7 +50172,7 @@ var INKS = [
|
|
|
50165
50172
|
["Forest", "#315d46"],
|
|
50166
50173
|
["Oxblood", "#773f3b"]
|
|
50167
50174
|
];
|
|
50168
|
-
function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots }) {
|
|
50175
|
+
function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots, icons }) {
|
|
50169
50176
|
const [panel, setPanel] = useState(null);
|
|
50170
50177
|
const [query, setQuery] = useState("");
|
|
50171
50178
|
const [announcement, setAnnouncement] = useState("Board controls ready");
|
|
@@ -50319,7 +50326,10 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
50319
50326
|
onClick: () => chooseTool(tool),
|
|
50320
50327
|
title: `${label} (${key})`,
|
|
50321
50328
|
type: "button",
|
|
50322
|
-
children:
|
|
50329
|
+
children: icons?.[tool] ? /* @__PURE__ */ jsx("span", {
|
|
50330
|
+
"aria-hidden": "true",
|
|
50331
|
+
children: icons[tool]
|
|
50332
|
+
}) : label
|
|
50323
50333
|
}, tool)), /* @__PURE__ */ jsxs("label", {
|
|
50324
50334
|
className: "scrawl-board__tool-select",
|
|
50325
50335
|
children: [/* @__PURE__ */ jsx("span", { children: "More tools" }), /* @__PURE__ */ jsxs("select", {
|
|
@@ -50782,7 +50792,7 @@ function ScrawlProvider({ controller, children, preset = "light", theme, portalC
|
|
|
50782
50792
|
})
|
|
50783
50793
|
});
|
|
50784
50794
|
}
|
|
50785
|
-
function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, ...options }) {
|
|
50795
|
+
function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, icons, ...options }) {
|
|
50786
50796
|
const [runtime, setRuntime] = useState(null);
|
|
50787
50797
|
const runtimeRef = useRef(null);
|
|
50788
50798
|
const ownedCanvasRef = useRef(null);
|
|
@@ -50878,7 +50888,7 @@ function Scrawl({ children, preset, theme, portalContainer, className, style, on
|
|
|
50878
50888
|
onThemeDiagnostic,
|
|
50879
50889
|
className,
|
|
50880
50890
|
style,
|
|
50881
|
-
children: [runtime.canvas && /* @__PURE__ */ jsx(ScrawlCanvas, { element: runtime.canvas }), children ?? /* @__PURE__ */ jsx(ScrawlDefaultUI, {})]
|
|
50891
|
+
children: [runtime.canvas && /* @__PURE__ */ jsx(ScrawlCanvas, { element: runtime.canvas }), children ?? /* @__PURE__ */ jsx(ScrawlDefaultUI, { icons })]
|
|
50882
50892
|
});
|
|
50883
50893
|
}
|
|
50884
50894
|
function ScrawlCanvas({ element, className, style, "aria-label": ariaLabel = "Scrawl Board" }) {
|
|
@@ -50914,10 +50924,11 @@ function ScrawlCanvas({ element, className, style, "aria-label": ariaLabel = "Sc
|
|
|
50914
50924
|
style
|
|
50915
50925
|
});
|
|
50916
50926
|
}
|
|
50917
|
-
function ScrawlDefaultUI({ className, style, regions, slots }) {
|
|
50927
|
+
function ScrawlDefaultUI({ className, style, regions, slots, icons }) {
|
|
50918
50928
|
return /* @__PURE__ */ jsx(DefaultBoardChrome, {
|
|
50919
50929
|
className,
|
|
50920
50930
|
controller: useScrawlController(),
|
|
50931
|
+
icons,
|
|
50921
50932
|
regions,
|
|
50922
50933
|
renderPortal: (children) => /* @__PURE__ */ jsx(ScrawlPortal, { children }),
|
|
50923
50934
|
slots,
|
package/dist/react.d.ts
CHANGED
|
@@ -371,8 +371,13 @@ interface StrokePoint extends BoardPoint {
|
|
|
371
371
|
*/
|
|
372
372
|
erase?: number;
|
|
373
373
|
}
|
|
374
|
-
/**
|
|
375
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Which drawing tool made a stroke; undefined means marker (back-compat).
|
|
376
|
+
* `"shape"` (rect/ellipse/line/arrow/polygon/star/heart) renders at its
|
|
377
|
+
* exact configured width with no pressure variance or end taper — a
|
|
378
|
+
* geometric outline, not an expressive ink mark.
|
|
379
|
+
*/
|
|
380
|
+
type StrokeTool = "marker" | "highlighter" | "shape";
|
|
376
381
|
interface Stroke extends Lockable {
|
|
377
382
|
id: string;
|
|
378
383
|
color: string;
|
|
@@ -1064,6 +1069,13 @@ interface ScrawlTheme {
|
|
|
1064
1069
|
textMuted?: string;
|
|
1065
1070
|
/** Borders and dividers between UI chrome elements. */
|
|
1066
1071
|
edge?: string;
|
|
1072
|
+
/**
|
|
1073
|
+
* Brand accent color. Drives the active/selected state of toolbar
|
|
1074
|
+
* controls (e.g. the active tool button): its icon/text render in this
|
|
1075
|
+
* color, and its background is automatically derived as a light tint of
|
|
1076
|
+
* it (via `color-mix`) — set this one token and both follow.
|
|
1077
|
+
*/
|
|
1078
|
+
primary?: string;
|
|
1067
1079
|
/** Focus ring color. Checked for contrast against `surface`. */
|
|
1068
1080
|
focus?: string;
|
|
1069
1081
|
/** Selection highlight color (e.g. selected list items, not board object selection). */
|
|
@@ -1136,6 +1148,19 @@ interface DefaultBoardChromeProps {
|
|
|
1136
1148
|
style?: React.CSSProperties;
|
|
1137
1149
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
1138
1150
|
slots?: DefaultUISlots;
|
|
1151
|
+
/**
|
|
1152
|
+
* Swap in your own icon for a tool's primary toolbar button — pass any
|
|
1153
|
+
* icon from any library (`<Pencil />`, `<Icon icon="..." />`, an inline
|
|
1154
|
+
* `<svg>`, whatever). A tool with no entry here keeps its default text
|
|
1155
|
+
* label, so this is opt-in per tool, not all-or-nothing. Aria labeling
|
|
1156
|
+
* and the keyboard-shortcut tooltip stay text-based regardless, so the
|
|
1157
|
+
* board remains fully accessible even with icon-only buttons.
|
|
1158
|
+
*
|
|
1159
|
+
* Only affects the six primary toolbar buttons (select/marker/
|
|
1160
|
+
* highlighter/eraser/note/text) — the "more tools" overflow menu is a
|
|
1161
|
+
* native `<select>`, which can only render plain text `<option>`s.
|
|
1162
|
+
*/
|
|
1163
|
+
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
1139
1164
|
}
|
|
1140
1165
|
type DefaultUIRegion = "tools" | "history" | "view" | "style" | "search" | "import" | "export" | "inlineEditing" | "styleShelf";
|
|
1141
1166
|
/** Props for the toolbar/topBar/stylePanel/contextMenu slots. */
|
|
@@ -1165,7 +1190,7 @@ interface DefaultUISlots {
|
|
|
1165
1190
|
*/
|
|
1166
1191
|
contextMenu?: ComponentType<BoardSlotProps> | null;
|
|
1167
1192
|
}
|
|
1168
|
-
declare function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots }: DefaultBoardChromeProps): react.JSX.Element;
|
|
1193
|
+
declare function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots, icons }: DefaultBoardChromeProps): react.JSX.Element;
|
|
1169
1194
|
|
|
1170
1195
|
interface InlineEditorsProps {
|
|
1171
1196
|
controller: BoardController;
|
|
@@ -1222,8 +1247,14 @@ interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
|
|
|
1222
1247
|
onThemeDiagnostic?: (diagnostic: ScrawlThemeDiagnostic) => void;
|
|
1223
1248
|
/** Provide null for a headless Board, or an existing canvas to control its identity. */
|
|
1224
1249
|
canvas?: HTMLCanvasElement | null;
|
|
1250
|
+
/**
|
|
1251
|
+
* Per-tool icon override for the default toolbar's primary buttons —
|
|
1252
|
+
* only applies when you don't supply `children` (i.e. you're using the
|
|
1253
|
+
* SDK's default UI). See DefaultBoardChromeProps.icons.
|
|
1254
|
+
*/
|
|
1255
|
+
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
1225
1256
|
}
|
|
1226
|
-
declare function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, ...options }: ScrawlProps): react.JSX.Element;
|
|
1257
|
+
declare function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, icons, ...options }: ScrawlProps): react.JSX.Element;
|
|
1227
1258
|
interface ScrawlCanvasProps {
|
|
1228
1259
|
element?: HTMLCanvasElement;
|
|
1229
1260
|
className?: string;
|
|
@@ -1238,8 +1269,10 @@ interface ScrawlDefaultUIProps {
|
|
|
1238
1269
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
1239
1270
|
/** Replace (component) or hide (null) a coarse region; omit for the SDK default. */
|
|
1240
1271
|
slots?: DefaultUISlots;
|
|
1272
|
+
/** Per-tool icon override for the primary toolbar buttons — see DefaultBoardChromeProps.icons. */
|
|
1273
|
+
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
1241
1274
|
}
|
|
1242
|
-
declare function ScrawlDefaultUI({ className, style, regions, slots }: ScrawlDefaultUIProps): react.JSX.Element;
|
|
1275
|
+
declare function ScrawlDefaultUI({ className, style, regions, slots, icons }: ScrawlDefaultUIProps): react.JSX.Element;
|
|
1243
1276
|
declare function ScrawlPortal({ children }: {
|
|
1244
1277
|
children: ReactNode;
|
|
1245
1278
|
}): react.ReactPortal | null;
|
package/dist/react.js
CHANGED
|
@@ -1748,7 +1748,15 @@ function changeToOps(change, restoring = false) {
|
|
|
1748
1748
|
//#region src/core/shapes/ribbonEdges.ts
|
|
1749
1749
|
var MIN_WIDTH_FACTOR = .35;
|
|
1750
1750
|
var END_TAPER = .55;
|
|
1751
|
-
|
|
1751
|
+
/**
|
|
1752
|
+
* @param handDrawn Default `true`: ink's organic feel — width follows
|
|
1753
|
+
* per-point pressure, and both ends taper. Pass `false` for a geometric
|
|
1754
|
+
* shape outline, which has no real pressure signal and isn't a pen stroke
|
|
1755
|
+
* with a lift-off: it renders at the exact `baseWidth`, uniformly, with no
|
|
1756
|
+
* taper at its start/end (which, for a closed shape, is the same point —
|
|
1757
|
+
* tapering it would visibly pinch just that one corner).
|
|
1758
|
+
*/
|
|
1759
|
+
function ribbonEdges(points, baseWidth, handDrawn = true) {
|
|
1752
1760
|
const pts = points.length === 1 ? [points[0], {
|
|
1753
1761
|
...points[0],
|
|
1754
1762
|
x: points[0].x + baseWidth * .05
|
|
@@ -1763,8 +1771,8 @@ function ribbonEdges(points, baseWidth) {
|
|
|
1763
1771
|
const len = Math.hypot(dx, dy) || 1;
|
|
1764
1772
|
dx /= len;
|
|
1765
1773
|
dy /= len;
|
|
1766
|
-
let width = baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure);
|
|
1767
|
-
if (i === 0 || i === n - 1) width *= END_TAPER;
|
|
1774
|
+
let width = handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * pts[i].pressure) : baseWidth;
|
|
1775
|
+
if (handDrawn && (i === 0 || i === n - 1)) width *= END_TAPER;
|
|
1768
1776
|
const hw = width / 2;
|
|
1769
1777
|
out[i] = {
|
|
1770
1778
|
lx: pts[i].x - dy * hw,
|
|
@@ -2073,7 +2081,7 @@ function strokeToPaths(stroke) {
|
|
|
2073
2081
|
y,
|
|
2074
2082
|
pressure,
|
|
2075
2083
|
...erase > 0 ? { erase } : {}
|
|
2076
|
-
})), stroke.baseWidth);
|
|
2084
|
+
})), stroke.baseWidth, stroke.tool !== "shape");
|
|
2077
2085
|
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])})"` : "";
|
|
2078
2086
|
const baseOpacity = stroke.tool === "highlighter" ? .4 : 1;
|
|
2079
2087
|
const paths = [];
|
|
@@ -36350,8 +36358,8 @@ var INK_Z = .02;
|
|
|
36350
36358
|
var HIGHLIGHT_Z = .016;
|
|
36351
36359
|
/** Ghosts sit under everything so live strokes always read on top. */
|
|
36352
36360
|
var GHOST_Z = .012;
|
|
36353
|
-
function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
36354
|
-
const edges = ribbonEdges(points, baseWidth);
|
|
36361
|
+
function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
|
|
36362
|
+
const edges = ribbonEdges(points, baseWidth, handDrawn);
|
|
36355
36363
|
const n = edges.length;
|
|
36356
36364
|
const positions = new Float32Array(n * 2 * 3);
|
|
36357
36365
|
const uvs = new Float32Array(n * 2 * 2);
|
|
@@ -36396,6 +36404,8 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z) {
|
|
|
36396
36404
|
//#endregion
|
|
36397
36405
|
//#region src/renderer/shapes/strokeRenderer.ts
|
|
36398
36406
|
var strokeZ = (stroke) => stroke.tool === "highlighter" ? HIGHLIGHT_Z : INK_Z;
|
|
36407
|
+
/** Shapes (rect/ellipse/line/arrow/...) are geometric outlines, not pressure-sensitive ink. */
|
|
36408
|
+
var handDrawn = (stroke) => stroke.tool !== "shape";
|
|
36399
36409
|
/**
|
|
36400
36410
|
* Derives ink meshes from the document by subscription — the document never
|
|
36401
36411
|
* knows the renderer exists. Also hosts session-only ghosts (decision Q7)
|
|
@@ -36420,12 +36430,12 @@ var StrokeRenderer = class {
|
|
|
36420
36430
|
this.ghostGroup.visible = visible;
|
|
36421
36431
|
}
|
|
36422
36432
|
/** Leave a smeared residue of an erased span (never persisted). */
|
|
36423
|
-
addGhost(points, color, baseWidth) {
|
|
36433
|
+
addGhost(points, color, baseWidth, handDrawn = true) {
|
|
36424
36434
|
if (points.length < 2) return;
|
|
36425
36435
|
const mesh = new Mesh(buildRibbonGeometry(points.map((p) => ({
|
|
36426
36436
|
...p,
|
|
36427
36437
|
erase: 0
|
|
36428
|
-
})), baseWidth * 1.7, GHOST_Z), this.materials.ghost(color));
|
|
36438
|
+
})), baseWidth * 1.7, GHOST_Z, handDrawn), this.materials.ghost(color));
|
|
36429
36439
|
this.ghostGroup.add(mesh);
|
|
36430
36440
|
}
|
|
36431
36441
|
/**
|
|
@@ -36437,7 +36447,7 @@ var StrokeRenderer = class {
|
|
|
36437
36447
|
if (this.meshes.has(stroke.id) || this.retiredLive.has(stroke.id) || stroke.points.length < 2) return;
|
|
36438
36448
|
this.liveUser.set(stroke.id, userId);
|
|
36439
36449
|
const existing = this.liveMeshes.get(stroke.id);
|
|
36440
|
-
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36450
|
+
const geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36441
36451
|
if (existing) {
|
|
36442
36452
|
existing.geometry.dispose();
|
|
36443
36453
|
existing.geometry = geometry;
|
|
@@ -36497,7 +36507,7 @@ var StrokeRenderer = class {
|
|
|
36497
36507
|
const mesh = this.meshes.get(stroke.id);
|
|
36498
36508
|
if (mesh) {
|
|
36499
36509
|
mesh.geometry.dispose();
|
|
36500
|
-
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke));
|
|
36510
|
+
mesh.geometry = buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke));
|
|
36501
36511
|
}
|
|
36502
36512
|
}
|
|
36503
36513
|
for (const stroke of change.transformed) {
|
|
@@ -36506,7 +36516,7 @@ var StrokeRenderer = class {
|
|
|
36506
36516
|
}
|
|
36507
36517
|
}
|
|
36508
36518
|
create(stroke) {
|
|
36509
|
-
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36519
|
+
const mesh = new Mesh(buildRibbonGeometry(stroke.points, stroke.baseWidth, strokeZ(stroke), handDrawn(stroke)), stroke.tool === "highlighter" ? this.materials.highlight(stroke.color) : this.materials.get(stroke.color));
|
|
36510
36520
|
mesh.matrixAutoUpdate = false;
|
|
36511
36521
|
syncMatrix(mesh, stroke);
|
|
36512
36522
|
this.meshes.set(stroke.id, mesh);
|
|
@@ -44645,9 +44655,10 @@ function hitStroke(doc, index, p, slop) {
|
|
|
44645
44655
|
const scale = m ? avgScale(m) : 1;
|
|
44646
44656
|
let prev = null;
|
|
44647
44657
|
let prevWidth = 0;
|
|
44658
|
+
const handDrawn = stroke.tool !== "shape";
|
|
44648
44659
|
for (const point of stroke.points) {
|
|
44649
44660
|
const world = m ? apply(m, point) : point;
|
|
44650
|
-
const width = stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) * scale;
|
|
44661
|
+
const width = (handDrawn ? stroke.baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : stroke.baseWidth) * scale;
|
|
44651
44662
|
if (prev) {
|
|
44652
44663
|
const reach = Math.max(width, prevWidth) / 2 + slop;
|
|
44653
44664
|
if (distToSegmentSq(p, prev, world) <= reach * reach) {
|
|
@@ -44781,7 +44792,7 @@ var EraserTool = class {
|
|
|
44781
44792
|
...p,
|
|
44782
44793
|
...apply(m, p)
|
|
44783
44794
|
})) : run.points;
|
|
44784
|
-
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1));
|
|
44795
|
+
this.ctx.renderer.addGhost(world, current.color, current.baseWidth * (m ? avgScale(m) : 1), current.tool !== "shape");
|
|
44785
44796
|
}
|
|
44786
44797
|
}
|
|
44787
44798
|
doc.removeStrokes(before.map((s) => s.id));
|
|
@@ -45014,10 +45025,7 @@ function rect(a, b, step) {
|
|
|
45014
45025
|
a
|
|
45015
45026
|
];
|
|
45016
45027
|
const out = [];
|
|
45017
|
-
for (let i = 0; i < corners.length - 1; i++)
|
|
45018
|
-
const edge = sampleSegment(corners[i], corners[i + 1], step);
|
|
45019
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45020
|
-
}
|
|
45028
|
+
for (let i = 0; i < corners.length - 1; i++) out.push(...sampleSegment(corners[i], corners[i + 1], step));
|
|
45021
45029
|
return out;
|
|
45022
45030
|
}
|
|
45023
45031
|
function ellipse(a, b, step) {
|
|
@@ -45088,10 +45096,7 @@ function polygon(sides, a, b, step, startAngle = -Math.PI / 2) {
|
|
|
45088
45096
|
}
|
|
45089
45097
|
vertices.push(vertices[0]);
|
|
45090
45098
|
const out = [];
|
|
45091
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45092
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45093
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45094
|
-
}
|
|
45099
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45095
45100
|
return out;
|
|
45096
45101
|
}
|
|
45097
45102
|
function star(points, a, b, step) {
|
|
@@ -45115,10 +45120,7 @@ function star(points, a, b, step) {
|
|
|
45115
45120
|
}
|
|
45116
45121
|
vertices.push(vertices[0]);
|
|
45117
45122
|
const out = [];
|
|
45118
|
-
for (let i = 0; i < vertices.length - 1; i++)
|
|
45119
|
-
const edge = sampleSegment(vertices[i], vertices[i + 1], step);
|
|
45120
|
-
out.push(...i === 0 ? edge : edge.slice(1));
|
|
45121
|
-
}
|
|
45123
|
+
for (let i = 0; i < vertices.length - 1; i++) out.push(...sampleSegment(vertices[i], vertices[i + 1], step));
|
|
45122
45124
|
return out;
|
|
45123
45125
|
}
|
|
45124
45126
|
function heart(a, b, step) {
|
|
@@ -45195,6 +45197,7 @@ var ShapeTool = class {
|
|
|
45195
45197
|
id: crypto.randomUUID(),
|
|
45196
45198
|
color: this.ctx.inkColor(),
|
|
45197
45199
|
baseWidth: this.ctx.shapeStrokeWidth(),
|
|
45200
|
+
tool: "shape",
|
|
45198
45201
|
points
|
|
45199
45202
|
}));
|
|
45200
45203
|
this.ctx.clusters.assign(strokes[0]);
|
|
@@ -45228,7 +45231,7 @@ var ShapeTool = class {
|
|
|
45228
45231
|
this.previews[i].visible = polyline !== void 0;
|
|
45229
45232
|
if (polyline) {
|
|
45230
45233
|
this.previews[i].geometry.dispose();
|
|
45231
|
-
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth());
|
|
45234
|
+
this.previews[i].geometry = buildRibbonGeometry(polyline, this.ctx.shapeStrokeWidth(), void 0, false);
|
|
45232
45235
|
}
|
|
45233
45236
|
}
|
|
45234
45237
|
}
|
|
@@ -49389,6 +49392,7 @@ var scrawlThemePresets = Object.freeze({
|
|
|
49389
49392
|
text: "#1c1c1a",
|
|
49390
49393
|
textMuted: "#62625c",
|
|
49391
49394
|
edge: "#d4d4cc",
|
|
49395
|
+
primary: "#1856a3",
|
|
49392
49396
|
focus: "#1856a3",
|
|
49393
49397
|
selection: "#d9e8f7",
|
|
49394
49398
|
danger: "#a3302b",
|
|
@@ -49421,6 +49425,7 @@ var scrawlThemePresets = Object.freeze({
|
|
|
49421
49425
|
text: "#f4f4ee",
|
|
49422
49426
|
textMuted: "#b7b8ae",
|
|
49423
49427
|
edge: "#484a42",
|
|
49428
|
+
primary: "#9dc8ef",
|
|
49424
49429
|
focus: "#9dc8ef",
|
|
49425
49430
|
selection: "#314b63",
|
|
49426
49431
|
danger: "#ffaaa3",
|
|
@@ -49454,6 +49459,7 @@ var variableNames = {
|
|
|
49454
49459
|
text: "--scrawl-color-text",
|
|
49455
49460
|
textMuted: "--scrawl-color-text-muted",
|
|
49456
49461
|
edge: "--scrawl-color-edge",
|
|
49462
|
+
primary: "--scrawl-color-primary",
|
|
49457
49463
|
focus: "--scrawl-color-focus",
|
|
49458
49464
|
selection: "--scrawl-color-selection",
|
|
49459
49465
|
danger: "--scrawl-color-danger",
|
|
@@ -49486,6 +49492,7 @@ var colorTokens = /* @__PURE__ */ new Set([
|
|
|
49486
49492
|
"text",
|
|
49487
49493
|
"textMuted",
|
|
49488
49494
|
"edge",
|
|
49495
|
+
"primary",
|
|
49489
49496
|
"focus",
|
|
49490
49497
|
"selection",
|
|
49491
49498
|
"danger",
|
|
@@ -50127,7 +50134,7 @@ var INKS = [
|
|
|
50127
50134
|
["Forest", "#315d46"],
|
|
50128
50135
|
["Oxblood", "#773f3b"]
|
|
50129
50136
|
];
|
|
50130
|
-
function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots }) {
|
|
50137
|
+
function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots, icons }) {
|
|
50131
50138
|
const [panel, setPanel] = useState(null);
|
|
50132
50139
|
const [query, setQuery] = useState("");
|
|
50133
50140
|
const [announcement, setAnnouncement] = useState("Board controls ready");
|
|
@@ -50281,7 +50288,10 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
50281
50288
|
onClick: () => chooseTool(tool),
|
|
50282
50289
|
title: `${label} (${key})`,
|
|
50283
50290
|
type: "button",
|
|
50284
|
-
children:
|
|
50291
|
+
children: icons?.[tool] ? /* @__PURE__ */ jsx("span", {
|
|
50292
|
+
"aria-hidden": "true",
|
|
50293
|
+
children: icons[tool]
|
|
50294
|
+
}) : label
|
|
50285
50295
|
}, tool)), /* @__PURE__ */ jsxs("label", {
|
|
50286
50296
|
className: "scrawl-board__tool-select",
|
|
50287
50297
|
children: [/* @__PURE__ */ jsx("span", { children: "More tools" }), /* @__PURE__ */ jsxs("select", {
|
|
@@ -50744,7 +50754,7 @@ function ScrawlProvider({ controller, children, preset = "light", theme, portalC
|
|
|
50744
50754
|
})
|
|
50745
50755
|
});
|
|
50746
50756
|
}
|
|
50747
|
-
function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, ...options }) {
|
|
50757
|
+
function Scrawl({ children, preset, theme, portalContainer, className, style, onReady, onError, onThemeDiagnostic, canvas: suppliedCanvas, icons, ...options }) {
|
|
50748
50758
|
const [runtime, setRuntime] = useState(null);
|
|
50749
50759
|
const runtimeRef = useRef(null);
|
|
50750
50760
|
const ownedCanvasRef = useRef(null);
|
|
@@ -50840,7 +50850,7 @@ function Scrawl({ children, preset, theme, portalContainer, className, style, on
|
|
|
50840
50850
|
onThemeDiagnostic,
|
|
50841
50851
|
className,
|
|
50842
50852
|
style,
|
|
50843
|
-
children: [runtime.canvas && /* @__PURE__ */ jsx(ScrawlCanvas, { element: runtime.canvas }), children ?? /* @__PURE__ */ jsx(ScrawlDefaultUI, {})]
|
|
50853
|
+
children: [runtime.canvas && /* @__PURE__ */ jsx(ScrawlCanvas, { element: runtime.canvas }), children ?? /* @__PURE__ */ jsx(ScrawlDefaultUI, { icons })]
|
|
50844
50854
|
});
|
|
50845
50855
|
}
|
|
50846
50856
|
function ScrawlCanvas({ element, className, style, "aria-label": ariaLabel = "Scrawl Board" }) {
|
|
@@ -50876,10 +50886,11 @@ function ScrawlCanvas({ element, className, style, "aria-label": ariaLabel = "Sc
|
|
|
50876
50886
|
style
|
|
50877
50887
|
});
|
|
50878
50888
|
}
|
|
50879
|
-
function ScrawlDefaultUI({ className, style, regions, slots }) {
|
|
50889
|
+
function ScrawlDefaultUI({ className, style, regions, slots, icons }) {
|
|
50880
50890
|
return /* @__PURE__ */ jsx(DefaultBoardChrome, {
|
|
50881
50891
|
className,
|
|
50882
50892
|
controller: useScrawlController(),
|
|
50893
|
+
icons,
|
|
50883
50894
|
regions,
|
|
50884
50895
|
renderPortal: (children) => /* @__PURE__ */ jsx(ScrawlPortal, { children }),
|
|
50885
50896
|
slots,
|
package/dist/styles.css
CHANGED
|
@@ -151,7 +151,8 @@
|
|
|
151
151
|
|
|
152
152
|
[data-scrawl-root] [data-scrawl-default-ui] button[aria-pressed="true"],
|
|
153
153
|
[data-scrawl-portal] .scrawl-board__panel button[aria-pressed="true"] {
|
|
154
|
-
background: var(--scrawl-color-
|
|
154
|
+
background: color-mix(in srgb, var(--scrawl-color-primary), var(--scrawl-color-surface) 82%);
|
|
155
|
+
color: var(--scrawl-color-primary);
|
|
155
156
|
font-weight: var(--scrawl-font-weight-strong);
|
|
156
157
|
}
|
|
157
158
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrawl-board/board",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
4
|
"description": "The Scrawl Board SDK: an embeddable, collaborative infinite-canvas whiteboard for React and vanilla JS/TS apps.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|