@scrawl-board/board 0.1.0-beta.5 → 0.1.0-beta.6

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.js CHANGED
@@ -36336,13 +36336,26 @@ var INK_Z = .02;
36336
36336
  var HIGHLIGHT_Z = .016;
36337
36337
  /** Ghosts sit under everything so live strokes always read on top. */
36338
36338
  var GHOST_Z = .012;
36339
+ /**
36340
+ * A miter join at each centerline point — averaging the incoming and
36341
+ * outgoing segment directions into one width offset — under-covers the
36342
+ * outside of a sharp turn: the two segments' edges diverge there, leaving a
36343
+ * real geometric gap (the board shows through, not just a shader artifact).
36344
+ * Barely visible on opaque ink, but glaring on the highlighter's lower,
36345
+ * multiply-blended opacity, which is why this reads as "goes transparent
36346
+ * at corners" specifically there even though the underlying strip geometry
36347
+ * is shared with ink. Below this turn angle a miter join is a fine
36348
+ * approximation and not worth the extra geometry.
36349
+ */
36350
+ var JOIN_ANGLE_THRESHOLD = .35;
36351
+ var JOIN_SEGMENTS = 10;
36339
36352
  function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
36340
36353
  const edges = ribbonEdges(points, baseWidth, handDrawn);
36341
36354
  const n = edges.length;
36342
- const positions = new Float32Array(n * 2 * 3);
36343
- const uvs = new Float32Array(n * 2 * 2);
36344
- const alphas = new Float32Array(n * 2);
36345
- const indices = new Uint32Array((n - 1) * 6);
36355
+ const positions = new Array(n * 2 * 3);
36356
+ const uvs = new Array(n * 2 * 2);
36357
+ const alphas = new Array(n * 2);
36358
+ const indices = new Array((n - 1) * 6);
36346
36359
  for (let i = 0; i < n; i++) {
36347
36360
  const e = edges[i];
36348
36361
  const vi = i * 6;
@@ -36371,11 +36384,45 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
36371
36384
  indices[ii + 5] = a + 2;
36372
36385
  }
36373
36386
  }
36387
+ for (let i = 1; i < n - 1; i++) {
36388
+ const prev = points[Math.max(0, i - 1)];
36389
+ const point = points[i];
36390
+ const next = points[Math.min(n - 1, i + 1)];
36391
+ const inX = point.x - prev.x, inY = point.y - prev.y;
36392
+ const outX = next.x - point.x, outY = next.y - point.y;
36393
+ const inLen = Math.hypot(inX, inY) || 1;
36394
+ const outLen = Math.hypot(outX, outY) || 1;
36395
+ const inDx = inX / inLen, inDy = inY / inLen;
36396
+ const outDx = outX / outLen, outDy = outY / outLen;
36397
+ const cos = inDx * outDx + inDy * outDy;
36398
+ if (Math.acos(Math.min(1, Math.max(-1, cos))) < JOIN_ANGLE_THRESHOLD) continue;
36399
+ const radius = (handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : baseWidth) / 2;
36400
+ const t = i / (n - 1);
36401
+ const alpha = edges[i].alpha;
36402
+ const side = inDx * outDy - inDy * outDx > 0 ? -1 : 1;
36403
+ const inEdgeX = point.x + side * -inDy * radius, inEdgeY = point.y + side * inDx * radius;
36404
+ const outEdgeX = point.x + side * -outDy * radius, outEdgeY = point.y + side * outDx * radius;
36405
+ const angle1 = Math.atan2(inEdgeY - point.y, inEdgeX - point.x);
36406
+ let sweep = Math.atan2(outEdgeY - point.y, outEdgeX - point.x) - angle1;
36407
+ while (sweep > Math.PI) sweep -= Math.PI * 2;
36408
+ while (sweep < -Math.PI) sweep += Math.PI * 2;
36409
+ const center = positions.length / 3;
36410
+ positions.push(point.x, point.y, z);
36411
+ uvs.push(t, .5);
36412
+ alphas.push(alpha);
36413
+ for (let s = 0; s <= JOIN_SEGMENTS; s++) {
36414
+ const theta = angle1 + sweep * (s / JOIN_SEGMENTS);
36415
+ positions.push(point.x + Math.cos(theta) * radius, point.y + Math.sin(theta) * radius, z);
36416
+ uvs.push(t, .5);
36417
+ alphas.push(alpha);
36418
+ if (s > 0) indices.push(center, center + s, center + s + 1);
36419
+ }
36420
+ }
36374
36421
  const geometry = new BufferGeometry();
36375
- geometry.setAttribute("position", new BufferAttribute(positions, 3));
36376
- geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
36377
- geometry.setAttribute("aAlpha", new BufferAttribute(alphas, 1));
36378
- geometry.setIndex(new BufferAttribute(indices, 1));
36422
+ geometry.setAttribute("position", new BufferAttribute(new Float32Array(positions), 3));
36423
+ geometry.setAttribute("uv", new BufferAttribute(new Float32Array(uvs), 2));
36424
+ geometry.setAttribute("aAlpha", new BufferAttribute(new Float32Array(alphas), 1));
36425
+ geometry.setIndex(indices);
36379
36426
  geometry.computeBoundingSphere();
36380
36427
  return geometry;
36381
36428
  }
@@ -36517,9 +36564,9 @@ function syncMatrix(mesh, stroke) {
36517
36564
  }
36518
36565
  //#endregion
36519
36566
  //#region src/renderer/shapes/markerProp.ts
36520
- var BODY_LENGTH = 11;
36521
- var BODY_RADIUS = .85;
36522
- var TIP_LENGTH = 1.6;
36567
+ var BODY_LENGTH = 7;
36568
+ var BODY_RADIUS = .55;
36569
+ var TIP_LENGTH = 1.05;
36523
36570
  var BASE_TILT = .42;
36524
36571
  var VELOCITY_TILT = .22;
36525
36572
  var MAX_EXTRA_TILT = .35;
@@ -36550,12 +36597,12 @@ var MarkerProp = class {
36550
36597
  const tip = new Mesh(new ConeGeometry(BODY_RADIUS * .55, TIP_LENGTH, 14), tipMaterial);
36551
36598
  tip.rotation.x = Math.PI;
36552
36599
  tip.position.y = TIP_LENGTH / 2;
36553
- this.body.position.y = 7.1;
36600
+ this.body.position.y = 4.55;
36554
36601
  const pen = new Group();
36555
36602
  pen.add(this.body, tip);
36556
36603
  pen.rotation.x = Math.PI / 2;
36557
36604
  this.group.add(pen);
36558
- this.shadow = new Mesh(new CircleGeometry(1.4, 24), new MeshBasicMaterial({
36605
+ this.shadow = new Mesh(new CircleGeometry(.9, 24), new MeshBasicMaterial({
36559
36606
  color: 0,
36560
36607
  transparent: true,
36561
36608
  opacity: .13,
package/dist/index.js CHANGED
@@ -36416,13 +36416,26 @@ var INK_Z = .02;
36416
36416
  var HIGHLIGHT_Z = .016;
36417
36417
  /** Ghosts sit under everything so live strokes always read on top. */
36418
36418
  var GHOST_Z = .012;
36419
+ /**
36420
+ * A miter join at each centerline point — averaging the incoming and
36421
+ * outgoing segment directions into one width offset — under-covers the
36422
+ * outside of a sharp turn: the two segments' edges diverge there, leaving a
36423
+ * real geometric gap (the board shows through, not just a shader artifact).
36424
+ * Barely visible on opaque ink, but glaring on the highlighter's lower,
36425
+ * multiply-blended opacity, which is why this reads as "goes transparent
36426
+ * at corners" specifically there even though the underlying strip geometry
36427
+ * is shared with ink. Below this turn angle a miter join is a fine
36428
+ * approximation and not worth the extra geometry.
36429
+ */
36430
+ var JOIN_ANGLE_THRESHOLD = .35;
36431
+ var JOIN_SEGMENTS = 10;
36419
36432
  function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
36420
36433
  const edges = ribbonEdges(points, baseWidth, handDrawn);
36421
36434
  const n = edges.length;
36422
- const positions = new Float32Array(n * 2 * 3);
36423
- const uvs = new Float32Array(n * 2 * 2);
36424
- const alphas = new Float32Array(n * 2);
36425
- const indices = new Uint32Array((n - 1) * 6);
36435
+ const positions = new Array(n * 2 * 3);
36436
+ const uvs = new Array(n * 2 * 2);
36437
+ const alphas = new Array(n * 2);
36438
+ const indices = new Array((n - 1) * 6);
36426
36439
  for (let i = 0; i < n; i++) {
36427
36440
  const e = edges[i];
36428
36441
  const vi = i * 6;
@@ -36451,11 +36464,45 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
36451
36464
  indices[ii + 5] = a + 2;
36452
36465
  }
36453
36466
  }
36467
+ for (let i = 1; i < n - 1; i++) {
36468
+ const prev = points[Math.max(0, i - 1)];
36469
+ const point = points[i];
36470
+ const next = points[Math.min(n - 1, i + 1)];
36471
+ const inX = point.x - prev.x, inY = point.y - prev.y;
36472
+ const outX = next.x - point.x, outY = next.y - point.y;
36473
+ const inLen = Math.hypot(inX, inY) || 1;
36474
+ const outLen = Math.hypot(outX, outY) || 1;
36475
+ const inDx = inX / inLen, inDy = inY / inLen;
36476
+ const outDx = outX / outLen, outDy = outY / outLen;
36477
+ const cos = inDx * outDx + inDy * outDy;
36478
+ if (Math.acos(Math.min(1, Math.max(-1, cos))) < JOIN_ANGLE_THRESHOLD) continue;
36479
+ const radius = (handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : baseWidth) / 2;
36480
+ const t = i / (n - 1);
36481
+ const alpha = edges[i].alpha;
36482
+ const side = inDx * outDy - inDy * outDx > 0 ? -1 : 1;
36483
+ const inEdgeX = point.x + side * -inDy * radius, inEdgeY = point.y + side * inDx * radius;
36484
+ const outEdgeX = point.x + side * -outDy * radius, outEdgeY = point.y + side * outDx * radius;
36485
+ const angle1 = Math.atan2(inEdgeY - point.y, inEdgeX - point.x);
36486
+ let sweep = Math.atan2(outEdgeY - point.y, outEdgeX - point.x) - angle1;
36487
+ while (sweep > Math.PI) sweep -= Math.PI * 2;
36488
+ while (sweep < -Math.PI) sweep += Math.PI * 2;
36489
+ const center = positions.length / 3;
36490
+ positions.push(point.x, point.y, z);
36491
+ uvs.push(t, .5);
36492
+ alphas.push(alpha);
36493
+ for (let s = 0; s <= JOIN_SEGMENTS; s++) {
36494
+ const theta = angle1 + sweep * (s / JOIN_SEGMENTS);
36495
+ positions.push(point.x + Math.cos(theta) * radius, point.y + Math.sin(theta) * radius, z);
36496
+ uvs.push(t, .5);
36497
+ alphas.push(alpha);
36498
+ if (s > 0) indices.push(center, center + s, center + s + 1);
36499
+ }
36500
+ }
36454
36501
  const geometry = new BufferGeometry();
36455
- geometry.setAttribute("position", new BufferAttribute(positions, 3));
36456
- geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
36457
- geometry.setAttribute("aAlpha", new BufferAttribute(alphas, 1));
36458
- geometry.setIndex(new BufferAttribute(indices, 1));
36502
+ geometry.setAttribute("position", new BufferAttribute(new Float32Array(positions), 3));
36503
+ geometry.setAttribute("uv", new BufferAttribute(new Float32Array(uvs), 2));
36504
+ geometry.setAttribute("aAlpha", new BufferAttribute(new Float32Array(alphas), 1));
36505
+ geometry.setIndex(indices);
36459
36506
  geometry.computeBoundingSphere();
36460
36507
  return geometry;
36461
36508
  }
@@ -36597,9 +36644,9 @@ function syncMatrix(mesh, stroke) {
36597
36644
  }
36598
36645
  //#endregion
36599
36646
  //#region src/renderer/shapes/markerProp.ts
36600
- var BODY_LENGTH = 11;
36601
- var BODY_RADIUS = .85;
36602
- var TIP_LENGTH = 1.6;
36647
+ var BODY_LENGTH = 7;
36648
+ var BODY_RADIUS = .55;
36649
+ var TIP_LENGTH = 1.05;
36603
36650
  var BASE_TILT = .42;
36604
36651
  var VELOCITY_TILT = .22;
36605
36652
  var MAX_EXTRA_TILT = .35;
@@ -36630,12 +36677,12 @@ var MarkerProp = class {
36630
36677
  const tip = new Mesh(new ConeGeometry(BODY_RADIUS * .55, TIP_LENGTH, 14), tipMaterial);
36631
36678
  tip.rotation.x = Math.PI;
36632
36679
  tip.position.y = TIP_LENGTH / 2;
36633
- this.body.position.y = 7.1;
36680
+ this.body.position.y = 4.55;
36634
36681
  const pen = new Group();
36635
36682
  pen.add(this.body, tip);
36636
36683
  pen.rotation.x = Math.PI / 2;
36637
36684
  this.group.add(pen);
36638
- this.shadow = new Mesh(new CircleGeometry(1.4, 24), new MeshBasicMaterial({
36685
+ this.shadow = new Mesh(new CircleGeometry(.9, 24), new MeshBasicMaterial({
36639
36686
  color: 0,
36640
36687
  transparent: true,
36641
36688
  opacity: .13,
@@ -49896,6 +49943,24 @@ function InlineEditors({ controller, renderPortal }) {
49896
49943
  };
49897
49944
  useEffect(() => controller.on("edit-request", (event) => {
49898
49945
  const offset = rootOffset();
49946
+ if (event.kind === "text" && event.id === null) {
49947
+ const newId = controller.content.add({
49948
+ type: "text",
49949
+ x: event.boardX,
49950
+ y: event.boardY,
49951
+ text: "",
49952
+ color: event.color,
49953
+ fontSize: TEXT_DEFAULT_SIZE
49954
+ });
49955
+ controller.content.select([newId]);
49956
+ setEditor({
49957
+ ...event,
49958
+ id: newId,
49959
+ left: event.screenRect.left - offset.left,
49960
+ top: event.screenRect.top - offset.top
49961
+ });
49962
+ return;
49963
+ }
49899
49964
  setEditor({
49900
49965
  ...event,
49901
49966
  left: event.screenRect.left - offset.left,
@@ -49907,22 +49972,11 @@ function InlineEditors({ controller, renderPortal }) {
49907
49972
  setEditor(null);
49908
49973
  };
49909
49974
  const commitText = () => {
49910
- if (editor?.kind === "text") {
49975
+ if (editor?.kind === "text" && editor.id !== null) {
49911
49976
  const text = editor.text;
49912
- if (editor.id === null) {
49913
- if (text.trim()) {
49914
- const newId = controller.content.add({
49915
- type: "text",
49916
- x: editor.boardX,
49917
- y: editor.boardY,
49918
- text,
49919
- color: editor.color,
49920
- fontSize: TEXT_DEFAULT_SIZE
49921
- });
49922
- controller.content.select([newId]);
49923
- }
49924
- } else if (!text.trim()) controller.content.remove([editor.id]);
49925
- else controller.content.update(editor.id, { text });
49977
+ const existing = controller.query.get(editor.id);
49978
+ if (!text.trim()) controller.content.remove([editor.id]);
49979
+ else if (existing?.type !== "text" || existing.text !== text) controller.content.update(editor.id, { text });
49926
49980
  }
49927
49981
  setEditor(null);
49928
49982
  };
package/dist/react.js CHANGED
@@ -36378,13 +36378,26 @@ var INK_Z = .02;
36378
36378
  var HIGHLIGHT_Z = .016;
36379
36379
  /** Ghosts sit under everything so live strokes always read on top. */
36380
36380
  var GHOST_Z = .012;
36381
+ /**
36382
+ * A miter join at each centerline point — averaging the incoming and
36383
+ * outgoing segment directions into one width offset — under-covers the
36384
+ * outside of a sharp turn: the two segments' edges diverge there, leaving a
36385
+ * real geometric gap (the board shows through, not just a shader artifact).
36386
+ * Barely visible on opaque ink, but glaring on the highlighter's lower,
36387
+ * multiply-blended opacity, which is why this reads as "goes transparent
36388
+ * at corners" specifically there even though the underlying strip geometry
36389
+ * is shared with ink. Below this turn angle a miter join is a fine
36390
+ * approximation and not worth the extra geometry.
36391
+ */
36392
+ var JOIN_ANGLE_THRESHOLD = .35;
36393
+ var JOIN_SEGMENTS = 10;
36381
36394
  function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
36382
36395
  const edges = ribbonEdges(points, baseWidth, handDrawn);
36383
36396
  const n = edges.length;
36384
- const positions = new Float32Array(n * 2 * 3);
36385
- const uvs = new Float32Array(n * 2 * 2);
36386
- const alphas = new Float32Array(n * 2);
36387
- const indices = new Uint32Array((n - 1) * 6);
36397
+ const positions = new Array(n * 2 * 3);
36398
+ const uvs = new Array(n * 2 * 2);
36399
+ const alphas = new Array(n * 2);
36400
+ const indices = new Array((n - 1) * 6);
36388
36401
  for (let i = 0; i < n; i++) {
36389
36402
  const e = edges[i];
36390
36403
  const vi = i * 6;
@@ -36413,11 +36426,45 @@ function buildRibbonGeometry(points, baseWidth, z = INK_Z, handDrawn = true) {
36413
36426
  indices[ii + 5] = a + 2;
36414
36427
  }
36415
36428
  }
36429
+ for (let i = 1; i < n - 1; i++) {
36430
+ const prev = points[Math.max(0, i - 1)];
36431
+ const point = points[i];
36432
+ const next = points[Math.min(n - 1, i + 1)];
36433
+ const inX = point.x - prev.x, inY = point.y - prev.y;
36434
+ const outX = next.x - point.x, outY = next.y - point.y;
36435
+ const inLen = Math.hypot(inX, inY) || 1;
36436
+ const outLen = Math.hypot(outX, outY) || 1;
36437
+ const inDx = inX / inLen, inDy = inY / inLen;
36438
+ const outDx = outX / outLen, outDy = outY / outLen;
36439
+ const cos = inDx * outDx + inDy * outDy;
36440
+ if (Math.acos(Math.min(1, Math.max(-1, cos))) < JOIN_ANGLE_THRESHOLD) continue;
36441
+ const radius = (handDrawn ? baseWidth * (MIN_WIDTH_FACTOR + (1 - MIN_WIDTH_FACTOR) * point.pressure) : baseWidth) / 2;
36442
+ const t = i / (n - 1);
36443
+ const alpha = edges[i].alpha;
36444
+ const side = inDx * outDy - inDy * outDx > 0 ? -1 : 1;
36445
+ const inEdgeX = point.x + side * -inDy * radius, inEdgeY = point.y + side * inDx * radius;
36446
+ const outEdgeX = point.x + side * -outDy * radius, outEdgeY = point.y + side * outDx * radius;
36447
+ const angle1 = Math.atan2(inEdgeY - point.y, inEdgeX - point.x);
36448
+ let sweep = Math.atan2(outEdgeY - point.y, outEdgeX - point.x) - angle1;
36449
+ while (sweep > Math.PI) sweep -= Math.PI * 2;
36450
+ while (sweep < -Math.PI) sweep += Math.PI * 2;
36451
+ const center = positions.length / 3;
36452
+ positions.push(point.x, point.y, z);
36453
+ uvs.push(t, .5);
36454
+ alphas.push(alpha);
36455
+ for (let s = 0; s <= JOIN_SEGMENTS; s++) {
36456
+ const theta = angle1 + sweep * (s / JOIN_SEGMENTS);
36457
+ positions.push(point.x + Math.cos(theta) * radius, point.y + Math.sin(theta) * radius, z);
36458
+ uvs.push(t, .5);
36459
+ alphas.push(alpha);
36460
+ if (s > 0) indices.push(center, center + s, center + s + 1);
36461
+ }
36462
+ }
36416
36463
  const geometry = new BufferGeometry();
36417
- geometry.setAttribute("position", new BufferAttribute(positions, 3));
36418
- geometry.setAttribute("uv", new BufferAttribute(uvs, 2));
36419
- geometry.setAttribute("aAlpha", new BufferAttribute(alphas, 1));
36420
- geometry.setIndex(new BufferAttribute(indices, 1));
36464
+ geometry.setAttribute("position", new BufferAttribute(new Float32Array(positions), 3));
36465
+ geometry.setAttribute("uv", new BufferAttribute(new Float32Array(uvs), 2));
36466
+ geometry.setAttribute("aAlpha", new BufferAttribute(new Float32Array(alphas), 1));
36467
+ geometry.setIndex(indices);
36421
36468
  geometry.computeBoundingSphere();
36422
36469
  return geometry;
36423
36470
  }
@@ -36559,9 +36606,9 @@ function syncMatrix(mesh, stroke) {
36559
36606
  }
36560
36607
  //#endregion
36561
36608
  //#region src/renderer/shapes/markerProp.ts
36562
- var BODY_LENGTH = 11;
36563
- var BODY_RADIUS = .85;
36564
- var TIP_LENGTH = 1.6;
36609
+ var BODY_LENGTH = 7;
36610
+ var BODY_RADIUS = .55;
36611
+ var TIP_LENGTH = 1.05;
36565
36612
  var BASE_TILT = .42;
36566
36613
  var VELOCITY_TILT = .22;
36567
36614
  var MAX_EXTRA_TILT = .35;
@@ -36592,12 +36639,12 @@ var MarkerProp = class {
36592
36639
  const tip = new Mesh(new ConeGeometry(BODY_RADIUS * .55, TIP_LENGTH, 14), tipMaterial);
36593
36640
  tip.rotation.x = Math.PI;
36594
36641
  tip.position.y = TIP_LENGTH / 2;
36595
- this.body.position.y = 7.1;
36642
+ this.body.position.y = 4.55;
36596
36643
  const pen = new Group();
36597
36644
  pen.add(this.body, tip);
36598
36645
  pen.rotation.x = Math.PI / 2;
36599
36646
  this.group.add(pen);
36600
- this.shadow = new Mesh(new CircleGeometry(1.4, 24), new MeshBasicMaterial({
36647
+ this.shadow = new Mesh(new CircleGeometry(.9, 24), new MeshBasicMaterial({
36601
36648
  color: 0,
36602
36649
  transparent: true,
36603
36650
  opacity: .13,
@@ -49858,6 +49905,24 @@ function InlineEditors({ controller, renderPortal }) {
49858
49905
  };
49859
49906
  useEffect(() => controller.on("edit-request", (event) => {
49860
49907
  const offset = rootOffset();
49908
+ if (event.kind === "text" && event.id === null) {
49909
+ const newId = controller.content.add({
49910
+ type: "text",
49911
+ x: event.boardX,
49912
+ y: event.boardY,
49913
+ text: "",
49914
+ color: event.color,
49915
+ fontSize: TEXT_DEFAULT_SIZE
49916
+ });
49917
+ controller.content.select([newId]);
49918
+ setEditor({
49919
+ ...event,
49920
+ id: newId,
49921
+ left: event.screenRect.left - offset.left,
49922
+ top: event.screenRect.top - offset.top
49923
+ });
49924
+ return;
49925
+ }
49861
49926
  setEditor({
49862
49927
  ...event,
49863
49928
  left: event.screenRect.left - offset.left,
@@ -49869,22 +49934,11 @@ function InlineEditors({ controller, renderPortal }) {
49869
49934
  setEditor(null);
49870
49935
  };
49871
49936
  const commitText = () => {
49872
- if (editor?.kind === "text") {
49937
+ if (editor?.kind === "text" && editor.id !== null) {
49873
49938
  const text = editor.text;
49874
- if (editor.id === null) {
49875
- if (text.trim()) {
49876
- const newId = controller.content.add({
49877
- type: "text",
49878
- x: editor.boardX,
49879
- y: editor.boardY,
49880
- text,
49881
- color: editor.color,
49882
- fontSize: TEXT_DEFAULT_SIZE
49883
- });
49884
- controller.content.select([newId]);
49885
- }
49886
- } else if (!text.trim()) controller.content.remove([editor.id]);
49887
- else controller.content.update(editor.id, { text });
49939
+ const existing = controller.query.get(editor.id);
49940
+ if (!text.trim()) controller.content.remove([editor.id]);
49941
+ else if (existing?.type !== "text" || existing.text !== text) controller.content.update(editor.id, { text });
49888
49942
  }
49889
49943
  setEditor(null);
49890
49944
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scrawl-board/board",
3
- "version": "0.1.0-beta.5",
3
+ "version": "0.1.0-beta.6",
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": {