@pixldocs/canvas-renderer 0.5.241 → 0.5.243

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.
@@ -2,7 +2,7 @@
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
- var _a;
5
+ var _a, _b;
6
6
  const jsxRuntime = require("react/jsx-runtime");
7
7
  const react = require("react");
8
8
  const reactDom = require("react-dom");
@@ -291,13 +291,13 @@ function lineToString(line) {
291
291
  return Array.isArray(line) ? line.join("") : String(line ?? "");
292
292
  }
293
293
  function measureTextLineWithCanvas(textbox, lineText, lineIndex) {
294
- var _a2, _b, _c, _d, _e;
294
+ var _a2, _b2, _c, _d, _e;
295
295
  if (!lineText) return 0;
296
296
  const ctx = getMeasureContext();
297
297
  if (!ctx) return null;
298
298
  const tb = textbox;
299
299
  const fontSize = Number(((_a2 = tb.getValueOfPropertyAt) == null ? void 0 : _a2.call(tb, lineIndex, 0, "fontSize")) ?? textbox.fontSize ?? 16);
300
- const fontStyle = String(((_b = tb.getValueOfPropertyAt) == null ? void 0 : _b.call(tb, lineIndex, 0, "fontStyle")) ?? textbox.fontStyle ?? "normal");
300
+ const fontStyle = String(((_b2 = tb.getValueOfPropertyAt) == null ? void 0 : _b2.call(tb, lineIndex, 0, "fontStyle")) ?? textbox.fontStyle ?? "normal");
301
301
  const fontWeight = String(((_c = tb.getValueOfPropertyAt) == null ? void 0 : _c.call(tb, lineIndex, 0, "fontWeight")) ?? textbox.fontWeight ?? "400");
302
302
  const fontFamily = String(((_d = tb.getValueOfPropertyAt) == null ? void 0 : _d.call(tb, lineIndex, 0, "fontFamily")) ?? textbox.fontFamily ?? "Open Sans");
303
303
  const charSpacing = Number(((_e = tb.getValueOfPropertyAt) == null ? void 0 : _e.call(tb, lineIndex, 0, "charSpacing")) ?? textbox.charSpacing ?? 0);
@@ -786,7 +786,7 @@ function resolveStackGroupEffectivePositions(group, pageChildren, options) {
786
786
  return out;
787
787
  }
788
788
  function groupBoundsFromChildren(group, pageChildren, options) {
789
- var _a2, _b;
789
+ var _a2, _b2;
790
790
  const kids = group.children ?? [];
791
791
  if (kids.length === 0) {
792
792
  const w = group.width;
@@ -802,7 +802,7 @@ function groupBoundsFromChildren(group, pageChildren, options) {
802
802
  return { width: b.width, height: b.height };
803
803
  })();
804
804
  const cl = positions ? ((_a2 = positions.get(child.id)) == null ? void 0 : _a2.left) ?? getNodeLeft(child) : getNodeLeft(child);
805
- const ct = positions ? ((_b = positions.get(child.id)) == null ? void 0 : _b.top) ?? getNodeTop(child) : getNodeTop(child);
805
+ const ct = positions ? ((_b2 = positions.get(child.id)) == null ? void 0 : _b2.top) ?? getNodeTop(child) : getNodeTop(child);
806
806
  minX = Math.min(minX, cl);
807
807
  minY = Math.min(minY, ct);
808
808
  maxX = Math.max(maxX, cl + sz.width);
@@ -840,14 +840,14 @@ function getNodeBoundsFromChildren(node) {
840
840
  return getNodeBounds(node);
841
841
  }
842
842
  function absoluteBoundsRecur(node, pageChildren, options) {
843
- var _a2, _b;
843
+ var _a2, _b2;
844
844
  const parent = pageChildren ? findParentGroup(pageChildren, node.id) : null;
845
845
  const b = getNodeBounds(node, pageChildren);
846
846
  if (!parent) return b;
847
847
  const parentAbs = absoluteBoundsRecur(parent, pageChildren);
848
848
  const isStackParent = isStackLayoutMode(parent.layoutMode);
849
849
  const inParentLeft = isStackParent ? ((_a2 = resolveStackGroupEffectivePositions(parent, pageChildren).get(node.id)) == null ? void 0 : _a2.left) ?? b.left : b.left;
850
- const inParentTop = isStackParent ? ((_b = resolveStackGroupEffectivePositions(parent, pageChildren).get(node.id)) == null ? void 0 : _b.top) ?? b.top : b.top;
850
+ const inParentTop = isStackParent ? ((_b2 = resolveStackGroupEffectivePositions(parent, pageChildren).get(node.id)) == null ? void 0 : _b2.top) ?? b.top : b.top;
851
851
  return {
852
852
  left: parentAbs.left + inParentLeft,
853
853
  top: parentAbs.top + inParentTop,
@@ -3565,7 +3565,7 @@ const clearFontCacheAndRerender = (canvas, options = {}) => {
3565
3565
  }
3566
3566
  };
3567
3567
  const collectUnderlineMetrics = (obj) => {
3568
- var _a2, _b;
3568
+ var _a2, _b2;
3569
3569
  if (obj instanceof fabric__namespace.Textbox) {
3570
3570
  if (!obj.underline) return [];
3571
3571
  const lineWidths = obj.__lineWidths;
@@ -3580,7 +3580,7 @@ const clearFontCacheAndRerender = (canvas, options = {}) => {
3580
3580
  height: obj.height ?? null,
3581
3581
  scaleX: obj.scaleX,
3582
3582
  scaleY: obj.scaleY,
3583
- lineCount: ((_b = obj.textLines) == null ? void 0 : _b.length) ?? 0,
3583
+ lineCount: ((_b2 = obj.textLines) == null ? void 0 : _b2.length) ?? 0,
3584
3584
  maxLineWidth: lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : null
3585
3585
  }];
3586
3586
  }
@@ -3610,13 +3610,13 @@ const clearFontCacheAndRerender = (canvas, options = {}) => {
3610
3610
  canvas.requestRenderAll();
3611
3611
  };
3612
3612
  const ensureFontLoaded = async (fontFamily) => {
3613
- var _a2, _b, _c;
3613
+ var _a2, _b2, _c;
3614
3614
  if (!fontFamily) return;
3615
3615
  if (LOCAL_FONTS.has(fontFamily)) {
3616
3616
  try {
3617
3617
  const isLoaded = (_a2 = document.fonts) == null ? void 0 : _a2.check(`16px "${fontFamily}"`);
3618
3618
  if (isLoaded) return;
3619
- await ((_b = document.fonts) == null ? void 0 : _b.load(`16px "${fontFamily}"`));
3619
+ await ((_b2 = document.fonts) == null ? void 0 : _b2.load(`16px "${fontFamily}"`));
3620
3620
  await ((_c = document.fonts) == null ? void 0 : _c.load(`bold 16px "${fontFamily}"`));
3621
3621
  } catch (e) {
3622
3622
  console.warn(`Failed to ensure local font loaded: ${fontFamily}`, e);
@@ -4111,7 +4111,7 @@ function createImageClipPath(element, imgWidth, imgHeight) {
4111
4111
  }
4112
4112
  }
4113
4113
  async function loadImageAsync(element, placeholder, fc, fabricRef, syncLockedRef, isTransforming) {
4114
- var _a2, _b, _c, _d;
4114
+ var _a2, _b2, _c, _d;
4115
4115
  const imageUrl = element.src || element.imageUrl;
4116
4116
  if (!imageUrl) return;
4117
4117
  const nextSvgColorMap = element.svgColorMap ? JSON.stringify(element.svgColorMap) : "";
@@ -4275,7 +4275,7 @@ async function loadImageAsync(element, placeholder, fc, fabricRef, syncLockedRef
4275
4275
  if (existingCropGroup) {
4276
4276
  const existingImg = (_a2 = existingCropGroup.__cropData) == null ? void 0 : _a2._img;
4277
4277
  if (existingImg) {
4278
- panX = ((_b = existingImg._ct) == null ? void 0 : _b.panX) ?? existingImg.__panX ?? 0.5;
4278
+ panX = ((_b2 = existingImg._ct) == null ? void 0 : _b2.panX) ?? existingImg.__panX ?? 0.5;
4279
4279
  panY = ((_c = existingImg._ct) == null ? void 0 : _c.panY) ?? existingImg.__panY ?? 0.5;
4280
4280
  zoom = ((_d = existingImg._ct) == null ? void 0 : _d.zoom) ?? 1;
4281
4281
  }
@@ -4483,11 +4483,11 @@ function isLuminanceMaskClipPath(clipPath) {
4483
4483
  return Boolean(clipPath && clipPath.__svgMaskType === "luminance");
4484
4484
  }
4485
4485
  function syncSvgMaskClipPath(cropGroup) {
4486
- var _a2, _b;
4486
+ var _a2, _b2;
4487
4487
  const clipPath = cropGroup.clipPath;
4488
4488
  if (!isCropGroup(cropGroup)) return;
4489
4489
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4490
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4490
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4491
4491
  if (frameW <= 0 || frameH <= 0) return;
4492
4492
  if (isSvgMaskClipPath(clipPath)) {
4493
4493
  fitMaskGroupToFrame(clipPath, frameW, frameH);
@@ -4511,12 +4511,12 @@ async function detectMaskType(svgUrl) {
4511
4511
  }
4512
4512
  }
4513
4513
  async function applySvgMaskToCropGroup(cropGroup, svgUrl) {
4514
- var _a2, _b, _c;
4514
+ var _a2, _b2, _c;
4515
4515
  if (!isCropGroup(cropGroup)) {
4516
4516
  throw new Error("Selected object is not a crop group / image");
4517
4517
  }
4518
4518
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4519
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4519
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4520
4520
  if (frameW <= 0 || frameH <= 0) {
4521
4521
  throw new Error("Crop group has no frame dimensions");
4522
4522
  }
@@ -4602,12 +4602,12 @@ async function buildLuminanceAlphaCanvas(svgUrl, frameW, frameH) {
4602
4602
  return canvas;
4603
4603
  }
4604
4604
  async function applyLuminanceMaskToCropGroup(cropGroup, svgUrl) {
4605
- var _a2, _b, _c;
4605
+ var _a2, _b2, _c;
4606
4606
  if (!isCropGroup(cropGroup)) {
4607
4607
  throw new Error("Selected object is not a crop group / image");
4608
4608
  }
4609
4609
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4610
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4610
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4611
4611
  if (frameW <= 0 || frameH <= 0) {
4612
4612
  throw new Error("Crop group has no frame dimensions");
4613
4613
  }
@@ -4656,10 +4656,10 @@ function getAppliedSvgMaskType(obj) {
4656
4656
  return t === "luminance" || t === "shape" ? t : null;
4657
4657
  }
4658
4658
  function clearSvgMaskFromCropGroup(cropGroup) {
4659
- var _a2, _b, _c;
4659
+ var _a2, _b2, _c;
4660
4660
  if (!isCropGroup(cropGroup)) return;
4661
4661
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4662
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4662
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4663
4663
  const rect = new fabric__namespace.Rect({
4664
4664
  width: frameW,
4665
4665
  height: frameH,
@@ -4693,17 +4693,16 @@ const svgMaskApply = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
4693
4693
  isSvgMaskClipPath,
4694
4694
  syncSvgMaskClipPath
4695
4695
  }, Symbol.toStringTag, { value: "Module" }));
4696
+ const SELECTION_BORDER_SCALE$1 = 2;
4696
4697
  function clamp$1(v, min, max) {
4697
4698
  return Math.max(min, Math.min(max, v));
4698
4699
  }
4699
4700
  function applyControlSizeForZoom(canvas, obj) {
4700
4701
  if (!canvas || !obj) return;
4701
- const z = canvas.getZoom() || 1;
4702
- const size = Math.max(6, Math.round(8 * z));
4703
4702
  obj.set({
4704
- cornerSize: size,
4705
- // Subtle but visible border at any zoom (min ~1.25px).
4706
- borderScaleFactor: Math.max(1.25, 1.25 * z)
4703
+ cornerSize: 10,
4704
+ touchCornerSize: 20,
4705
+ borderScaleFactor: SELECTION_BORDER_SCALE$1
4707
4706
  });
4708
4707
  obj.setCoords();
4709
4708
  }
@@ -5080,6 +5079,21 @@ function resizeFrameFromSide(g, side, localDx, localDy) {
5080
5079
  function installCanvaMaskControls(g) {
5081
5080
  const ct = g.__cropData;
5082
5081
  if (ct) ct.fit = ct.fit ?? "cover";
5082
+ const controlStyle = {
5083
+ cornerSize: 10,
5084
+ touchCornerSize: 20,
5085
+ borderScaleFactor: SELECTION_BORDER_SCALE$1,
5086
+ transparentCorners: false,
5087
+ cornerStyle: "circle",
5088
+ cornerColor: "hsl(217, 91%, 60%)",
5089
+ cornerStrokeColor: "#ffffff",
5090
+ borderColor: "hsl(217, 91%, 60%)"
5091
+ };
5092
+ g.set(controlStyle);
5093
+ const notifyResizeSnap = (target, corner) => {
5094
+ const handler = target.__resizeSnapHandler;
5095
+ if (typeof handler === "function") handler(target, corner);
5096
+ };
5083
5097
  g.setControlsVisibility({
5084
5098
  mt: true,
5085
5099
  mb: true,
@@ -5109,6 +5123,7 @@ function installCanvaMaskControls(g) {
5109
5123
  const { localDx, localDy } = getLocalDeltaStable(t, eventData);
5110
5124
  t.__lockScaleDuringCrop = true;
5111
5125
  resizeFrameFromSide(t, side, localDx, localDy);
5126
+ notifyResizeSnap(t, side);
5112
5127
  (_a2 = t.canvas) == null ? void 0 : _a2.requestRenderAll();
5113
5128
  return true;
5114
5129
  }
@@ -5133,7 +5148,9 @@ function installCanvaMaskControls(g) {
5133
5148
  if (canvas && canvas.__editLockRef) {
5134
5149
  canvas.__editLockRef.current = true;
5135
5150
  }
5136
- return resizeFrameFromCornerUniform(eventData, transform);
5151
+ const resized = resizeFrameFromCornerUniform(eventData, transform);
5152
+ if (resized) notifyResizeSnap(t, key);
5153
+ return resized;
5137
5154
  }
5138
5155
  });
5139
5156
  };
@@ -5142,6 +5159,8 @@ function installCanvaMaskControls(g) {
5142
5159
  g.controls.bl = makeCornerControl("bl", -0.5, 0.5, "nesw-resize");
5143
5160
  g.controls.br = makeCornerControl("br", 0.5, 0.5, "nwse-resize");
5144
5161
  g.__hasCustomControls = true;
5162
+ g.set(controlStyle);
5163
+ g.setCoords();
5145
5164
  }
5146
5165
  async function createMaskedImageElement({
5147
5166
  url,
@@ -5712,12 +5731,15 @@ function exitCropMode(g, commit = true) {
5712
5731
  g.hasControls = true;
5713
5732
  g.borderDashArray = void 0;
5714
5733
  installCanvaMaskControls(g);
5715
- g.borderColor = "#4f46e5";
5734
+ g.borderColor = "hsl(217, 91%, 60%)";
5716
5735
  g.borderDashArray = void 0;
5717
- g.cornerColor = "#ffffff";
5718
- g.cornerStrokeColor = "#4f46e5";
5719
- g.cornerStyle = "rect";
5736
+ g.cornerColor = "hsl(217, 91%, 60%)";
5737
+ g.cornerStrokeColor = "#ffffff";
5738
+ g.cornerStyle = "circle";
5720
5739
  g.transparentCorners = false;
5740
+ g.cornerSize = 10;
5741
+ g.touchCornerSize = 20;
5742
+ g.borderScaleFactor = 2;
5721
5743
  g[CROP_MODE_FLAG] = false;
5722
5744
  g.__cropZoomLastPointer = void 0;
5723
5745
  g.__lastPointerForCrop = void 0;
@@ -5726,6 +5748,16 @@ function exitCropMode(g, commit = true) {
5726
5748
  }
5727
5749
  canvas == null ? void 0 : canvas.requestRenderAll();
5728
5750
  }
5751
+ function boundsToSnapPoints(bounds) {
5752
+ return {
5753
+ left: bounds.left,
5754
+ right: bounds.left + bounds.width,
5755
+ top: bounds.top,
5756
+ bottom: bounds.top + bounds.height,
5757
+ centerX: bounds.left + bounds.width / 2,
5758
+ centerY: bounds.top + bounds.height / 2
5759
+ };
5760
+ }
5729
5761
  function getObjectSnapPoints(obj) {
5730
5762
  try {
5731
5763
  obj.setCoords();
@@ -5756,6 +5788,7 @@ function getObjectSnapPoints(obj) {
5756
5788
  function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5757
5789
  if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };
5758
5790
  const threshold = snapThreshold || 8;
5791
+ const centerThreshold = threshold * 1.75;
5759
5792
  const newGuides = [];
5760
5793
  const horizontalSnaps = [];
5761
5794
  const verticalSnaps = [];
@@ -5775,12 +5808,24 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5775
5808
  horizontalSnaps.push({ delta: targetPoint - objPoint, distance, guide });
5776
5809
  }
5777
5810
  };
5811
+ const addCanvasCenterVerticalSnap = (objPoint, targetPoint, guide) => {
5812
+ const distance = Math.abs(objPoint - targetPoint);
5813
+ if (distance < centerThreshold) {
5814
+ verticalSnaps.push({ delta: targetPoint - objPoint, distance: distance * 0.85, guide });
5815
+ }
5816
+ };
5817
+ const addCanvasCenterHorizontalSnap = (objPoint, targetPoint, guide) => {
5818
+ const distance = Math.abs(objPoint - targetPoint);
5819
+ if (distance < centerThreshold) {
5820
+ horizontalSnaps.push({ delta: targetPoint - objPoint, distance: distance * 0.85, guide });
5821
+ }
5822
+ };
5778
5823
  addVerticalSnap(moving.left, 0, { type: "vertical", position: 0 });
5779
5824
  addVerticalSnap(moving.right, canvasWidth, { type: "vertical", position: canvasWidth });
5780
5825
  addHorizontalSnap(moving.top, 0, { type: "horizontal", position: 0 });
5781
5826
  addHorizontalSnap(moving.bottom, canvasHeight, { type: "horizontal", position: canvasHeight });
5782
- addVerticalSnap(moving.centerX, canvasCenterX, { type: "vertical", position: canvasCenterX });
5783
- addHorizontalSnap(moving.centerY, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5827
+ addCanvasCenterVerticalSnap(moving.centerX, canvasCenterX, { type: "vertical", position: canvasCenterX });
5828
+ addCanvasCenterHorizontalSnap(moving.centerY, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5784
5829
  const rawObjects = canvas.getObjects();
5785
5830
  const allObjects = [];
5786
5831
  for (const obj of rawObjects) {
@@ -5800,16 +5845,22 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5800
5845
  }
5801
5846
  for (const otherObj of allObjects) {
5802
5847
  const other = getObjectSnapPoints(otherObj);
5803
- addVerticalSnap(moving.left, other.left, { type: "vertical", position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5804
- addVerticalSnap(moving.right, other.right, { type: "vertical", position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5805
- addVerticalSnap(moving.left, other.right, { type: "vertical", position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5806
- addVerticalSnap(moving.right, other.left, { type: "vertical", position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5807
- addVerticalSnap(moving.centerX, other.centerX, { type: "vertical", position: other.centerX, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5808
- addHorizontalSnap(moving.top, other.top, { type: "horizontal", position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5809
- addHorizontalSnap(moving.bottom, other.bottom, { type: "horizontal", position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5810
- addHorizontalSnap(moving.top, other.bottom, { type: "horizontal", position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5811
- addHorizontalSnap(moving.bottom, other.top, { type: "horizontal", position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5812
- addHorizontalSnap(moving.centerY, other.centerY, { type: "horizontal", position: other.centerY, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5848
+ const tb = {
5849
+ left: other.left,
5850
+ top: other.top,
5851
+ width: other.right - other.left,
5852
+ height: other.bottom - other.top
5853
+ };
5854
+ addVerticalSnap(moving.left, other.left, { type: "vertical", position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom), targetBounds: tb });
5855
+ addVerticalSnap(moving.right, other.right, { type: "vertical", position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom), targetBounds: tb });
5856
+ addVerticalSnap(moving.left, other.right, { type: "vertical", position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom), targetBounds: tb });
5857
+ addVerticalSnap(moving.right, other.left, { type: "vertical", position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom), targetBounds: tb });
5858
+ addVerticalSnap(moving.centerX, other.centerX, { type: "vertical", position: other.centerX, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom), targetBounds: tb });
5859
+ addHorizontalSnap(moving.top, other.top, { type: "horizontal", position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right), targetBounds: tb });
5860
+ addHorizontalSnap(moving.bottom, other.bottom, { type: "horizontal", position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right), targetBounds: tb });
5861
+ addHorizontalSnap(moving.top, other.bottom, { type: "horizontal", position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right), targetBounds: tb });
5862
+ addHorizontalSnap(moving.bottom, other.top, { type: "horizontal", position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right), targetBounds: tb });
5863
+ addHorizontalSnap(moving.centerY, other.centerY, { type: "horizontal", position: other.centerY, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right), targetBounds: tb });
5813
5864
  }
5814
5865
  let snapDx = 0;
5815
5866
  let snapDy = 0;
@@ -5858,6 +5909,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5858
5909
  const equalGap = Math.round(newLeft - bestPair.A.right);
5859
5910
  const bracketY = (Math.max(bestPair.A.top, projectedTop, bestPair.B.top) + Math.min(bestPair.A.bottom, projectedBottom, bestPair.B.bottom)) / 2;
5860
5911
  if (equalGap > 0) {
5912
+ const aTb = { left: bestPair.A.left, top: bestPair.A.top, width: bestPair.A.right - bestPair.A.left, height: bestPair.A.bottom - bestPair.A.top };
5913
+ const bTb = { left: bestPair.B.left, top: bestPair.B.top, width: bestPair.B.right - bestPair.B.left, height: bestPair.B.bottom - bestPair.B.top };
5861
5914
  newGuides.push({
5862
5915
  type: "horizontal",
5863
5916
  position: bracketY,
@@ -5865,7 +5918,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5865
5918
  gap: equalGap,
5866
5919
  start: bestPair.A.right,
5867
5920
  end: newLeft,
5868
- bracketAt: bracketY
5921
+ bracketAt: bracketY,
5922
+ targetBoundsList: [aTb, bTb]
5869
5923
  });
5870
5924
  newGuides.push({
5871
5925
  type: "horizontal",
@@ -5874,7 +5928,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5874
5928
  gap: equalGap,
5875
5929
  start: newRight,
5876
5930
  end: bestPair.B.left,
5877
- bracketAt: bracketY
5931
+ bracketAt: bracketY,
5932
+ targetBoundsList: [aTb, bTb]
5878
5933
  });
5879
5934
  }
5880
5935
  }
@@ -5906,6 +5961,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5906
5961
  const equalGap = Math.round(newTop - bestPair.A.bottom);
5907
5962
  const bracketX = (Math.max(bestPair.A.left, projectedLeft, bestPair.B.left) + Math.min(bestPair.A.right, projectedRight, bestPair.B.right)) / 2;
5908
5963
  if (equalGap > 0) {
5964
+ const aTb = { left: bestPair.A.left, top: bestPair.A.top, width: bestPair.A.right - bestPair.A.left, height: bestPair.A.bottom - bestPair.A.top };
5965
+ const bTb = { left: bestPair.B.left, top: bestPair.B.top, width: bestPair.B.right - bestPair.B.left, height: bestPair.B.bottom - bestPair.B.top };
5909
5966
  newGuides.push({
5910
5967
  type: "vertical",
5911
5968
  position: bracketX,
@@ -5913,7 +5970,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5913
5970
  gap: equalGap,
5914
5971
  start: bestPair.A.bottom,
5915
5972
  end: newTop,
5916
- bracketAt: bracketX
5973
+ bracketAt: bracketX,
5974
+ targetBoundsList: [aTb, bTb]
5917
5975
  });
5918
5976
  newGuides.push({
5919
5977
  type: "vertical",
@@ -5922,16 +5980,17 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5922
5980
  gap: equalGap,
5923
5981
  start: newBottom,
5924
5982
  end: bestPair.B.top,
5925
- bracketAt: bracketX
5983
+ bracketAt: bracketX,
5984
+ targetBoundsList: [aTb, bTb]
5926
5985
  });
5927
5986
  }
5928
5987
  }
5929
5988
  }
5930
5989
  return { guides: newGuides, snapDx, snapDy };
5931
5990
  }
5932
- function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5991
+ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold, additionalBounds = []) {
5933
5992
  if (!snapToGuides) return [];
5934
- const threshold = snapThreshold || 5;
5993
+ const threshold = snapThreshold;
5935
5994
  const newGuides = [];
5936
5995
  const scaling = getObjectSnapPoints(scalingObj);
5937
5996
  const scalingId = getObjectId(scalingObj);
@@ -5944,7 +6003,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5944
6003
  const checkVerticalSnap = (edgePosition, targetPosition, guide) => {
5945
6004
  const dist = Math.abs(edgePosition - targetPosition);
5946
6005
  if (dist < threshold) {
5947
- newGuides.push({ ...guide, distance: Math.round(dist) });
6006
+ newGuides.push({ ...guide, kind: "alignment" });
5948
6007
  return true;
5949
6008
  }
5950
6009
  return false;
@@ -5952,7 +6011,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5952
6011
  const checkHorizontalSnap = (edgePosition, targetPosition, guide) => {
5953
6012
  const dist = Math.abs(edgePosition - targetPosition);
5954
6013
  if (dist < threshold) {
5955
- newGuides.push({ ...guide, distance: Math.round(dist) });
6014
+ newGuides.push({ ...guide, kind: "alignment" });
5956
6015
  return true;
5957
6016
  }
5958
6017
  return false;
@@ -5974,12 +6033,39 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5974
6033
  checkHorizontalSnap(scaling.bottom, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5975
6034
  }
5976
6035
  const rawObjects = canvas.getObjects();
6036
+ const objectBounds = [];
5977
6037
  for (const obj of rawObjects) {
5978
6038
  if (obj === scalingObj) continue;
5979
6039
  const objId = getObjectId(obj);
5980
6040
  if (objId === "__background__") continue;
5981
6041
  if (objId && objId === scalingId) continue;
5982
- const other = getObjectSnapPoints(obj);
6042
+ if (scalingObj instanceof fabric__namespace.ActiveSelection && scalingObj.contains(obj)) continue;
6043
+ if (scalingObj instanceof fabric__namespace.Group && typeof scalingObj.getObjects === "function") {
6044
+ const inner = scalingObj.getObjects();
6045
+ if (inner.includes(obj)) continue;
6046
+ }
6047
+ objectBounds.push(getObjectSnapPoints(obj));
6048
+ }
6049
+ for (const bounds of additionalBounds) {
6050
+ objectBounds.push(boundsToSnapPoints(bounds));
6051
+ }
6052
+ let bestWidthMatch = null;
6053
+ let bestWidthDiff = threshold + 1;
6054
+ let bestHeightMatch = null;
6055
+ let bestHeightDiff = threshold + 1;
6056
+ for (const other of objectBounds) {
6057
+ const otherWidth = other.right - other.left;
6058
+ const otherHeight = other.bottom - other.top;
6059
+ const widthDiff = Math.abs(scaling.right - scaling.left - otherWidth);
6060
+ const heightDiff = Math.abs(scaling.bottom - scaling.top - otherHeight);
6061
+ if (resizingLeft !== resizingRight && widthDiff < bestWidthDiff) {
6062
+ bestWidthDiff = widthDiff;
6063
+ bestWidthMatch = other;
6064
+ }
6065
+ if (resizingTop !== resizingBottom && heightDiff < bestHeightDiff) {
6066
+ bestHeightDiff = heightDiff;
6067
+ bestHeightMatch = other;
6068
+ }
5983
6069
  if (resizingLeft) {
5984
6070
  checkVerticalSnap(scaling.left, other.left, {
5985
6071
  type: "vertical",
@@ -6061,6 +6147,18 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
6061
6147
  });
6062
6148
  }
6063
6149
  }
6150
+ if (bestWidthMatch && bestWidthDiff < threshold) {
6151
+ newGuides.push(
6152
+ { type: "horizontal", position: scaling.top, start: scaling.left, end: scaling.right, kind: "alignment" },
6153
+ { type: "horizontal", position: bestWidthMatch.top, start: bestWidthMatch.left, end: bestWidthMatch.right, kind: "alignment" }
6154
+ );
6155
+ }
6156
+ if (bestHeightMatch && bestHeightDiff < threshold) {
6157
+ newGuides.push(
6158
+ { type: "vertical", position: scaling.left, start: scaling.top, end: scaling.bottom, kind: "alignment" },
6159
+ { type: "vertical", position: bestHeightMatch.left, start: bestHeightMatch.top, end: bestHeightMatch.bottom, kind: "alignment" }
6160
+ );
6161
+ }
6064
6162
  const seen = /* @__PURE__ */ new Set();
6065
6163
  return newGuides.filter((guide) => {
6066
6164
  const key = `${guide.type}-${guide.position.toFixed(1)}`;
@@ -6069,6 +6167,349 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
6069
6167
  return true;
6070
6168
  });
6071
6169
  }
6170
+ const MIN_SNAP_BOX = 20;
6171
+ const SNAP_HYSTERESIS_PX = 6;
6172
+ function applyScaleSnapToBox(box, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold, excludeObjectId, options) {
6173
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
6174
+ const hysteresis = (options == null ? void 0 : options.hysteresis) ?? SNAP_HYSTERESIS_PX;
6175
+ const activeSnapRef = options == null ? void 0 : options.activeSnapRef;
6176
+ const roundSnappedOnly = (options == null ? void 0 : options.roundSnappedOnly) ?? false;
6177
+ if (!snapToGuides || !canvas) {
6178
+ if (roundSnappedOnly) return { ...box };
6179
+ return {
6180
+ left: Math.round(box.left),
6181
+ top: Math.round(box.top),
6182
+ width: Math.round(box.width),
6183
+ height: Math.round(box.height)
6184
+ };
6185
+ }
6186
+ const threshold = snapThreshold || 5;
6187
+ const releaseDist = threshold + hysteresis;
6188
+ const matchDimensions = (options == null ? void 0 : options.matchDimensions) ?? true;
6189
+ const excludedIds = new Set((options == null ? void 0 : options.excludeObjectIds) ?? []);
6190
+ if (excludeObjectId) excludedIds.add(excludeObjectId);
6191
+ const right = box.left + box.width;
6192
+ const bottom = box.top + box.height;
6193
+ const canvasCenterX = canvasWidth / 2;
6194
+ const canvasCenterY = canvasHeight / 2;
6195
+ const resizingLeft = corner.includes("l");
6196
+ const resizingRight = corner.includes("r");
6197
+ const resizingTop = corner.includes("t");
6198
+ const resizingBottom = corner.includes("b");
6199
+ const verticalTargets = [0, canvasWidth, canvasCenterX];
6200
+ const horizontalTargets = [0, canvasHeight, canvasCenterY];
6201
+ const widthTargets = [];
6202
+ const heightTargets = [];
6203
+ if ((_a2 = options == null ? void 0 : options.verticalTargetsExtra) == null ? void 0 : _a2.length) verticalTargets.push(...options.verticalTargetsExtra);
6204
+ if ((_b2 = options == null ? void 0 : options.horizontalTargetsExtra) == null ? void 0 : _b2.length) horizontalTargets.push(...options.horizontalTargetsExtra);
6205
+ const rawObjects = canvas.getObjects();
6206
+ for (const obj of rawObjects) {
6207
+ const objId = getObjectId(obj);
6208
+ if (objId === "__background__") continue;
6209
+ if (objId && excludedIds.has(objId)) continue;
6210
+ const pts = getObjectSnapPoints(obj);
6211
+ verticalTargets.push(pts.left, pts.right, pts.centerX);
6212
+ horizontalTargets.push(pts.top, pts.bottom, pts.centerY);
6213
+ widthTargets.push(Math.max(1, pts.right - pts.left));
6214
+ heightTargets.push(Math.max(1, pts.bottom - pts.top));
6215
+ }
6216
+ for (const bounds of (options == null ? void 0 : options.additionalBounds) ?? []) {
6217
+ const pts = boundsToSnapPoints(bounds);
6218
+ verticalTargets.push(pts.left, pts.right, pts.centerX);
6219
+ horizontalTargets.push(pts.top, pts.bottom, pts.centerY);
6220
+ widthTargets.push(Math.max(1, bounds.width));
6221
+ heightTargets.push(Math.max(1, bounds.height));
6222
+ }
6223
+ const matchAsEdgeEnabled = (options == null ? void 0 : options.matchDimensions) ?? true;
6224
+ if (matchAsEdgeEnabled) {
6225
+ if (resizingLeft && !resizingRight) {
6226
+ for (const w of widthTargets) verticalTargets.push(right - w);
6227
+ } else if (resizingRight && !resizingLeft) {
6228
+ for (const w of widthTargets) verticalTargets.push(box.left + w);
6229
+ }
6230
+ if (resizingTop && !resizingBottom) {
6231
+ for (const h of heightTargets) horizontalTargets.push(bottom - h);
6232
+ } else if (resizingBottom && !resizingTop) {
6233
+ for (const h of heightTargets) horizontalTargets.push(box.top + h);
6234
+ }
6235
+ }
6236
+ const findBestSnap = (edgePos, targets) => {
6237
+ let best = edgePos;
6238
+ let bestDist = threshold + 1;
6239
+ for (const t of targets) {
6240
+ const d = Math.abs(edgePos - t);
6241
+ if (d < bestDist) {
6242
+ bestDist = d;
6243
+ best = t;
6244
+ }
6245
+ }
6246
+ return { value: best, dist: bestDist };
6247
+ };
6248
+ const findBestDimensionSnap = (size, targets) => {
6249
+ let best = size;
6250
+ let bestDist = threshold + 1;
6251
+ for (const t of targets) {
6252
+ const d = Math.abs(size - t);
6253
+ if (d < bestDist) {
6254
+ bestDist = d;
6255
+ best = t;
6256
+ }
6257
+ }
6258
+ return { value: best, dist: bestDist };
6259
+ };
6260
+ let left = box.left;
6261
+ let top = box.top;
6262
+ let width = box.width;
6263
+ let height = box.height;
6264
+ let snappedLeft = false;
6265
+ let snappedRight = false;
6266
+ let snappedTop = false;
6267
+ let snappedBottom = false;
6268
+ if (resizingLeft) {
6269
+ const stick = (_c = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _c.left;
6270
+ if (stick !== void 0) {
6271
+ const dist = Math.abs(box.left - stick);
6272
+ if (dist <= releaseDist) {
6273
+ left = stick;
6274
+ width = right - left;
6275
+ snappedLeft = true;
6276
+ } else if (dist > releaseDist) {
6277
+ if (activeSnapRef.current) delete activeSnapRef.current.left;
6278
+ const { value, dist: d } = findBestSnap(box.left, verticalTargets);
6279
+ if (d <= threshold) {
6280
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6281
+ activeSnapRef.current.left = value;
6282
+ left = value;
6283
+ width = right - left;
6284
+ snappedLeft = true;
6285
+ } else {
6286
+ left = box.left;
6287
+ width = right - left;
6288
+ }
6289
+ } else {
6290
+ left = box.left;
6291
+ width = right - left;
6292
+ }
6293
+ } else {
6294
+ const { value, dist } = findBestSnap(box.left, verticalTargets);
6295
+ if (dist <= threshold) {
6296
+ if (activeSnapRef) {
6297
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6298
+ activeSnapRef.current.left = value;
6299
+ }
6300
+ left = value;
6301
+ width = right - left;
6302
+ snappedLeft = true;
6303
+ } else {
6304
+ left = box.left;
6305
+ width = right - left;
6306
+ }
6307
+ }
6308
+ }
6309
+ if (resizingRight) {
6310
+ const stick = (_d = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _d.right;
6311
+ if (stick !== void 0) {
6312
+ const dist = Math.abs(right - stick);
6313
+ if (dist <= releaseDist) {
6314
+ width = stick - left;
6315
+ snappedRight = true;
6316
+ } else if (dist > releaseDist) {
6317
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.right;
6318
+ const { value, dist: d } = findBestSnap(right, verticalTargets);
6319
+ if (d <= threshold) {
6320
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6321
+ activeSnapRef.current.right = value;
6322
+ width = value - left;
6323
+ snappedRight = true;
6324
+ } else {
6325
+ width = box.width;
6326
+ }
6327
+ } else {
6328
+ width = box.width;
6329
+ }
6330
+ } else {
6331
+ const { value, dist } = findBestSnap(right, verticalTargets);
6332
+ if (dist <= threshold) {
6333
+ if (activeSnapRef) {
6334
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6335
+ activeSnapRef.current.right = value;
6336
+ }
6337
+ width = value - left;
6338
+ snappedRight = true;
6339
+ } else {
6340
+ width = box.width;
6341
+ }
6342
+ }
6343
+ }
6344
+ if (resizingTop) {
6345
+ const stick = (_e = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _e.top;
6346
+ if (stick !== void 0) {
6347
+ const dist = Math.abs(box.top - stick);
6348
+ if (dist <= releaseDist) {
6349
+ top = stick;
6350
+ height = bottom - top;
6351
+ snappedTop = true;
6352
+ } else if (dist > releaseDist) {
6353
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.top;
6354
+ const { value, dist: d } = findBestSnap(box.top, horizontalTargets);
6355
+ if (d <= threshold) {
6356
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6357
+ activeSnapRef.current.top = value;
6358
+ top = value;
6359
+ height = bottom - top;
6360
+ snappedTop = true;
6361
+ } else {
6362
+ top = box.top;
6363
+ height = bottom - top;
6364
+ }
6365
+ } else {
6366
+ top = box.top;
6367
+ height = bottom - top;
6368
+ }
6369
+ } else {
6370
+ const { value, dist } = findBestSnap(box.top, horizontalTargets);
6371
+ if (dist <= threshold) {
6372
+ if (activeSnapRef) {
6373
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6374
+ activeSnapRef.current.top = value;
6375
+ }
6376
+ top = value;
6377
+ height = bottom - top;
6378
+ snappedTop = true;
6379
+ } else {
6380
+ top = box.top;
6381
+ height = bottom - top;
6382
+ }
6383
+ }
6384
+ }
6385
+ if (resizingBottom) {
6386
+ const stick = (_f = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _f.bottom;
6387
+ if (stick !== void 0) {
6388
+ const dist = Math.abs(bottom - stick);
6389
+ if (dist <= releaseDist) {
6390
+ height = stick - top;
6391
+ snappedBottom = true;
6392
+ } else if (dist > releaseDist) {
6393
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.bottom;
6394
+ const { value, dist: d } = findBestSnap(bottom, horizontalTargets);
6395
+ if (d <= threshold) {
6396
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6397
+ activeSnapRef.current.bottom = value;
6398
+ height = value - top;
6399
+ snappedBottom = true;
6400
+ } else {
6401
+ height = box.height;
6402
+ }
6403
+ } else {
6404
+ height = box.height;
6405
+ }
6406
+ } else {
6407
+ const { value, dist } = findBestSnap(bottom, horizontalTargets);
6408
+ if (dist <= threshold) {
6409
+ if (activeSnapRef) {
6410
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6411
+ activeSnapRef.current.bottom = value;
6412
+ }
6413
+ height = value - top;
6414
+ snappedBottom = true;
6415
+ } else {
6416
+ height = box.height;
6417
+ }
6418
+ }
6419
+ }
6420
+ if (matchDimensions && resizingLeft !== resizingRight && !snappedLeft && !snappedRight && widthTargets.length > 0) {
6421
+ const stick = (_g = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _g.width;
6422
+ const applyWidth = (targetWidth) => {
6423
+ width = Math.max(MIN_SNAP_BOX, targetWidth);
6424
+ if (resizingLeft) {
6425
+ left = right - width;
6426
+ snappedLeft = true;
6427
+ } else {
6428
+ snappedRight = true;
6429
+ }
6430
+ };
6431
+ if (stick !== void 0) {
6432
+ const dist = Math.abs(box.width - stick);
6433
+ if (dist <= releaseDist) {
6434
+ applyWidth(stick);
6435
+ } else if (dist > releaseDist) {
6436
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.width;
6437
+ const { value, dist: d } = findBestDimensionSnap(box.width, widthTargets);
6438
+ if (d <= threshold) {
6439
+ if (activeSnapRef) {
6440
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6441
+ activeSnapRef.current.width = value;
6442
+ }
6443
+ applyWidth(value);
6444
+ }
6445
+ }
6446
+ } else {
6447
+ const { value, dist } = findBestDimensionSnap(box.width, widthTargets);
6448
+ if (dist <= threshold) {
6449
+ if (activeSnapRef) {
6450
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6451
+ activeSnapRef.current.width = value;
6452
+ }
6453
+ applyWidth(value);
6454
+ }
6455
+ }
6456
+ }
6457
+ if (matchDimensions && resizingTop !== resizingBottom && !snappedTop && !snappedBottom && heightTargets.length > 0) {
6458
+ const stick = (_h = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _h.height;
6459
+ const applyHeight = (targetHeight) => {
6460
+ height = Math.max(MIN_SNAP_BOX, targetHeight);
6461
+ if (resizingTop) {
6462
+ top = bottom - height;
6463
+ snappedTop = true;
6464
+ } else {
6465
+ snappedBottom = true;
6466
+ }
6467
+ };
6468
+ if (stick !== void 0) {
6469
+ const dist = Math.abs(box.height - stick);
6470
+ if (dist <= releaseDist) {
6471
+ applyHeight(stick);
6472
+ } else if (dist > releaseDist) {
6473
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.height;
6474
+ const { value, dist: d } = findBestDimensionSnap(box.height, heightTargets);
6475
+ if (d <= threshold) {
6476
+ if (activeSnapRef) {
6477
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6478
+ activeSnapRef.current.height = value;
6479
+ }
6480
+ applyHeight(value);
6481
+ }
6482
+ }
6483
+ } else {
6484
+ const { value, dist } = findBestDimensionSnap(box.height, heightTargets);
6485
+ if (dist <= threshold) {
6486
+ if (activeSnapRef) {
6487
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6488
+ activeSnapRef.current.height = value;
6489
+ }
6490
+ applyHeight(value);
6491
+ }
6492
+ }
6493
+ }
6494
+ width = Math.max(MIN_SNAP_BOX, width);
6495
+ height = Math.max(MIN_SNAP_BOX, height);
6496
+ if (resizingLeft && !resizingRight) left = right - width;
6497
+ if (resizingTop && !resizingBottom) top = bottom - height;
6498
+ if (roundSnappedOnly) {
6499
+ return {
6500
+ left: snappedLeft ? Math.round(left) : left,
6501
+ top: snappedTop ? Math.round(top) : top,
6502
+ width: snappedLeft || snappedRight ? Math.round(width) : width,
6503
+ height: snappedTop || snappedBottom ? Math.round(height) : height
6504
+ };
6505
+ }
6506
+ return {
6507
+ left: Math.round(left),
6508
+ top: Math.round(top),
6509
+ width: Math.round(width),
6510
+ height: Math.round(height)
6511
+ };
6512
+ }
6072
6513
  const clamp01$1 = (v) => Math.max(0, Math.min(1, Number.isFinite(v) ? v : 0));
6073
6514
  function arcPath(w, sag, up) {
6074
6515
  if (sag <= 0.5) return `M 0 0 L ${w} 0`;
@@ -6239,12 +6680,87 @@ if (Array.isArray(stateProps)) {
6239
6680
  if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
6240
6681
  if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
6241
6682
  if (!stateProps.includes("textPath")) stateProps.push("textPath");
6683
+ if (!stateProps.includes("smartWrap")) stateProps.push("smartWrap");
6242
6684
  }
6243
6685
  const cacheProps = fabric__namespace.Textbox.prototype.cacheProperties;
6244
6686
  if (Array.isArray(cacheProps)) {
6245
6687
  if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
6246
6688
  if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
6247
6689
  if (!cacheProps.includes("textPath")) cacheProps.push("textPath");
6690
+ if (!cacheProps.includes("smartWrap")) cacheProps.push("smartWrap");
6691
+ }
6692
+ fabric__namespace.Textbox.prototype.smartWrap = true;
6693
+ if (TextboxProto._wrapLine && !TextboxProto.__pixldocsOrigWrapLine) {
6694
+ TextboxProto.__pixldocsOrigWrapLine = TextboxProto._wrapLine;
6695
+ TextboxProto._wrapLine = function(lineIndex, desiredWidth, ref, reservedSpace = 0) {
6696
+ var _a2;
6697
+ const orig = TextboxProto.__pixldocsOrigWrapLine;
6698
+ if (!this.smartWrap || this.splitByGrapheme) {
6699
+ return orig.call(this, lineIndex, desiredWidth, ref, reservedSpace);
6700
+ }
6701
+ try {
6702
+ const additionalSpace = TextboxProto._getWidthOfCharSpacing.call(this);
6703
+ const data = ((_a2 = ref == null ? void 0 : ref.wordsData) == null ? void 0 : _a2[lineIndex]) ?? [];
6704
+ const effective = Math.max(1, desiredWidth - reservedSpace);
6705
+ const infix = " ";
6706
+ const measureWord = TextboxProto._measureWord;
6707
+ const infixWidth = measureWord.call(this, [infix], lineIndex, 0);
6708
+ const graphemeLines = [];
6709
+ let line = [];
6710
+ let lineWidth = 0;
6711
+ let lineJustStarted = true;
6712
+ let largestWordWidth = 0;
6713
+ let offset = 0;
6714
+ const pushLine = () => {
6715
+ graphemeLines.push(line);
6716
+ line = [];
6717
+ lineWidth = 0;
6718
+ lineJustStarted = true;
6719
+ };
6720
+ for (let i = 0; i < data.length; i++) {
6721
+ const { word, width: wordWidth } = data[i];
6722
+ if (wordWidth <= effective) {
6723
+ if (wordWidth > largestWordWidth) largestWordWidth = wordWidth;
6724
+ const projected = lineJustStarted ? wordWidth : lineWidth + infixWidth + wordWidth - additionalSpace;
6725
+ if (!lineJustStarted && projected > effective) {
6726
+ pushLine();
6727
+ }
6728
+ if (!lineJustStarted) {
6729
+ line.push(infix);
6730
+ lineWidth += infixWidth;
6731
+ }
6732
+ line = line.concat(word);
6733
+ lineWidth += wordWidth;
6734
+ lineJustStarted = false;
6735
+ } else {
6736
+ if (!lineJustStarted) pushLine();
6737
+ for (const g of word) {
6738
+ const gw = measureWord.call(this, [g], lineIndex, offset);
6739
+ if (gw > largestWordWidth) largestWordWidth = gw;
6740
+ const projected = lineJustStarted ? gw : lineWidth + gw - additionalSpace;
6741
+ if (!lineJustStarted && projected > effective) {
6742
+ pushLine();
6743
+ }
6744
+ line.push(g);
6745
+ lineWidth += gw;
6746
+ lineJustStarted = false;
6747
+ offset++;
6748
+ }
6749
+ offset -= word.length;
6750
+ }
6751
+ offset += word.length + 1;
6752
+ }
6753
+ if (line.length) graphemeLines.push(line);
6754
+ const minNeeded = Math.max(0, Math.min(largestWordWidth, effective) - additionalSpace + reservedSpace);
6755
+ if (minNeeded > this.dynamicMinWidth) {
6756
+ this.dynamicMinWidth = minNeeded;
6757
+ }
6758
+ return graphemeLines;
6759
+ } catch (e) {
6760
+ console.warn("[smartWrap] fell back to default wrap:", e);
6761
+ return orig.call(this, lineIndex, desiredWidth, ref, reservedSpace);
6762
+ }
6763
+ };
6248
6764
  }
6249
6765
  const hasActiveTextPath = (obj) => {
6250
6766
  const tp = obj.textPath;
@@ -6401,14 +6917,14 @@ function scaleLocalToScreen(target, p) {
6401
6917
  return new fabric__namespace.Point(p.x * sx * zx, p.y * sy * zy);
6402
6918
  }
6403
6919
  function applyTextPathControls(textbox) {
6404
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
6920
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
6405
6921
  const obj = textbox;
6406
6922
  if (!hasActiveTextPath(obj)) {
6407
6923
  obj.__pdTextPathHovered = false;
6408
6924
  if (obj.__pdTextPathControls) {
6409
6925
  try {
6410
6926
  const cu2 = fabric__namespace.controlsUtils;
6411
- const defaults = ((_a2 = cu2 == null ? void 0 : cu2.createTextboxDefaultControls) == null ? void 0 : _a2.call(cu2)) ?? ((_b = cu2 == null ? void 0 : cu2.createObjectDefaultControls) == null ? void 0 : _b.call(cu2));
6927
+ const defaults = ((_a2 = cu2 == null ? void 0 : cu2.createTextboxDefaultControls) == null ? void 0 : _a2.call(cu2)) ?? ((_b2 = cu2 == null ? void 0 : cu2.createObjectDefaultControls) == null ? void 0 : _b2.call(cu2));
6412
6928
  if (defaults) obj.controls = defaults;
6413
6929
  } catch {
6414
6930
  }
@@ -6672,7 +7188,7 @@ function applyTextPathControls(textbox) {
6672
7188
  actionName: "tpPivot",
6673
7189
  positionHandler: (_d2, finalMatrix, fabricObject) => scaleLocalToScreen(fabricObject, getPivotLocalCentered(fabricObject)).transform(finalMatrix),
6674
7190
  actionHandler: (_e2, transform, x, y) => {
6675
- var _a3, _b2;
7191
+ var _a3, _b3;
6676
7192
  const target = transform.target;
6677
7193
  const state = transform.__pdCirclePivotDrag || (transform.__pdCirclePivotDrag = {
6678
7194
  startMatrix: target.calcTransformMatrix(),
@@ -6689,7 +7205,7 @@ function applyTextPathControls(textbox) {
6689
7205
  y: (state.startOffset.y || 0) + dy
6690
7206
  };
6691
7207
  target.setCoords();
6692
- (_b2 = target.canvas) == null ? void 0 : _b2.requestRenderAll();
7208
+ (_b3 = target.canvas) == null ? void 0 : _b3.requestRenderAll();
6693
7209
  return true;
6694
7210
  },
6695
7211
  render: renderPivot
@@ -6740,13 +7256,13 @@ function applyTextPathControls(textbox) {
6740
7256
  }
6741
7257
  if (!obj.__pdCirclePivotDblWired) {
6742
7258
  obj.on("mousedblclick", () => {
6743
- var _a3, _b2;
7259
+ var _a3, _b3;
6744
7260
  if (((_a3 = obj.textPath) == null ? void 0 : _a3.preset) !== "circle") return;
6745
7261
  if (obj.__corner !== "tpPivot") return;
6746
7262
  if (!obj.textPath.pivot) return;
6747
7263
  obj.textPath.pivot = { x: 0, y: 0 };
6748
7264
  obj.setCoords();
6749
- (_b2 = obj.canvas) == null ? void 0 : _b2.requestRenderAll();
7265
+ (_b3 = obj.canvas) == null ? void 0 : _b3.requestRenderAll();
6750
7266
  });
6751
7267
  obj.__pdCirclePivotDblWired = true;
6752
7268
  }
@@ -7114,10 +7630,10 @@ function textPathBoundsContainScenePoint(obj, point) {
7114
7630
  }
7115
7631
  }
7116
7632
  function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7117
- var _a2, _b, _c;
7633
+ var _a2, _b2, _c;
7118
7634
  const host = hostCanvas || obj.canvas || ((_a2 = obj.group) == null ? void 0 : _a2.canvas);
7119
7635
  if (!host) return;
7120
- const retina = ((_b = host.getRetinaScaling) == null ? void 0 : _b.call(host)) || ((_c = obj.getCanvasRetinaScaling) == null ? void 0 : _c.call(obj)) || 1;
7636
+ const retina = ((_b2 = host.getRetinaScaling) == null ? void 0 : _b2.call(host)) || ((_c = obj.getCanvasRetinaScaling) == null ? void 0 : _c.call(obj)) || 1;
7121
7637
  const matrix = fabric__namespace.util.multiplyTransformMatrices(host.viewportTransform || [1, 0, 0, 1, 0, 0], obj.calcTransformMatrix());
7122
7638
  const corners = [
7123
7639
  new fabric__namespace.Point(bounds.minX, bounds.minY),
@@ -7139,8 +7655,8 @@ function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7139
7655
  }
7140
7656
  if (typeof TextboxProto._renderControls === "function" && !TextboxProto.__pixldocsOrigRenderControls) {
7141
7657
  let drawWarpGuides = function(ctx) {
7142
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7143
- const hostCanvas = this.canvas || ((_a2 = this.group) == null ? void 0 : _a2.canvas) || ((_c = (_b = this.group) == null ? void 0 : _b.group) == null ? void 0 : _c.canvas);
7658
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
7659
+ const hostCanvas = this.canvas || ((_a2 = this.group) == null ? void 0 : _a2.canvas) || ((_c = (_b2 = this.group) == null ? void 0 : _b2.group) == null ? void 0 : _c.canvas);
7144
7660
  if (!hostCanvas) return;
7145
7661
  const hoverBounds = getTextPathHitBounds(this);
7146
7662
  const active = (_d = hostCanvas.getActiveObject) == null ? void 0 : _d.call(hostCanvas);
@@ -7563,12 +8079,12 @@ function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
7563
8079
  ctx.closePath();
7564
8080
  }
7565
8081
  function measureLineGlyphWidth(obj, lineIndex) {
7566
- var _a2, _b, _c, _d, _e, _f;
8082
+ var _a2, _b2, _c, _d, _e, _f;
7567
8083
  try {
7568
8084
  const rawLine = (_a2 = obj == null ? void 0 : obj._textLines) == null ? void 0 : _a2[lineIndex];
7569
8085
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
7570
8086
  if (!lineText) return 0;
7571
- const fontSize = Number(((_b = obj.getValueOfPropertyAt) == null ? void 0 : _b.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
8087
+ const fontSize = Number(((_b2 = obj.getValueOfPropertyAt) == null ? void 0 : _b2.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
7572
8088
  if (!fontSize) return 0;
7573
8089
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
7574
8090
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
@@ -7837,7 +8353,7 @@ function applyTextBackground(obj, cfg) {
7837
8353
  const originalToSVG = (_a2 = obj.toSVG) == null ? void 0 : _a2.bind(obj);
7838
8354
  if (typeof originalToSVG === "function") {
7839
8355
  obj.toSVG = function(reviver) {
7840
- var _a3, _b;
8356
+ var _a3, _b2;
7841
8357
  let svg = originalToSVG(reviver);
7842
8358
  const bg = this[PD_BG_KEY];
7843
8359
  const shadow = this.shadow;
@@ -7886,7 +8402,7 @@ function applyTextBackground(obj, cfg) {
7886
8402
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7887
8403
  let bgGradDefs = "";
7888
8404
  let bgFillAttr = escapeXmlAttr(bgFill);
7889
- if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b = bg.gradient.stops) == null ? void 0 : _b.length) >= 2) {
8405
+ if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b2 = bg.gradient.stops) == null ? void 0 : _b2.length) >= 2) {
7890
8406
  const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7891
8407
  const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7892
8408
  const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
@@ -8915,9 +9431,9 @@ function createShape(element) {
8915
9431
  });
8916
9432
  }
8917
9433
  case "circle": {
8918
- const radius = Math.min(w, h) / 2;
8919
- return new fabric__namespace.Circle({
8920
- radius,
9434
+ return new fabric__namespace.Ellipse({
9435
+ rx: w / 2,
9436
+ ry: h / 2,
8921
9437
  fill,
8922
9438
  stroke,
8923
9439
  strokeWidth,
@@ -8926,8 +9442,7 @@ function createShape(element) {
8926
9442
  objectCaching: true,
8927
9443
  strokeUniform: true,
8928
9444
  strokeLineJoin: "round",
8929
- strokeLineCap: "round",
8930
- lockUniScaling: true
9445
+ strokeLineCap: "round"
8931
9446
  });
8932
9447
  }
8933
9448
  case "triangle": {
@@ -8962,7 +9477,7 @@ function createShape(element) {
8962
9477
  }
8963
9478
  }
8964
9479
  function createText(element) {
8965
- var _a2, _b, _c, _d, _e;
9480
+ var _a2, _b2, _c, _d, _e;
8966
9481
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
8967
9482
  let text = element.text || "Text";
8968
9483
  let fontSize = element.fontSize || 16;
@@ -9163,7 +9678,7 @@ function createText(element) {
9163
9678
  textbox.setCoords();
9164
9679
  const widthAfterSet = textbox.width ?? 0;
9165
9680
  try {
9166
- (_b = textbox.setControlsVisibility) == null ? void 0 : _b.call(textbox, {
9681
+ (_b2 = textbox.setControlsVisibility) == null ? void 0 : _b2.call(textbox, {
9167
9682
  tl: true,
9168
9683
  tr: true,
9169
9684
  bl: true,
@@ -9962,34 +10477,595 @@ function bakeEdgeFade(source, fade) {
9962
10477
  return canvas;
9963
10478
  }
9964
10479
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10480
+ const SELECTION_BORDER_SCALE = 2;
10481
+ let ensureCanvaControlRenders = () => {
10482
+ };
9965
10483
  try {
9966
10484
  const InteractiveBase = fabric__namespace.InteractiveFabricObject ?? fabric__namespace.Object;
10485
+ if ((InteractiveBase == null ? void 0 : InteractiveBase.prototype) && !InteractiveBase.prototype.__pixldocsCenteredSelectionBorder) {
10486
+ InteractiveBase.prototype.__pixldocsCenteredSelectionBorder = true;
10487
+ InteractiveBase.prototype.strokeBorders = function(ctx, size) {
10488
+ const border = Number(this.borderScaleFactor ?? SELECTION_BORDER_SCALE) || SELECTION_BORDER_SCALE;
10489
+ const width = Math.max(0, ((size == null ? void 0 : size.x) ?? 0) - border);
10490
+ const height = Math.max(0, ((size == null ? void 0 : size.y) ?? 0) - border);
10491
+ ctx.strokeRect(-width / 2, -height / 2, width, height);
10492
+ };
10493
+ }
9967
10494
  if (InteractiveBase == null ? void 0 : InteractiveBase.ownDefaults) {
9968
10495
  Object.assign(InteractiveBase.ownDefaults, {
9969
10496
  borderColor: SELECTION_PRIMARY,
9970
- borderScaleFactor: 1.25,
10497
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9971
10498
  cornerColor: SELECTION_PRIMARY,
9972
10499
  cornerStrokeColor: "#ffffff",
9973
- cornerStyle: "rect",
10500
+ cornerStyle: "circle",
9974
10501
  transparentCorners: false,
9975
- cornerSize: 8,
10502
+ cornerSize: 10,
9976
10503
  borderOpacityWhenMoving: 0.9
9977
10504
  });
9978
10505
  } else if (InteractiveBase == null ? void 0 : InteractiveBase.prototype) {
9979
10506
  Object.assign(InteractiveBase.prototype, {
9980
10507
  borderColor: SELECTION_PRIMARY,
9981
- borderScaleFactor: 1.25,
10508
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9982
10509
  cornerColor: SELECTION_PRIMARY,
9983
10510
  cornerStrokeColor: "#ffffff",
9984
- cornerStyle: "rect",
10511
+ cornerStyle: "circle",
9985
10512
  transparentCorners: false,
9986
- cornerSize: 8,
10513
+ cornerSize: 10,
9987
10514
  borderOpacityWhenMoving: 0.9
9988
10515
  });
9989
10516
  }
9990
10517
  } catch (e) {
9991
10518
  console.warn("[PageCanvas] Failed to apply global selection defaults:", e);
9992
10519
  }
10520
+ try {
10521
+ const cu = fabric__namespace.controlsUtils;
10522
+ if (cu && typeof cu.createObjectDefaultControls === "function") {
10523
+ const PILL_LEN = 20;
10524
+ const PILL_THICK = 6;
10525
+ const PILL_RADIUS = 3;
10526
+ const CORNER_RADIUS = 6;
10527
+ const EDGE_HIT_PERP = 22;
10528
+ const EDGE_HIT_ALONG = 44;
10529
+ const HOVER_FILL = SELECTION_PRIMARY;
10530
+ const HOVER_STROKE = "#ffffff";
10531
+ const IDLE_FILL = "#ffffff";
10532
+ const IDLE_STROKE = "rgba(15, 23, 42, 0.18)";
10533
+ const isHovered = (fabricObject, controlKey) => fabricObject && fabricObject.__corner === controlKey;
10534
+ const getHoverProgress = (fabricObject, controlKey) => {
10535
+ const map = fabricObject == null ? void 0 : fabricObject.__handleHoverProgress;
10536
+ const p = map ? map[controlKey] : void 0;
10537
+ if (typeof p === "number") return Math.max(0, Math.min(1, p));
10538
+ return isHovered(fabricObject, controlKey) ? 1 : 0;
10539
+ };
10540
+ const HOVER_RGB = [59, 130, 246];
10541
+ const IDLE_RGB = [255, 255, 255];
10542
+ const lerpFill = (p) => {
10543
+ const r = Math.round(IDLE_RGB[0] + (HOVER_RGB[0] - IDLE_RGB[0]) * p);
10544
+ const g = Math.round(IDLE_RGB[1] + (HOVER_RGB[1] - IDLE_RGB[1]) * p);
10545
+ const b = Math.round(IDLE_RGB[2] + (HOVER_RGB[2] - IDLE_RGB[2]) * p);
10546
+ return `rgb(${r}, ${g}, ${b})`;
10547
+ };
10548
+ const getVisualScale = (_fabricObject) => {
10549
+ return 1;
10550
+ };
10551
+ const COLLAPSE_THRESHOLD_PX = 32;
10552
+ const shouldCollapseHandles = (fabricObject) => {
10553
+ var _a2, _b2, _c;
10554
+ try {
10555
+ const canvas = fabricObject == null ? void 0 : fabricObject.canvas;
10556
+ if (!canvas) return false;
10557
+ const zoom = ((_a2 = canvas.getZoom) == null ? void 0 : _a2.call(canvas)) ?? 1;
10558
+ const w = (((_b2 = fabricObject.getScaledWidth) == null ? void 0 : _b2.call(fabricObject)) ?? fabricObject.width ?? 0) * zoom;
10559
+ const h = (((_c = fabricObject.getScaledHeight) == null ? void 0 : _c.call(fabricObject)) ?? fabricObject.height ?? 0) * zoom;
10560
+ return Math.min(w, h) < COLLAPSE_THRESHOLD_PX;
10561
+ } catch {
10562
+ return false;
10563
+ }
10564
+ };
10565
+ const isVisibleControlWhenCollapsed = (controlKey) => controlKey === "tl" || controlKey === "mr" || controlKey === "mtr" || controlKey === "mvh";
10566
+ const wrapCollapseVisibility = (control, controlKey) => {
10567
+ if (!control || control.__pixldocsCollapseVisibilityWrapped) return;
10568
+ const originalGetVisibility = typeof control.getVisibility === "function" ? control.getVisibility.bind(control) : null;
10569
+ control.getVisibility = (fabricObject, key) => {
10570
+ const baseVisible = originalGetVisibility ? originalGetVisibility(fabricObject, key) : control.visible !== false;
10571
+ if (!baseVisible) return false;
10572
+ return !shouldCollapseHandles(fabricObject) || isVisibleControlWhenCollapsed(controlKey);
10573
+ };
10574
+ control.__pixldocsCollapseVisibilityWrapped = true;
10575
+ };
10576
+ const renderPill = (ctx, left, top, _styleOverride, fabricObject, orientation, controlKey) => {
10577
+ var _a2, _b2, _c;
10578
+ const action = (_b2 = (_a2 = fabricObject.canvas) == null ? void 0 : _a2._currentTransform) == null ? void 0 : _b2.action;
10579
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10580
+ if (shouldCollapseHandles(fabricObject) && !isVisibleControlWhenCollapsed(controlKey)) return;
10581
+ const angle = ((_c = fabricObject.getTotalAngle) == null ? void 0 : _c.call(fabricObject)) ?? (fabricObject.angle ?? 0);
10582
+ const p = getHoverProgress(fabricObject, controlKey);
10583
+ const scale = getVisualScale(fabricObject);
10584
+ const pillLen = PILL_LEN * scale;
10585
+ const pillThick = PILL_THICK * scale;
10586
+ const w = orientation === "horizontal" ? pillLen : pillThick;
10587
+ const h = orientation === "horizontal" ? pillThick : pillLen;
10588
+ ctx.save();
10589
+ ctx.translate(left, top);
10590
+ ctx.rotate(angle * Math.PI / 180);
10591
+ ctx.beginPath();
10592
+ const x = -w / 2;
10593
+ const y = -h / 2;
10594
+ const r = Math.min(PILL_RADIUS * scale, Math.min(w, h) / 2);
10595
+ ctx.moveTo(x + r, y);
10596
+ ctx.lineTo(x + w - r, y);
10597
+ ctx.quadraticCurveTo(x + w, y, x + w, y + r);
10598
+ ctx.lineTo(x + w, y + h - r);
10599
+ ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
10600
+ ctx.lineTo(x + r, y + h);
10601
+ ctx.quadraticCurveTo(x, y + h, x, y + h - r);
10602
+ ctx.lineTo(x, y + r);
10603
+ ctx.quadraticCurveTo(x, y, x + r, y);
10604
+ ctx.closePath();
10605
+ ctx.fillStyle = lerpFill(p);
10606
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10607
+ ctx.lineWidth = 0.75 * (1 - p);
10608
+ ctx.shadowColor = "rgba(15, 23, 42, 0.38)";
10609
+ ctx.shadowBlur = 8;
10610
+ ctx.shadowOffsetY = 2;
10611
+ ctx.fill();
10612
+ if (p < 1) ctx.stroke();
10613
+ ctx.restore();
10614
+ };
10615
+ const renderCornerDot = (ctx, left, top, _styleOverride, fabricObject, controlKey) => {
10616
+ var _a2, _b2;
10617
+ const action = (_b2 = (_a2 = fabricObject.canvas) == null ? void 0 : _a2._currentTransform) == null ? void 0 : _b2.action;
10618
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10619
+ if (shouldCollapseHandles(fabricObject) && !isVisibleControlWhenCollapsed(controlKey)) return;
10620
+ const p = getHoverProgress(fabricObject, controlKey);
10621
+ const scale = getVisualScale(fabricObject);
10622
+ const r = CORNER_RADIUS * scale;
10623
+ ctx.save();
10624
+ ctx.beginPath();
10625
+ ctx.arc(left, top, r, 0, Math.PI * 2);
10626
+ ctx.closePath();
10627
+ ctx.fillStyle = lerpFill(p);
10628
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10629
+ ctx.lineWidth = 0.75 * (1 - p);
10630
+ ctx.shadowColor = "rgba(15, 23, 42, 0.38)";
10631
+ ctx.shadowBlur = 8;
10632
+ ctx.shadowOffsetY = 2;
10633
+ ctx.fill();
10634
+ if (p < 1) ctx.stroke();
10635
+ ctx.restore();
10636
+ };
10637
+ const installPillRenders = (controls) => {
10638
+ var _a2;
10639
+ const CUR_SIZE = 22;
10640
+ const HOT = Math.round(CUR_SIZE / 2);
10641
+ const makeCursor = (angleDeg) => {
10642
+ const svg = `<?xml version="1.0" encoding="UTF-8"?>
10643
+ <svg xmlns="http://www.w3.org/2000/svg" width="${CUR_SIZE}" height="${CUR_SIZE}" viewBox="0 0 22 22">
10644
+ <g transform="rotate(${angleDeg} 11 11)" fill="none" stroke="#000" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
10645
+ <path d="M2.5 11 L19.5 11" stroke="#fff" stroke-width="3.2"/>
10646
+ <path d="M5 8.5 L2.5 11 L5 13.5" stroke="#fff" stroke-width="3.2"/>
10647
+ <path d="M17 8.5 L19.5 11 L17 13.5" stroke="#fff" stroke-width="3.2"/>
10648
+ <path d="M2.5 11 L19.5 11"/>
10649
+ <path d="M5 8.5 L2.5 11 L5 13.5"/>
10650
+ <path d="M17 8.5 L19.5 11 L17 13.5"/>
10651
+ </g>
10652
+ </svg>`;
10653
+ const encoded = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
10654
+ const fallback = angleDeg % 180 === 0 ? "ew-resize" : angleDeg % 180 === 90 ? "ns-resize" : angleDeg === 45 || angleDeg === 225 ? "nwse-resize" : "nesw-resize";
10655
+ return `url("data:image/svg+xml;utf8,${encoded}") ${HOT} ${HOT}, ${fallback}`;
10656
+ };
10657
+ const cursorFor = {
10658
+ ml: makeCursor(0),
10659
+ mr: makeCursor(0),
10660
+ mt: makeCursor(90),
10661
+ mb: makeCursor(90),
10662
+ tl: makeCursor(45),
10663
+ br: makeCursor(45),
10664
+ tr: makeCursor(135),
10665
+ bl: makeCursor(135)
10666
+ };
10667
+ const ROTATE_ICON_PATH = "M505.4 122.5l-92.2-62.7c-6.7-4.5-15.6-3.1-20.7 3.1l-69.2 87.4c-5.2 6.6-4.1 16.1 2.5 21.3s16.1 4.1 21.3-2.5l50.4-63.8c2.9 14.6 4.3 29.4 4.3 44.3 0 125.2-101.9 227-227 227-45.8 0-90.5-13.8-128.2-39.6l79-27.8c8-2.5 12.4-11 9.9-18.9s-11-12.4-18.9-9.9c-.3.1-.7.2-1 .4l-105.2 37c-2 .7-3.9 1.8-5.4 3.3-4.2 3.8-6 9.7-4.5 15.2l29.3 107.5c1.8 6.6 7.8 11.1 14.6 11.1 1.4 0 2.7-.2 4-.5 8.1-2.2 12.8-10.5 10.6-18.6l-18-66.4c40.3 24.6 86.6 37.5 133.8 37.5 68.3.2 133.8-27 181.9-75.4 48.4-48.1 75.5-113.7 75.4-181.9 0-14.3-1.2-28.6-3.5-42.8l59.8 40.7c7.1 4.4 16.4 2.2 20.8-4.9 4.1-6.6 2.5-15.4-3.8-20.1z";
10668
+ const CUR_R_SIZE = 18;
10669
+ const HOT_R = Math.round(CUR_R_SIZE / 2);
10670
+ const makeRotateCursor = (angleDeg) => {
10671
+ const a = ((angleDeg + 215) % 360 + 360) % 360;
10672
+ const halo = `<path d="${ROTATE_ICON_PATH}" fill="#fff" stroke="#fff" stroke-width="40" stroke-linejoin="round"/>`;
10673
+ const fg = `<path d="${ROTATE_ICON_PATH}" fill="#0f172a" stroke="#0f172a" stroke-width="6" stroke-linejoin="round"/>`;
10674
+ const svg = `<?xml version="1.0" encoding="UTF-8"?>
10675
+ <svg xmlns="http://www.w3.org/2000/svg" width="${CUR_R_SIZE}" height="${CUR_R_SIZE}" viewBox="0 0 512 512">
10676
+ <g transform="rotate(${a} 256 256)">${halo}${fg}</g>
10677
+ </svg>`;
10678
+ const encoded = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
10679
+ return `url("data:image/svg+xml;utf8,${encoded}") ${HOT_R} ${HOT_R}, crosshair`;
10680
+ };
10681
+ const rotateCursorCache = {};
10682
+ const getRotateCursor = (fabricObject) => {
10683
+ let objAngle = 0;
10684
+ try {
10685
+ objAngle = typeof (fabricObject == null ? void 0 : fabricObject.getTotalAngle) === "function" ? fabricObject.getTotalAngle() : (fabricObject == null ? void 0 : fabricObject.angle) ?? 0;
10686
+ } catch {
10687
+ objAngle = 0;
10688
+ }
10689
+ let a = (objAngle % 360 + 360) % 360;
10690
+ a = Math.round(a / 15) * 15;
10691
+ if (a === 360) a = 0;
10692
+ if (!rotateCursorCache[a]) rotateCursorCache[a] = makeRotateCursor(a);
10693
+ return rotateCursorCache[a];
10694
+ };
10695
+ const MOVE_PATHS_2D = [
10696
+ new Path2D("M12 3L12.3648 2.65803L12 2.26894L11.6352 2.65803L12 3ZM11.5 9C11.5 9.27614 11.7239 9.5 12 9.5C12.2761 9.5 12.5 9.27614 12.5 9H11.5ZM15.3648 5.85803L12.3648 2.65803L11.6352 3.34197L14.6352 6.54197L15.3648 5.85803ZM11.6352 2.65803L8.63523 5.85803L9.36477 6.54197L12.3648 3.34197L11.6352 2.65803ZM11.5 3V9H12.5V3H11.5Z"),
10697
+ new Path2D("M21 12L21.342 12.3648L21.7311 12L21.342 11.6352L21 12ZM15 11.5C14.7239 11.5 14.5 11.7239 14.5 12C14.5 12.2761 14.7239 12.5 15 12.5L15 11.5ZM18.142 15.3648L21.342 12.3648L20.658 11.6352L17.458 14.6352L18.142 15.3648ZM21.342 11.6352L18.142 8.63523L17.458 9.36477L20.658 12.3648L21.342 11.6352ZM21 11.5L15 11.5L15 12.5L21 12.5L21 11.5Z"),
10698
+ new Path2D("M12 21L12.3648 21.342L12 21.7311L11.6352 21.342L12 21ZM11.5 15C11.5 14.7239 11.7239 14.5 12 14.5C12.2761 14.5 12.5 14.7239 12.5 15H11.5ZM15.3648 18.142L12.3648 21.342L11.6352 20.658L14.6352 17.458L15.3648 18.142ZM11.6352 21.342L8.63523 18.142L9.36477 17.458L12.3648 20.658L11.6352 21.342ZM11.5 21V15H12.5V21H11.5Z"),
10699
+ new Path2D("M3 12L2.65803 12.3648L2.26894 12L2.65803 11.6352L3 12ZM9 11.5C9.27614 11.5 9.5 11.7239 9.5 12C9.5 12.2761 9.27614 12.5 9 12.5L9 11.5ZM5.85803 15.3648L2.65803 12.3648L3.34197 11.6352L6.54197 14.6352L5.85803 15.3648ZM2.65803 11.6352L5.85803 8.63523L6.54197 9.36477L3.34197 12.3648L2.65803 11.6352ZM3 11.5L9 11.5L9 12.5L3 12.5L3 11.5Z")
10700
+ ];
10701
+ const ROTATE_PATHS_2D = [
10702
+ new Path2D("M22 12l-3 3-3-3"),
10703
+ new Path2D("M2 12l3-3 3 3"),
10704
+ new Path2D("M19.016 14v-1.95A7.05 7.05 0 0 0 8 6.22"),
10705
+ new Path2D("M16.016 17.845A7.05 7.05 0 0 1 5 12.015V10"),
10706
+ new Path2D("M5 10V9"),
10707
+ new Path2D("M19 15v-1")
10708
+ ];
10709
+ const baseAngleFor = {
10710
+ ml: 0,
10711
+ mr: 0,
10712
+ mt: 90,
10713
+ mb: 90,
10714
+ tl: 45,
10715
+ br: 45,
10716
+ tr: 135,
10717
+ bl: 135
10718
+ };
10719
+ const cursorCache = {};
10720
+ const getRotatedCursor = (key, fabricObject) => {
10721
+ const base = baseAngleFor[key];
10722
+ if (base === void 0) return cursorFor[key];
10723
+ let objAngle = 0;
10724
+ try {
10725
+ objAngle = typeof (fabricObject == null ? void 0 : fabricObject.getTotalAngle) === "function" ? fabricObject.getTotalAngle() : (fabricObject == null ? void 0 : fabricObject.angle) ?? 0;
10726
+ } catch {
10727
+ objAngle = 0;
10728
+ }
10729
+ let a = ((base + objAngle) % 180 + 180) % 180;
10730
+ a = Math.round(a / 15) * 15;
10731
+ if (a === 180) a = 0;
10732
+ if (!cursorCache[a]) cursorCache[a] = makeCursor(a);
10733
+ return cursorCache[a];
10734
+ };
10735
+ const sides = [
10736
+ ["ml", "vertical"],
10737
+ ["mr", "vertical"],
10738
+ ["mt", "horizontal"],
10739
+ ["mb", "horizontal"]
10740
+ ];
10741
+ for (const [key, orient] of sides) {
10742
+ const c = controls[key];
10743
+ if (!c) continue;
10744
+ c.sizeX = orient === "horizontal" ? EDGE_HIT_ALONG : EDGE_HIT_PERP;
10745
+ c.sizeY = orient === "horizontal" ? EDGE_HIT_PERP : EDGE_HIT_ALONG;
10746
+ c.touchSizeX = c.sizeX;
10747
+ c.touchSizeY = c.sizeY;
10748
+ wrapCollapseVisibility(c, key);
10749
+ c.cursorStyle = cursorFor[key];
10750
+ c.cursorStyleHandler = (_eventData, _control, fabricObject) => getRotatedCursor(key, fabricObject);
10751
+ c.render = (ctx, left, top, styleOverride, fabricObject) => renderPill(ctx, left, top, styleOverride, fabricObject, orient, key);
10752
+ }
10753
+ const corners = ["tl", "tr", "bl", "br"];
10754
+ for (const key of corners) {
10755
+ const c = controls[key];
10756
+ if (!c) continue;
10757
+ c.sizeX = 16;
10758
+ c.sizeY = 16;
10759
+ c.touchSizeX = 24;
10760
+ c.touchSizeY = 24;
10761
+ wrapCollapseVisibility(c, key);
10762
+ c.cursorStyle = cursorFor[key];
10763
+ c.cursorStyleHandler = (_eventData, _control, fabricObject) => getRotatedCursor(key, fabricObject);
10764
+ c.render = (ctx, left, top, styleOverride, fabricObject) => renderCornerDot(ctx, left, top, styleOverride, fabricObject, key);
10765
+ }
10766
+ const mtr = controls.mtr;
10767
+ if (mtr) {
10768
+ mtr.sizeX = 22;
10769
+ mtr.sizeY = 22;
10770
+ mtr.touchSizeX = 32;
10771
+ mtr.touchSizeY = 32;
10772
+ wrapCollapseVisibility(mtr, "mtr");
10773
+ mtr.offsetY = -28;
10774
+ mtr.withConnection = false;
10775
+ mtr.x = 0;
10776
+ mtr.y = -0.5;
10777
+ mtr.cursorStyle = getRotateCursor(null);
10778
+ mtr.cursorStyleHandler = (_e, _c, fabricObject) => {
10779
+ const cursor = getRotateCursor(fabricObject);
10780
+ try {
10781
+ const canvas = fabricObject == null ? void 0 : fabricObject.canvas;
10782
+ if (canvas) canvas.__pixldocsGetRotateCursor = getRotateCursor;
10783
+ const upper = canvas == null ? void 0 : canvas.upperCanvasEl;
10784
+ if (upper) upper.style.cursor = cursor;
10785
+ } catch {
10786
+ }
10787
+ return cursor;
10788
+ };
10789
+ mtr.render = (ctx, left, top, _styleOverride, fabricObject) => {
10790
+ var _a3, _b2;
10791
+ const action = (_b2 = (_a3 = fabricObject.canvas) == null ? void 0 : _a3._currentTransform) == null ? void 0 : _b2.action;
10792
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10793
+ const scale = getVisualScale(fabricObject);
10794
+ const p = getHoverProgress(fabricObject, "mtr");
10795
+ const r = 11 * scale;
10796
+ ctx.save();
10797
+ ctx.beginPath();
10798
+ ctx.arc(left, top, r, 0, Math.PI * 2);
10799
+ ctx.closePath();
10800
+ ctx.fillStyle = lerpFill(p);
10801
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10802
+ ctx.lineWidth = 0.75 * (1 - p);
10803
+ ctx.shadowColor = "rgba(15, 23, 42, 0.4)";
10804
+ ctx.shadowBlur = 10;
10805
+ ctx.shadowOffsetY = 2;
10806
+ ctx.fill();
10807
+ if (p < 1) ctx.stroke();
10808
+ ctx.shadowColor = "transparent";
10809
+ ctx.shadowBlur = 0;
10810
+ ctx.shadowOffsetY = 0;
10811
+ const iconColor = p > 0.5 ? "#ffffff" : "rgba(15, 23, 42, 0.85)";
10812
+ ctx.strokeStyle = iconColor;
10813
+ ctx.fillStyle = iconColor;
10814
+ ctx.lineJoin = "miter";
10815
+ ctx.miterLimit = 4;
10816
+ {
10817
+ const target = r * 1.55;
10818
+ const s = target / 24;
10819
+ ctx.translate(left, top);
10820
+ ctx.scale(s, s);
10821
+ ctx.translate(-12, -12);
10822
+ ctx.strokeStyle = iconColor;
10823
+ ctx.lineWidth = 1 / s;
10824
+ ROTATE_PATHS_2D.forEach((p2d, index) => {
10825
+ ctx.lineCap = index >= 4 ? "round" : "square";
10826
+ ctx.stroke(p2d);
10827
+ });
10828
+ }
10829
+ ctx.restore();
10830
+ };
10831
+ }
10832
+ const moveActionHandler = (eventData, transform, x, y) => {
10833
+ const target = transform.target;
10834
+ if (!target) return false;
10835
+ if (!transform.__pixldocsMoveStart) {
10836
+ transform.__pixldocsMoveStart = {
10837
+ x,
10838
+ y,
10839
+ left: target.left ?? 0,
10840
+ top: target.top ?? 0
10841
+ };
10842
+ }
10843
+ const s = transform.__pixldocsMoveStart;
10844
+ target.set({ left: s.left + (x - s.x), top: s.top + (y - s.y) });
10845
+ target.setCoords();
10846
+ try {
10847
+ const canvas = target.canvas;
10848
+ if (canvas) {
10849
+ canvas.fire("object:moving", { target, e: eventData == null ? void 0 : eventData.e, transform, pointer: { x, y } });
10850
+ target.fire("moving", { e: eventData == null ? void 0 : eventData.e, transform, pointer: { x, y } });
10851
+ }
10852
+ } catch {
10853
+ }
10854
+ return true;
10855
+ };
10856
+ const renderMoveHandle = (ctx, left, top, _styleOverride, fabricObject) => {
10857
+ var _a3, _b2;
10858
+ if (!shouldCollapseHandles(fabricObject)) return;
10859
+ const action = (_b2 = (_a3 = fabricObject.canvas) == null ? void 0 : _a3._currentTransform) == null ? void 0 : _b2.action;
10860
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10861
+ const scale = getVisualScale(fabricObject);
10862
+ const p = getHoverProgress(fabricObject, "mvh");
10863
+ const r = 11 * scale;
10864
+ ctx.save();
10865
+ ctx.beginPath();
10866
+ ctx.arc(left, top, r, 0, Math.PI * 2);
10867
+ ctx.closePath();
10868
+ ctx.fillStyle = lerpFill(p);
10869
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10870
+ ctx.lineWidth = 0.75 * (1 - p);
10871
+ ctx.shadowColor = "rgba(15, 23, 42, 0.4)";
10872
+ ctx.shadowBlur = 10;
10873
+ ctx.shadowOffsetY = 2;
10874
+ ctx.fill();
10875
+ if (p < 1) ctx.stroke();
10876
+ ctx.shadowColor = "transparent";
10877
+ ctx.shadowBlur = 0;
10878
+ ctx.shadowOffsetY = 0;
10879
+ const iconColor = p > 0.5 ? "#ffffff" : "rgba(15, 23, 42, 0.85)";
10880
+ ctx.fillStyle = iconColor;
10881
+ const target = r * 1.5;
10882
+ const s = target / 24;
10883
+ ctx.translate(left, top);
10884
+ ctx.scale(s, s);
10885
+ ctx.translate(-12, -12);
10886
+ for (const p2d of MOVE_PATHS_2D) ctx.fill(p2d);
10887
+ ctx.restore();
10888
+ };
10889
+ if (!controls.mvh) {
10890
+ const mvh = new fabric__namespace.Control({
10891
+ x: 0,
10892
+ y: 0.5,
10893
+ offsetY: 28,
10894
+ sizeX: 22,
10895
+ sizeY: 22,
10896
+ touchSizeX: 32,
10897
+ touchSizeY: 32,
10898
+ cursorStyle: "move",
10899
+ actionName: "drag",
10900
+ actionHandler: moveActionHandler,
10901
+ render: renderMoveHandle
10902
+ });
10903
+ mvh.withConnection = false;
10904
+ controls.mvh = mvh;
10905
+ } else {
10906
+ controls.mvh.render = renderMoveHandle;
10907
+ controls.mvh.actionHandler = moveActionHandler;
10908
+ controls.mvh.cursorStyle = "move";
10909
+ }
10910
+ wrapCollapseVisibility(controls.mvh, "mvh");
10911
+ const baseGetVisibility = (_a2 = controls.mvh.getVisibility) == null ? void 0 : _a2.bind(controls.mvh);
10912
+ if (baseGetVisibility && !controls.mvh.__pixldocsMvhVisibilityWrapped) {
10913
+ controls.mvh.getVisibility = (fabricObject, key) => {
10914
+ if (!shouldCollapseHandles(fabricObject)) return false;
10915
+ return baseGetVisibility(fabricObject, key);
10916
+ };
10917
+ controls.mvh.__pixldocsMvhVisibilityWrapped = true;
10918
+ }
10919
+ return controls;
10920
+ };
10921
+ ensureCanvaControlRenders = (obj) => {
10922
+ try {
10923
+ if (obj && obj.controls) installPillRenders(obj.controls);
10924
+ if (obj && Array.isArray(obj._objects)) {
10925
+ for (const child of obj._objects) {
10926
+ if (child && child.controls) installPillRenders(child.controls);
10927
+ }
10928
+ }
10929
+ } catch (e) {
10930
+ }
10931
+ };
10932
+ const origObj = cu.createObjectDefaultControls.bind(cu);
10933
+ cu.createObjectDefaultControls = () => installPillRenders(origObj());
10934
+ if (typeof cu.createTextboxDefaultControls === "function") {
10935
+ const origTb = cu.createTextboxDefaultControls.bind(cu);
10936
+ cu.createTextboxDefaultControls = () => installPillRenders(origTb());
10937
+ }
10938
+ const wrapClassCreateControls = (Klass) => {
10939
+ if (!Klass || typeof Klass.createControls !== "function") return;
10940
+ const orig = Klass.createControls.bind(Klass);
10941
+ Klass.createControls = () => {
10942
+ const res = orig();
10943
+ if (res && res.controls) installPillRenders(res.controls);
10944
+ return res;
10945
+ };
10946
+ };
10947
+ wrapClassCreateControls(fabric__namespace.InteractiveFabricObject);
10948
+ wrapClassCreateControls(fabric__namespace.FabricObject);
10949
+ wrapClassCreateControls(fabric__namespace.Textbox);
10950
+ wrapClassCreateControls(fabric__namespace.IText);
10951
+ const CanvasProto = (_b = fabric__namespace.Canvas) == null ? void 0 : _b.prototype;
10952
+ if (CanvasProto && typeof CanvasProto._setCursorFromEvent === "function") {
10953
+ const origSet = CanvasProto._setCursorFromEvent;
10954
+ CanvasProto._setCursorFromEvent = function(e, target) {
10955
+ const prev = target && target.__corner;
10956
+ const res = origSet.call(this, e, target);
10957
+ const next = target && target.__corner;
10958
+ if (prev !== next) {
10959
+ try {
10960
+ this.requestRenderAll();
10961
+ } catch {
10962
+ }
10963
+ }
10964
+ return res;
10965
+ };
10966
+ }
10967
+ }
10968
+ } catch (e) {
10969
+ console.warn("[PageCanvas] Failed to install Canva-style control handles:", e);
10970
+ }
10971
+ const scaleTextPathConfig = (textPath, sx, sy, uniform) => {
10972
+ if (!textPath || typeof textPath !== "object") return textPath;
10973
+ const next = JSON.parse(JSON.stringify(textPath));
10974
+ if (typeof next.radius === "number") next.radius *= uniform;
10975
+ if (next.bbox) {
10976
+ if (typeof next.bbox.width === "number") next.bbox.width *= sx;
10977
+ if (typeof next.bbox.height === "number") next.bbox.height *= sy;
10978
+ }
10979
+ if (next.endpoints) {
10980
+ if (typeof next.endpoints.leftY === "number") next.endpoints.leftY *= sy;
10981
+ if (typeof next.endpoints.rightY === "number") next.endpoints.rightY *= sy;
10982
+ if (typeof next.endpoints.centerY === "number") next.endpoints.centerY *= sy;
10983
+ }
10984
+ if (next.pivot) {
10985
+ if (typeof next.pivot.x === "number") next.pivot.x *= sx;
10986
+ if (typeof next.pivot.y === "number") next.pivot.y *= sy;
10987
+ }
10988
+ if (next.bezier) {
10989
+ ["p0", "c0", "c1", "p1"].forEach((key) => {
10990
+ if (Array.isArray(next.bezier[key])) {
10991
+ next.bezier[key][0] *= sx;
10992
+ next.bezier[key][1] *= sy;
10993
+ }
10994
+ });
10995
+ }
10996
+ return next;
10997
+ };
10998
+ const scaleUpdateNumber = (updates, source, key, factor) => {
10999
+ const value = Number(source == null ? void 0 : source[key]);
11000
+ if (Number.isFinite(value)) updates[key] = value * factor;
11001
+ };
11002
+ const bakeTextboxScaleIntoTypography = (obj, sourceElement) => {
11003
+ const sx = Math.abs(obj.scaleX ?? 1) || 1;
11004
+ const sy = Math.abs(obj.scaleY ?? 1) || 1;
11005
+ if (Math.abs(sx - 1) < 1e-3 && Math.abs(sy - 1) < 1e-3) return null;
11006
+ const isUniform = Math.abs(sx - sy) < 0.01;
11007
+ const fontScale = isUniform ? (sx + sy) / 2 : Math.abs(sy - 1) > 1e-3 ? sy : 1;
11008
+ const effectScale = isUniform ? fontScale : Math.max(1e-3, Math.sqrt(sx * sy));
11009
+ const updates = {
11010
+ width: Math.max(20, (obj.width ?? (sourceElement == null ? void 0 : sourceElement.width) ?? 20) * sx),
11011
+ scaleX: 1,
11012
+ scaleY: 1
11013
+ };
11014
+ if (fontScale !== 1) {
11015
+ updates.fontSize = Math.max(1, Number(obj.fontSize || (sourceElement == null ? void 0 : sourceElement.fontSize) || 16) * fontScale);
11016
+ const minBoxHeight = Number(obj.minBoxHeight ?? (sourceElement == null ? void 0 : sourceElement.minBoxHeight));
11017
+ if (Number.isFinite(minBoxHeight) && minBoxHeight > 0) updates.minBoxHeight = minBoxHeight * sy;
11018
+ }
11019
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "strokeWidth", effectScale);
11020
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowBlur", effectScale);
11021
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowDistance", effectScale);
11022
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowOffsetX", sx);
11023
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowOffsetY", sy);
11024
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingTop", sy);
11025
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingBottom", sy);
11026
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingLeft", sx);
11027
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingRight", sx);
11028
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPadding", effectScale);
11029
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxTL", effectScale);
11030
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxTR", effectScale);
11031
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxBR", effectScale);
11032
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxBL", effectScale);
11033
+ const textPath = obj.textPath ?? (sourceElement == null ? void 0 : sourceElement.textPath);
11034
+ if (textPath) updates.textPath = scaleTextPathConfig(textPath, sx, sy, effectScale);
11035
+ const center = obj.getCenterPoint();
11036
+ obj.set({
11037
+ width: updates.width,
11038
+ scaleX: 1,
11039
+ scaleY: 1,
11040
+ ...updates.fontSize ? { fontSize: updates.fontSize } : {},
11041
+ ...updates.strokeWidth !== void 0 ? { strokeWidth: updates.strokeWidth } : {}
11042
+ });
11043
+ if (updates.minBoxHeight !== void 0) obj.minBoxHeight = updates.minBoxHeight;
11044
+ if (updates.textPath) obj.textPath = updates.textPath;
11045
+ const shadow = obj.shadow;
11046
+ if (shadow) {
11047
+ shadow.blur = updates.textShadowBlur ?? shadow.blur;
11048
+ shadow.offsetX = updates.textShadowOffsetX ?? shadow.offsetX;
11049
+ shadow.offsetY = updates.textShadowOffsetY ?? shadow.offsetY;
11050
+ }
11051
+ if ((sourceElement == null ? void 0 : sourceElement.type) === "text") {
11052
+ const bakedElement = { ...sourceElement, ...updates };
11053
+ applyTextBackground(obj, extractTextBgConfig(bakedElement));
11054
+ applyTextShadow(obj, bakedElement);
11055
+ }
11056
+ try {
11057
+ obj.initDimensions();
11058
+ } catch {
11059
+ }
11060
+ obj.setPositionByOrigin(center, "center", "center");
11061
+ obj.setCoords();
11062
+ obj.dirty = true;
11063
+ obj.__pixldocsBakedTextScaleUpdates = {
11064
+ ...obj.__pixldocsBakedTextScaleUpdates || {},
11065
+ ...updates
11066
+ };
11067
+ return updates;
11068
+ };
9993
11069
  function applyWarpAwareSelectionBorders(selection) {
9994
11070
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
9995
11071
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -10057,6 +11133,8 @@ const PageCanvas = react.forwardRef(
10057
11133
  const hasRunPostReadyReflowForPageRef = react.useRef(null);
10058
11134
  const hasNotifiedReadyForPageRef = react.useRef(null);
10059
11135
  const hasClearedCachesBeforeFirstSyncRef = react.useRef(false);
11136
+ const projectSettingsRef = react.useRef(projectSettings);
11137
+ projectSettingsRef.current = projectSettings;
10060
11138
  const [guides, setGuides] = react.useState([]);
10061
11139
  const [gridResizeLabel, setGridResizeLabel] = react.useState(null);
10062
11140
  const [hoverBounds, setHoverBounds] = react.useState(null);
@@ -10126,7 +11204,8 @@ const PageCanvas = react.forwardRef(
10126
11204
  react.useRef(null);
10127
11205
  react.useRef(null);
10128
11206
  react.useRef(/* @__PURE__ */ new Map());
10129
- react.useRef(null);
11207
+ const groupResizeActiveSnapRef = react.useRef(null);
11208
+ const objectResizeActiveSnapRef = react.useRef(null);
10130
11209
  react.useRef(null);
10131
11210
  react.useRef(null);
10132
11211
  react.useRef(null);
@@ -10268,33 +11347,358 @@ const PageCanvas = react.forwardRef(
10268
11347
  (movingObj) => {
10269
11348
  const fabricCanvas = fabricRef.current;
10270
11349
  if (!fabricCanvas) return { guides: [], snapDx: 0, snapDy: 0 };
11350
+ const ps = projectSettingsRef.current;
10271
11351
  return calculateSnapGuides(
10272
11352
  movingObj,
10273
11353
  fabricCanvas,
10274
11354
  canvasWidth,
10275
11355
  canvasHeight,
10276
- projectSettings.snapToGuides,
10277
- projectSettings.snapThreshold
11356
+ ps.snapToGuides,
11357
+ ps.snapThreshold
10278
11358
  );
10279
11359
  },
10280
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11360
+ [canvasWidth, canvasHeight]
10281
11361
  );
11362
+ const getResizeExcludeIdsCallback = react.useCallback((obj) => {
11363
+ const ids = /* @__PURE__ */ new Set();
11364
+ const ownId = getObjectId(obj);
11365
+ if (ownId) ids.add(ownId);
11366
+ if (obj instanceof fabric__namespace.ActiveSelection) {
11367
+ obj.getObjects().forEach((member) => {
11368
+ const id = getObjectId(member);
11369
+ if (id) ids.add(id);
11370
+ });
11371
+ } else if (obj instanceof fabric__namespace.Group && typeof obj.getObjects === "function") {
11372
+ obj.getObjects().forEach((member) => {
11373
+ const id = getObjectId(member);
11374
+ if (id) ids.add(id);
11375
+ });
11376
+ }
11377
+ return ids;
11378
+ }, []);
11379
+ const getLogicalGroupSnapBoundsCallback = react.useCallback((excludeIds = []) => {
11380
+ const fabricCanvas = fabricRef.current;
11381
+ if (!fabricCanvas) return [];
11382
+ const excluded = new Set(excludeIds);
11383
+ const page = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11384
+ const children = (page == null ? void 0 : page.children) ?? pageChildren ?? [];
11385
+ const objectBounds = /* @__PURE__ */ new Map();
11386
+ for (const object of fabricCanvas.getObjects()) {
11387
+ const id = getObjectId(object);
11388
+ if (!id || id === "__background__") continue;
11389
+ try {
11390
+ object.setCoords();
11391
+ } catch {
11392
+ }
11393
+ const bounds2 = object.getBoundingRect();
11394
+ objectBounds.set(id, { left: bounds2.left, top: bounds2.top, width: bounds2.width, height: bounds2.height });
11395
+ }
11396
+ const bounds = [];
11397
+ const visit = (nodes) => {
11398
+ for (const node of nodes) {
11399
+ if (!isGroup(node)) continue;
11400
+ const memberIds = getAllElementIds(node.children ?? []);
11401
+ if (memberIds.length === 0) continue;
11402
+ if (memberIds.some((id) => excluded.has(id)) || excluded.has(node.id)) {
11403
+ visit(node.children ?? []);
11404
+ continue;
11405
+ }
11406
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
11407
+ for (const memberId of memberIds) {
11408
+ const b = objectBounds.get(memberId);
11409
+ if (!b) continue;
11410
+ minX = Math.min(minX, b.left);
11411
+ minY = Math.min(minY, b.top);
11412
+ maxX = Math.max(maxX, b.left + b.width);
11413
+ maxY = Math.max(maxY, b.top + b.height);
11414
+ }
11415
+ if (Number.isFinite(minX) && Number.isFinite(minY) && maxX > minX && maxY > minY) {
11416
+ bounds.push({ left: minX, top: minY, width: maxX - minX, height: maxY - minY });
11417
+ }
11418
+ visit(node.children ?? []);
11419
+ }
11420
+ };
11421
+ visit(children);
11422
+ const seen = /* @__PURE__ */ new Set();
11423
+ return bounds.filter((b) => {
11424
+ const key = `${Math.round(b.left)}:${Math.round(b.top)}:${Math.round(b.width)}:${Math.round(b.height)}`;
11425
+ if (seen.has(key)) return false;
11426
+ seen.add(key);
11427
+ return true;
11428
+ });
11429
+ }, [pageId, pageChildren]);
10282
11430
  const calculateScaleSnapGuidesCallback = react.useCallback(
10283
11431
  (scalingObj, corner) => {
10284
11432
  const fabricCanvas = fabricRef.current;
10285
11433
  if (!fabricCanvas) return [];
11434
+ const ps = projectSettingsRef.current;
11435
+ const excludeIds = getResizeExcludeIdsCallback(scalingObj);
10286
11436
  return calculateScaleSnapGuides(
10287
11437
  scalingObj,
10288
11438
  corner,
10289
11439
  fabricCanvas,
10290
11440
  canvasWidth,
10291
11441
  canvasHeight,
10292
- projectSettings.snapToGuides,
10293
- projectSettings.snapThreshold
11442
+ ps.snapToGuides,
11443
+ ps.snapThreshold || 4,
11444
+ getLogicalGroupSnapBoundsCallback(excludeIds)
10294
11445
  );
10295
11446
  },
10296
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11447
+ [canvasWidth, canvasHeight, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback]
11448
+ );
11449
+ const snapDuringScaleCallback = react.useCallback(
11450
+ (obj, corner) => {
11451
+ const fc = fabricRef.current;
11452
+ if (!fc || !obj || !corner) return;
11453
+ const ps = projectSettingsRef.current;
11454
+ if (!ps.snapToGuides) return;
11455
+ if (obj instanceof fabric__namespace.Textbox && (corner === "ml" || corner === "mr")) {
11456
+ const sourceEl = getObjectId(obj) ? elementsRef.current.find((el) => el.id === getObjectId(obj)) : void 0;
11457
+ bakeTextboxScaleIntoTypography(obj, sourceEl);
11458
+ }
11459
+ try {
11460
+ obj.setCoords();
11461
+ } catch {
11462
+ }
11463
+ const br = obj.getBoundingRect();
11464
+ const excludeIds = getResizeExcludeIdsCallback(obj);
11465
+ const snapThreshold = ps.snapThreshold || 4;
11466
+ const snapped = applyScaleSnapToBox(
11467
+ { left: br.left, top: br.top, width: br.width, height: br.height },
11468
+ corner,
11469
+ fc,
11470
+ canvasWidth,
11471
+ canvasHeight,
11472
+ true,
11473
+ snapThreshold,
11474
+ getObjectId(obj),
11475
+ {
11476
+ hysteresis: 3,
11477
+ activeSnapRef: objectResizeActiveSnapRef,
11478
+ roundSnappedOnly: true,
11479
+ additionalBounds: getLogicalGroupSnapBoundsCallback(excludeIds),
11480
+ excludeObjectIds: excludeIds,
11481
+ matchDimensions: true
11482
+ }
11483
+ );
11484
+ const maxSnapShift = Math.max(snapThreshold + 3, 10);
11485
+ const brRight = br.left + br.width;
11486
+ const brBottom = br.top + br.height;
11487
+ const snappedRight = snapped.left + snapped.width;
11488
+ const snappedBottom = snapped.top + snapped.height;
11489
+ const xJump = Math.max(Math.abs(snapped.left - br.left), Math.abs(snappedRight - brRight));
11490
+ const yJump = Math.max(Math.abs(snapped.top - br.top), Math.abs(snappedBottom - brBottom));
11491
+ if (xJump > maxSnapShift) {
11492
+ snapped.left = br.left;
11493
+ snapped.width = br.width;
11494
+ if (objectResizeActiveSnapRef.current) {
11495
+ delete objectResizeActiveSnapRef.current.left;
11496
+ delete objectResizeActiveSnapRef.current.right;
11497
+ delete objectResizeActiveSnapRef.current.width;
11498
+ }
11499
+ }
11500
+ if (yJump > maxSnapShift) {
11501
+ snapped.top = br.top;
11502
+ snapped.height = br.height;
11503
+ if (objectResizeActiveSnapRef.current) {
11504
+ delete objectResizeActiveSnapRef.current.top;
11505
+ delete objectResizeActiveSnapRef.current.bottom;
11506
+ delete objectResizeActiveSnapRef.current.height;
11507
+ }
11508
+ }
11509
+ if (Math.abs(snapped.left - br.left) < 0.01 && Math.abs(snapped.top - br.top) < 0.01 && Math.abs(snapped.width - br.width) < 0.01 && Math.abs(snapped.height - br.height) < 0.01) return;
11510
+ if (obj instanceof fabric__namespace.Textbox && (corner === "ml" || corner === "mr")) {
11511
+ if (corner.includes("l") !== corner.includes("r")) {
11512
+ obj.set({ width: Math.max(20, snapped.width) });
11513
+ obj.initDimensions();
11514
+ obj.setCoords();
11515
+ const after2 = obj.getBoundingRect();
11516
+ obj.set({ left: (obj.left ?? 0) + (snapped.left - after2.left), top: (obj.top ?? 0) + (snapped.top - after2.top) });
11517
+ obj.setCoords();
11518
+ }
11519
+ return;
11520
+ }
11521
+ const baseW = obj.width ?? 1;
11522
+ const baseH = obj.height ?? 1;
11523
+ const signX = (obj.scaleX ?? 1) < 0 ? -1 : 1;
11524
+ const signY = (obj.scaleY ?? 1) < 0 ? -1 : 1;
11525
+ const newScaleX = snapped.width / Math.max(1, baseW) * signX;
11526
+ const newScaleY = snapped.height / Math.max(1, baseH) * signY;
11527
+ obj.set({ scaleX: newScaleX, scaleY: newScaleY });
11528
+ obj.setCoords();
11529
+ const after = obj.getBoundingRect();
11530
+ obj.set({ left: (obj.left ?? 0) + (snapped.left - after.left), top: (obj.top ?? 0) + (snapped.top - after.top) });
11531
+ obj.setCoords();
11532
+ },
11533
+ [canvasWidth, canvasHeight, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback]
10297
11534
  );
11535
+ const installImageResizeControlsWithSnap = react.useCallback((group) => {
11536
+ group.__resizeSnapHandler = (target, corner) => {
11537
+ const fc = fabricRef.current ?? target.canvas;
11538
+ if (!fc || !isActiveRef.current || !corner) return;
11539
+ fc.__isUserTransforming = true;
11540
+ didTransformRef.current = true;
11541
+ lastResizeScaleTargetRef.current = target;
11542
+ const targetId = getObjectId(target);
11543
+ if (targetId && targetId !== "__background__") {
11544
+ preserveSelectionAfterTransformIdRef.current = targetId;
11545
+ transformingIdsRef.current.add(targetId);
11546
+ }
11547
+ target.getObjects().forEach((member) => {
11548
+ const memberId = getObjectId(member);
11549
+ if (memberId) transformingIdsRef.current.add(memberId);
11550
+ });
11551
+ const gridGuidesForResize = [];
11552
+ try {
11553
+ target.setCoords();
11554
+ const br = target.getBoundingRect();
11555
+ const excludeIds = getResizeExcludeIdsCallback(target);
11556
+ const ps = projectSettingsRef.current;
11557
+ const snapThreshold = ps.snapThreshold || 4;
11558
+ const snapped = applyScaleSnapToBox(
11559
+ { left: br.left, top: br.top, width: br.width, height: br.height },
11560
+ corner,
11561
+ fc,
11562
+ canvasWidth,
11563
+ canvasHeight,
11564
+ ps.snapToGuides,
11565
+ snapThreshold,
11566
+ targetId ?? void 0,
11567
+ {
11568
+ hysteresis: 3,
11569
+ activeSnapRef: objectResizeActiveSnapRef,
11570
+ roundSnappedOnly: true,
11571
+ additionalBounds: getLogicalGroupSnapBoundsCallback(excludeIds),
11572
+ excludeObjectIds: excludeIds,
11573
+ matchDimensions: true
11574
+ }
11575
+ );
11576
+ const maxSnapShift = Math.max(snapThreshold + 3, 10);
11577
+ const brRight = br.left + br.width;
11578
+ const brBottom = br.top + br.height;
11579
+ const snappedRight = snapped.left + snapped.width;
11580
+ const snappedBottom = snapped.top + snapped.height;
11581
+ const xJump = Math.max(Math.abs(snapped.left - br.left), Math.abs(snappedRight - brRight));
11582
+ const yJump = Math.max(Math.abs(snapped.top - br.top), Math.abs(snappedBottom - brBottom));
11583
+ if (xJump > maxSnapShift) {
11584
+ snapped.left = br.left;
11585
+ snapped.width = br.width;
11586
+ if (objectResizeActiveSnapRef.current) {
11587
+ delete objectResizeActiveSnapRef.current.left;
11588
+ delete objectResizeActiveSnapRef.current.right;
11589
+ delete objectResizeActiveSnapRef.current.width;
11590
+ }
11591
+ }
11592
+ if (yJump > maxSnapShift) {
11593
+ snapped.top = br.top;
11594
+ snapped.height = br.height;
11595
+ if (objectResizeActiveSnapRef.current) {
11596
+ delete objectResizeActiveSnapRef.current.top;
11597
+ delete objectResizeActiveSnapRef.current.bottom;
11598
+ delete objectResizeActiveSnapRef.current.height;
11599
+ }
11600
+ }
11601
+ const gridX = ps.gridSizeX ?? ps.gridSize ?? 0;
11602
+ const gridY = ps.gridSizeY ?? ps.gridSize ?? 0;
11603
+ if (ps.snapToGrid && gridX > 0 && gridY > 0) {
11604
+ const hasL = corner.includes("l");
11605
+ const hasR = corner.includes("r");
11606
+ const hasT = corner.includes("t");
11607
+ const hasB = corner.includes("b");
11608
+ const anchorRight = snapped.left + snapped.width;
11609
+ const anchorBottom = snapped.top + snapped.height;
11610
+ if (hasL) {
11611
+ const newLeft = Math.round(snapped.left / gridX) * gridX;
11612
+ const newWidth = Math.max(20, anchorRight - newLeft);
11613
+ snapped.left = anchorRight - newWidth;
11614
+ snapped.width = newWidth;
11615
+ gridGuidesForResize.push({ type: "vertical", position: snapped.left, kind: "grid" });
11616
+ } else if (hasR) {
11617
+ const newRight = Math.round(anchorRight / gridX) * gridX;
11618
+ snapped.width = Math.max(20, newRight - snapped.left);
11619
+ gridGuidesForResize.push({ type: "vertical", position: snapped.left + snapped.width, kind: "grid" });
11620
+ }
11621
+ if (hasT) {
11622
+ const newTop = Math.round(snapped.top / gridY) * gridY;
11623
+ const newHeight = Math.max(20, anchorBottom - newTop);
11624
+ snapped.top = anchorBottom - newHeight;
11625
+ snapped.height = newHeight;
11626
+ gridGuidesForResize.push({ type: "horizontal", position: snapped.top, kind: "grid" });
11627
+ } else if (hasB) {
11628
+ const newBottom = Math.round(anchorBottom / gridY) * gridY;
11629
+ snapped.height = Math.max(20, newBottom - snapped.top);
11630
+ gridGuidesForResize.push({ type: "horizontal", position: snapped.top + snapped.height, kind: "grid" });
11631
+ }
11632
+ }
11633
+ const changed = Math.abs(snapped.left - br.left) > 0.01 || Math.abs(snapped.top - br.top) > 0.01 || Math.abs(snapped.width - br.width) > 0.01 || Math.abs(snapped.height - br.height) > 0.01;
11634
+ if (changed) {
11635
+ const ct = target.__cropData;
11636
+ if (ct) {
11637
+ const next = { ...snapped };
11638
+ const isCornerHandle = corner.includes("l") !== corner.includes("r") && corner.includes("t") !== corner.includes("b");
11639
+ const widthChanged = Math.abs(snapped.width - br.width) > 0.01;
11640
+ const heightChanged = Math.abs(snapped.height - br.height) > 0.01;
11641
+ if (isCornerHandle && br.width > 0 && br.height > 0 && widthChanged !== heightChanged) {
11642
+ const aspect = br.width / br.height;
11643
+ if (widthChanged) {
11644
+ next.height = Math.max(20, next.width / aspect);
11645
+ if (corner.includes("t")) next.top = br.top + br.height - next.height;
11646
+ } else {
11647
+ next.width = Math.max(20, next.height * aspect);
11648
+ if (corner.includes("l")) next.left = br.left + br.width - next.width;
11649
+ }
11650
+ } else if (isCornerHandle && br.width > 0 && br.height > 0) {
11651
+ const aspect = br.width / br.height;
11652
+ const nextAspect = next.width / Math.max(1, next.height);
11653
+ if (Math.abs(nextAspect - aspect) > 0.01) {
11654
+ const widthDelta = Math.abs(next.width - br.width);
11655
+ const heightDelta = Math.abs(next.height - br.height);
11656
+ if (widthDelta <= heightDelta) {
11657
+ next.height = Math.max(20, next.width / aspect);
11658
+ if (corner.includes("t")) next.top = br.top + br.height - next.height;
11659
+ } else {
11660
+ next.width = Math.max(20, next.height * aspect);
11661
+ if (corner.includes("l")) next.left = br.left + br.width - next.width;
11662
+ }
11663
+ }
11664
+ }
11665
+ ct.frameW = Math.max(20, next.width);
11666
+ ct.frameH = Math.max(20, next.height);
11667
+ target.set({
11668
+ left: next.left + ct.frameW / 2,
11669
+ top: next.top + ct.frameH / 2,
11670
+ width: ct.frameW,
11671
+ height: ct.frameH,
11672
+ scaleX: 1,
11673
+ scaleY: 1,
11674
+ originX: "center",
11675
+ originY: "center"
11676
+ });
11677
+ updateCoverLayout(target);
11678
+ }
11679
+ }
11680
+ } catch {
11681
+ snapDuringScaleCallback(target, corner);
11682
+ }
11683
+ try {
11684
+ target.setCoords();
11685
+ const br = target.getBoundingRect();
11686
+ setSizeLabel({
11687
+ width: Math.round(br.width),
11688
+ height: Math.round(br.height),
11689
+ x: br.left + br.width / 2,
11690
+ y: br.top + br.height + 18
11691
+ });
11692
+ } catch {
11693
+ }
11694
+ const smartGuides = calculateScaleSnapGuidesCallback(target, corner);
11695
+ setGuides(gridGuidesForResize.length ? [...smartGuides, ...gridGuidesForResize] : smartGuides);
11696
+ setHoverBounds(null);
11697
+ };
11698
+ installCanvaMaskControls(group);
11699
+ applyControlSizeForZoom(group.canvas ?? fabricRef.current, group);
11700
+ ensureCanvaControlRenders(group);
11701
+ }, [calculateScaleSnapGuidesCallback, canvasHeight, canvasWidth, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback, snapDuringScaleCallback]);
10298
11702
  const isTransforming = react.useCallback((canvas2) => {
10299
11703
  if (!canvas2) return false;
10300
11704
  return !!canvas2._currentTransform || !!canvas2.__isUserTransforming;
@@ -10420,6 +11824,8 @@ const PageCanvas = react.forwardRef(
10420
11824
  // Transparent so underlay (page bg + group bgs) shows through
10421
11825
  backgroundColor: "transparent"
10422
11826
  });
11827
+ fabricCanvas.hoverCursor = "default";
11828
+ fabricCanvas.moveCursor = "move";
10423
11829
  if (!allowSelection) {
10424
11830
  fabricCanvas.selection = false;
10425
11831
  fabricCanvas.on("selection:created", () => {
@@ -10441,6 +11847,61 @@ const PageCanvas = react.forwardRef(
10441
11847
  fabricRef.current = fabricCanvas;
10442
11848
  const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
10443
11849
  fabricCanvas.__storeRegistryKey = storeRegistryKey;
11850
+ {
11851
+ const TWEEN_MS = 130;
11852
+ const active = /* @__PURE__ */ new Map();
11853
+ const ensureMap = (obj) => {
11854
+ if (!obj.__handleHoverProgress) obj.__handleHoverProgress = {};
11855
+ return obj.__handleHoverProgress;
11856
+ };
11857
+ const stateKey = (obj, key) => `${obj.__docuforgeId || ""}:${key}`;
11858
+ const step = (sk) => {
11859
+ const s = active.get(sk);
11860
+ if (!s) return;
11861
+ const t = Math.min(1, (performance.now() - s.start) / TWEEN_MS);
11862
+ const eased = 1 - Math.pow(1 - t, 3);
11863
+ const value = s.from + (s.to - s.from) * eased;
11864
+ const map = ensureMap(s.obj);
11865
+ map[s.key] = value;
11866
+ try {
11867
+ fabricCanvas.requestRenderAll();
11868
+ } catch {
11869
+ }
11870
+ if (t < 1) {
11871
+ s.raf = requestAnimationFrame(() => step(sk));
11872
+ } else {
11873
+ map[s.key] = s.to;
11874
+ active.delete(sk);
11875
+ }
11876
+ };
11877
+ const startTween = (obj, key, to) => {
11878
+ const sk = stateKey(obj, key);
11879
+ const map = ensureMap(obj);
11880
+ const from = map[key] ?? (to === 1 ? 0 : 1);
11881
+ if (from === to) return;
11882
+ const existing = active.get(sk);
11883
+ if (existing && existing.raf != null) cancelAnimationFrame(existing.raf);
11884
+ const s = { obj, key, start: performance.now(), from, to, raf: null };
11885
+ active.set(sk, s);
11886
+ s.raf = requestAnimationFrame(() => step(sk));
11887
+ };
11888
+ let prevTarget = null;
11889
+ let prevKey = null;
11890
+ fabricCanvas.on("mouse:move", (opt) => {
11891
+ const t = opt == null ? void 0 : opt.target;
11892
+ const key = t && t.__corner ? String(t.__corner) : null;
11893
+ if (t === prevTarget && key === prevKey) return;
11894
+ if (prevTarget && prevKey) startTween(prevTarget, prevKey, 0);
11895
+ if (t && key) startTween(t, key, 1);
11896
+ prevTarget = t;
11897
+ prevKey = key;
11898
+ });
11899
+ fabricCanvas.on("mouse:out", () => {
11900
+ if (prevTarget && prevKey) startTween(prevTarget, prevKey, 0);
11901
+ prevTarget = null;
11902
+ prevKey = null;
11903
+ });
11904
+ }
10444
11905
  const initFonts = async () => {
10445
11906
  try {
10446
11907
  await preloadAllFonts();
@@ -10530,6 +11991,17 @@ const PageCanvas = react.forwardRef(
10530
11991
  });
10531
11992
  fabricCanvas.on("mouse:up", () => {
10532
11993
  fabricCanvas.__isUserTransforming = false;
11994
+ objectResizeActiveSnapRef.current = null;
11995
+ groupResizeActiveSnapRef.current = null;
11996
+ try {
11997
+ for (const o of fabricCanvas.getObjects()) {
11998
+ if (o.__pixldocsDragMoved) o.__pixldocsDragMoved = false;
11999
+ }
12000
+ const active = fabricCanvas.getActiveObject();
12001
+ if (active == null ? void 0 : active.__pixldocsDragMoved) active.__pixldocsDragMoved = false;
12002
+ fabricCanvas.requestRenderAll();
12003
+ } catch {
12004
+ }
10533
12005
  });
10534
12006
  fabricCanvas.on("object:scaling", () => {
10535
12007
  fabricCanvas.__isUserTransforming = true;
@@ -10576,7 +12048,7 @@ const PageCanvas = react.forwardRef(
10576
12048
  didTransformRef.current = true;
10577
12049
  });
10578
12050
  const syncSelectionToStore = () => {
10579
- var _a2, _b, _c, _d;
12051
+ var _a2, _b2, _c, _d;
10580
12052
  if (!isActiveRef.current || isRebuildingRef.current || isSyncingSelectionToFabricRef.current || !allowSelection) return;
10581
12053
  const walkToTopmostGroup = (childId, children, activeEditingGroupId) => {
10582
12054
  let topmost = null;
@@ -10615,7 +12087,7 @@ const PageCanvas = react.forwardRef(
10615
12087
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
10616
12088
  const clickedId = ids[0];
10617
12089
  const state = useEditorStore.getState();
10618
- const currentPage2 = (_b = state.canvas.pages) == null ? void 0 : _b.find((p) => p.id === pageId);
12090
+ const currentPage2 = (_b2 = state.canvas.pages) == null ? void 0 : _b2.find((p) => p.id === pageId);
10619
12091
  const children = (currentPage2 == null ? void 0 : currentPage2.children) ?? [];
10620
12092
  const parent = walkToTopmostGroup(clickedId, children, activeEditingGroupId);
10621
12093
  const targetIsInCrop = !!(active instanceof fabric__namespace.Group && isCropGroupInCropMode(active) || (active == null ? void 0 : active.group) instanceof fabric__namespace.Group && isCropGroupInCropMode(active.group));
@@ -10762,8 +12234,10 @@ const PageCanvas = react.forwardRef(
10762
12234
  const activeObj = fabricCanvas.getActiveObject();
10763
12235
  if (activeObj instanceof fabric__namespace.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10764
12236
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12237
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10765
12238
  if (activeObj && !(activeObj instanceof fabric__namespace.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10766
- installCanvaMaskControls(activeObj);
12239
+ installImageResizeControlsWithSnap(activeObj);
12240
+ ensureCanvaControlRenders(activeObj);
10767
12241
  }
10768
12242
  });
10769
12243
  fabricCanvas.on("selection:updated", () => {
@@ -10775,12 +12249,14 @@ const PageCanvas = react.forwardRef(
10775
12249
  const activeObj = fabricCanvas.getActiveObject();
10776
12250
  if (activeObj instanceof fabric__namespace.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10777
12251
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12252
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10778
12253
  if (activeObj && !(activeObj instanceof fabric__namespace.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10779
- installCanvaMaskControls(activeObj);
12254
+ installImageResizeControlsWithSnap(activeObj);
12255
+ ensureCanvaControlRenders(activeObj);
10780
12256
  }
10781
12257
  });
10782
12258
  fabricCanvas.on("mouse:dblclick", (opt) => {
10783
- var _a2, _b;
12259
+ var _a2, _b2;
10784
12260
  const target = opt == null ? void 0 : opt.target;
10785
12261
  if (!target) return;
10786
12262
  if (target.isEditing) return;
@@ -10804,7 +12280,7 @@ const PageCanvas = react.forwardRef(
10804
12280
  const childId = getObjectId(hitChild);
10805
12281
  if (!childId) return;
10806
12282
  const stateNow = useEditorStore.getState();
10807
- const pageNow = (_b = stateNow.canvas.pages) == null ? void 0 : _b.find((p) => p.id === pageId);
12283
+ const pageNow = (_b2 = stateNow.canvas.pages) == null ? void 0 : _b2.find((p) => p.id === pageId);
10808
12284
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
10809
12285
  const chain = [];
10810
12286
  {
@@ -10931,7 +12407,7 @@ const PageCanvas = react.forwardRef(
10931
12407
  transformingIdsRef.current.clear();
10932
12408
  };
10933
12409
  const prepareGroupSelectionTransformStart = (target) => {
10934
- var _a2, _b;
12410
+ var _a2, _b2;
10935
12411
  const active = target instanceof fabric__namespace.ActiveSelection ? target : fabricCanvas.getActiveObject();
10936
12412
  if (!(active instanceof fabric__namespace.ActiveSelection)) return;
10937
12413
  if (!activeSelectionMoveStartRef.current || activeSelectionMoveStartRef.current.selection !== active) {
@@ -10945,7 +12421,7 @@ const PageCanvas = react.forwardRef(
10945
12421
  const groupId = active.__pixldocsGroupSelection;
10946
12422
  if (!groupId) return;
10947
12423
  if (((_a2 = groupSelectionTransformStartRef.current) == null ? void 0 : _a2.groupId) === groupId && groupSelectionTransformStartRef.current.selection === active) return;
10948
- const pageChildren2 = ((_b = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b.children) ?? [];
12424
+ const pageChildren2 = ((_b2 = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b2.children) ?? [];
10949
12425
  const groupNode = findNodeById(pageChildren2, groupId);
10950
12426
  if (!groupNode) return;
10951
12427
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
@@ -11029,7 +12505,7 @@ const PageCanvas = react.forwardRef(
11029
12505
  };
11030
12506
  let pendingShiftMultiSelect = null;
11031
12507
  const applyShiftMultiSelect = (target, event, baselineActive = fabricCanvas.getActiveObject(), baselineObjects = fabricCanvas.getActiveObjects()) => {
11032
- var _a2, _b, _c;
12508
+ var _a2, _b2, _c;
11033
12509
  if (!target || !target.selectable) return false;
11034
12510
  const active = baselineActive;
11035
12511
  if (!active || ((_a2 = active.getActiveControl) == null ? void 0 : _a2.call(active))) return false;
@@ -11063,7 +12539,7 @@ const PageCanvas = react.forwardRef(
11063
12539
  isSyncingSelectionToFabricRef.current = false;
11064
12540
  });
11065
12541
  }
11066
- (_b = event == null ? void 0 : event.preventDefault) == null ? void 0 : _b.call(event);
12542
+ (_b2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _b2.call(event);
11067
12543
  (_c = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _c.call(event);
11068
12544
  return true;
11069
12545
  };
@@ -11114,9 +12590,9 @@ const PageCanvas = react.forwardRef(
11114
12590
  return !!(((_a2 = o == null ? void 0 : o._ct) == null ? void 0 : _a2.isCropGroup) || (o == null ? void 0 : o.__cropGroup));
11115
12591
  };
11116
12592
  const promoteToCropGroup = (opt) => {
11117
- var _a2, _b, _c, _d, _e, _f;
12593
+ var _a2, _b2, _c, _d, _e, _f;
11118
12594
  const t = opt.target;
11119
- if (((_a2 = opt.e) == null ? void 0 : _a2.type) !== "mousedown" && ((_b = opt.e) == null ? void 0 : _b.type) !== "pointerdown" && ((_c = opt.e) == null ? void 0 : _c.type) !== "touchstart") {
12595
+ if (((_a2 = opt.e) == null ? void 0 : _a2.type) !== "mousedown" && ((_b2 = opt.e) == null ? void 0 : _b2.type) !== "pointerdown" && ((_c = opt.e) == null ? void 0 : _c.type) !== "touchstart") {
11120
12596
  if (t && isCropGroup2(t)) {
11121
12597
  fabricCanvas._hoveredTarget = t;
11122
12598
  } else if ((t == null ? void 0 : t.group) && isCropGroup2(t.group)) {
@@ -11132,6 +12608,8 @@ const PageCanvas = react.forwardRef(
11132
12608
  const objects = fabricCanvas.getObjects();
11133
12609
  for (const obj of objects) {
11134
12610
  if (isCropGroup2(obj) && obj.containsPoint(pointer)) {
12611
+ installImageResizeControlsWithSnap(obj);
12612
+ ensureCanvaControlRenders(obj);
11135
12613
  fabricCanvas.setActiveObject(obj);
11136
12614
  opt.target = obj;
11137
12615
  fabricCanvas._hoveredTarget = obj;
@@ -11142,12 +12620,16 @@ const PageCanvas = react.forwardRef(
11142
12620
  }
11143
12621
  const g = t.group;
11144
12622
  if (g && isCropGroup2(g)) {
12623
+ installImageResizeControlsWithSnap(g);
12624
+ ensureCanvaControlRenders(g);
11145
12625
  fabricCanvas.setActiveObject(g);
11146
12626
  opt.target = g;
11147
12627
  fabricCanvas._hoveredTarget = g;
11148
12628
  return;
11149
12629
  }
11150
12630
  if (isCropGroup2(t)) {
12631
+ installImageResizeControlsWithSnap(t);
12632
+ ensureCanvaControlRenders(t);
11151
12633
  fabricCanvas.setActiveObject(t);
11152
12634
  t.set({
11153
12635
  selectable: true,
@@ -11200,7 +12682,7 @@ const PageCanvas = react.forwardRef(
11200
12682
  });
11201
12683
  }
11202
12684
  fabricCanvas.on("mouse:down", (opt) => {
11203
- var _a2, _b;
12685
+ var _a2, _b2;
11204
12686
  if (pendingShiftMultiSelect) {
11205
12687
  const pending = pendingShiftMultiSelect;
11206
12688
  pendingShiftMultiSelect = null;
@@ -11208,17 +12690,19 @@ const PageCanvas = react.forwardRef(
11208
12690
  return;
11209
12691
  }
11210
12692
  const target = opt.target;
11211
- const cropGroup = ((_a2 = target == null ? void 0 : target._ct) == null ? void 0 : _a2.isCropGroup) || (target == null ? void 0 : target.__cropGroup) ? target : (target == null ? void 0 : target.group) && (((_b = target.group._ct) == null ? void 0 : _b.isCropGroup) || target.group.__cropGroup) ? target.group : null;
12693
+ const cropGroup = ((_a2 = target == null ? void 0 : target._ct) == null ? void 0 : _a2.isCropGroup) || (target == null ? void 0 : target.__cropGroup) ? target : (target == null ? void 0 : target.group) && (((_b2 = target.group._ct) == null ? void 0 : _b2.isCropGroup) || target.group.__cropGroup) ? target.group : null;
11212
12694
  if (cropGroup) {
11213
12695
  lockEdits();
11214
12696
  didTransformRef.current = false;
12697
+ installImageResizeControlsWithSnap(cropGroup);
12698
+ ensureCanvaControlRenders(cropGroup);
11215
12699
  fabricCanvas.setActiveObject(cropGroup);
11216
12700
  cropGroup.setCoords();
11217
12701
  fabricCanvas.requestRenderAll();
11218
12702
  }
11219
12703
  });
11220
12704
  const groupFabricUnionBBox = (g) => {
11221
- var _a2, _b;
12705
+ var _a2, _b2;
11222
12706
  const memberIds = new Set(getAllElementIds(g.children ?? []));
11223
12707
  if (memberIds.size === 0) return null;
11224
12708
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -11226,7 +12710,7 @@ const PageCanvas = react.forwardRef(
11226
12710
  for (const o of fabricCanvas.getObjects()) {
11227
12711
  const oid = getObjectId(o);
11228
12712
  if (!oid || !memberIds.has(oid)) continue;
11229
- const br = ((_a2 = o.getBoundingRect) == null ? void 0 : _a2.call(o, true, true)) ?? ((_b = o.getBoundingRect) == null ? void 0 : _b.call(o));
12713
+ const br = ((_a2 = o.getBoundingRect) == null ? void 0 : _a2.call(o, true, true)) ?? ((_b2 = o.getBoundingRect) == null ? void 0 : _b2.call(o));
11230
12714
  if (!br) continue;
11231
12715
  minX = Math.min(minX, br.left);
11232
12716
  minY = Math.min(minY, br.top);
@@ -11252,13 +12736,13 @@ const PageCanvas = react.forwardRef(
11252
12736
  return pick;
11253
12737
  };
11254
12738
  fabricCanvas.on("mouse:down:before", (opt) => {
11255
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
12739
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
11256
12740
  if (editLockRef.current) {
11257
12741
  const active = fabricCanvas.getActiveObject();
11258
12742
  if (active && (((_a2 = active._ct) == null ? void 0 : _a2.isCropGroup) || active.__cropGroup)) {
11259
12743
  opt.target = active;
11260
12744
  if (opt.e) {
11261
- (_c = (_b = opt.e).preventDefault) == null ? void 0 : _c.call(_b);
12745
+ (_c = (_b2 = opt.e).preventDefault) == null ? void 0 : _c.call(_b2);
11262
12746
  (_e = (_d = opt.e).stopPropagation) == null ? void 0 : _e.call(_d);
11263
12747
  }
11264
12748
  }
@@ -11268,11 +12752,34 @@ const PageCanvas = react.forwardRef(
11268
12752
  syncLockedRef.current = true;
11269
12753
  lockEdits();
11270
12754
  }
11271
- const target = opt.target;
11272
- const targetId = target ? getObjectId(target) : null;
12755
+ let target = opt.target;
12756
+ let targetId = target ? getObjectId(target) : null;
12757
+ if (target instanceof fabric__namespace.ActiveSelection && target.__pixldocsGroupSelection && target === fabricCanvas.getActiveObject()) {
12758
+ try {
12759
+ const pointer = fabricCanvas.getViewportPoint(opt.e);
12760
+ const members = target.getObjects();
12761
+ for (let i = members.length - 1; i >= 0; i--) {
12762
+ const m = members[i];
12763
+ if (m.visible === false) continue;
12764
+ if (typeof m.containsPoint === "function" && m.containsPoint(pointer)) {
12765
+ const mid = getObjectId(m);
12766
+ if (mid) {
12767
+ target = m;
12768
+ targetId = mid;
12769
+ }
12770
+ break;
12771
+ }
12772
+ }
12773
+ } catch {
12774
+ }
12775
+ }
11273
12776
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11274
12777
  const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11275
12778
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12779
+ if (target instanceof fabric__namespace.Textbox && target.__corner && (target.__corner === "ml" || target.__corner === "mr")) {
12780
+ const sourceEl = targetId ? elementsRef.current.find((el) => el.id === targetId) : void 0;
12781
+ bakeTextboxScaleIntoTypography(target, sourceEl);
12782
+ }
11276
12783
  if (isMultiSelectModifier(opt.e)) {
11277
12784
  const manualTarget = target && !(target instanceof fabric__namespace.ActiveSelection) && targetId && targetId !== "__background__" ? target : pickSelectableObjectAtPointer(opt.e);
11278
12785
  if (manualTarget) {
@@ -11299,11 +12806,39 @@ const PageCanvas = react.forwardRef(
11299
12806
  return topmost;
11300
12807
  };
11301
12808
  if (target && targetId && targetId !== "__background__") {
11302
- const parent = findTopmostPromotableGroup(targetId);
11303
- const targetIsInCrop = !!(target instanceof fabric__namespace.Group && isCropGroupInCropMode(target) || (target == null ? void 0 : target.group) instanceof fabric__namespace.Group && isCropGroupInCropMode(target.group));
12809
+ let effectiveTarget = target;
12810
+ let effectiveTargetId = targetId;
12811
+ const activeNowEarly = fabricCanvas.getActiveObject();
12812
+ const asGroupId = target instanceof fabric__namespace.ActiveSelection ? target.__pixldocsGroupSelection : void 0;
12813
+ if (target instanceof fabric__namespace.ActiveSelection && asGroupId && target === activeNowEarly) {
12814
+ try {
12815
+ const pointer = fabricCanvas.getViewportPoint(opt.e);
12816
+ const members = target.getObjects();
12817
+ for (let i = members.length - 1; i >= 0; i--) {
12818
+ const m = members[i];
12819
+ if (m.visible === false) continue;
12820
+ if (typeof m.containsPoint === "function" && m.containsPoint(pointer)) {
12821
+ const mid = getObjectId(m);
12822
+ if (mid) {
12823
+ effectiveTarget = m;
12824
+ effectiveTargetId = mid;
12825
+ }
12826
+ break;
12827
+ }
12828
+ }
12829
+ } catch {
12830
+ }
12831
+ }
12832
+ const parent = findTopmostPromotableGroup(effectiveTargetId);
12833
+ const targetIsInCrop = !!(effectiveTarget instanceof fabric__namespace.Group && isCropGroupInCropMode(effectiveTarget) || (effectiveTarget == null ? void 0 : effectiveTarget.group) instanceof fabric__namespace.Group && isCropGroupInCropMode(effectiveTarget.group));
11304
12834
  const activeNow = fabricCanvas.getActiveObject();
11305
12835
  const alreadyThisGroup = activeNow instanceof fabric__namespace.ActiveSelection && activeNow.__pixldocsGroupSelection === (parent == null ? void 0 : parent.id);
11306
12836
  const isMultiSelectKey = !!(((_f = opt.e) == null ? void 0 : _f.shiftKey) || ((_g = opt.e) == null ? void 0 : _g.metaKey) || ((_h = opt.e) == null ? void 0 : _h.ctrlKey));
12837
+ const isDeepSelectKey = !!((_i = opt.e) == null ? void 0 : _i.altKey);
12838
+ if (isDeepSelectKey) {
12839
+ pendingGroupPromotionRef.current = null;
12840
+ return;
12841
+ }
11307
12842
  if (parent && !parent.backgroundColor && !targetIsInCrop && activeEditingGroupId !== parent.id && !alreadyThisGroup && !isMultiSelectKey) {
11308
12843
  const memberIds = new Set(getAllElementIds(parent.children ?? []));
11309
12844
  const memberObjs = fabricCanvas.getObjects().filter((o) => {
@@ -11327,10 +12862,33 @@ const PageCanvas = react.forwardRef(
11327
12862
  opt.target = only;
11328
12863
  pendingGroupPromotionRef.current = { groupId: parent.id, selection: only };
11329
12864
  }
12865
+ } else if (parent && !parent.backgroundColor && !targetIsInCrop && alreadyThisGroup && !isMultiSelectKey && effectiveTarget !== activeNow) {
12866
+ try {
12867
+ skipSelectionClearOnDiscardRef.current = true;
12868
+ preserveEditingScopeOnSelectionClearRef.current = true;
12869
+ restoreSuppressedGroupBorders();
12870
+ fabricCanvas.discardActiveObject();
12871
+ } finally {
12872
+ skipSelectionClearOnDiscardRef.current = false;
12873
+ preserveEditingScopeOnSelectionClearRef.current = false;
12874
+ }
12875
+ fabricCanvas.__activeEditingGroupId = parent.id;
12876
+ delete effectiveTarget.__pixldocsGroupSelection;
12877
+ delete effectiveTarget.__pixldocsLogicalGroupIds;
12878
+ try {
12879
+ (_j = effectiveTarget.set) == null ? void 0 : _j.call(effectiveTarget, { selectable: true, evented: true, hasBorders: true, hasControls: true });
12880
+ } catch {
12881
+ }
12882
+ fabricCanvas.setActiveObject(effectiveTarget);
12883
+ effectiveTarget.setCoords();
12884
+ fabricCanvas._target = effectiveTarget;
12885
+ opt.target = effectiveTarget;
12886
+ pendingGroupPromotionRef.current = null;
11330
12887
  }
11331
12888
  } else if (!target || targetId === "__background__") {
11332
- const isMultiSelectKey = !!(((_i = opt.e) == null ? void 0 : _i.shiftKey) || ((_j = opt.e) == null ? void 0 : _j.metaKey) || ((_k = opt.e) == null ? void 0 : _k.ctrlKey));
12889
+ const isMultiSelectKey = !!(((_k = opt.e) == null ? void 0 : _k.shiftKey) || ((_l = opt.e) == null ? void 0 : _l.metaKey) || ((_m = opt.e) == null ? void 0 : _m.ctrlKey));
11333
12890
  if (isMultiSelectKey) return;
12891
+ if ((_n = opt.e) == null ? void 0 : _n.altKey) return;
11334
12892
  try {
11335
12893
  const pointer = fabricCanvas.getPointer(opt.e);
11336
12894
  const px = pointer.x;
@@ -11403,11 +12961,28 @@ const PageCanvas = react.forwardRef(
11403
12961
  const tid = t ? getObjectId(t) : null;
11404
12962
  try {
11405
12963
  const activeIds = new Set(selectedIdsRef.current);
12964
+ const pointer = fabricCanvas.getPointer(opt.e);
12965
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12966
+ const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12967
+ const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
12968
+ const groupPick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
12969
+ if (groupPick && !activeIds.has(groupPick.group.id)) {
12970
+ const b = groupFabricUnionBBox(groupPick.group);
12971
+ if (b) {
12972
+ setHoverBounds({
12973
+ left: b.left,
12974
+ top: b.top,
12975
+ width: b.right - b.left,
12976
+ height: b.bottom - b.top,
12977
+ angle: 0
12978
+ });
12979
+ return;
12980
+ }
12981
+ }
11406
12982
  const isHoveringSelected = !!(tid && activeIds.has(tid));
11407
12983
  if (t && tid && tid !== "__background__" && !isHoveringSelected) {
11408
- const outer = t.group && !t.group.__pixldocsGroupSelection ? t.group : t;
11409
- outer.setCoords();
11410
- const br = outer.getBoundingRect();
12984
+ t.setCoords();
12985
+ const br = t.getBoundingRect();
11411
12986
  setHoverBounds({
11412
12987
  left: br.left,
11413
12988
  top: br.top,
@@ -11428,7 +13003,7 @@ const PageCanvas = react.forwardRef(
11428
13003
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
11429
13004
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11430
13005
  const pick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
11431
- fabricCanvas.defaultCursor = pick ? "move" : "default";
13006
+ fabricCanvas.defaultCursor = "default";
11432
13007
  } catch {
11433
13008
  fabricCanvas.defaultCursor = "default";
11434
13009
  }
@@ -11436,6 +13011,11 @@ const PageCanvas = react.forwardRef(
11436
13011
  fabricCanvas.on("mouse:out", () => {
11437
13012
  setHoverBounds(null);
11438
13013
  });
13014
+ fabricCanvas.on("mouse:down", () => {
13015
+ setHoverBounds(null);
13016
+ });
13017
+ fabricCanvas.on("selection:created", () => setHoverBounds(null));
13018
+ fabricCanvas.on("selection:updated", () => setHoverBounds(null));
11439
13019
  fabricCanvas.on("mouse:down", (ev) => {
11440
13020
  if (fabricCanvas._currentTransform) {
11441
13021
  lockEdits();
@@ -11451,6 +13031,8 @@ const PageCanvas = react.forwardRef(
11451
13031
  setGuides([]);
11452
13032
  setRotationLabel(null);
11453
13033
  setSizeLabel(null);
13034
+ objectResizeActiveSnapRef.current = null;
13035
+ groupResizeActiveSnapRef.current = null;
11454
13036
  dragStarted = false;
11455
13037
  const pendingPromotion = pendingGroupPromotionRef.current;
11456
13038
  pendingGroupPromotionRef.current = null;
@@ -11605,7 +13187,9 @@ const PageCanvas = react.forwardRef(
11605
13187
  const intrH = obj.height ?? 1;
11606
13188
  let newW = Math.max(1, intrW * Math.abs(sx));
11607
13189
  let newH = Math.max(1, intrH * Math.abs(sy));
11608
- if (obj instanceof fabric__namespace.Circle) {
13190
+ if (obj instanceof fabric__namespace.Ellipse) {
13191
+ obj.set({ rx: newW / 2, ry: newH / 2 });
13192
+ } else if (obj instanceof fabric__namespace.Circle) {
11609
13193
  const diameter = Math.max(1, Math.min(newW, newH));
11610
13194
  newW = diameter;
11611
13195
  newH = diameter;
@@ -11722,8 +13306,128 @@ const PageCanvas = react.forwardRef(
11722
13306
  }
11723
13307
  const transform = e.transform;
11724
13308
  const corner = (transform == null ? void 0 : transform.corner) || "";
13309
+ if (obj instanceof fabric__namespace.ActiveSelection && (corner === "ml" || corner === "mr" || corner === "mt" || corner === "mb")) {
13310
+ const isXSide = corner === "ml" || corner === "mr";
13311
+ const sAxis = isXSide ? Math.abs(obj.scaleX ?? 1) : Math.abs(obj.scaleY ?? 1);
13312
+ if (sAxis > 1e-3) {
13313
+ for (const child of obj.getObjects()) {
13314
+ if (!(child instanceof fabric__namespace.Textbox)) continue;
13315
+ if (isXSide) {
13316
+ if (child.__asLiveOrigW == null) {
13317
+ child.__asLiveOrigW = (child.width ?? 0) * (child.scaleX ?? 1);
13318
+ }
13319
+ const origW = child.__asLiveOrigW;
13320
+ const newW = Math.max(20, origW * sAxis);
13321
+ if (Math.abs((child.width ?? 0) - newW) > 0.5) {
13322
+ child.set({ width: newW, scaleX: 1 / sAxis });
13323
+ try {
13324
+ child.initDimensions();
13325
+ } catch {
13326
+ }
13327
+ child.dirty = true;
13328
+ }
13329
+ } else {
13330
+ if (child.__asLiveOrigH == null) {
13331
+ child.__asLiveOrigH = (child.height ?? 0) * (child.scaleY ?? 1);
13332
+ }
13333
+ const origH = child.__asLiveOrigH;
13334
+ const newH = Math.max(20, origH * sAxis);
13335
+ child.minBoxHeight = newH;
13336
+ child.set({ scaleY: 1 / sAxis });
13337
+ try {
13338
+ child.initDimensions();
13339
+ } catch {
13340
+ }
13341
+ child.dirty = true;
13342
+ }
13343
+ }
13344
+ }
13345
+ }
13346
+ snapDuringScaleCallback(obj, corner);
11725
13347
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11726
- setGuides(scaleGuides);
13348
+ const gridGuidesForScale = [];
13349
+ try {
13350
+ const psGrid = projectSettingsRef.current;
13351
+ const canApplyGridSnap = psGrid.snapToGrid && corner && !obj.__cropGroup && !obj.__resizeSnapHandler;
13352
+ if (canApplyGridSnap) {
13353
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13354
+ const gridY = psGrid.gridSizeY ?? psGrid.gridSize ?? 0;
13355
+ if (gridX > 0 && gridY > 0) {
13356
+ obj.setCoords();
13357
+ const br = obj.getBoundingRect();
13358
+ if (br.width > 1 && br.height > 1) {
13359
+ const hasL = corner.includes("l");
13360
+ const hasR = corner.includes("r");
13361
+ const hasT = corner.includes("t");
13362
+ const hasB = corner.includes("b");
13363
+ const anchorRight = br.left + br.width;
13364
+ const anchorBottom = br.top + br.height;
13365
+ let newLeft = br.left;
13366
+ let newTop = br.top;
13367
+ let newWidth = br.width;
13368
+ let newHeight = br.height;
13369
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13370
+ const yAlreadySnapped = scaleGuides.some((g) => g.type === "horizontal");
13371
+ if (!xAlreadySnapped) {
13372
+ if (hasL) {
13373
+ const nL = Math.round(br.left / gridX) * gridX;
13374
+ newWidth = Math.max(20, anchorRight - nL);
13375
+ newLeft = anchorRight - newWidth;
13376
+ } else if (hasR) {
13377
+ const nR = Math.round(anchorRight / gridX) * gridX;
13378
+ newWidth = Math.max(20, nR - br.left);
13379
+ }
13380
+ }
13381
+ if (!yAlreadySnapped) {
13382
+ if (hasT) {
13383
+ const nT = Math.round(br.top / gridY) * gridY;
13384
+ newHeight = Math.max(20, anchorBottom - nT);
13385
+ newTop = anchorBottom - newHeight;
13386
+ } else if (hasB) {
13387
+ const nB = Math.round(anchorBottom / gridY) * gridY;
13388
+ newHeight = Math.max(20, nB - br.top);
13389
+ }
13390
+ }
13391
+ const widthChanged = Math.abs(newWidth - br.width) > 0.5;
13392
+ const heightChanged = Math.abs(newHeight - br.height) > 0.5;
13393
+ if (widthChanged || heightChanged) {
13394
+ const isTextWidth = obj instanceof fabric__namespace.Textbox && widthChanged && !heightChanged;
13395
+ if (isTextWidth) {
13396
+ obj.set({ width: Math.max(20, newWidth) });
13397
+ try {
13398
+ obj.initDimensions();
13399
+ } catch {
13400
+ }
13401
+ } else {
13402
+ const ratioX = widthChanged ? newWidth / br.width : 1;
13403
+ const ratioY = heightChanged ? newHeight / br.height : 1;
13404
+ const curSx = obj.scaleX ?? 1;
13405
+ const curSy = obj.scaleY ?? 1;
13406
+ obj.set({ scaleX: curSx * ratioX, scaleY: curSy * ratioY });
13407
+ }
13408
+ obj.setCoords();
13409
+ const after = obj.getBoundingRect();
13410
+ const dx = newLeft - after.left;
13411
+ const dy = newTop - after.top;
13412
+ if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
13413
+ obj.set({ left: (obj.left ?? 0) + dx, top: (obj.top ?? 0) + dy });
13414
+ obj.setCoords();
13415
+ }
13416
+ if (!xAlreadySnapped) {
13417
+ if (hasL) gridGuidesForScale.push({ type: "vertical", position: newLeft, kind: "grid" });
13418
+ else if (hasR) gridGuidesForScale.push({ type: "vertical", position: newLeft + newWidth, kind: "grid" });
13419
+ }
13420
+ if (!yAlreadySnapped) {
13421
+ if (hasT) gridGuidesForScale.push({ type: "horizontal", position: newTop, kind: "grid" });
13422
+ else if (hasB) gridGuidesForScale.push({ type: "horizontal", position: newTop + newHeight, kind: "grid" });
13423
+ }
13424
+ }
13425
+ }
13426
+ }
13427
+ }
13428
+ } catch {
13429
+ }
13430
+ setGuides(gridGuidesForScale.length ? [...scaleGuides, ...gridGuidesForScale] : scaleGuides);
11727
13431
  });
11728
13432
  fabricCanvas.on("object:resizing", (e) => {
11729
13433
  if (!isActiveRef.current) return;
@@ -11749,13 +13453,70 @@ const PageCanvas = react.forwardRef(
11749
13453
  }
11750
13454
  const transform = e.transform;
11751
13455
  const corner = (transform == null ? void 0 : transform.corner) || "";
13456
+ if (obj instanceof fabric__namespace.Textbox && (corner === "ml" || corner === "mr")) {
13457
+ const objId = getObjectId(obj);
13458
+ const sourceEl = objId ? elementsRef.current.find((el) => el.id === objId) : void 0;
13459
+ bakeTextboxScaleIntoTypography(obj, sourceEl);
13460
+ }
13461
+ snapDuringScaleCallback(obj, corner);
11752
13462
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11753
- setGuides(scaleGuides);
13463
+ const gridGuidesForTextResize = [];
13464
+ try {
13465
+ const psGrid = projectSettingsRef.current;
13466
+ if (psGrid.snapToGrid && corner && obj instanceof fabric__namespace.Textbox && (corner === "ml" || corner === "mr")) {
13467
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13468
+ if (gridX > 0) {
13469
+ obj.setCoords();
13470
+ const br = obj.getBoundingRect();
13471
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13472
+ if (!xAlreadySnapped && br.width > 1) {
13473
+ const anchorRight = br.left + br.width;
13474
+ let newLeft = br.left;
13475
+ let newWidth = br.width;
13476
+ if (corner === "ml") {
13477
+ const nL = Math.round(br.left / gridX) * gridX;
13478
+ newWidth = Math.max(20, anchorRight - nL);
13479
+ newLeft = anchorRight - newWidth;
13480
+ } else {
13481
+ const nR = Math.round(anchorRight / gridX) * gridX;
13482
+ newWidth = Math.max(20, nR - br.left);
13483
+ }
13484
+ if (Math.abs(newWidth - br.width) > 0.5) {
13485
+ obj.set({ width: Math.max(20, newWidth) });
13486
+ try {
13487
+ obj.initDimensions();
13488
+ } catch {
13489
+ }
13490
+ obj.setCoords();
13491
+ const after = obj.getBoundingRect();
13492
+ const dx = newLeft - after.left;
13493
+ if (Math.abs(dx) > 0.01) {
13494
+ obj.set({ left: (obj.left ?? 0) + dx });
13495
+ obj.setCoords();
13496
+ }
13497
+ gridGuidesForTextResize.push({
13498
+ type: "vertical",
13499
+ position: corner === "ml" ? newLeft : newLeft + newWidth,
13500
+ kind: "grid"
13501
+ });
13502
+ }
13503
+ }
13504
+ }
13505
+ }
13506
+ } catch {
13507
+ }
13508
+ setGuides(gridGuidesForTextResize.length ? [...scaleGuides, ...gridGuidesForTextResize] : scaleGuides);
11754
13509
  });
11755
13510
  fabricCanvas.on("object:rotating", (e) => {
11756
13511
  markSimpleTransform(e);
11757
13512
  didTransformRef.current = true;
11758
13513
  const tr = e.target;
13514
+ try {
13515
+ const getCursor = fabricCanvas.__pixldocsGetRotateCursor;
13516
+ const upper = fabricCanvas.upperCanvasEl;
13517
+ if (typeof getCursor === "function" && upper && tr) upper.style.cursor = getCursor(tr);
13518
+ } catch {
13519
+ }
11759
13520
  const rotateTargetId = tr ? getObjectId(tr) : null;
11760
13521
  if (rotateTargetId && rotateTargetId !== "__background__") {
11761
13522
  preserveSelectionAfterTransformIdRef.current = rotateTargetId;
@@ -11782,6 +13543,7 @@ const PageCanvas = react.forwardRef(
11782
13543
  prepareGroupSelectionTransformStart(e.target);
11783
13544
  markTransforming(e.target);
11784
13545
  didTransformRef.current = true;
13546
+ if (e.target) e.target.__pixldocsDragMoved = true;
11785
13547
  const moveTargetId = e.target ? getObjectId(e.target) : null;
11786
13548
  if (moveTargetId && moveTargetId !== "__background__") {
11787
13549
  preserveSelectionAfterTransformIdRef.current = moveTargetId;
@@ -11798,20 +13560,57 @@ const PageCanvas = react.forwardRef(
11798
13560
  if (!obj) return;
11799
13561
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11800
13562
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11801
- setGuides(newGuides);
13563
+ let finalDx = snapDx;
13564
+ let finalDy = snapDy;
13565
+ let mergedGuides = newGuides;
13566
+ const psLive = projectSettingsRef.current;
13567
+ const gridX = psLive.gridSizeX ?? psLive.gridSize ?? 0;
13568
+ const gridY = psLive.gridSizeY ?? psLive.gridSize ?? 0;
13569
+ if (psLive.snapToGrid && gridX > 0 && gridY > 0) {
13570
+ try {
13571
+ snapTarget.setCoords();
13572
+ } catch {
13573
+ }
13574
+ const br = snapTarget.getBoundingRect();
13575
+ const gridGuides = [];
13576
+ if (finalDx === 0) {
13577
+ const newLeft = Math.round(br.left / gridX) * gridX;
13578
+ finalDx = newLeft - br.left;
13579
+ gridGuides.push({ type: "vertical", position: newLeft, kind: "grid" });
13580
+ }
13581
+ if (finalDy === 0) {
13582
+ const newTop = Math.round(br.top / gridY) * gridY;
13583
+ finalDy = newTop - br.top;
13584
+ gridGuides.push({ type: "horizontal", position: newTop, kind: "grid" });
13585
+ }
13586
+ if (gridGuides.length) mergedGuides = [...newGuides, ...gridGuides];
13587
+ }
13588
+ setGuides(mergedGuides);
11802
13589
  setHoverBounds(null);
11803
- if (snapDx !== 0 || snapDy !== 0) {
11804
- snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
13590
+ if (finalDx !== 0 || finalDy !== 0) {
13591
+ snapTarget.set({ left: (snapTarget.left ?? 0) + finalDx, top: (snapTarget.top ?? 0) + finalDy });
11805
13592
  }
11806
13593
  });
11807
13594
  let cropGroupSaveTimer = null;
11808
13595
  fabricCanvas.on("object:modified", (e) => {
11809
- var _a2, _b, _c, _d, _e, _f, _g;
13596
+ var _a2, _b2, _c, _d, _e, _f, _g;
11810
13597
  try {
11811
13598
  dragStarted = false;
11812
13599
  setGuides([]);
11813
13600
  setGroupOverlayLiveBoundsRef.current(null);
13601
+ objectResizeActiveSnapRef.current = null;
13602
+ groupResizeActiveSnapRef.current = null;
11814
13603
  onDragEnd == null ? void 0 : onDragEnd();
13604
+ try {
13605
+ const t = e.target;
13606
+ if (t instanceof fabric__namespace.ActiveSelection) {
13607
+ for (const child of t.getObjects()) {
13608
+ delete child.__asLiveOrigW;
13609
+ delete child.__asLiveOrigH;
13610
+ }
13611
+ }
13612
+ } catch {
13613
+ }
11815
13614
  lockEdits();
11816
13615
  const modifiedTarget = e.target;
11817
13616
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -11920,7 +13719,7 @@ const PageCanvas = react.forwardRef(
11920
13719
  useEditorStore.getState().reflowStackGroupInPage(pageId, groupId);
11921
13720
  }
11922
13721
  const stateAfter = useEditorStore.getState();
11923
- const pageAfter = ((_b = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b.children) ?? [];
13722
+ const pageAfter = ((_b2 = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b2.children) ?? [];
11924
13723
  const groupNodeAfter = findNodeById(pageAfter, groupId);
11925
13724
  if (groupNodeAfter) {
11926
13725
  const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);
@@ -11947,11 +13746,11 @@ const PageCanvas = react.forwardRef(
11947
13746
  clearTimeout(cropGroupSaveTimer);
11948
13747
  }
11949
13748
  cropGroupSaveTimer = setTimeout(() => {
11950
- var _a3, _b2, _c2;
13749
+ var _a3, _b3, _c2;
11951
13750
  const { updateElement: updateElement2 } = useEditorStore.getState();
11952
13751
  const img = ct._img;
11953
13752
  const zoom3 = ((_a3 = img == null ? void 0 : img._ct) == null ? void 0 : _a3.zoom) ?? 1;
11954
- const panX = ((_b2 = img == null ? void 0 : img._ct) == null ? void 0 : _b2.panX) ?? 0.5;
13753
+ const panX = ((_b3 = img == null ? void 0 : img._ct) == null ? void 0 : _b3.panX) ?? 0.5;
11955
13754
  const panY = ((_c2 = img == null ? void 0 : img._ct) == null ? void 0 : _c2.panY) ?? 0.5;
11956
13755
  const stateCrop = useEditorStore.getState();
11957
13756
  const pageCrop = stateCrop.canvas.pages.find((p) => p.id === pageId);
@@ -11970,6 +13769,8 @@ const PageCanvas = react.forwardRef(
11970
13769
  cropZoom: zoom3
11971
13770
  }, { recordHistory: false });
11972
13771
  active.__isInternalCropUpdate = false;
13772
+ installImageResizeControlsWithSnap(active);
13773
+ ensureCanvaControlRenders(active);
11973
13774
  fabricCanvas.setActiveObject(active);
11974
13775
  setTimeout(() => justModifiedIdsRef.current.delete(objId), 150);
11975
13776
  }, 0);
@@ -12223,6 +14024,10 @@ const PageCanvas = react.forwardRef(
12223
14024
  for (const obj of activeObjects) {
12224
14025
  const objId = getObjectId(obj);
12225
14026
  if (!objId || objId === "__background__") continue;
14027
+ const sourceElement = elementsRef.current.find((el) => el.id === objId);
14028
+ if (obj instanceof fabric__namespace.Textbox && !isActiveSelection) {
14029
+ bakeTextboxScaleIntoTypography(obj, sourceElement);
14030
+ }
12226
14031
  let intrinsicWidth;
12227
14032
  let intrinsicHeight;
12228
14033
  if (obj instanceof fabric__namespace.Circle) {
@@ -12289,7 +14094,6 @@ const PageCanvas = react.forwardRef(
12289
14094
  absoluteLeft = (absoluteLeft ?? 0) - w / 2;
12290
14095
  absoluteTop = (absoluteTop ?? 0) - h / 2;
12291
14096
  }
12292
- const sourceElement = elementsRef.current.find((el) => el.id === objId);
12293
14097
  const preserveCornerGeometry = (sourceElement == null ? void 0 : sourceElement.type) === "shape" && (sourceElement.shapeType === "circle" || sourceElement.shapeType === "rounded-rect" || sourceElement.shapeType === "triangle");
12294
14098
  let finalWidth = intrinsicWidth;
12295
14099
  let finalHeight = intrinsicHeight;
@@ -12375,17 +14179,35 @@ const PageCanvas = react.forwardRef(
12375
14179
  finalHeight = 0;
12376
14180
  finalScaleX = 1;
12377
14181
  finalScaleY = 1;
14182
+ } else if (obj instanceof fabric__namespace.Textbox && isActiveSelection && (Math.abs((decomposed.scaleX ?? 1) - 1) > 1e-3 || Math.abs((decomposed.scaleY ?? 1) - 1) > 1e-3)) {
14183
+ const sx = Math.abs(decomposed.scaleX || 1);
14184
+ const sy = Math.abs(decomposed.scaleY || 1);
14185
+ const bakedWidth = Math.max(20, intrinsicWidth * sx);
14186
+ const bakedHeight = Math.max(1, intrinsicHeight * sy);
14187
+ finalWidth = bakedWidth;
14188
+ finalHeight = bakedHeight;
14189
+ finalScaleX = 1;
14190
+ finalScaleY = 1;
14191
+ try {
14192
+ obj.set({ width: bakedWidth, scaleX: 1, scaleY: 1 });
14193
+ obj.initDimensions();
14194
+ obj.setCoords();
14195
+ } catch {
14196
+ }
14197
+ finalAbsoluteMatrix = fabric__namespace.util.composeMatrix({
14198
+ translateX: decomposed.translateX,
14199
+ translateY: decomposed.translateY,
14200
+ angle: decomposed.angle ?? 0,
14201
+ scaleX: 1,
14202
+ scaleY: 1,
14203
+ skewX: 0,
14204
+ skewY: 0
14205
+ });
12378
14206
  } else if (preserveCornerGeometry) {
12379
14207
  const scaledW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));
12380
14208
  const scaledH = Math.max(1, intrinsicHeight * Math.abs(decomposed.scaleY || 1));
12381
- if ((sourceElement == null ? void 0 : sourceElement.shapeType) === "circle") {
12382
- const diameter = Math.max(1, Math.min(scaledW, scaledH));
12383
- finalWidth = diameter;
12384
- finalHeight = diameter;
12385
- } else {
12386
- finalWidth = scaledW;
12387
- finalHeight = scaledH;
12388
- }
14209
+ finalWidth = scaledW;
14210
+ finalHeight = scaledH;
12389
14211
  finalScaleX = 1;
12390
14212
  finalScaleY = 1;
12391
14213
  obj.set({ scaleX: 1, scaleY: 1 });
@@ -12424,6 +14246,11 @@ const PageCanvas = react.forwardRef(
12424
14246
  transformMatrix: finalAbsoluteMatrix
12425
14247
  };
12426
14248
  if (obj instanceof fabric__namespace.Textbox) {
14249
+ const bakedTextScaleUpdates = obj.__pixldocsBakedTextScaleUpdates;
14250
+ if (bakedTextScaleUpdates && typeof bakedTextScaleUpdates === "object") {
14251
+ Object.assign(elementUpdate, bakedTextScaleUpdates);
14252
+ delete obj.__pixldocsBakedTextScaleUpdates;
14253
+ }
12427
14254
  const baked = obj.minBoxHeight;
12428
14255
  if (typeof baked === "number" && baked > 0) {
12429
14256
  elementUpdate.minBoxHeight = baked;
@@ -12532,7 +14359,7 @@ const PageCanvas = react.forwardRef(
12532
14359
  }
12533
14360
  });
12534
14361
  fabricCanvas.on("mouse:dblclick", (e) => {
12535
- var _a2, _b;
14362
+ var _a2, _b2;
12536
14363
  if (!isActiveRef.current || !allowEditing) return;
12537
14364
  let target = e.target;
12538
14365
  if (!target) {
@@ -12542,7 +14369,7 @@ const PageCanvas = react.forwardRef(
12542
14369
  if (target && target instanceof fabric__namespace.Group && target.__cropGroup) {
12543
14370
  const ct = target.__cropData;
12544
14371
  const innerImg = ct == null ? void 0 : ct._img;
12545
- const innerSrc = ((_a2 = innerImg == null ? void 0 : innerImg.getSrc) == null ? void 0 : _a2.call(innerImg)) || ((_b = innerImg == null ? void 0 : innerImg._originalElement) == null ? void 0 : _b.src) || (innerImg == null ? void 0 : innerImg.src) || "";
14372
+ const innerSrc = ((_a2 = innerImg == null ? void 0 : innerImg.getSrc) == null ? void 0 : _a2.call(innerImg)) || ((_b2 = innerImg == null ? void 0 : innerImg._originalElement) == null ? void 0 : _b2.src) || (innerImg == null ? void 0 : innerImg.src) || "";
12546
14373
  const isPlaceholder = !innerSrc || innerSrc === EMPTY_IMAGE_PLACEHOLDER_DATA_URL;
12547
14374
  if (innerImg && !isPlaceholder && !isCropGroupInCropMode(target)) {
12548
14375
  enterCropMode(target);
@@ -12721,13 +14548,13 @@ const PageCanvas = react.forwardRef(
12721
14548
  visibilityUpdateInProgressRef.current = false;
12722
14549
  }
12723
14550
  doSyncRef.current = () => {
12724
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
14551
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
12725
14552
  const shouldSkipUpdates2 = syncLockedRef.current || editLockRef.current;
12726
14553
  const state = useEditorStore.getState();
12727
14554
  const elementsToSync = elements;
12728
14555
  elementsRef.current = elementsToSync;
12729
14556
  const pageTree = isPreviewMode && (pageChildren == null ? void 0 : pageChildren.length) ? pageChildren ?? [] : ((_a2 = state.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _a2.children) ?? [];
12730
- const selectedIdsFromStore = new Set(((_b = state.canvas) == null ? void 0 : _b.selectedIds) ?? []);
14557
+ const selectedIdsFromStore = new Set(((_b2 = state.canvas) == null ? void 0 : _b2.selectedIds) ?? []);
12731
14558
  isRebuildingRef.current = true;
12732
14559
  const allElementIds = new Set(elementsToSync.map((el) => el.id));
12733
14560
  const sectionGroups = pageTree.filter(
@@ -12754,7 +14581,8 @@ const PageCanvas = react.forwardRef(
12754
14581
  const activeObj = fc.getActiveObject();
12755
14582
  const activeObjId = activeObj ? getObjectId(activeObj) : null;
12756
14583
  const isTextBeingEdited = activeObjId && editingTextIdRef.current === activeObjId;
12757
- if (activeObj && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
14584
+ const isMultiSelect = activeObj instanceof fabric__namespace.ActiveSelection;
14585
+ if (activeObj && isMultiSelect && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
12758
14586
  fc.discardActiveObject();
12759
14587
  }
12760
14588
  }
@@ -13074,7 +14902,7 @@ const PageCanvas = react.forwardRef(
13074
14902
  updateCoverLayout(existingObj);
13075
14903
  applyEdgeFadeFrameClipPath(existingObj, element, ct.frameW, ct.frameH, ct.shape || "rect", ct.rx || 0);
13076
14904
  if (allowEditing) {
13077
- installCanvaMaskControls(existingObj);
14905
+ installImageResizeControlsWithSnap(existingObj);
13078
14906
  } else {
13079
14907
  existingObj.set({
13080
14908
  hasControls: false,
@@ -13156,7 +14984,7 @@ const PageCanvas = react.forwardRef(
13156
14984
  hoverCursor: isDynamicField && isPreviewMode ? "pointer" : void 0
13157
14985
  });
13158
14986
  if (allowEditing) {
13159
- installCanvaMaskControls(existingObj);
14987
+ installImageResizeControlsWithSnap(existingObj);
13160
14988
  }
13161
14989
  existingObj.setCoords();
13162
14990
  fc.requestRenderAll();
@@ -13882,7 +15710,7 @@ const PageCanvas = react.forwardRef(
13882
15710
  return unsub;
13883
15711
  }, []);
13884
15712
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
13885
- var _a2, _b, _c;
15713
+ var _a2, _b2, _c;
13886
15714
  const fc = fabricRef.current;
13887
15715
  if (fc && isTransforming(fc)) {
13888
15716
  return;
@@ -13950,11 +15778,12 @@ const PageCanvas = react.forwardRef(
13950
15778
  // Disable rotation for crop groups (simplifies resize math)
13951
15779
  hasRotatingPoint: false,
13952
15780
  // Hide rotation handle
13953
- // Scale with zoom so handles stay same visual size (controls drawn in canvas pixel space)
13954
- cornerSize: Math.max(6, Math.round(8 * (fc.getZoom() || 1))),
13955
- borderScaleFactor: Math.max(1.25, 1.25 * (fc.getZoom() || 1)),
15781
+ // Handles are drawn in screen-space keep them constant on-screen.
15782
+ // Match the global Canva-style defaults (circular dots, pill sides).
15783
+ cornerSize: 10,
15784
+ borderScaleFactor: SELECTION_BORDER_SCALE,
13956
15785
  transparentCorners: false,
13957
- cornerStyle: "rect",
15786
+ cornerStyle: "circle",
13958
15787
  cornerColor: SELECTION_PRIMARY,
13959
15788
  cornerStrokeColor: "#ffffff",
13960
15789
  borderColor: SELECTION_PRIMARY,
@@ -14064,7 +15893,8 @@ const PageCanvas = react.forwardRef(
14064
15893
  obj.clipPath.dirty = true;
14065
15894
  obj.clipPath.setCoords();
14066
15895
  }
14067
- installCanvaMaskControls(obj);
15896
+ installImageResizeControlsWithSnap(obj);
15897
+ ensureCanvaControlRenders(obj);
14068
15898
  obj.set({
14069
15899
  selectable: true,
14070
15900
  evented: true,
@@ -14159,7 +15989,7 @@ const PageCanvas = react.forwardRef(
14159
15989
  obj.setCoords();
14160
15990
  }
14161
15991
  if (!isLine) {
14162
- const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
15992
+ const angleTextPathActive = isTextbox && ((_b2 = element.textPath) == null ? void 0 : _b2.preset) === "rise";
14163
15993
  const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
14164
15994
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
14165
15995
  if (!skipPositionUpdate && (obj instanceof fabric__namespace.FabricImage && obj.originX === "center" || obj instanceof fabric__namespace.Group && obj.__cropGroup)) {
@@ -14236,7 +16066,17 @@ const PageCanvas = react.forwardRef(
14236
16066
  objectCaching: true
14237
16067
  });
14238
16068
  } else if (obj instanceof fabric__namespace.Ellipse) {
14239
- obj.set({ rx: rW / 2, ry: rH / 2 });
16069
+ obj.set({
16070
+ rx: cornerSafeW / 2,
16071
+ ry: cornerSafeH / 2,
16072
+ fill: element.fill || "transparent",
16073
+ stroke: element.stroke || "transparent",
16074
+ strokeWidth: element.strokeWidth || 0,
16075
+ strokeUniform: true,
16076
+ strokeLineJoin: "round",
16077
+ strokeLineCap: "round",
16078
+ objectCaching: true
16079
+ });
14240
16080
  } else if (obj instanceof fabric__namespace.Textbox) {
14241
16081
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
14242
16082
  let text = element.text || "Text";
@@ -14430,6 +16270,16 @@ const PageCanvas = react.forwardRef(
14430
16270
  strokeUniform: true,
14431
16271
  objectCaching: true
14432
16272
  });
16273
+ } else if (obj instanceof fabric__namespace.Ellipse) {
16274
+ obj.set({
16275
+ rx: cornerSafeW / 2,
16276
+ ry: cornerSafeH / 2,
16277
+ fill: element.fill || "transparent",
16278
+ stroke: element.stroke || "transparent",
16279
+ strokeWidth: element.strokeWidth || 0,
16280
+ strokeUniform: true,
16281
+ objectCaching: true
16282
+ });
14433
16283
  } else if (obj instanceof fabric__namespace.Rect && element.shapeType === "rounded-rect") {
14434
16284
  const toRadius = (value, fallback) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
14435
16285
  const baseRx = Math.max(0, Number(element.rx ?? 0));
@@ -14689,7 +16539,7 @@ const PageCanvas = react.forwardRef(
14689
16539
  return Math.max(min, Math.min(max, v));
14690
16540
  };
14691
16541
  const applyEdgeFadeFrameClipPath = (group, element, frameW, frameH, shape, rxRatio) => {
14692
- var _a2, _b, _c;
16542
+ var _a2, _b2, _c;
14693
16543
  const fadeElement = element;
14694
16544
  const fadeGroup = group;
14695
16545
  const inputKey = edgeFadeKey(fadeElement);
@@ -14810,7 +16660,7 @@ const PageCanvas = react.forwardRef(
14810
16660
  delete fadeGroup.__edgeFadeRenderConfig;
14811
16661
  delete fadeGroup.__edgeFadeKey;
14812
16662
  delete fadeGroup.__edgeFadeInputKey;
14813
- if ((_b = group.clipPath) == null ? void 0 : _b.__edgeFadeMask) {
16663
+ if ((_b2 = group.clipPath) == null ? void 0 : _b2.__edgeFadeMask) {
14814
16664
  group.clipPath = void 0;
14815
16665
  updateCoverLayout(group);
14816
16666
  }
@@ -14827,7 +16677,7 @@ const PageCanvas = react.forwardRef(
14827
16677
  (_c = group.canvas) == null ? void 0 : _c.requestRenderAll();
14828
16678
  };
14829
16679
  const loadImageAsync2 = async (element, placeholder, fc) => {
14830
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
16680
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
14831
16681
  const imageUrl = element.src || element.imageUrl;
14832
16682
  if (!imageUrl) return;
14833
16683
  const elementId = element.id;
@@ -14858,7 +16708,7 @@ const PageCanvas = react.forwardRef(
14858
16708
  await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);
14859
16709
  if (!isLatestRequest()) return;
14860
16710
  const imageFitForFade = element.imageFit || ((_a2 = element.style) == null ? void 0 : _a2.imageFit) || "cover";
14861
- const clipShapeForFade = element.clipShape ?? ((_b = element.style) == null ? void 0 : _b.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
16711
+ const clipShapeForFade = element.clipShape ?? ((_b2 = element.style) == null ? void 0 : _b2.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
14862
16712
  const willUseCropGroupForFade = imageFitForFade !== "fill" || clipShapeForFade && clipShapeForFade !== "none";
14863
16713
  try {
14864
16714
  if (hasEdgeFade(element) && !willUseCropGroupForFade) {
@@ -15076,7 +16926,7 @@ const PageCanvas = react.forwardRef(
15076
16926
  evented: canBeEvented && !isHidden
15077
16927
  });
15078
16928
  } else {
15079
- installCanvaMaskControls(cropGroup);
16929
+ installImageResizeControlsWithSnap(cropGroup);
15080
16930
  }
15081
16931
  const cropImg = (_o = cropGroup.__cropData) == null ? void 0 : _o._img;
15082
16932
  if (cropImg) {
@@ -15287,38 +17137,7 @@ const PageCanvas = react.forwardRef(
15287
17137
  ),
15288
17138
  sectionsOverlay,
15289
17139
  groupBoundsOverlay,
15290
- canvas.projectSettings.showGrid && (canvas.projectSettings.gridSize ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
15291
- "svg",
15292
- {
15293
- className: "absolute inset-0 pointer-events-none",
15294
- style: { width: scaledWidth, height: scaledHeight },
15295
- viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15296
- children: [
15297
- /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
15298
- "pattern",
15299
- {
15300
- id: `pixldocs-grid-${pageId}`,
15301
- width: canvas.projectSettings.gridSize,
15302
- height: canvas.projectSettings.gridSize,
15303
- patternUnits: "userSpaceOnUse",
15304
- children: /* @__PURE__ */ jsxRuntime.jsx(
15305
- "path",
15306
- {
15307
- d: `M ${canvas.projectSettings.gridSize} 0 L 0 0 0 ${canvas.projectSettings.gridSize}`,
15308
- fill: "none",
15309
- stroke: "currentColor",
15310
- strokeWidth: 0.5,
15311
- opacity: 0.18,
15312
- className: "text-foreground"
15313
- }
15314
- )
15315
- }
15316
- ) }),
15317
- /* @__PURE__ */ jsxRuntime.jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
15318
- ]
15319
- }
15320
- ),
15321
- hoverBounds && !guides.length && /* @__PURE__ */ jsxRuntime.jsx(
17140
+ hoverBounds && !guides.length && !(selectedIdsRef.current && selectedIdsRef.current.length) && /* @__PURE__ */ jsxRuntime.jsx(
15322
17141
  "svg",
15323
17142
  {
15324
17143
  className: "absolute inset-0 pointer-events-none",
@@ -15332,108 +17151,177 @@ const PageCanvas = react.forwardRef(
15332
17151
  width: hoverBounds.width,
15333
17152
  height: hoverBounds.height,
15334
17153
  fill: "none",
15335
- stroke: "hsl(256 80% 58%)",
15336
- strokeWidth: 1,
17154
+ stroke: SELECTION_PRIMARY,
17155
+ strokeWidth: 2,
17156
+ vectorEffect: "non-scaling-stroke",
15337
17157
  strokeDasharray: "0",
15338
- opacity: 0.65
17158
+ opacity: 1
15339
17159
  }
15340
17160
  )
15341
17161
  }
15342
17162
  ),
15343
- guides.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
17163
+ canvas.projectSettings.showGrid && (() => {
17164
+ const ps = canvas.projectSettings;
17165
+ const gx = Math.max(1, ps.gridSizeX ?? ps.gridSize ?? 0);
17166
+ const gy = Math.max(1, ps.gridSizeY ?? ps.gridSize ?? 0);
17167
+ if (gx <= 0 || gy <= 0) return null;
17168
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17169
+ "svg",
17170
+ {
17171
+ className: "absolute inset-0 pointer-events-none",
17172
+ style: { width: scaledWidth, height: scaledHeight },
17173
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
17174
+ children: [
17175
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
17176
+ "pattern",
17177
+ {
17178
+ id: `pixldocs-grid-${pageId}`,
17179
+ width: gx,
17180
+ height: gy,
17181
+ patternUnits: "userSpaceOnUse",
17182
+ children: /* @__PURE__ */ jsxRuntime.jsx(
17183
+ "path",
17184
+ {
17185
+ d: `M ${gx} 0 L 0 0 0 ${gy}`,
17186
+ fill: "none",
17187
+ stroke: ps.gridColor || "#0f172a",
17188
+ strokeWidth: 0.5,
17189
+ opacity: 0.22
17190
+ }
17191
+ )
17192
+ }
17193
+ ) }),
17194
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
17195
+ ]
17196
+ }
17197
+ );
17198
+ })(),
17199
+ guides.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
15344
17200
  "svg",
15345
17201
  {
15346
17202
  className: "absolute inset-0 pointer-events-none",
15347
17203
  style: { width: scaledWidth, height: scaledHeight },
15348
17204
  viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15349
- children: (() => {
15350
- const seen = /* @__PURE__ */ new Set();
15351
- return guides.filter((guide) => {
15352
- if (guide.kind === "gap") return true;
15353
- const key = `${guide.type}-${guide.position.toFixed(1)}`;
15354
- if (seen.has(key)) return false;
15355
- seen.add(key);
15356
- return true;
15357
- }).map((guide, i) => {
15358
- if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
15359
- const gapColor = "#ec4899";
15360
- const label = `${guide.gap}`;
15361
- const labelW2 = Math.max(22, label.length * 7 + 8);
15362
- if (guide.type === "horizontal") {
15363
- const y = guide.bracketAt ?? guide.position;
15364
- const x1 = guide.start;
15365
- const x2 = guide.end;
15366
- const mid = (x1 + x2) / 2;
17205
+ children: [
17206
+ (() => {
17207
+ const seenBoxes = /* @__PURE__ */ new Set();
17208
+ const boxes = [];
17209
+ for (const g of guides) {
17210
+ const list = g.targetBoundsList ?? (g.targetBounds ? [g.targetBounds] : []);
17211
+ for (const b of list) {
17212
+ const key = `${b.left.toFixed(1)}-${b.top.toFixed(1)}-${b.width.toFixed(1)}-${b.height.toFixed(1)}`;
17213
+ if (seenBoxes.has(key)) continue;
17214
+ seenBoxes.add(key);
17215
+ boxes.push(b);
17216
+ }
17217
+ }
17218
+ return boxes.map((b, i) => /* @__PURE__ */ jsxRuntime.jsx(
17219
+ "rect",
17220
+ {
17221
+ x: b.left,
17222
+ y: b.top,
17223
+ width: b.width,
17224
+ height: b.height,
17225
+ fill: "none",
17226
+ stroke: "#f43f5e",
17227
+ strokeWidth: 1.5,
17228
+ strokeDasharray: "4,3",
17229
+ opacity: 0.9,
17230
+ vectorEffect: "non-scaling-stroke"
17231
+ },
17232
+ `tb-${i}`
17233
+ ));
17234
+ })(),
17235
+ (() => {
17236
+ const seen = /* @__PURE__ */ new Set();
17237
+ return guides.filter((guide) => {
17238
+ if (guide.kind === "gap") return true;
17239
+ const key = `${guide.type}-${guide.position.toFixed(1)}`;
17240
+ if (seen.has(key)) return false;
17241
+ seen.add(key);
17242
+ return true;
17243
+ }).map((guide, i) => {
17244
+ if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
17245
+ const gapColor = "#ec4899";
17246
+ const label = `${guide.gap}`;
17247
+ const labelW2 = Math.max(22, label.length * 7 + 8);
17248
+ if (guide.type === "horizontal") {
17249
+ const y = guide.bracketAt ?? guide.position;
17250
+ const x1 = guide.start;
17251
+ const x2 = guide.end;
17252
+ const mid = (x1 + x2) / 2;
17253
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17254
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17255
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17256
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17257
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
17258
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17259
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17260
+ ] })
17261
+ ] }, i);
17262
+ } else {
17263
+ const x = guide.bracketAt ?? guide.position;
17264
+ const y1 = guide.start;
17265
+ const y2 = guide.end;
17266
+ const mid = (y1 + y2) / 2;
17267
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17268
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17269
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17270
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17271
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
17272
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17273
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17274
+ ] })
17275
+ ] }, i);
17276
+ }
17277
+ }
17278
+ const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
17279
+ const isActive2 = guide.active === true;
17280
+ const isGrid = guide.kind === "grid";
17281
+ const strokeColor = isGrid ? "#f59e0b" : isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#ec4899";
17282
+ const strokeWidth = isGrid ? 1.75 : 1.75;
17283
+ const strokeDasharray = isGrid ? "2,3" : isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
17284
+ const showDistance = typeof guide.distance === "number";
17285
+ const labelText = showDistance ? String(Math.round(guide.distance)) : "";
17286
+ const labelW = Math.max(24, labelText.length * 7);
17287
+ if (guide.type === "vertical") {
17288
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17289
+ const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17290
+ const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
17291
+ const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
15367
17292
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15368
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1 }),
15369
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15370
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15371
- /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
15372
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15373
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17293
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17294
+ isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
17295
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
17296
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
17297
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
17298
+ ] }),
17299
+ showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
17300
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17301
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15374
17302
  ] })
15375
17303
  ] }, i);
15376
17304
  } else {
15377
- const x = guide.bracketAt ?? guide.position;
15378
- const y1 = guide.start;
15379
- const y2 = guide.end;
15380
- const mid = (y1 + y2) / 2;
17305
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17306
+ const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17307
+ const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
17308
+ const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
15381
17309
  return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15382
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1 }),
15383
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1 }),
15384
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1 }),
15385
- /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
15386
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15387
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17310
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17311
+ isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
17312
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
17313
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
17314
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
17315
+ ] }),
17316
+ showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
17317
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17318
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15388
17319
  ] })
15389
17320
  ] }, i);
15390
17321
  }
15391
- }
15392
- const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
15393
- const isActive2 = guide.active === true;
15394
- const strokeColor = isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#3b82f6";
15395
- const strokeWidth = isActive2 ? 2.5 : 1;
15396
- const strokeDasharray = isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
15397
- const showDistance = typeof guide.distance === "number";
15398
- const labelText = showDistance ? String(Math.round(guide.distance)) : "";
15399
- const labelW = Math.max(24, labelText.length * 7);
15400
- if (guide.type === "vertical") {
15401
- const padding = isElementRelative || isActive2 ? 20 : 0;
15402
- const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15403
- const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
15404
- const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
15405
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15406
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray }),
15407
- isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
15408
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15409
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
15410
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
15411
- ] }),
15412
- showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
15413
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15414
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15415
- ] })
15416
- ] }, i);
15417
- } else {
15418
- const padding = isElementRelative || isActive2 ? 20 : 0;
15419
- const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15420
- const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
15421
- const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
15422
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15423
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray }),
15424
- isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
15425
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15426
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
15427
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
15428
- ] }),
15429
- showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
15430
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15431
- /* @__PURE__ */ jsxRuntime.jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15432
- ] })
15433
- ] }, i);
15434
- }
15435
- });
15436
- })()
17322
+ });
17323
+ })()
17324
+ ]
15437
17325
  }
15438
17326
  ),
15439
17327
  gridResizeLabel && /* @__PURE__ */ jsxRuntime.jsx(
@@ -15569,13 +17457,13 @@ function PreviewCanvas({
15569
17457
  onDynamicFieldClick,
15570
17458
  onReady
15571
17459
  }) {
15572
- var _a2, _b, _c, _d, _e;
17460
+ var _a2, _b2, _c, _d, _e;
15573
17461
  const canvasRef = react.useRef(null);
15574
17462
  const containerRef = react.useRef(null);
15575
17463
  const [containerWidth, setContainerWidth] = react.useState(0);
15576
17464
  const [hoveredFieldId, setHoveredFieldId] = react.useState(null);
15577
17465
  const page = (_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2[pageIndex];
15578
- const canvasWidth = ((_b = config == null ? void 0 : config.canvas) == null ? void 0 : _b.width) || 612;
17466
+ const canvasWidth = ((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.width) || 612;
15579
17467
  const canvasHeight = ((_c = config == null ? void 0 : config.canvas) == null ? void 0 : _c.height) || 792;
15580
17468
  const elementToFieldMap = react.useMemo(
15581
17469
  () => buildElementToFieldMap(config == null ? void 0 : config.dynamicFields),
@@ -15625,14 +17513,14 @@ function PreviewCanvas({
15625
17513
  }
15626
17514
  }, [elements]);
15627
17515
  const pageSettings = react.useMemo(() => {
15628
- var _a3, _b2;
17516
+ var _a3, _b3;
15629
17517
  return {
15630
17518
  backgroundColor: ((_a3 = page == null ? void 0 : page.settings) == null ? void 0 : _a3.backgroundColor) || "#ffffff",
15631
- backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
17519
+ backgroundGradient: (_b3 = page == null ? void 0 : page.settings) == null ? void 0 : _b3.backgroundGradient
15632
17520
  };
15633
17521
  }, [(_d = page == null ? void 0 : page.settings) == null ? void 0 : _d.backgroundColor, (_e = page == null ? void 0 : page.settings) == null ? void 0 : _e.backgroundGradient]);
15634
17522
  const projectSettings = react.useMemo(() => {
15635
- var _a3, _b2, _c2;
17523
+ var _a3, _b3, _c2;
15636
17524
  const vars = ((_a3 = config.themeConfig) == null ? void 0 : _a3.variables) || {};
15637
17525
  return {
15638
17526
  showGrid: false,
@@ -15640,7 +17528,7 @@ function PreviewCanvas({
15640
17528
  gridSize: 10,
15641
17529
  snapToGuides: false,
15642
17530
  snapThreshold: 5,
15643
- primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
17531
+ primaryColor: (_b3 = vars.primary) == null ? void 0 : _b3.value,
15644
17532
  secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
15645
17533
  };
15646
17534
  }, [config.themeConfig]);
@@ -15792,7 +17680,7 @@ const PreviewCanvas$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
15792
17680
  PreviewCanvas
15793
17681
  }, Symbol.toStringTag, { value: "Module" }));
15794
17682
  function applyThemeToConfig(config, themeOverrides) {
15795
- var _a2, _b, _c;
17683
+ var _a2, _b2, _c;
15796
17684
  if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;
15797
17685
  const cloned = JSON.parse(JSON.stringify(config));
15798
17686
  if ((_a2 = cloned.themeConfig) == null ? void 0 : _a2.variables) {
@@ -15803,7 +17691,7 @@ function applyThemeToConfig(config, themeOverrides) {
15803
17691
  }
15804
17692
  }
15805
17693
  const varMap = /* @__PURE__ */ new Map();
15806
- if ((_b = cloned.themeConfig) == null ? void 0 : _b.variables) {
17694
+ if ((_b2 = cloned.themeConfig) == null ? void 0 : _b2.variables) {
15807
17695
  for (const [key, def] of Object.entries(cloned.themeConfig.variables)) {
15808
17696
  varMap.set(key, themeOverrides[key] ?? def.value);
15809
17697
  }
@@ -15840,7 +17728,7 @@ function mapFormDefFieldType(t) {
15840
17728
  function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15841
17729
  const sections = [];
15842
17730
  function convert(defs, parentId) {
15843
- var _a2, _b;
17731
+ var _a2, _b2;
15844
17732
  for (const def of defs) {
15845
17733
  const isRepeatable = def.repeatable === true || def.type === "repeatable";
15846
17734
  const defFields = def.fields ?? def.entryFields ?? [];
@@ -15892,7 +17780,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15892
17780
  placeholder: f.placeholder
15893
17781
  }))
15894
17782
  });
15895
- if ((_b = def.children) == null ? void 0 : _b.length) {
17783
+ if ((_b2 = def.children) == null ? void 0 : _b2.length) {
15896
17784
  convert(def.children, def.id);
15897
17785
  }
15898
17786
  }
@@ -16521,7 +18409,7 @@ function findAllRepeatableElementsBySourceId(pages, baseNodeId, oneBasedIndex, s
16521
18409
  return out;
16522
18410
  }
16523
18411
  function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi, childBaseNodeId, childCi, sourceElementId) {
16524
- var _a2, _b;
18412
+ var _a2, _b2;
16525
18413
  const parentGroups = collectGroupsByBaseId(pages, parentBaseNodeId);
16526
18414
  const parentGroup = parentGroups[parentPi - 1];
16527
18415
  if (!parentGroup || !isGroup(parentGroup) || !((_a2 = parentGroup.children) == null ? void 0 : _a2.length)) return void 0;
@@ -16536,7 +18424,7 @@ function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi
16536
18424
  }
16537
18425
  collectChild(parentGroup.children);
16538
18426
  const childGroup = childGroups[childCi - 1];
16539
- if (!childGroup || !isGroup(childGroup) || !((_b = childGroup.children) == null ? void 0 : _b.length)) return void 0;
18427
+ if (!childGroup || !isGroup(childGroup) || !((_b2 = childGroup.children) == null ? void 0 : _b2.length)) return void 0;
16540
18428
  return findElementBySourceIdInSubtree(childGroup.children, sourceElementId) ?? (childGroup.__sourceId === sourceElementId ? childGroup.id : void 0);
16541
18429
  }
16542
18430
  function repeatableLabelKey(label) {
@@ -16586,7 +18474,7 @@ function getRepeatableFromConfig(pages) {
16586
18474
  var _a2;
16587
18475
  const result = [];
16588
18476
  function walk(children) {
16589
- var _a3, _b;
18477
+ var _a3, _b2;
16590
18478
  if (!children) return;
16591
18479
  for (const node of children) {
16592
18480
  if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && ((_a3 = node.children) == null ? void 0 : _a3.length)) {
@@ -16595,7 +18483,7 @@ function getRepeatableFromConfig(pages) {
16595
18483
  if (rep == null ? void 0 : rep.label) result.push({ nodeId: child.id, label: rep.label });
16596
18484
  }
16597
18485
  }
16598
- if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) walk(node.children);
18486
+ if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) walk(node.children);
16599
18487
  }
16600
18488
  }
16601
18489
  for (const page of pages) if ((_a2 = page.children) == null ? void 0 : _a2.length) walk(page.children);
@@ -16611,7 +18499,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16611
18499
  return !!((_a3 = n.repeatableSection) == null ? void 0 : _a3.label);
16612
18500
  };
16613
18501
  function walk(children, parentRepeatableBaseId, parentInfo, parentRepeatableEntryIndex) {
16614
- var _a3, _b;
18502
+ var _a3, _b2;
16615
18503
  if (!Array.isArray(children)) return;
16616
18504
  for (let i = 0; i < children.length; i++) {
16617
18505
  const node = children[i];
@@ -16654,7 +18542,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16654
18542
  if (isGroup(child) && Array.isArray(child.children)) walk(child.children, effectiveChildBase, void 0, childEntryIndex);
16655
18543
  j += count;
16656
18544
  }
16657
- } else if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) {
18545
+ } else if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) {
16658
18546
  const nodeBase = baseId(node.id);
16659
18547
  const selfMatch = schemaBaseIds.has(node.id) || schemaBaseIds.has(nodeBase) || node.__baseNodeId != null && schemaBaseIds.has(node.__baseNodeId);
16660
18548
  const isSelfRepeatable = selfMatch && hasRepeatableSection(node);
@@ -16743,7 +18631,7 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
16743
18631
  return Math.max(1, maxIndex);
16744
18632
  }
16745
18633
  function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
16746
- var _a2, _b, _c;
18634
+ var _a2, _b2, _c;
16747
18635
  const cloned = JSON.parse(JSON.stringify(config));
16748
18636
  if (!cloned.pages) return cloned;
16749
18637
  const dynamicFields = cloned.dynamicFields;
@@ -16924,7 +18812,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
16924
18812
  const { node, baseNodeId, startIndex, count } = block;
16925
18813
  const occIdx = block.__occurrenceIndex ?? 1;
16926
18814
  const N = computeN(baseNodeId, node.id);
16927
- const entryFilter = ((_b = node.repeatableSection) == null ? void 0 : _b.entryFilter) ?? entryFilterFromList(baseNodeId);
18815
+ const entryFilter = ((_b2 = node.repeatableSection) == null ? void 0 : _b2.entryFilter) ?? entryFilterFromList(baseNodeId);
16928
18816
  let entryIndices;
16929
18817
  if (entryFilter && entryFilter.mode === "range" && entryFilter.range) {
16930
18818
  const parsed = parseEntryRange(entryFilter.range, N, entryMetaFromList(baseNodeId));
@@ -17381,7 +19269,7 @@ function findAllFlowStacks(pageChildren) {
17381
19269
  return result;
17382
19270
  }
17383
19271
  function findNestedFlowStack(entry) {
17384
- var _a2, _b;
19272
+ var _a2, _b2;
17385
19273
  const queue = [...entry.children ?? []];
17386
19274
  while (queue.length > 0) {
17387
19275
  const node = queue.shift();
@@ -17389,7 +19277,7 @@ function findNestedFlowStack(entry) {
17389
19277
  const g = node;
17390
19278
  const hasRepeatableChildren = ((_a2 = g.children) == null ? void 0 : _a2.length) && (g.children.some(hasBaseNodeId) || g.children.some((c) => isGroup(c)));
17391
19279
  if (isVerticalStackLayoutMode(g.layoutMode) && hasRepeatableChildren) return g;
17392
- if ((_b = g.children) == null ? void 0 : _b.length) queue.push(...g.children);
19280
+ if ((_b2 = g.children) == null ? void 0 : _b2.length) queue.push(...g.children);
17393
19281
  }
17394
19282
  return null;
17395
19283
  }
@@ -17520,9 +19408,9 @@ function splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overf
17520
19408
  return { stayChildren: newStay, overflowChildren: newOverflow };
17521
19409
  }
17522
19410
  function paginateSinglePage(sourcePage, pageOffsetIndex) {
17523
- var _a2, _b, _c, _d, _e, _f;
19411
+ var _a2, _b2, _c, _d, _e, _f;
17524
19412
  const contentTop = (_a2 = sourcePage.settings) == null ? void 0 : _a2.contentTop;
17525
- const contentBottom = (_b = sourcePage.settings) == null ? void 0 : _b.contentBottom;
19413
+ const contentBottom = (_b2 = sourcePage.settings) == null ? void 0 : _b2.contentBottom;
17526
19414
  if (contentTop == null || contentBottom == null || contentBottom <= contentTop) {
17527
19415
  return [sourcePage];
17528
19416
  }
@@ -18712,7 +20600,7 @@ function splitIntoRuns(text, mainSupportsChar) {
18712
20600
  return runs;
18713
20601
  }
18714
20602
  function rewriteSvgFontsForJsPDF(svgStr) {
18715
- var _a2, _b;
20603
+ var _a2, _b2;
18716
20604
  const parser = new DOMParser();
18717
20605
  const doc = parser.parseFromString(svgStr, "image/svg+xml");
18718
20606
  const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
@@ -18817,7 +20705,7 @@ function rewriteSvgFontsForJsPDF(svgStr) {
18817
20705
  for (const node of childNodes) {
18818
20706
  if (node.nodeType !== 3 || !node.textContent) continue;
18819
20707
  const runs = splitIntoRuns(node.textContent, mainSupportsChar);
18820
- if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) === "main") continue;
20708
+ if (runs.length <= 1 && ((_b2 = runs[0]) == null ? void 0 : _b2.runType) === "main") continue;
18821
20709
  const fragment = doc.createDocumentFragment();
18822
20710
  for (const run of runs) {
18823
20711
  const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
@@ -19127,9 +21015,9 @@ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
19127
21015
  styleEl.appendChild(document.createTextNode(cssText));
19128
21016
  }
19129
21017
  function resolveHarnessFontProxyUrl() {
19130
- var _a2, _b;
21018
+ var _a2, _b2;
19131
21019
  try {
19132
- const runtimeBase = typeof window !== "undefined" && (window.__PIXLDOCS_SUPABASE_URL || ((_a2 = window.__CONFIG__) == null ? void 0 : _a2.supabaseUrl)) || typeof globalThis !== "undefined" && (globalThis.__PIXLDOCS_SUPABASE_URL || ((_b = globalThis.__CONFIG__) == null ? void 0 : _b.supabaseUrl)) || "";
21020
+ const runtimeBase = typeof window !== "undefined" && (window.__PIXLDOCS_SUPABASE_URL || ((_a2 = window.__CONFIG__) == null ? void 0 : _a2.supabaseUrl)) || typeof globalThis !== "undefined" && (globalThis.__PIXLDOCS_SUPABASE_URL || ((_b2 = globalThis.__CONFIG__) == null ? void 0 : _b2.supabaseUrl)) || "";
19133
21021
  const base = String(runtimeBase || "").replace(/\/$/, "");
19134
21022
  return base ? `${base}/functions/v1/font-proxy` : "";
19135
21023
  } catch {
@@ -19791,7 +21679,7 @@ async function resolveTemplateData(options) {
19791
21679
  };
19792
21680
  }
19793
21681
  async function resolveFromForm(options) {
19794
- var _a2, _b, _c;
21682
+ var _a2, _b2, _c;
19795
21683
  const { templateId, formSchemaId, sectionState, flatFormData: directFlatFormData, themeId, supabaseUrl, supabaseAnonKey, prefetched } = options;
19796
21684
  const hasSectionStateInput = !!sectionState && Object.keys(sectionState).length > 0;
19797
21685
  if (!formSchemaId && !hasSectionStateInput) {
@@ -19841,7 +21729,7 @@ async function resolveFromForm(options) {
19841
21729
  inferredSections = inferFormSchemaFromTemplate(
19842
21730
  templateConfig.dynamicFields,
19843
21731
  groups,
19844
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
21732
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
19845
21733
  );
19846
21734
  } else {
19847
21735
  inferredSections = [];
@@ -20028,10 +21916,10 @@ function themeBaseId(id) {
20028
21916
  return out;
20029
21917
  }
20030
21918
  function applyThemeVariantToConfig(config, themeConfig, themeId) {
20031
- var _a2, _b, _c, _d;
21919
+ var _a2, _b2, _c, _d;
20032
21920
  if (!themeConfig) return config;
20033
21921
  const variant = themeId && themeId !== "default" ? (_a2 = themeConfig.variants) == null ? void 0 : _a2.find((v) => v.id === themeId) : null;
20034
- const shouldApplyDefaults = !variant && ((_b = themeConfig.properties) == null ? void 0 : _b.length);
21922
+ const shouldApplyDefaults = !variant && ((_b2 = themeConfig.properties) == null ? void 0 : _b2.length);
20035
21923
  if (!variant && !shouldApplyDefaults) return config;
20036
21924
  if (!((_c = themeConfig.properties) == null ? void 0 : _c.length)) return config;
20037
21925
  const result = JSON.parse(JSON.stringify(config));
@@ -20052,8 +21940,8 @@ function applyThemeVariantToConfig(config, themeConfig, themeId) {
20052
21940
  if (stopMatch) {
20053
21941
  const stopIndex = parseInt(stopMatch[1], 10);
20054
21942
  result.pages.forEach((p) => {
20055
- var _a3, _b2;
20056
- if ((_b2 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b2[stopIndex]) {
21943
+ var _a3, _b3;
21944
+ if ((_b3 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b3[stopIndex]) {
20057
21945
  p.settings.backgroundGradient = {
20058
21946
  ...p.settings.backgroundGradient,
20059
21947
  stops: p.settings.backgroundGradient.stops.map(
@@ -20130,7 +22018,7 @@ function normalizeLayoutModes(config) {
20130
22018
  }
20131
22019
  }
20132
22020
  function paintRepeatableSections(config, repeatableSections, formSchema) {
20133
- var _a2, _b;
22021
+ var _a2, _b2;
20134
22022
  const pages = config.pages ?? [];
20135
22023
  const entryFilters = (formSchema == null ? void 0 : formSchema.entryFilters) ?? [];
20136
22024
  const entryFilterBases = new Set(entryFilters.map((f) => baseId(f.nodeId)));
@@ -20158,7 +22046,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
20158
22046
  return painted;
20159
22047
  }
20160
22048
  for (const section of repeatableSections) {
20161
- const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b = section.entryFilter.range) == null ? void 0 : _b.trim() : void 0;
22049
+ const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b2 = section.entryFilter.range) == null ? void 0 : _b2.trim() : void 0;
20162
22050
  const shouldUseInlineFilter = !!inlineRange && !entryFilterBases.has(baseId(section.nodeId));
20163
22051
  const payload = { label: section.label };
20164
22052
  if (section.minEntries !== void 0) payload.minEntries = section.minEntries;
@@ -20194,7 +22082,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
20194
22082
  }
20195
22083
  }
20196
22084
  async function getTemplateForm(options) {
20197
- var _a2, _b;
22085
+ var _a2, _b2;
20198
22086
  const { templateId, supabaseUrl, supabaseAnonKey } = options;
20199
22087
  if (!supabaseUrl || !supabaseAnonKey) {
20200
22088
  throw new Error("[getTemplateForm] supabaseUrl and supabaseAnonKey are required");
@@ -20238,7 +22126,7 @@ async function getTemplateForm(options) {
20238
22126
  sections = inferFormSchemaFromTemplate(
20239
22127
  templateConfig.dynamicFields,
20240
22128
  templateConfig.fieldGroups || [],
20241
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
22129
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
20242
22130
  );
20243
22131
  } else {
20244
22132
  sections = [];
@@ -20622,7 +22510,7 @@ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExact
20622
22510
  return out;
20623
22511
  }
20624
22512
  function PixldocsPreview(props) {
20625
- var _a2, _b;
22513
+ var _a2, _b2;
20626
22514
  const {
20627
22515
  pageIndex = 0,
20628
22516
  zoom = 1,
@@ -20690,10 +22578,10 @@ function PixldocsPreview(props) {
20690
22578
  supabaseUrl: p.supabaseUrl,
20691
22579
  supabaseAnonKey: p.supabaseAnonKey
20692
22580
  }).then((resolved) => {
20693
- var _a3, _b2;
22581
+ var _a3, _b3;
20694
22582
  if (!cancelled) {
20695
22583
  console.log(PREVIEW_DEBUG_PREFIX, "resolve-done", {
20696
- pages: ((_b2 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b2.length) ?? 0,
22584
+ pages: ((_b3 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b3.length) ?? 0,
20697
22585
  underlinedNodes: countUnderlinedNodes(resolved.config)
20698
22586
  });
20699
22587
  setResolvedConfig(resolved.config);
@@ -20803,7 +22691,7 @@ function PixldocsPreview(props) {
20803
22691
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
20804
22692
  const bgTint = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.background) ?? "rgba(255,255,255,0.12)";
20805
22693
  const canvasW = getNum((_a2 = config == null ? void 0 : config.canvas) == null ? void 0 : _a2.width, 0);
20806
- const canvasH = getNum((_b = config == null ? void 0 : config.canvas) == null ? void 0 : _b.height, 0);
22694
+ const canvasH = getNum((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.height, 0);
20807
22695
  const hasOverlays = frostedBounds.length > 0 && canvasW > 0 && canvasH > 0;
20808
22696
  if (isLoading) {
20809
22697
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, position: "relative", minHeight: 200, display: "flex", alignItems: "center", justifyContent: "center" }, children: loadingFallback ?? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
@@ -20944,7 +22832,7 @@ function ensureFabricGradientDef(doc, obj, width, height) {
20944
22832
  return `url(#${id})`;
20945
22833
  }
20946
22834
  function warpTextboxSvgAlongPath(svg, obj) {
20947
- var _a2, _b, _c, _d, _e;
22835
+ var _a2, _b2, _c, _d, _e;
20948
22836
  const tp = obj == null ? void 0 : obj.textPath;
20949
22837
  if (!tp || !tp.preset || tp.preset === "none") return svg;
20950
22838
  if (tp.preset === "rise" || tp.preset === "angle") {
@@ -21008,7 +22896,7 @@ function warpTextboxSvgAlongPath(svg, obj) {
21008
22896
  }
21009
22897
  for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
21010
22898
  try {
21011
- (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
22899
+ (_b2 = clone.parentNode) == null ? void 0 : _b2.removeChild(clone);
21012
22900
  } catch {
21013
22901
  }
21014
22902
  }
@@ -21028,9 +22916,9 @@ function warpTextboxSvgAlongPath(svg, obj) {
21028
22916
  const fw = obj.fontWeight ?? 400;
21029
22917
  const fst = obj.fontStyle || "normal";
21030
22918
  const readFill = (el) => {
21031
- var _a3, _b2;
22919
+ var _a3, _b3;
21032
22920
  if (!el) return "";
21033
- return el.getAttribute("fill") || ((_b2 = (_a3 = (el.getAttribute("style") || "").match(/(?:^|;)\s*fill\s*:\s*([^;]+)/i)) == null ? void 0 : _a3[1]) == null ? void 0 : _b2.trim()) || "";
22921
+ return el.getAttribute("fill") || ((_b3 = (_a3 = (el.getAttribute("style") || "").match(/(?:^|;)\s*fill\s*:\s*([^;]+)/i)) == null ? void 0 : _a3[1]) == null ? void 0 : _b3.trim()) || "";
21034
22922
  };
21035
22923
  const w = Number(obj.width) || 0;
21036
22924
  const h = Number(obj.height) || 0;
@@ -21383,9 +23271,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21383
23271
  }
21384
23272
  return svgString;
21385
23273
  }
21386
- const resolvedPackageVersion = "0.5.241";
23274
+ const resolvedPackageVersion = "0.5.243";
21387
23275
  const PACKAGE_VERSION = resolvedPackageVersion;
21388
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.241";
23276
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.243";
21389
23277
  const roundParityValue = (value) => {
21390
23278
  if (typeof value !== "number") return value;
21391
23279
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21562,11 +23450,11 @@ function installUnderlineFix(fab) {
21562
23450
  if (!TextProto || typeof TextProto._renderTextDecoration !== "function") return;
21563
23451
  const original = TextProto._renderTextDecoration;
21564
23452
  const measureLineTextWidth = (obj, ctx, lineIndex) => {
21565
- var _a3, _b, _c, _d, _e, _f;
23453
+ var _a3, _b2, _c, _d, _e, _f;
21566
23454
  const rawLine = (_a3 = obj._textLines) == null ? void 0 : _a3[lineIndex];
21567
23455
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
21568
23456
  if (!lineText) return 0;
21569
- const fontSize = Number(((_b = obj.getValueOfPropertyAt) == null ? void 0 : _b.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
23457
+ const fontSize = Number(((_b2 = obj.getValueOfPropertyAt) == null ? void 0 : _b2.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
21570
23458
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
21571
23459
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
21572
23460
  const fontFamily = String(((_e = obj.getValueOfPropertyAt) == null ? void 0 : _e.call(obj, lineIndex, 0, "fontFamily")) ?? obj.fontFamily ?? "sans-serif");
@@ -21706,11 +23594,78 @@ function installTextboxBoxExtensions(fab) {
21706
23594
  if (Array.isArray(stateProps2)) {
21707
23595
  if (!stateProps2.includes("minBoxHeight")) stateProps2.push("minBoxHeight");
21708
23596
  if (!stateProps2.includes("verticalAlign")) stateProps2.push("verticalAlign");
23597
+ if (!stateProps2.includes("smartWrap")) stateProps2.push("smartWrap");
21709
23598
  }
21710
23599
  const cacheProps2 = TextboxProto2.cacheProperties;
21711
23600
  if (Array.isArray(cacheProps2)) {
21712
23601
  if (!cacheProps2.includes("minBoxHeight")) cacheProps2.push("minBoxHeight");
21713
23602
  if (!cacheProps2.includes("verticalAlign")) cacheProps2.push("verticalAlign");
23603
+ if (!cacheProps2.includes("smartWrap")) cacheProps2.push("smartWrap");
23604
+ }
23605
+ TextboxProto2.smartWrap = true;
23606
+ if (typeof TextboxProto2._wrapLine === "function" && !TextboxProto2.__pixldocsOrigWrapLine) {
23607
+ const origWrapLine = TextboxProto2._wrapLine;
23608
+ TextboxProto2.__pixldocsOrigWrapLine = origWrapLine;
23609
+ TextboxProto2._wrapLine = function(lineIndex, desiredWidth, ref, reservedSpace = 0) {
23610
+ var _a3;
23611
+ if (!this.smartWrap || this.splitByGrapheme) {
23612
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23613
+ }
23614
+ try {
23615
+ const additionalSpace = this._getWidthOfCharSpacing();
23616
+ const data = ((_a3 = ref == null ? void 0 : ref.wordsData) == null ? void 0 : _a3[lineIndex]) || [];
23617
+ const effective = Math.max(1, desiredWidth - reservedSpace);
23618
+ const infix = " ";
23619
+ const infixWidth = this._measureWord([infix], lineIndex, 0);
23620
+ const graphemeLines = [];
23621
+ let line = [];
23622
+ let lineWidth = 0;
23623
+ let lineJustStarted = true;
23624
+ let largestWordWidth = 0;
23625
+ let offset = 0;
23626
+ const pushLine = () => {
23627
+ graphemeLines.push(line);
23628
+ line = [];
23629
+ lineWidth = 0;
23630
+ lineJustStarted = true;
23631
+ };
23632
+ for (let i = 0; i < data.length; i++) {
23633
+ const { word, width: wordWidth } = data[i];
23634
+ if (wordWidth <= effective) {
23635
+ if (wordWidth > largestWordWidth) largestWordWidth = wordWidth;
23636
+ const projected = lineJustStarted ? wordWidth : lineWidth + infixWidth + wordWidth - additionalSpace;
23637
+ if (!lineJustStarted && projected > effective) pushLine();
23638
+ if (!lineJustStarted) {
23639
+ line.push(infix);
23640
+ lineWidth += infixWidth;
23641
+ }
23642
+ line = line.concat(word);
23643
+ lineWidth += wordWidth;
23644
+ lineJustStarted = false;
23645
+ } else {
23646
+ if (!lineJustStarted) pushLine();
23647
+ for (const g of word) {
23648
+ const gw = this._measureWord([g], lineIndex, offset);
23649
+ if (gw > largestWordWidth) largestWordWidth = gw;
23650
+ const gProj = lineJustStarted ? gw : lineWidth + gw - additionalSpace;
23651
+ if (!lineJustStarted && gProj > effective) pushLine();
23652
+ line.push(g);
23653
+ lineWidth += gw;
23654
+ lineJustStarted = false;
23655
+ offset++;
23656
+ }
23657
+ offset -= word.length;
23658
+ }
23659
+ offset += word.length + 1;
23660
+ }
23661
+ if (line.length) graphemeLines.push(line);
23662
+ const minNeeded = Math.max(0, Math.min(largestWordWidth, effective) - additionalSpace + reservedSpace);
23663
+ if (minNeeded > this.dynamicMinWidth) this.dynamicMinWidth = minNeeded;
23664
+ return graphemeLines;
23665
+ } catch (e) {
23666
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23667
+ }
23668
+ };
21714
23669
  }
21715
23670
  TextboxProto2.__pixldocsTextboxExtended = true;
21716
23671
  __textboxBoxExtensionsInstalled = true;
@@ -22132,7 +24087,7 @@ class PixldocsRenderer {
22132
24087
  await this.waitForCanvasScene(container, cloned, i);
22133
24088
  }
22134
24089
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
22135
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BQhOl3Zh.cjs"));
24090
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-V2qBQ4Xs.cjs"));
22136
24091
  const prepared = preparePagesForExport(
22137
24092
  cloned.pages,
22138
24093
  canvasWidth,
@@ -22349,9 +24304,9 @@ class PixldocsRenderer {
22349
24304
  }
22350
24305
  waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
22351
24306
  return new Promise((resolve) => {
22352
- var _a2, _b;
24307
+ var _a2, _b2;
22353
24308
  const start = Date.now();
22354
- const pageHasContent = (((_b = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b.length) ?? 0) > 0;
24309
+ const pageHasContent = (((_b2 = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) ?? 0) > 0;
22355
24310
  const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
22356
24311
  const check = () => {
22357
24312
  const fabricCanvas = this.getFabricCanvasFromContainer(container);
@@ -22426,9 +24381,9 @@ class PixldocsRenderer {
22426
24381
  return normalized;
22427
24382
  }
22428
24383
  paintPageBackground(ctx, page, width, height) {
22429
- var _a2, _b;
24384
+ var _a2, _b2;
22430
24385
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22431
- const gradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
24386
+ const gradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22432
24387
  ctx.clearRect(0, 0, width, height);
22433
24388
  ctx.fillStyle = backgroundColor;
22434
24389
  ctx.fillRect(0, 0, width, height);
@@ -22676,7 +24631,7 @@ class PixldocsRenderer {
22676
24631
  };
22677
24632
  const onReady = () => {
22678
24633
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
22679
- var _a2, _b;
24634
+ var _a2, _b2;
22680
24635
  try {
22681
24636
  const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
22682
24637
  await this.waitForCanvasImages(container, expectedImageIds);
@@ -22695,7 +24650,7 @@ class PixldocsRenderer {
22695
24650
  );
22696
24651
  const page = renderConfig.pages[pageIndex];
22697
24652
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22698
- const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
24653
+ const backgroundGradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22699
24654
  cleanup();
22700
24655
  resolve({
22701
24656
  svg: svgString,
@@ -22816,7 +24771,7 @@ class PixldocsRenderer {
22816
24771
  logJsonLine("[canvas-renderer][fabric-text-parity]", { stage, textboxes: sample.length, sample });
22817
24772
  }
22818
24773
  async waitForStableTextMetrics(container, config, options = {}) {
22819
- var _a2, _b, _c;
24774
+ var _a2, _b2, _c;
22820
24775
  if (typeof document !== "undefined") {
22821
24776
  void ensureFontsForResolvedConfig(config);
22822
24777
  await this.waitForRelevantFonts(config);
@@ -22858,7 +24813,7 @@ class PixldocsRenderer {
22858
24813
  }
22859
24814
  fabricInstance.getObjects().forEach(primeCharBounds);
22860
24815
  (_a2 = fabricInstance.calcOffset) == null ? void 0 : _a2.call(fabricInstance);
22861
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
24816
+ (_b2 = fabricInstance.renderAll) == null ? void 0 : _b2.call(fabricInstance);
22862
24817
  await waitForPaint();
22863
24818
  (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
22864
24819
  await waitForPaint();
@@ -22899,7 +24854,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22899
24854
  };
22900
24855
  logParityJson(tag, stage, { kind: "summary", ...summary });
22901
24856
  const sample = texts.slice(0, maxItems).map((t, idx) => {
22902
- var _a2, _b;
24857
+ var _a2, _b2;
22903
24858
  const tspans = Array.from(t.querySelectorAll("tspan"));
22904
24859
  const tspanInfo = tspans.slice(0, 8).map((s) => ({
22905
24860
  x: s.getAttribute("x"),
@@ -22916,7 +24871,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22916
24871
  while (cursor && !containerWidth) {
22917
24872
  containerWidth = (_a2 = cursor.getAttribute) == null ? void 0 : _a2.call(cursor, "width");
22918
24873
  cursor = cursor.parentElement;
22919
- if (cursor && ((_b = cursor.tagName) == null ? void 0 : _b.toLowerCase()) === "svg") break;
24874
+ if (cursor && ((_b2 = cursor.tagName) == null ? void 0 : _b2.toLowerCase()) === "svg") break;
22920
24875
  }
22921
24876
  return {
22922
24877
  idx,
@@ -23304,10 +25259,10 @@ function normalizeSvgExplicitColors(svg) {
23304
25259
  return normalizeGradientPaintRef(v);
23305
25260
  };
23306
25261
  const hasExplicitNonePaint = (el, attr) => {
23307
- var _a2, _b;
25262
+ var _a2, _b2;
23308
25263
  const raw = (el.getAttribute(attr) ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue(attr)) ?? "").trim().toLowerCase();
23309
25264
  if (raw === "none" || raw === "transparent") return true;
23310
- const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b = el.style) == null ? void 0 : _b.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
25265
+ const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
23311
25266
  return rawOpacity === "0" || rawOpacity === "0%" || rawOpacity === "0.0";
23312
25267
  };
23313
25268
  function walk(el, parentFill, parentStroke, parentColor) {
@@ -23374,14 +25329,14 @@ function normalizeSvgExplicitColors(svg) {
23374
25329
  function bakeGroupOpacityIntoChildren(svg) {
23375
25330
  const DRAWABLE = /* @__PURE__ */ new Set(["path", "rect", "circle", "ellipse", "polygon", "polyline", "line", "text", "tspan"]);
23376
25331
  function walkAndBake(el, inheritedOpacity) {
23377
- var _a2, _b, _c, _d;
25332
+ var _a2, _b2, _c, _d;
23378
25333
  if (isInSvgDefinitionSubtree(el)) {
23379
25334
  for (let i = 0; i < el.children.length; i++) walkAndBake(el.children[i], 1);
23380
25335
  return;
23381
25336
  }
23382
25337
  const tag = ((_a2 = el.tagName) == null ? void 0 : _a2.toLowerCase()) ?? "";
23383
25338
  const opacityAttr = parseSvgOpacity(el.getAttribute("opacity"));
23384
- const styleOpacity = parseSvgOpacity(((_b = el.style) == null ? void 0 : _b.getPropertyValue("opacity")) || null);
25339
+ const styleOpacity = parseSvgOpacity(((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("opacity")) || null);
23385
25340
  const ownOpacity = opacityAttr ?? styleOpacity ?? 1;
23386
25341
  const combinedOpacity = inheritedOpacity * ownOpacity;
23387
25342
  if (ownOpacity < 0.999 && tag !== "image") {
@@ -23904,10 +25859,10 @@ function getFirstExplicitColorFromSvg(svg) {
23904
25859
  return getGradientStopColorAsHex(svg, gradientId);
23905
25860
  };
23906
25861
  function walk(el) {
23907
- var _a2, _b;
25862
+ var _a2, _b2;
23908
25863
  if (fill && stroke) return;
23909
25864
  const f = el.getAttribute("fill") ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue("fill"));
23910
- const s = el.getAttribute("stroke") ?? ((_b = el.style) == null ? void 0 : _b.getPropertyValue("stroke"));
25865
+ const s = el.getAttribute("stroke") ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("stroke"));
23911
25866
  if (!fill) {
23912
25867
  if (isRealColor(f)) fill = f;
23913
25868
  else if (f) {
@@ -23935,8 +25890,8 @@ function isNearWhite(hex) {
23935
25890
  return r >= 250 && g >= 250 && b >= 250;
23936
25891
  }
23937
25892
  function getStopColorRaw(stop) {
23938
- var _a2, _b;
23939
- return stop.getAttribute("stop-color") ?? ((_b = (_a2 = stop.style) == null ? void 0 : _a2.getPropertyValue("stop-color")) == null ? void 0 : _b.trim()) ?? getInlineStyleValue(stop, "stop-color");
25893
+ var _a2, _b2;
25894
+ return stop.getAttribute("stop-color") ?? ((_b2 = (_a2 = stop.style) == null ? void 0 : _a2.getPropertyValue("stop-color")) == null ? void 0 : _b2.trim()) ?? getInlineStyleValue(stop, "stop-color");
23940
25895
  }
23941
25896
  function getGradientStopColorAsHex(svgRoot, gradientId, visited = /* @__PURE__ */ new Set()) {
23942
25897
  var _a2;
@@ -24000,12 +25955,12 @@ async function convertTextDecorationsToLines(svg) {
24000
25955
  else el.removeAttribute("style");
24001
25956
  };
24002
25957
  const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
24003
- var _a2, _b;
25958
+ var _a2, _b2;
24004
25959
  let current = el;
24005
25960
  while (current) {
24006
25961
  const attrValue = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
24007
25962
  if (attrValue) return attrValue;
24008
- const styleValue = (_b = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b.trim();
25963
+ const styleValue = (_b2 = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b2.trim();
24009
25964
  if (styleValue) return styleValue;
24010
25965
  current = current.parentElement;
24011
25966
  }
@@ -24183,7 +26138,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
24183
26138
  }
24184
26139
  }
24185
26140
  async function rasterizeShadowMarkers(svg) {
24186
- var _a2, _b, _c, _d, _e, _f;
26141
+ var _a2, _b2, _c, _d, _e, _f;
24187
26142
  if (typeof window === "undefined" || typeof document === "undefined") return;
24188
26143
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
24189
26144
  if (markers.length === 0) return;
@@ -24207,7 +26162,7 @@ async function rasterizeShadowMarkers(svg) {
24207
26162
  const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
24208
26163
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
24209
26164
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
24210
- (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
26165
+ (_b2 = marker.parentNode) == null ? void 0 : _b2.removeChild(marker);
24211
26166
  continue;
24212
26167
  }
24213
26168
  const innerXml = restoreSourceFontsForShadowRaster(
@@ -24452,7 +26407,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24452
26407
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24453
26408
  sanitizeSvgTreeForPdf(svgToDraw);
24454
26409
  try {
24455
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BQhOl3Zh.cjs"));
26410
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-V2qBQ4Xs.cjs"));
24456
26411
  try {
24457
26412
  await logTextMeasurementDiagnostic(svgToDraw);
24458
26413
  } catch {
@@ -24468,7 +26423,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24468
26423
  }
24469
26424
  }
24470
26425
  function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundColor, backgroundGradient) {
24471
- var _a2, _b;
26426
+ var _a2, _b2;
24472
26427
  if (backgroundGradient && ((_a2 = backgroundGradient.stops) == null ? void 0 : _a2.length) >= 2) {
24473
26428
  const grad = backgroundGradient;
24474
26429
  const colorStops = grad.stops.map((s) => {
@@ -24523,7 +26478,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24523
26478
  doc.fill({ key: patternKey, matrix: doc.Matrix(1, 0, 0, 1, 0, 0) });
24524
26479
  });
24525
26480
  } catch {
24526
- const fallback = ((_b = colorStops[0]) == null ? void 0 : _b.color) || [255, 255, 255];
26481
+ const fallback = ((_b2 = colorStops[0]) == null ? void 0 : _b2.color) || [255, 255, 255];
24527
26482
  pdf.setFillColor(fallback[0], fallback[1], fallback[2]);
24528
26483
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
24529
26484
  }
@@ -24536,7 +26491,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24536
26491
  }
24537
26492
  }
24538
26493
  async function assemblePdfFromSvgs(svgResults, options = {}) {
24539
- var _a2, _b;
26494
+ var _a2, _b2;
24540
26495
  if (svgResults.length === 0) throw new Error("No pages to export");
24541
26496
  const { title, stripPageBackground } = options;
24542
26497
  const firstPage = svgResults[0];
@@ -24569,7 +26524,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
24569
26524
  const pageOrientation = page.width > page.height ? "landscape" : "portrait";
24570
26525
  pdf.addPage([page.width, page.height], pageOrientation);
24571
26526
  }
24572
- const hasGradient = !!((_b = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b.length);
26527
+ const hasGradient = !!((_b2 = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b2.length);
24573
26528
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
24574
26529
  const shouldStripBg = stripPageBackground ?? hasGradient;
24575
26530
  const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
@@ -24849,4 +26804,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
24849
26804
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
24850
26805
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
24851
26806
  exports.warmTemplateFromForm = warmTemplateFromForm;
24852
- //# sourceMappingURL=index-BRI84Wy6.cjs.map
26807
+ //# sourceMappingURL=index-UuNicsNZ.cjs.map