canvu-react 0.3.38 → 0.3.40

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.
Files changed (45) hide show
  1. package/dist/{asset-hydration-EtEuBwb7.d.cts → asset-hydration-B7yMDQE-.d.cts} +2 -2
  2. package/dist/{asset-hydration-DrTOgDdd.d.ts → asset-hydration-CbwQVAwh.d.ts} +2 -2
  3. package/dist/{camera-Di5R_Rwl.d.cts → camera-CVVG7z56.d.cts} +1 -1
  4. package/dist/{camera-AoTwBSoE.d.ts → camera-CoRYN_IV.d.ts} +1 -1
  5. package/dist/chatbot.d.cts +4 -4
  6. package/dist/chatbot.d.ts +4 -4
  7. package/dist/index.cjs +164 -15
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +6 -6
  10. package/dist/index.d.ts +6 -6
  11. package/dist/index.js +159 -16
  12. package/dist/index.js.map +1 -1
  13. package/dist/native.cjs +57 -14
  14. package/dist/native.cjs.map +1 -1
  15. package/dist/native.d.cts +2 -2
  16. package/dist/native.d.ts +2 -2
  17. package/dist/native.js +57 -14
  18. package/dist/native.js.map +1 -1
  19. package/dist/react.cjs +731 -258
  20. package/dist/react.cjs.map +1 -1
  21. package/dist/react.d.cts +10 -10
  22. package/dist/react.d.ts +10 -10
  23. package/dist/react.js +731 -258
  24. package/dist/react.js.map +1 -1
  25. package/dist/realtime.cjs +36 -0
  26. package/dist/realtime.cjs.map +1 -1
  27. package/dist/realtime.d.cts +6 -6
  28. package/dist/realtime.d.ts +6 -6
  29. package/dist/realtime.js +36 -0
  30. package/dist/realtime.js.map +1 -1
  31. package/dist/{shape-builders-CsbSRZnQ.d.cts → shape-builders-BAWu-PxX.d.cts} +46 -4
  32. package/dist/{shape-builders-CsSXKCcs.d.ts → shape-builders-ClKv9tz9.d.ts} +46 -4
  33. package/dist/tldraw.cjs +189 -78
  34. package/dist/tldraw.cjs.map +1 -1
  35. package/dist/tldraw.d.cts +1 -1
  36. package/dist/tldraw.d.ts +1 -1
  37. package/dist/tldraw.js +189 -78
  38. package/dist/tldraw.js.map +1 -1
  39. package/dist/{types-DWGk2_GZ.d.cts → types-BC9Xgfu6.d.cts} +20 -6
  40. package/dist/{types-Bnq2HtHQ.d.cts → types-BCCvY6ie.d.cts} +2 -0
  41. package/dist/{types-Bnq2HtHQ.d.ts → types-BCCvY6ie.d.ts} +2 -0
  42. package/dist/{types-B2Na677H.d.cts → types-BUPc2Zgw.d.cts} +1 -1
  43. package/dist/{types-zmUah-vP.d.ts → types-CYtq9Pr9.d.ts} +1 -1
  44. package/dist/{types-B6PAYKzx.d.ts → types-DlSVGX0w.d.ts} +20 -6
  45. package/package.json +1 -1
package/dist/react.js CHANGED
@@ -84,6 +84,12 @@ function lineHeightFor(fontSize) {
84
84
  function firstLineBaselineY(fontSize) {
85
85
  return fontSize * FIRST_LINE_BASELINE_RATIO;
86
86
  }
87
+ function textBaselineYFor(fontSize) {
88
+ return firstLineBaselineY(fontSize);
89
+ }
90
+ function textLineHeightFor(fontSize) {
91
+ return lineHeightFor(fontSize);
92
+ }
87
93
  function measureTextBoundsLocal(content, fontSize = DEFAULT_TEXT_FONT_SIZE) {
88
94
  const cacheKey = textMeasureCacheKey(content, fontSize);
89
95
  const cached = textMeasureCache.get(cacheKey);
@@ -97,7 +103,7 @@ function measureTextBoundsLocal(content, fontSize = DEFAULT_TEXT_FONT_SIZE) {
97
103
  let maxInnerW = 0;
98
104
  const ctx = getSharedMeasureContext();
99
105
  if (ctx) {
100
- ctx.font = `${fontSize}px system-ui, sans-serif`;
106
+ ctx.font = `${fontSize}px ${TEXT_FONT_FAMILY}`;
101
107
  for (const line of lines) {
102
108
  const toMeasure = trimmed.length === 0 ? PLACEHOLDER : line.length === 0 ? " " : line;
103
109
  maxInnerW = Math.max(maxInnerW, ctx.measureText(toMeasure).width);
@@ -113,22 +119,22 @@ function measureTextBoundsLocal(content, fontSize = DEFAULT_TEXT_FONT_SIZE) {
113
119
  const width = Math.max(minW, TEXT_PAD_X * 2 + maxInnerW);
114
120
  const height = Math.max(
115
121
  MIN_TEXT_BOX_H,
116
- baselineY + (lines.length - 1) * lh + Math.max(8, fontSize * 0.35)
122
+ baselineY + (lines.length - 1) * lh + Math.max(4, fontSize * BOTTOM_PAD_RATIO)
117
123
  );
118
124
  const measured = { width, height };
119
125
  cacheMeasuredBounds(cacheKey, measured);
120
126
  return measured;
121
127
  }
122
- function buildTextSvg(content, _width, _height, fillColor = "#2563eb", fontSize = DEFAULT_TEXT_FONT_SIZE) {
128
+ function buildTextSvg(content, _width, _height, fillColor = "#1d1d1d", fontSize = DEFAULT_TEXT_FONT_SIZE) {
123
129
  const lh = lineHeightFor(fontSize);
124
130
  const y0 = firstLineBaselineY(fontSize);
125
131
  const trimmed = content.trim();
126
132
  if (trimmed.length === 0) {
127
- return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="system-ui,sans-serif" fill="#94a3b8" font-style="italic">${escapeSvgTextContent(PLACEHOLDER)}</text>`;
133
+ return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="${TEXT_FONT_FAMILY}" fill="#94a3b8" font-style="italic">${escapeSvgTextContent(PLACEHOLDER)}</text>`;
128
134
  }
129
135
  const lines = content.split("\n");
130
136
  if (lines.length === 1) {
131
- return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="system-ui,sans-serif" fill="${fillColor}">${escapeSvgTextContent(lines[0] ?? "")}</text>`;
137
+ return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="${TEXT_FONT_FAMILY}" fill="${fillColor}">${escapeSvgTextContent(lines[0] ?? "")}</text>`;
132
138
  }
133
139
  const parts = [];
134
140
  for (let i = 0; i < lines.length; i++) {
@@ -139,28 +145,32 @@ function buildTextSvg(content, _width, _height, fillColor = "#2563eb", fontSize
139
145
  parts.push(`<tspan x="4" dy="${lh}">${escapeSvgTextContent(line)}</tspan>`);
140
146
  }
141
147
  }
142
- return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="system-ui,sans-serif" fill="${fillColor}">${parts.join("")}</text>`;
148
+ return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="${TEXT_FONT_FAMILY}" fill="${fillColor}">${parts.join("")}</text>`;
143
149
  }
144
- function buildTextFixedBoundsSvg(content, width, height, fillColor = "#2563eb", fontSize = DEFAULT_TEXT_FONT_SIZE) {
150
+ function buildTextFixedBoundsSvg(content, width, height, fillColor = "#1d1d1d", fontSize = DEFAULT_TEXT_FONT_SIZE) {
145
151
  const w = Math.max(1, width);
146
152
  const h = Math.max(1, height);
147
153
  const lh = lineHeightFor(fontSize);
148
154
  const trimmed = content.trim();
155
+ const padTop = EDIT_TOP_PAD_RATIO * fontSize;
149
156
  if (trimmed.length === 0) {
150
- return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:2px 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:system-ui,sans-serif;white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:#94a3b8;font-style:italic">${escapeHtmlText(PLACEHOLDER)}</div></foreignObject>`;
157
+ return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:#94a3b8;font-style:italic">${escapeHtmlText(PLACEHOLDER)}</div></foreignObject>`;
151
158
  }
152
159
  const body = escapeHtmlText(content);
153
- return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:2px 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:system-ui,sans-serif;white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:${fillColor}">${body}</div></foreignObject>`;
160
+ return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:${fillColor}">${body}</div></foreignObject>`;
154
161
  }
155
- var DEFAULT_TEXT_FONT_SIZE, LINE_HEIGHT_RATIO, FIRST_LINE_BASELINE_RATIO, PLACEHOLDER, MIN_TEXT_BOX_W, MIN_TEXT_BOX_H, TEXT_PAD_X, MAX_TEXT_MEASURE_CACHE_ENTRIES, sharedMeasureContext, textMeasureCache;
162
+ var DEFAULT_TEXT_FONT_SIZE, TEXT_FONT_FAMILY, LINE_HEIGHT_RATIO, FIRST_LINE_BASELINE_RATIO, EDIT_TOP_PAD_RATIO, BOTTOM_PAD_RATIO, PLACEHOLDER, MIN_TEXT_BOX_W, MIN_TEXT_BOX_H, TEXT_PAD_X, MAX_TEXT_MEASURE_CACHE_ENTRIES, sharedMeasureContext, textMeasureCache;
156
163
  var init_text_svg = __esm({
157
164
  "src/scene/text-svg.ts"() {
158
165
  DEFAULT_TEXT_FONT_SIZE = 18;
166
+ TEXT_FONT_FAMILY = "Inter, -apple-system, BlinkMacSystemFont, ui-sans-serif, system-ui, sans-serif";
159
167
  LINE_HEIGHT_RATIO = 22 / 18;
160
- FIRST_LINE_BASELINE_RATIO = 24 / 18;
168
+ FIRST_LINE_BASELINE_RATIO = 20 / 18;
169
+ EDIT_TOP_PAD_RATIO = 4 / 18;
170
+ BOTTOM_PAD_RATIO = 4 / 18;
161
171
  PLACEHOLDER = "Tap to type";
162
172
  MIN_TEXT_BOX_W = 40;
163
- MIN_TEXT_BOX_H = 36;
173
+ MIN_TEXT_BOX_H = 26;
164
174
  TEXT_PAD_X = 4;
165
175
  MAX_TEXT_MEASURE_CACHE_ENTRIES = 2e3;
166
176
  textMeasureCache = /* @__PURE__ */ new Map();
@@ -273,7 +283,8 @@ function resolveStrokeStyle(item) {
273
283
  return {
274
284
  stroke: item.stroke ?? DEFAULT_STROKE_STYLE.stroke,
275
285
  strokeWidth: item.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth,
276
- strokeOpacity: item.strokeOpacity
286
+ strokeOpacity: item.strokeOpacity,
287
+ strokeDash: item.strokeDash
277
288
  };
278
289
  }
279
290
  function strokeOpacityAttr(style) {
@@ -596,6 +607,30 @@ function buildDrawDotSvg(r, style = DEFAULT_STROKE_STYLE) {
596
607
  const op = style.strokeOpacity != null ? ` fill-opacity="${style.strokeOpacity}"` : "";
597
608
  return `<circle cx="${r}" cy="${r}" r="${r}" fill="${style.stroke}" shape-rendering="geometricPrecision"${op} />`;
598
609
  }
610
+ function dashArrayForDrawStroke(strokeWidth) {
611
+ const dash = Math.max(strokeWidth * 1.8, 4);
612
+ const gap = Math.max(strokeWidth * 1.4, 3);
613
+ return `${dash} ${gap}`;
614
+ }
615
+ function buildSmoothedCenterlinePath(points) {
616
+ if (points.length < 2) return null;
617
+ const first = points[0];
618
+ if (!first) return null;
619
+ let d = `M ${first.x} ${first.y}`;
620
+ for (let i = 1; i < points.length - 1; i++) {
621
+ const a = points[i];
622
+ const b = points[i + 1];
623
+ if (!a || !b) continue;
624
+ const midX = (a.x + b.x) / 2;
625
+ const midY = (a.y + b.y) / 2;
626
+ d += ` Q ${a.x} ${a.y} ${midX} ${midY}`;
627
+ }
628
+ const last = points[points.length - 1];
629
+ if (last) {
630
+ d += ` L ${last.x} ${last.y}`;
631
+ }
632
+ return d;
633
+ }
599
634
  function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeComplete = true) {
600
635
  if (pathPointsLocal.length === 0) return null;
601
636
  if (pathPointsLocal.length === 1) {
@@ -610,6 +645,18 @@ function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeCompl
610
645
  fillOpacity: style.strokeOpacity
611
646
  };
612
647
  }
648
+ if (style.strokeDash === "dashed" && (toolKind === "draw" || toolKind === "pencil")) {
649
+ const d2 = buildSmoothedCenterlinePath(pathPointsLocal);
650
+ if (!d2) return null;
651
+ return {
652
+ kind: "strokePath",
653
+ d: d2,
654
+ stroke: style.stroke,
655
+ strokeWidth: style.strokeWidth,
656
+ strokeOpacity: style.strokeOpacity,
657
+ strokeDasharray: dashArrayForDrawStroke(style.strokeWidth)
658
+ };
659
+ }
613
660
  const hasPressure = pathPointsLocal.some(
614
661
  (p) => p.pressure != null && Number.isFinite(p.pressure)
615
662
  );
@@ -652,7 +699,8 @@ function freehandPayloadToSvgString(payload) {
652
699
  strokeWidth: payload.strokeWidth,
653
700
  strokeOpacity: payload.strokeOpacity
654
701
  });
655
- return `<path d="${payload.d}" fill="none" stroke="${payload.stroke}" stroke-width="${payload.strokeWidth}" stroke-linecap="round" stroke-linejoin="round" shape-rendering="geometricPrecision"${op} />`;
702
+ const dash = payload.strokeDasharray ? ` stroke-dasharray="${payload.strokeDasharray}"` : "";
703
+ return `<path d="${payload.d}" fill="none" stroke="${payload.stroke}" stroke-width="${payload.strokeWidth}" stroke-linecap="round" stroke-linejoin="round" shape-rendering="geometricPrecision"${op}${dash} />`;
656
704
  }
657
705
  function buildFreehandPathSvg(pathPointsLocal, style, toolKind, strokeComplete = true) {
658
706
  const payload = computeFreehandSvgPayload(
@@ -864,7 +912,7 @@ function createDrawDotItem(id, worldX, worldY, radius, style) {
864
912
  childrenSvg: ""
865
913
  });
866
914
  }
867
- function createTextItem(id, bounds, text = "", style) {
915
+ function createTextItem(id, bounds, text = "", style, textFontSize) {
868
916
  const r = normalizeRect(bounds);
869
917
  const s = { ...DEFAULT_STROKE_STYLE, ...style };
870
918
  return rebuildItemSvg({
@@ -877,6 +925,7 @@ function createTextItem(id, bounds, text = "", style) {
877
925
  stroke: s.stroke,
878
926
  strokeWidth: s.strokeWidth,
879
927
  ...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
928
+ ...textFontSize != null ? { textFontSize } : {},
880
929
  childrenSvg: ""
881
930
  });
882
931
  }
@@ -913,6 +962,7 @@ function createFreehandStrokeItem(id, pointsWorld, toolKind, style) {
913
962
  stroke: merged.stroke,
914
963
  strokeWidth: merged.strokeWidth,
915
964
  ...merged.strokeOpacity != null ? { strokeOpacity: merged.strokeOpacity } : {},
965
+ ...merged.strokeDash != null ? { strokeDash: merged.strokeDash } : {},
916
966
  pathPointsLocal,
917
967
  childrenSvg: ""
918
968
  });
@@ -989,7 +1039,7 @@ var init_shape_builders = __esm({
989
1039
  init_custom_shape();
990
1040
  init_text_svg();
991
1041
  DEFAULT_STROKE_STYLE = {
992
- stroke: "#2563eb",
1042
+ stroke: "#1d1d1d",
993
1043
  strokeWidth: 2
994
1044
  };
995
1045
  TOOL_FREEHAND_DEFAULTS = {
@@ -3481,33 +3531,416 @@ var DEFAULT_VECTOR_TOOLS = [
3481
3531
  shortcutHint: "I"
3482
3532
  }
3483
3533
  ];
3534
+
3535
+ // src/react/VectorSelectionInspector.tsx
3536
+ init_text_svg();
3484
3537
  var shellLook = {
3485
3538
  display: "flex",
3486
3539
  flexDirection: "column",
3487
- gap: 8,
3488
- minWidth: 160,
3489
- padding: "10px 12px",
3490
- borderRadius: 8,
3491
- border: "1px solid rgba(0,0,0,0.12)",
3492
- background: "rgba(255,255,255,0.96)",
3493
- boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
3540
+ gap: 10,
3541
+ minWidth: 200,
3542
+ padding: "12px 14px",
3543
+ borderRadius: 10,
3544
+ border: "1px solid rgba(0,0,0,0.1)",
3545
+ background: "rgba(255,255,255,0.97)",
3546
+ boxShadow: "0 4px 16px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.08)",
3494
3547
  pointerEvents: "auto"
3495
3548
  };
3496
- var labelStyle2 = {
3549
+ var sectionLabelStyle = {
3497
3550
  display: "flex",
3498
3551
  flexDirection: "column",
3499
- gap: 4,
3552
+ gap: 6,
3500
3553
  fontSize: 11,
3501
3554
  fontWeight: 600,
3502
- color: "#52525b"
3555
+ color: "#52525b",
3556
+ letterSpacing: "0.02em",
3557
+ textTransform: "uppercase"
3558
+ };
3559
+ var mixedHintStyle = {
3560
+ fontWeight: 400,
3561
+ color: "#a1a1aa",
3562
+ textTransform: "none",
3563
+ letterSpacing: 0
3503
3564
  };
3565
+ var TLDRAW_PALETTE = [
3566
+ { name: "black", hex: "#1d1d1d" },
3567
+ { name: "grey", hex: "#9fa8b2" },
3568
+ { name: "light-violet", hex: "#e085f4" },
3569
+ { name: "violet", hex: "#ae3ec9" },
3570
+ { name: "blue", hex: "#4263eb" },
3571
+ { name: "light-blue", hex: "#4dabf7" },
3572
+ { name: "yellow", hex: "#ffc078" },
3573
+ { name: "orange", hex: "#f76707" },
3574
+ { name: "green", hex: "#099268" },
3575
+ { name: "light-green", hex: "#40c057" },
3576
+ { name: "light-red", hex: "#ff8787" },
3577
+ { name: "red", hex: "#e03131" }
3578
+ ];
3504
3579
  function normalizeHex(stroke) {
3505
3580
  if (/^#[0-9A-Fa-f]{6}$/.test(stroke)) return stroke;
3506
- return "#2563eb";
3581
+ return "#1d1d1d";
3582
+ }
3583
+ function hexesEqual(a, b) {
3584
+ return a.toLowerCase() === b.toLowerCase();
3507
3585
  }
3508
3586
  function isStylableKind(tk) {
3509
3587
  return tk === "rect" || tk === "ellipse" || tk === "architectural-cloud" || tk === "line" || tk === "arrow" || tk === "draw" || tk === "pencil" || tk === "brush" || tk === "marker" || tk === "text";
3510
3588
  }
3589
+ var FONT_SIZE_PRESETS = [
3590
+ { label: "S", size: 14 },
3591
+ { label: "M", size: 18 },
3592
+ { label: "L", size: 28 },
3593
+ { label: "XL", size: 44 }
3594
+ ];
3595
+ function ColorSwatch({ color, selected, onSelect }) {
3596
+ return /* @__PURE__ */ jsx(
3597
+ "button",
3598
+ {
3599
+ type: "button",
3600
+ "aria-label": color.name,
3601
+ "aria-pressed": selected,
3602
+ onClick: onSelect,
3603
+ onPointerDown: (e) => {
3604
+ if (e.pointerType === "mouse") e.preventDefault();
3605
+ },
3606
+ style: {
3607
+ width: 24,
3608
+ height: 24,
3609
+ padding: 0,
3610
+ borderRadius: "50%",
3611
+ border: selected ? "2px solid #18181b" : "1px solid rgba(0,0,0,0.12)",
3612
+ background: color.hex,
3613
+ cursor: "pointer",
3614
+ outline: "none",
3615
+ boxShadow: selected ? "0 0 0 2px rgba(255,255,255,0.95) inset" : "none",
3616
+ transition: "transform 0.08s ease",
3617
+ transform: selected ? "scale(1.08)" : "scale(1)",
3618
+ WebkitTapHighlightColor: "transparent"
3619
+ }
3620
+ }
3621
+ );
3622
+ }
3623
+ function ColorPalette({ value, onChange }) {
3624
+ const [showCustom, setShowCustom] = useState(false);
3625
+ const normalized = normalizeHex(value);
3626
+ const inPalette = TLDRAW_PALETTE.some((c) => hexesEqual(c.hex, normalized));
3627
+ return /* @__PURE__ */ jsxs(
3628
+ "div",
3629
+ {
3630
+ style: {
3631
+ display: "grid",
3632
+ gridTemplateColumns: "repeat(6, 24px)",
3633
+ gap: 6,
3634
+ rowGap: 6,
3635
+ alignItems: "center"
3636
+ },
3637
+ children: [
3638
+ TLDRAW_PALETTE.map((c) => /* @__PURE__ */ jsx(
3639
+ ColorSwatch,
3640
+ {
3641
+ color: c,
3642
+ selected: hexesEqual(c.hex, normalized),
3643
+ onSelect: () => onChange(c.hex)
3644
+ },
3645
+ c.name
3646
+ )),
3647
+ /* @__PURE__ */ jsx(
3648
+ "button",
3649
+ {
3650
+ type: "button",
3651
+ "aria-label": "Cor personalizada",
3652
+ "aria-pressed": showCustom || !inPalette,
3653
+ onClick: () => setShowCustom((v) => !v),
3654
+ onPointerDown: (e) => {
3655
+ if (e.pointerType === "mouse") e.preventDefault();
3656
+ },
3657
+ style: {
3658
+ width: 24,
3659
+ height: 24,
3660
+ padding: 0,
3661
+ borderRadius: "50%",
3662
+ border: !inPalette || showCustom ? "2px solid #18181b" : "1px solid rgba(0,0,0,0.12)",
3663
+ background: "conic-gradient(from 0deg, #ff5757, #ffbd59, #59ff7e, #59ddff, #b259ff, #ff59c1, #ff5757)",
3664
+ cursor: "pointer",
3665
+ outline: "none",
3666
+ WebkitTapHighlightColor: "transparent"
3667
+ }
3668
+ }
3669
+ ),
3670
+ showCustom || !inPalette ? /* @__PURE__ */ jsxs(
3671
+ "label",
3672
+ {
3673
+ style: {
3674
+ gridColumn: "1 / -1",
3675
+ display: "flex",
3676
+ alignItems: "center",
3677
+ gap: 6,
3678
+ fontSize: 11,
3679
+ fontWeight: 500,
3680
+ color: "#71717a",
3681
+ textTransform: "none",
3682
+ letterSpacing: 0
3683
+ },
3684
+ children: [
3685
+ /* @__PURE__ */ jsx(
3686
+ "input",
3687
+ {
3688
+ type: "color",
3689
+ value: normalized,
3690
+ onChange: (e) => onChange(e.target.value),
3691
+ style: {
3692
+ width: 28,
3693
+ height: 28,
3694
+ padding: 0,
3695
+ border: "1px solid rgba(0,0,0,0.12)",
3696
+ borderRadius: 6,
3697
+ cursor: "pointer",
3698
+ background: "transparent"
3699
+ }
3700
+ }
3701
+ ),
3702
+ /* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: normalized.toUpperCase() })
3703
+ ]
3704
+ }
3705
+ ) : null
3706
+ ]
3707
+ }
3708
+ );
3709
+ }
3710
+ function DashTabs({ value, onChange }) {
3711
+ const tabs = [
3712
+ { id: "solid", label: "Cont\xEDnuo" },
3713
+ { id: "dashed", label: "Tracejado" }
3714
+ ];
3715
+ return /* @__PURE__ */ jsx(
3716
+ "div",
3717
+ {
3718
+ role: "radiogroup",
3719
+ "aria-label": "Estilo do tra\xE7o",
3720
+ style: {
3721
+ display: "flex",
3722
+ background: "rgba(24,24,27,0.06)",
3723
+ borderRadius: 7,
3724
+ padding: 2,
3725
+ gap: 2
3726
+ },
3727
+ children: tabs.map((t) => {
3728
+ const selected = value === t.id;
3729
+ return (
3730
+ // biome-ignore lint/a11y/useSemanticElements: styled segmented control needs button, not input[type=radio]
3731
+ /* @__PURE__ */ jsxs(
3732
+ "button",
3733
+ {
3734
+ type: "button",
3735
+ role: "radio",
3736
+ "aria-checked": selected,
3737
+ onClick: () => onChange(t.id),
3738
+ onPointerDown: (e) => {
3739
+ if (e.pointerType === "mouse") e.preventDefault();
3740
+ },
3741
+ style: {
3742
+ flex: 1,
3743
+ display: "inline-flex",
3744
+ alignItems: "center",
3745
+ justifyContent: "center",
3746
+ padding: "5px 8px",
3747
+ fontSize: 11,
3748
+ fontWeight: 600,
3749
+ color: selected ? "#18181b" : "#71717a",
3750
+ background: selected ? "#fff" : "transparent",
3751
+ borderRadius: 5,
3752
+ border: "none",
3753
+ cursor: "pointer",
3754
+ boxShadow: selected ? "0 1px 2px rgba(0,0,0,0.1)" : "none",
3755
+ outline: "none",
3756
+ WebkitTapHighlightColor: "transparent"
3757
+ },
3758
+ children: [
3759
+ /* @__PURE__ */ jsx(DashTabSwatch, { dashed: t.id === "dashed" }),
3760
+ /* @__PURE__ */ jsx("span", { style: { marginLeft: 6 }, children: t.label })
3761
+ ]
3762
+ },
3763
+ t.id
3764
+ )
3765
+ );
3766
+ })
3767
+ }
3768
+ );
3769
+ }
3770
+ function DashTabSwatch({ dashed }) {
3771
+ return /* @__PURE__ */ jsxs(
3772
+ "svg",
3773
+ {
3774
+ width: 26,
3775
+ height: 6,
3776
+ "aria-hidden": true,
3777
+ style: { flexShrink: 0, display: "block" },
3778
+ children: [
3779
+ /* @__PURE__ */ jsx("title", { children: dashed ? "dashed" : "solid" }),
3780
+ /* @__PURE__ */ jsx(
3781
+ "line",
3782
+ {
3783
+ x1: 1,
3784
+ y1: 3,
3785
+ x2: 25,
3786
+ y2: 3,
3787
+ stroke: "currentColor",
3788
+ strokeWidth: 2,
3789
+ strokeLinecap: "round",
3790
+ strokeDasharray: dashed ? "4 3" : void 0
3791
+ }
3792
+ )
3793
+ ]
3794
+ }
3795
+ );
3796
+ }
3797
+ function FontSizeTabs({ value, onChange }) {
3798
+ return /* @__PURE__ */ jsx(
3799
+ "div",
3800
+ {
3801
+ role: "radiogroup",
3802
+ "aria-label": "Tamanho do texto",
3803
+ style: {
3804
+ display: "flex",
3805
+ background: "rgba(24,24,27,0.06)",
3806
+ borderRadius: 7,
3807
+ padding: 2,
3808
+ gap: 2
3809
+ },
3810
+ children: FONT_SIZE_PRESETS.map((p) => {
3811
+ const selected = Math.abs(value - p.size) < 0.5;
3812
+ return (
3813
+ // biome-ignore lint/a11y/useSemanticElements: styled segmented control needs button, not input[type=radio]
3814
+ /* @__PURE__ */ jsx(
3815
+ "button",
3816
+ {
3817
+ type: "button",
3818
+ role: "radio",
3819
+ "aria-checked": selected,
3820
+ "aria-label": `${p.label} (${p.size}px)`,
3821
+ onClick: () => onChange(p.size),
3822
+ onPointerDown: (e) => {
3823
+ if (e.pointerType === "mouse") e.preventDefault();
3824
+ },
3825
+ style: {
3826
+ flex: 1,
3827
+ padding: "5px 0",
3828
+ fontSize: p.label === "XL" ? 14 : p.label === "L" ? 13 : 12,
3829
+ fontWeight: 700,
3830
+ color: selected ? "#18181b" : "#71717a",
3831
+ background: selected ? "#fff" : "transparent",
3832
+ borderRadius: 5,
3833
+ border: "none",
3834
+ cursor: "pointer",
3835
+ boxShadow: selected ? "0 1px 2px rgba(0,0,0,0.1)" : "none",
3836
+ outline: "none",
3837
+ WebkitTapHighlightColor: "transparent",
3838
+ lineHeight: 1
3839
+ },
3840
+ children: p.label
3841
+ },
3842
+ p.label
3843
+ )
3844
+ );
3845
+ })
3846
+ }
3847
+ );
3848
+ }
3849
+ function viewModelFromActiveTool(activeToolStyle) {
3850
+ const hex = normalizeHex(activeToolStyle.stroke);
3851
+ const strokeWidth = activeToolStyle.strokeWidth;
3852
+ const strokeOpacity = activeToolStyle.strokeOpacity ?? 1;
3853
+ const strokeDash = activeToolStyle.strokeDash === "dashed" ? "dashed" : "solid";
3854
+ const textFontSize = activeToolStyle.textFontSize ?? DEFAULT_TEXT_FONT_SIZE;
3855
+ const isText = activeToolStyle.toolKind === "text";
3856
+ const isDraw = activeToolStyle.toolKind === "draw";
3857
+ const isMarker = activeToolStyle.toolKind === "marker";
3858
+ const current = {
3859
+ stroke: hex,
3860
+ strokeWidth,
3861
+ ...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {},
3862
+ ...activeToolStyle.strokeDash != null ? { strokeDash: activeToolStyle.strokeDash } : {},
3863
+ ...isText ? { textFontSize } : {}
3864
+ };
3865
+ return {
3866
+ label: activeToolStyle.label ?? "Estilo da ferramenta",
3867
+ count: 0,
3868
+ hex,
3869
+ strokeWidth,
3870
+ strokeOpacity,
3871
+ strokeDash,
3872
+ textFontSize,
3873
+ showStrokeWidth: !isText,
3874
+ showDash: isDraw,
3875
+ showFontSize: isText,
3876
+ showMarkerOpacity: isMarker,
3877
+ mixedStroke: false,
3878
+ mixedWidth: false,
3879
+ mixedDash: false,
3880
+ mixedFontSize: false,
3881
+ mixedOpacity: false,
3882
+ current
3883
+ };
3884
+ }
3885
+ function viewModelFromSelection(stylable) {
3886
+ const first = stylable[0];
3887
+ if (!first) return null;
3888
+ const hex = normalizeHex(first.stroke ?? "#1d1d1d");
3889
+ const strokeWidth = first.strokeWidth ?? 2;
3890
+ const allSameStroke = stylable.every(
3891
+ (it) => (it.stroke ?? "#1d1d1d") === (first.stroke ?? "#1d1d1d")
3892
+ );
3893
+ const allSameWidth = stylable.every(
3894
+ (it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
3895
+ );
3896
+ const draws = stylable.filter(
3897
+ (it) => it.toolKind === "draw" || it.toolKind === "pencil"
3898
+ );
3899
+ const showDash = draws.length > 0;
3900
+ const firstDraw = draws[0];
3901
+ const strokeDash = firstDraw?.strokeDash === "dashed" ? "dashed" : "solid";
3902
+ const allSameDash = draws.length === 0 || draws.every((it) => (it.strokeDash ?? "solid") === strokeDash);
3903
+ const markers = stylable.filter((it) => it.toolKind === "marker");
3904
+ const showMarkerOpacity = markers.length > 0;
3905
+ const firstMarker = markers[0];
3906
+ const strokeOpacity = firstMarker?.strokeOpacity ?? 1;
3907
+ const allSameOpacity = markers.length === 0 || markers.every(
3908
+ (it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
3909
+ );
3910
+ const texts = stylable.filter((it) => it.toolKind === "text");
3911
+ const showFontSize = texts.length > 0;
3912
+ const firstText = texts[0];
3913
+ const textFontSize = firstText?.textFontSize ?? DEFAULT_TEXT_FONT_SIZE;
3914
+ const allSameFontSize = texts.length === 0 || texts.every(
3915
+ (it) => (it.textFontSize ?? DEFAULT_TEXT_FONT_SIZE) === textFontSize
3916
+ );
3917
+ const current = {
3918
+ stroke: hex,
3919
+ strokeWidth,
3920
+ ...showMarkerOpacity ? { strokeOpacity } : {},
3921
+ ...showDash ? { strokeDash } : {},
3922
+ ...showFontSize ? { textFontSize } : {}
3923
+ };
3924
+ return {
3925
+ label: "Estilo da sele\xE7\xE3o",
3926
+ count: stylable.length,
3927
+ hex,
3928
+ strokeWidth,
3929
+ strokeOpacity,
3930
+ strokeDash,
3931
+ textFontSize,
3932
+ showStrokeWidth: true,
3933
+ showDash,
3934
+ showFontSize,
3935
+ showMarkerOpacity,
3936
+ mixedStroke: !allSameStroke,
3937
+ mixedWidth: !allSameWidth,
3938
+ mixedDash: !allSameDash,
3939
+ mixedFontSize: !allSameFontSize,
3940
+ mixedOpacity: !allSameOpacity,
3941
+ current
3942
+ };
3943
+ }
3511
3944
  function VectorSelectionInspector({
3512
3945
  items: itemsProp,
3513
3946
  activeToolStyle: activeToolStyleProp,
@@ -3523,175 +3956,93 @@ function VectorSelectionInspector({
3523
3956
  const activeToolStyle = activeToolStyleProp === void 0 ? ctx?.activeToolStyle ?? null : activeToolStyleProp;
3524
3957
  const onChange = onChangeProp ?? ctx?.onSelectionStyleChange ?? null;
3525
3958
  if (!onChange) return null;
3959
+ let vm = null;
3960
+ if (activeToolStyle) {
3961
+ vm = viewModelFromActiveTool(activeToolStyle);
3962
+ } else {
3963
+ const stylable = items.filter(
3964
+ (it) => !it.locked && it.toolKind && isStylableKind(it.toolKind)
3965
+ );
3966
+ if (stylable.length === 0) return null;
3967
+ vm = viewModelFromSelection(stylable);
3968
+ }
3969
+ if (!vm) return null;
3970
+ const apply = (changes) => onChange({ ...vm.current, ...changes });
3526
3971
  const shell = {
3527
3972
  ...getBoardPositionStyle(position, inset, zIndex),
3528
3973
  ...shellLook,
3529
3974
  ...style
3530
3975
  };
3531
- if (activeToolStyle) {
3532
- const stroke2 = activeToolStyle.stroke;
3533
- const strokeWidth2 = activeToolStyle.strokeWidth;
3534
- const hex2 = normalizeHex(stroke2);
3535
- const showMarkerOpacity2 = activeToolStyle.toolKind === "marker";
3536
- const opacityPct2 = Math.round((activeToolStyle.strokeOpacity ?? 1) * 100);
3537
- return /* @__PURE__ */ jsxs(
3538
- "section",
3539
- {
3540
- "data-slot": "vector-selection-inspector",
3541
- "data-position": position,
3542
- className,
3543
- "aria-label": activeToolStyle.label ?? "Estilo da ferramenta",
3544
- style: shell,
3545
- children: [
3546
- /* @__PURE__ */ jsxs("label", { style: labelStyle2, children: [
3547
- "Cor",
3548
- /* @__PURE__ */ jsx(
3549
- "input",
3550
- {
3551
- type: "color",
3552
- value: hex2,
3553
- onChange: (e) => onChange({
3554
- stroke: e.target.value,
3555
- strokeWidth: strokeWidth2,
3556
- ...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
3557
- }),
3558
- style: {
3559
- width: "100%",
3560
- height: 32,
3561
- padding: 0,
3562
- border: "none",
3563
- cursor: "pointer",
3564
- background: "transparent"
3565
- }
3566
- }
3567
- )
3568
- ] }),
3569
- /* @__PURE__ */ jsxs("label", { style: labelStyle2, children: [
3570
- "Grossura",
3571
- /* @__PURE__ */ jsx(
3572
- "input",
3573
- {
3574
- type: "range",
3575
- min: 1,
3576
- max: 48,
3577
- value: strokeWidth2,
3578
- onChange: (e) => onChange({
3579
- stroke: hex2,
3580
- strokeWidth: Number(e.target.value),
3581
- ...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
3582
- })
3583
- }
3584
- )
3585
- ] }),
3586
- showMarkerOpacity2 && /* @__PURE__ */ jsxs("label", { style: labelStyle2, children: [
3587
- "Opacidade (marcador)",
3588
- /* @__PURE__ */ jsx(
3589
- "input",
3590
- {
3591
- type: "range",
3592
- min: 10,
3593
- max: 100,
3594
- value: opacityPct2,
3595
- onChange: (e) => {
3596
- const v = Number(e.target.value) / 100;
3597
- onChange({
3598
- stroke: hex2,
3599
- strokeWidth: strokeWidth2,
3600
- strokeOpacity: v
3601
- });
3602
- }
3603
- }
3604
- ),
3605
- /* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
3606
- opacityPct2,
3607
- "%"
3608
- ] })
3609
- ] })
3610
- ]
3611
- }
3612
- );
3613
- }
3614
- const stylable = items.filter(
3615
- (it) => !it.locked && it.toolKind && isStylableKind(it.toolKind)
3616
- );
3617
- if (stylable.length === 0) return null;
3618
- const first = stylable[0];
3619
- if (!first) return null;
3620
- const allSameStroke = stylable.every(
3621
- (it) => (it.stroke ?? "#2563eb") === (first.stroke ?? "#2563eb")
3622
- );
3623
- const allSameWidth = stylable.every(
3624
- (it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
3625
- );
3626
- const stroke = first.stroke ?? "#2563eb";
3627
- const strokeWidth = first.strokeWidth ?? 2;
3628
- const hex = normalizeHex(stroke);
3629
- const markers = stylable.filter((it) => it.toolKind === "marker");
3630
- const showMarkerOpacity = markers.length > 0;
3631
- const firstMarker = markers[0];
3632
- const allSameMarkerOpacity = markers.length > 0 && markers.every(
3633
- (it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
3634
- );
3635
- const opacityPct = firstMarker ? Math.round((firstMarker.strokeOpacity ?? 1) * 100) : 100;
3976
+ const opacityPct = Math.round(vm.strokeOpacity * 100);
3636
3977
  return /* @__PURE__ */ jsxs(
3637
3978
  "section",
3638
3979
  {
3639
3980
  "data-slot": "vector-selection-inspector",
3640
3981
  "data-position": position,
3641
3982
  className,
3642
- "aria-label": "Estilo da sele\xE7\xE3o",
3983
+ "aria-label": vm.label,
3643
3984
  style: shell,
3644
3985
  children: [
3645
- stylable.length > 1 && /* @__PURE__ */ jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
3646
- stylable.length,
3986
+ vm.count > 1 ? /* @__PURE__ */ jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
3987
+ vm.count,
3647
3988
  " objetos selecionados"
3648
- ] }),
3649
- /* @__PURE__ */ jsxs("label", { style: labelStyle2, children: [
3650
- "Cor",
3651
- !allSameStroke && /* @__PURE__ */ jsxs("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: [
3652
- " ",
3653
- "(valores misturados \u2014 novo valor aplica a todos)"
3989
+ ] }) : null,
3990
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Cor", style: sectionLabelStyle, children: [
3991
+ /* @__PURE__ */ jsxs("span", { children: [
3992
+ "Cor",
3993
+ vm.mixedStroke ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (valores misturados)" }) : null
3654
3994
  ] }),
3655
- /* @__PURE__ */ jsx(
3656
- "input",
3657
- {
3658
- type: "color",
3659
- value: hex,
3660
- onChange: (e) => onChange({
3661
- stroke: e.target.value,
3662
- strokeWidth
3663
- }),
3664
- style: {
3665
- width: "100%",
3666
- height: 32,
3667
- padding: 0,
3668
- border: "none",
3669
- cursor: "pointer",
3670
- background: "transparent"
3671
- }
3672
- }
3673
- )
3995
+ /* @__PURE__ */ jsx(ColorPalette, { value: vm.hex, onChange: (h) => apply({ stroke: h }) })
3674
3996
  ] }),
3675
- /* @__PURE__ */ jsxs("label", { style: labelStyle2, children: [
3997
+ vm.showStrokeWidth ? /* @__PURE__ */ jsxs("label", { style: sectionLabelStyle, children: [
3676
3998
  "Grossura",
3677
- !allSameWidth && /* @__PURE__ */ jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
3999
+ vm.mixedWidth ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
3678
4000
  /* @__PURE__ */ jsx(
3679
4001
  "input",
3680
4002
  {
3681
4003
  type: "range",
3682
4004
  min: 1,
3683
4005
  max: 48,
3684
- value: strokeWidth,
3685
- onChange: (e) => onChange({
3686
- stroke: hex,
3687
- strokeWidth: Number(e.target.value)
3688
- })
4006
+ value: vm.strokeWidth,
4007
+ onChange: (e) => apply({ strokeWidth: Number(e.target.value) })
3689
4008
  }
3690
4009
  )
3691
- ] }),
3692
- showMarkerOpacity && firstMarker && /* @__PURE__ */ jsxs("label", { style: labelStyle2, children: [
3693
- "Opacidade (marcador)",
3694
- !allSameMarkerOpacity && markers.length > 1 && /* @__PURE__ */ jsx("span", { style: { fontWeight: 400, color: "#a1a1aa" }, children: " (misturado)" }),
4010
+ ] }) : null,
4011
+ vm.showDash ? (
4012
+ // biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
4013
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Tra\xE7o", style: sectionLabelStyle, children: [
4014
+ /* @__PURE__ */ jsxs("span", { children: [
4015
+ "Tra\xE7o",
4016
+ vm.mixedDash ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
4017
+ ] }),
4018
+ /* @__PURE__ */ jsx(
4019
+ DashTabs,
4020
+ {
4021
+ value: vm.strokeDash,
4022
+ onChange: (next) => apply({ strokeDash: next })
4023
+ }
4024
+ )
4025
+ ] })
4026
+ ) : null,
4027
+ vm.showFontSize ? (
4028
+ // biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
4029
+ /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Tamanho", style: sectionLabelStyle, children: [
4030
+ /* @__PURE__ */ jsxs("span", { children: [
4031
+ "Tamanho",
4032
+ vm.mixedFontSize ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
4033
+ ] }),
4034
+ /* @__PURE__ */ jsx(
4035
+ FontSizeTabs,
4036
+ {
4037
+ value: vm.textFontSize,
4038
+ onChange: (size) => apply({ textFontSize: size })
4039
+ }
4040
+ )
4041
+ ] })
4042
+ ) : null,
4043
+ vm.showMarkerOpacity ? /* @__PURE__ */ jsxs("label", { style: sectionLabelStyle, children: [
4044
+ "Opacidade",
4045
+ vm.mixedOpacity ? /* @__PURE__ */ jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
3695
4046
  /* @__PURE__ */ jsx(
3696
4047
  "input",
3697
4048
  {
@@ -3699,21 +4050,14 @@ function VectorSelectionInspector({
3699
4050
  min: 10,
3700
4051
  max: 100,
3701
4052
  value: opacityPct,
3702
- onChange: (e) => {
3703
- const v = Number(e.target.value) / 100;
3704
- onChange({
3705
- stroke: hex,
3706
- strokeWidth,
3707
- strokeOpacity: v
3708
- });
3709
- }
4053
+ onChange: (e) => apply({ strokeOpacity: Number(e.target.value) / 100 })
3710
4054
  }
3711
4055
  ),
3712
4056
  /* @__PURE__ */ jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
3713
4057
  opacityPct,
3714
4058
  "%"
3715
4059
  ] })
3716
- ] })
4060
+ ] }) : null
3717
4061
  ]
3718
4062
  }
3719
4063
  );
@@ -4192,6 +4536,24 @@ function splitToolbarTools(tools, overflowIds) {
4192
4536
  const overflow = overflowIds.map((id) => tools.find((t) => t.id === id)).filter((t) => Boolean(t));
4193
4537
  return { primary, overflow };
4194
4538
  }
4539
+ function usePromotedOverflowTool({
4540
+ overflowTools,
4541
+ selectedId,
4542
+ initialPromotedId
4543
+ }) {
4544
+ const [lastOverflowToolId, setLastOverflowToolId] = useState(() => {
4545
+ if (initialPromotedId == null) return null;
4546
+ return overflowTools.some((t) => t.id === initialPromotedId) ? initialPromotedId : null;
4547
+ });
4548
+ useEffect(() => {
4549
+ if (overflowTools.some((t) => t.id === selectedId)) {
4550
+ setLastOverflowToolId(selectedId);
4551
+ }
4552
+ }, [selectedId, overflowTools]);
4553
+ const promotedTool = lastOverflowToolId ? overflowTools.find((t) => t.id === lastOverflowToolId) : void 0;
4554
+ const remainingOverflowTools = promotedTool ? overflowTools.filter((t) => t.id !== promotedTool.id) : overflowTools;
4555
+ return { promotedTool, remainingOverflowTools };
4556
+ }
4195
4557
  var icOverflow = { size: 18, strokeWidth: 2 };
4196
4558
  function useOverflowDropdown() {
4197
4559
  const [open, setOpen] = useState(false);
@@ -4726,6 +5088,15 @@ function VectorToolbarComponent({
4726
5088
  const pluginContext = useContext(CanvuPluginContext);
4727
5089
  const runtimeTools = pluginContext?.resolvedTools;
4728
5090
  const resolvedTools = tools ?? runtimeTools ?? DEFAULT_VECTOR_TOOLS;
5091
+ const { primary: primaryTools, overflow: overflowTools } = splitToolbarTools(
5092
+ resolvedTools,
5093
+ overflowToolIds
5094
+ );
5095
+ const { promotedTool, remainingOverflowTools } = usePromotedOverflowTool({
5096
+ overflowTools,
5097
+ selectedId: value,
5098
+ initialPromotedId: overflowToolIds[0] ?? null
5099
+ });
4729
5100
  if (typeof children === "function") {
4730
5101
  const ctx = {
4731
5102
  tools: resolvedTools,
@@ -4770,11 +5141,8 @@ function VectorToolbarComponent({
4770
5141
  )
4771
5142
  ] });
4772
5143
  }
4773
- const { primary: primaryTools, overflow: overflowTools } = splitToolbarTools(
4774
- resolvedTools,
4775
- overflowToolIds
4776
- );
4777
5144
  const showOverflowMenu = overflowTools.length > 0;
5145
+ const showOverflowDropdown = remainingOverflowTools.length > 0;
4778
5146
  const inlineCtx = {
4779
5147
  selectedId: value,
4780
5148
  selectTool: onChange,
@@ -4825,6 +5193,7 @@ function VectorToolbarComponent({
4825
5193
  /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: toolLockDividerStyle })
4826
5194
  ] }) : null,
4827
5195
  primaryTools.map((tool) => renderInlineToolButton(tool, inlineCtx)),
5196
+ promotedTool ? renderInlineToolButton(promotedTool, inlineCtx) : null,
4828
5197
  showOverflowMenu && orientation === "horizontal" ? /* @__PURE__ */ jsx(
4829
5198
  "span",
4830
5199
  {
@@ -4833,10 +5202,10 @@ function VectorToolbarComponent({
4833
5202
  style: overflowSpacerStyle
4834
5203
  }
4835
5204
  ) : null,
4836
- showOverflowMenu ? /* @__PURE__ */ jsx(
5205
+ showOverflowDropdown ? /* @__PURE__ */ jsx(
4837
5206
  ToolbarOverflowMenu,
4838
5207
  {
4839
- tools: overflowTools,
5208
+ tools: remainingOverflowTools,
4840
5209
  selectedId: value,
4841
5210
  onSelect: onChange,
4842
5211
  density,
@@ -6456,6 +6825,18 @@ var SvgVectorRenderer = class {
6456
6825
  }
6457
6826
  };
6458
6827
 
6828
+ // src/scene/link-item.ts
6829
+ var LINK_PLUGIN_KEY = "canvuLink";
6830
+ var isCanvuLinkData = (value) => {
6831
+ if (!value || typeof value !== "object") return false;
6832
+ const candidate = value;
6833
+ return typeof candidate.href === "string" && candidate.href.length > 0;
6834
+ };
6835
+ function getLinkData(item) {
6836
+ const entry = item.pluginData?.[LINK_PLUGIN_KEY];
6837
+ return isCanvuLinkData(entry) ? entry : null;
6838
+ }
6839
+
6459
6840
  // src/scene/scene.ts
6460
6841
  var VectorScene = class {
6461
6842
  items = [];
@@ -6475,6 +6856,7 @@ var VectorScene = class {
6475
6856
 
6476
6857
  // src/react/VectorViewport.tsx
6477
6858
  init_shape_builders();
6859
+ init_text_svg();
6478
6860
 
6479
6861
  // src/react/blob-url-lifecycle.ts
6480
6862
  var SVG_BLOB_HREF_PATTERN = /\b(?:href|xlink:href)=["'](blob:[^"']+)["']/g;
@@ -6680,6 +7062,9 @@ function InteractionOverlay({
6680
7062
  );
6681
7063
  } else if (placementPreview.kind === "rect" || placementPreview.kind === "ellipse" || placementPreview.kind === "architectural-cloud") {
6682
7064
  const r = normalizeRect(placementPreview.rect);
7065
+ const shapeStroke = previewStrokeStyle?.stroke ?? "#1d1d1d";
7066
+ const shapeWidth = previewStrokeStyle?.strokeWidth ?? 2;
7067
+ const shapeOpacity = previewStrokeStyle?.strokeOpacity;
6683
7068
  preview = placementPreview.kind === "rect" ? /* @__PURE__ */ jsx(
6684
7069
  "rect",
6685
7070
  {
@@ -6687,11 +7072,11 @@ function InteractionOverlay({
6687
7072
  y: r.y,
6688
7073
  width: r.width,
6689
7074
  height: r.height,
7075
+ rx: 4,
6690
7076
  fill: "none",
6691
- stroke: "#64748b",
6692
- strokeWidth: overlayStrokePx,
6693
- strokeDasharray: dashPattern,
6694
- vectorEffect: "non-scaling-stroke"
7077
+ stroke: shapeStroke,
7078
+ strokeWidth: shapeWidth,
7079
+ strokeOpacity: shapeOpacity
6695
7080
  }
6696
7081
  ) : placementPreview.kind === "ellipse" ? /* @__PURE__ */ jsx(
6697
7082
  "ellipse",
@@ -6701,29 +7086,30 @@ function InteractionOverlay({
6701
7086
  rx: r.width / 2,
6702
7087
  ry: r.height / 2,
6703
7088
  fill: "none",
6704
- stroke: "#64748b",
6705
- strokeWidth: overlayStrokePx,
6706
- strokeDasharray: dashPattern,
6707
- vectorEffect: "non-scaling-stroke"
7089
+ stroke: shapeStroke,
7090
+ strokeWidth: shapeWidth,
7091
+ strokeOpacity: shapeOpacity
6708
7092
  }
6709
7093
  ) : /* @__PURE__ */ jsx("g", { transform: `translate(${r.x}, ${r.y})`, children: /* @__PURE__ */ jsx(
6710
7094
  "path",
6711
7095
  {
6712
- d: buildArchitecturalCloudPathD(r.width, r.height, overlayStrokePx),
7096
+ d: buildArchitecturalCloudPathD(r.width, r.height, shapeWidth),
6713
7097
  fill: "none",
6714
- stroke: "#64748b",
6715
- strokeWidth: overlayStrokePx,
6716
- strokeDasharray: dashPattern,
7098
+ stroke: shapeStroke,
7099
+ strokeWidth: shapeWidth,
7100
+ strokeOpacity: shapeOpacity,
6717
7101
  strokeLinecap: "round",
6718
- strokeLinejoin: "round",
6719
- vectorEffect: "non-scaling-stroke"
7102
+ strokeLinejoin: "round"
6720
7103
  }
6721
7104
  ) });
6722
7105
  } else if (placementPreview.kind === "line" || placementPreview.kind === "arrow") {
6723
7106
  const { start, end } = placementPreview;
7107
+ const shapeStroke = previewStrokeStyle?.stroke ?? "#1d1d1d";
7108
+ const shapeWidth = previewStrokeStyle?.strokeWidth ?? 2;
7109
+ const shapeOpacity = previewStrokeStyle?.strokeOpacity;
6724
7110
  const geometry = placementPreview.kind === "arrow" ? computeStraightArrowGeometry(
6725
7111
  { x1: start.x, y1: start.y, x2: end.x, y2: end.y },
6726
- overlayStrokePx
7112
+ shapeWidth
6727
7113
  ) : null;
6728
7114
  preview = /* @__PURE__ */ jsxs(Fragment, { children: [
6729
7115
  /* @__PURE__ */ jsx(
@@ -6733,11 +7119,10 @@ function InteractionOverlay({
6733
7119
  y1: start.y,
6734
7120
  x2: geometry?.shaftEndX ?? end.x,
6735
7121
  y2: geometry?.shaftEndY ?? end.y,
6736
- stroke: "#64748b",
6737
- strokeWidth: overlayStrokePx,
6738
- strokeDasharray: dashPattern,
6739
- strokeLinecap: "round",
6740
- vectorEffect: "non-scaling-stroke"
7122
+ stroke: shapeStroke,
7123
+ strokeWidth: shapeWidth,
7124
+ strokeOpacity: shapeOpacity,
7125
+ strokeLinecap: "round"
6741
7126
  }
6742
7127
  ),
6743
7128
  placementPreview.kind === "arrow" && geometry ? /* @__PURE__ */ jsx(
@@ -6745,10 +7130,9 @@ function InteractionOverlay({
6745
7130
  {
6746
7131
  d: `M ${geometry.headLeftX} ${geometry.headLeftY} L ${geometry.headTipX} ${geometry.headTipY} L ${geometry.headRightX} ${geometry.headRightY}`,
6747
7132
  fill: "none",
6748
- stroke: "#64748b",
6749
- strokeWidth: overlayStrokePx,
6750
- strokeOpacity: 0.9,
6751
- strokeDasharray: dashPattern,
7133
+ stroke: shapeStroke,
7134
+ strokeWidth: shapeWidth,
7135
+ strokeOpacity: shapeOpacity,
6752
7136
  strokeLinecap: "round",
6753
7137
  strokeLinejoin: "round"
6754
7138
  }
@@ -6762,7 +7146,8 @@ function InteractionOverlay({
6762
7146
  const previewStyle = {
6763
7147
  stroke: isLaser ? "#f43f5e" : previewStrokeStyle?.stroke ?? "#64748b",
6764
7148
  strokeWidth: isLaser ? 4 : previewStrokeStyle?.strokeWidth ?? (tool === "marker" ? 16 : 3),
6765
- ...previewStrokeStyle?.strokeOpacity != null && !isLaser ? { strokeOpacity: previewStrokeStyle.strokeOpacity } : {}
7149
+ ...previewStrokeStyle?.strokeOpacity != null && !isLaser ? { strokeOpacity: previewStrokeStyle.strokeOpacity } : {},
7150
+ ...previewStrokeStyle?.strokeDash != null && !isLaser ? { strokeDash: previewStrokeStyle.strokeDash } : {}
6766
7151
  };
6767
7152
  const payload = computeFreehandSvgPayload(
6768
7153
  raw,
@@ -6806,6 +7191,7 @@ function InteractionOverlay({
6806
7191
  strokeOpacity: isLaser ? 0.85 : payload.strokeOpacity,
6807
7192
  strokeLinecap: "round",
6808
7193
  strokeLinejoin: "round",
7194
+ strokeDasharray: payload.strokeDasharray,
6809
7195
  shapeRendering: "geometricPrecision"
6810
7196
  }
6811
7197
  );
@@ -7211,6 +7597,9 @@ function TextEditOverlay({
7211
7597
  const fontSize = fsWorld * z;
7212
7598
  const lineHeight = fsWorld * (22 / 18) * z;
7213
7599
  const fixedBox = !!item.textFixedBounds;
7600
+ const padTop = EDIT_TOP_PAD_RATIO * fsWorld * z;
7601
+ const padX = 4 * z;
7602
+ const textColor = item.stroke ?? "#1d1d1d";
7214
7603
  return /* @__PURE__ */ jsx(
7215
7604
  "div",
7216
7605
  {
@@ -7241,14 +7630,14 @@ function TextEditOverlay({
7241
7630
  width: "100%",
7242
7631
  height: "100%",
7243
7632
  margin: 0,
7244
- padding: 2,
7633
+ padding: `${padTop}px ${padX}px 0px ${padX}px`,
7245
7634
  border: "none",
7246
7635
  borderRadius: 0,
7247
7636
  resize: "none",
7248
- fontFamily: "system-ui, sans-serif",
7637
+ fontFamily: TEXT_FONT_FAMILY,
7249
7638
  fontSize,
7250
7639
  lineHeight: `${lineHeight}px`,
7251
- color: "#0f172a",
7640
+ color: textColor,
7252
7641
  background: "transparent",
7253
7642
  backgroundColor: "transparent",
7254
7643
  outline: "none",
@@ -7256,7 +7645,6 @@ function TextEditOverlay({
7256
7645
  appearance: "none",
7257
7646
  WebkitAppearance: "none",
7258
7647
  WebkitTapHighlightColor: "transparent",
7259
- /* Auto-sized text: no soft wrap / scrollbar; fixed box: wrap like the SVG. */
7260
7648
  whiteSpace: fixedBox ? "pre-wrap" : "pre",
7261
7649
  overflow: fixedBox ? "auto" : "hidden",
7262
7650
  overflowX: fixedBox ? "hidden" : "hidden",
@@ -7632,6 +8020,7 @@ var VectorViewport = forwardRef(
7632
8020
  selectedIds: selectedIdsProp,
7633
8021
  onSelectionChange,
7634
8022
  onItemsChange: consumerOnItemsChange,
8023
+ onActivateLink,
7635
8024
  onWorldPointerDown: consumerOnWorldPointerDown,
7636
8025
  toolbar,
7637
8026
  navMenu,
@@ -7812,6 +8201,8 @@ var VectorViewport = forwardRef(
7812
8201
  const itemsRef = useRef(items);
7813
8202
  const onWorldPointerDownRef = useRef(onWorldPointerDown);
7814
8203
  const onItemsChangeRef = useRef(onItemsChange);
8204
+ const onActivateLinkRef = useRef(onActivateLink);
8205
+ onActivateLinkRef.current = onActivateLink;
7815
8206
  const assetStoreRef = useRef(assetStore);
7816
8207
  assetStoreRef.current = assetStore;
7817
8208
  const customPlacementRef = useRef(customPlacement);
@@ -7943,14 +8334,32 @@ var VectorViewport = forwardRef(
7943
8334
  imageStoreRef.current = new IndexedDbImageStore();
7944
8335
  }
7945
8336
  const rememberedImageBlobHrefsRef = useRef(/* @__PURE__ */ new Set());
7946
- const strokeStyleRef = useRef({ ...DEFAULT_STROKE_STYLE });
8337
+ const strokeStyleRef = useRef({
8338
+ ...DEFAULT_STROKE_STYLE,
8339
+ textFontSize: DEFAULT_TEXT_FONT_SIZE
8340
+ });
7947
8341
  const [strokeStyleState, setStrokeStyleState] = useState({
7948
- ...DEFAULT_STROKE_STYLE
8342
+ ...DEFAULT_STROKE_STYLE,
8343
+ textFontSize: DEFAULT_TEXT_FONT_SIZE
7949
8344
  });
7950
8345
  const setCurrentStrokeStyle = useCallback((next) => {
7951
8346
  strokeStyleRef.current = next;
7952
8347
  setStrokeStyleState(next);
7953
8348
  }, []);
8349
+ const patchCurrentStrokeStyle = useCallback(
8350
+ (patch) => {
8351
+ const merged = { ...strokeStyleRef.current };
8352
+ for (const key of Object.keys(patch)) {
8353
+ const v = patch[key];
8354
+ if (v !== void 0) {
8355
+ merged[key] = v;
8356
+ }
8357
+ }
8358
+ strokeStyleRef.current = merged;
8359
+ setStrokeStyleState(merged);
8360
+ },
8361
+ []
8362
+ );
7954
8363
  const progressiveQueueRef = useRef(/* @__PURE__ */ new Set());
7955
8364
  useEffect(() => {
7956
8365
  const store = imageStoreRef.current;
@@ -8099,10 +8508,11 @@ var VectorViewport = forwardRef(
8099
8508
  change(
8100
8509
  exists ? replaceItem(itemsRef.current, id, item) : [...itemsRef.current, item]
8101
8510
  );
8102
- setCurrentStrokeStyle({
8511
+ patchCurrentStrokeStyle({
8103
8512
  stroke: item.stroke ?? DEFAULT_STROKE_STYLE.stroke,
8104
8513
  strokeWidth: item.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth,
8105
- ...item.strokeOpacity != null ? { strokeOpacity: item.strokeOpacity } : {}
8514
+ strokeOpacity: item.strokeOpacity,
8515
+ strokeDash: item.strokeDash
8106
8516
  });
8107
8517
  }
8108
8518
  if (!shouldKeepToolForContinuousPenInput(args.tool, args.pointerType)) {
@@ -8110,8 +8520,8 @@ var VectorViewport = forwardRef(
8110
8520
  }
8111
8521
  },
8112
8522
  [
8523
+ patchCurrentStrokeStyle,
8113
8524
  requestAutoResetTool,
8114
- setCurrentStrokeStyle,
8115
8525
  shouldKeepToolForContinuousPenInput
8116
8526
  ]
8117
8527
  );
@@ -8847,24 +9257,28 @@ var VectorViewport = forwardRef(
8847
9257
  renderFrame();
8848
9258
  }, [renderFrame]);
8849
9259
  useEffect(() => {
9260
+ const current = strokeStyleRef.current;
8850
9261
  if (toolId === "marker") {
8851
9262
  setCurrentStrokeStyle({
8852
- ...strokeStyleRef.current,
9263
+ ...current,
8853
9264
  ...MARKER_TOOL_STYLE
8854
9265
  });
8855
9266
  return;
8856
9267
  }
8857
- const current = strokeStyleRef.current;
8858
9268
  if (isDefaultMarkerToolStyle(current)) {
8859
9269
  setCurrentStrokeStyle({
8860
9270
  stroke: DEFAULT_STROKE_STYLE.stroke,
8861
- strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth
9271
+ strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth,
9272
+ strokeDash: current.strokeDash,
9273
+ textFontSize: current.textFontSize
8862
9274
  });
8863
9275
  return;
8864
9276
  }
8865
9277
  setCurrentStrokeStyle({
8866
9278
  stroke: current.stroke,
8867
- strokeWidth: toolId === "draw" ? 10 : current.strokeWidth
9279
+ strokeWidth: toolId === "draw" ? 10 : current.strokeWidth,
9280
+ strokeDash: current.strokeDash,
9281
+ textFontSize: current.textFontSize
8868
9282
  });
8869
9283
  }, [setCurrentStrokeStyle, toolId]);
8870
9284
  useEffect(() => {
@@ -8873,15 +9287,14 @@ var VectorViewport = forwardRef(
8873
9287
  const it = items.find((i) => i.id === primaryId);
8874
9288
  if (!it || it.locked) return;
8875
9289
  if (it.toolKind === "image") return;
8876
- const next = {
9290
+ patchCurrentStrokeStyle({
8877
9291
  stroke: it.stroke ?? DEFAULT_STROKE_STYLE.stroke,
8878
- strokeWidth: it.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth
8879
- };
8880
- if (it.strokeOpacity != null) {
8881
- next.strokeOpacity = it.strokeOpacity;
8882
- }
8883
- setCurrentStrokeStyle(next);
8884
- }, [effectiveSelectedIds, items, setCurrentStrokeStyle]);
9292
+ strokeWidth: it.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth,
9293
+ strokeOpacity: it.strokeOpacity,
9294
+ strokeDash: it.strokeDash,
9295
+ ...it.toolKind === "text" && it.textFontSize != null ? { textFontSize: it.textFontSize } : {}
9296
+ });
9297
+ }, [effectiveSelectedIds, items, patchCurrentStrokeStyle]);
8885
9298
  const handleSelectionStyleChange = useCallback(
8886
9299
  (patch) => {
8887
9300
  const change = onItemsChangeRef.current;
@@ -8897,25 +9310,15 @@ var VectorViewport = forwardRef(
8897
9310
  nextList = replaceItem(nextList, id, out);
8898
9311
  }
8899
9312
  change(nextList);
8900
- setCurrentStrokeStyle({
8901
- ...strokeStyleRef.current,
8902
- stroke: patch.stroke,
8903
- strokeWidth: patch.strokeWidth,
8904
- ...patch.strokeOpacity != null ? { strokeOpacity: patch.strokeOpacity } : {}
8905
- });
9313
+ patchCurrentStrokeStyle(patch);
8906
9314
  },
8907
- [setCurrentStrokeStyle]
9315
+ [patchCurrentStrokeStyle]
8908
9316
  );
8909
9317
  const handleActiveToolStyleChange = useCallback(
8910
9318
  (patch) => {
8911
- const current = strokeStyleRef.current;
8912
- setCurrentStrokeStyle({
8913
- stroke: patch.stroke,
8914
- strokeWidth: patch.strokeWidth,
8915
- ...patch.strokeOpacity != null ? { strokeOpacity: patch.strokeOpacity } : current.strokeOpacity != null ? { strokeOpacity: current.strokeOpacity } : {}
8916
- });
9319
+ patchCurrentStrokeStyle(patch);
8917
9320
  },
8918
- [setCurrentStrokeStyle]
9321
+ [patchCurrentStrokeStyle]
8919
9322
  );
8920
9323
  const commitTextEdit = useCallback(() => {
8921
9324
  const id = editingTextIdRef.current;
@@ -9087,13 +9490,27 @@ var VectorViewport = forwardRef(
9087
9490
  );
9088
9491
  const handleOverlayDoubleClick = useCallback(
9089
9492
  (e) => {
9090
- if (!interactiveRef.current || !onItemsChangeRef.current) return;
9091
- if (toolIdRef.current !== "select") return;
9092
- e.preventDefault();
9093
9493
  const cam = cameraRef.current;
9094
9494
  if (!cam) return;
9095
9495
  const { worldX, worldY } = screenToWorld(e.clientX, e.clientY);
9096
9496
  const lineHitWorld = 10 / cam.zoom;
9497
+ const linkHit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
9498
+ lineHitWorld,
9499
+ ignoreLocked: false
9500
+ });
9501
+ const link = linkHit ? getLinkData(linkHit) : null;
9502
+ if (linkHit && link) {
9503
+ e.preventDefault();
9504
+ if (onActivateLinkRef.current) {
9505
+ onActivateLinkRef.current({ link, item: linkHit });
9506
+ } else if (typeof window !== "undefined" && typeof window.open === "function") {
9507
+ window.open(link.href, "_blank", "noopener,noreferrer");
9508
+ }
9509
+ return;
9510
+ }
9511
+ if (!interactiveRef.current || !onItemsChangeRef.current) return;
9512
+ if (toolIdRef.current !== "select") return;
9513
+ e.preventDefault();
9097
9514
  const hit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
9098
9515
  lineHitWorld,
9099
9516
  ignoreLocked: true
@@ -10034,16 +10451,21 @@ var VectorViewport = forwardRef(
10034
10451
  const id = createShapeId();
10035
10452
  const { x: worldX, y: worldY } = st.startWorld;
10036
10453
  if (st.tool === "text") {
10454
+ const fs = strokeStyleRef.current.textFontSize;
10455
+ const baseline = textBaselineYFor(fs);
10456
+ const lh = textLineHeightFor(fs);
10457
+ const minH = Math.max(26, baseline + Math.max(4, lh * 0.2));
10037
10458
  const newItem = createTextItem(
10038
10459
  id,
10039
10460
  {
10040
10461
  x: worldX - 4,
10041
- y: worldY - 24,
10462
+ y: worldY - baseline,
10042
10463
  width: 160,
10043
- height: 36
10464
+ height: minH
10044
10465
  },
10045
10466
  "",
10046
- strokeStyleRef.current
10467
+ strokeStyleRef.current,
10468
+ fs
10047
10469
  );
10048
10470
  editingTextSnapshotRef.current = {
10049
10471
  ...newItem,
@@ -10194,12 +10616,63 @@ var VectorViewport = forwardRef(
10194
10616
  return effectiveSelectedIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
10195
10617
  }, [effectiveSelectedIds, resolvedItems]);
10196
10618
  const activeToolInspectorStyle = useMemo(() => {
10197
- if (toolId !== "draw") return void 0;
10198
- return {
10199
- toolKind: "draw",
10200
- label: "Estilo da ferramenta",
10201
- ...strokeStyleState
10202
- };
10619
+ if (toolId === "draw") {
10620
+ return {
10621
+ toolKind: "draw",
10622
+ label: "Caneta",
10623
+ ...strokeStyleState
10624
+ };
10625
+ }
10626
+ if (toolId === "marker") {
10627
+ return {
10628
+ toolKind: "marker",
10629
+ label: "Marcador",
10630
+ ...strokeStyleState
10631
+ };
10632
+ }
10633
+ if (toolId === "text") {
10634
+ return {
10635
+ toolKind: "text",
10636
+ label: "Texto",
10637
+ ...strokeStyleState
10638
+ };
10639
+ }
10640
+ if (toolId === "rect") {
10641
+ return {
10642
+ toolKind: "rect",
10643
+ label: "Ret\xE2ngulo",
10644
+ ...strokeStyleState
10645
+ };
10646
+ }
10647
+ if (toolId === "ellipse") {
10648
+ return {
10649
+ toolKind: "ellipse",
10650
+ label: "Elipse",
10651
+ ...strokeStyleState
10652
+ };
10653
+ }
10654
+ if (toolId === "architectural-cloud") {
10655
+ return {
10656
+ toolKind: "architectural-cloud",
10657
+ label: "Nuvem",
10658
+ ...strokeStyleState
10659
+ };
10660
+ }
10661
+ if (toolId === "line") {
10662
+ return {
10663
+ toolKind: "line",
10664
+ label: "Linha",
10665
+ ...strokeStyleState
10666
+ };
10667
+ }
10668
+ if (toolId === "arrow") {
10669
+ return {
10670
+ toolKind: "arrow",
10671
+ label: "Seta",
10672
+ ...strokeStyleState
10673
+ };
10674
+ }
10675
+ return void 0;
10203
10676
  }, [strokeStyleState, toolId]);
10204
10677
  const eraserPreviewItemsForOverlay = useMemo(() => {
10205
10678
  return eraserPreviewIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);