@pixldocs/canvas-renderer 0.5.240 → 0.5.242

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,
@@ -1027,7 +1027,7 @@ const defaultProjectSettings = {
1027
1027
  snapToGrid: false,
1028
1028
  gridSize: 20,
1029
1029
  snapToGuides: true,
1030
- snapThreshold: 5
1030
+ snapThreshold: 8
1031
1031
  };
1032
1032
  const defaultPageSettings = {
1033
1033
  backgroundColor: "#ffffff"
@@ -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();
@@ -5755,7 +5787,8 @@ function getObjectSnapPoints(obj) {
5755
5787
  }
5756
5788
  function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5757
5789
  if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };
5758
- const threshold = snapThreshold || 5;
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;
@@ -5817,19 +5868,129 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5817
5868
  verticalSnaps.sort((a, b) => a.distance - b.distance);
5818
5869
  const bestSnap = verticalSnaps[0];
5819
5870
  snapDx = bestSnap.delta;
5820
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5871
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5821
5872
  }
5822
5873
  if (horizontalSnaps.length > 0) {
5823
5874
  horizontalSnaps.sort((a, b) => a.distance - b.distance);
5824
5875
  const bestSnap = horizontalSnaps[0];
5825
5876
  snapDy = bestSnap.delta;
5826
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5877
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5878
+ }
5879
+ const projectedLeft = moving.left + snapDx;
5880
+ const projectedRight = moving.right + snapDx;
5881
+ moving.centerX + snapDx;
5882
+ const projectedTop = moving.top + snapDy;
5883
+ const projectedBottom = moving.bottom + snapDy;
5884
+ moving.centerY + snapDy;
5885
+ if (snapDx === 0) {
5886
+ let bestPair = null;
5887
+ for (const a of allObjects) {
5888
+ const A = getObjectSnapPoints(a);
5889
+ if (A.right > projectedLeft) continue;
5890
+ if (A.bottom < projectedTop || A.top > projectedBottom) continue;
5891
+ for (const b of allObjects) {
5892
+ if (b === a) continue;
5893
+ const B = getObjectSnapPoints(b);
5894
+ if (B.left < projectedRight) continue;
5895
+ if (B.bottom < projectedTop || B.top > projectedBottom) continue;
5896
+ const gapL = projectedLeft - A.right;
5897
+ const gapR = B.left - projectedRight;
5898
+ const diff = gapR - gapL;
5899
+ const absDiff = Math.abs(diff);
5900
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5901
+ bestPair = { A, B, delta: diff / 2, absDiff };
5902
+ }
5903
+ }
5904
+ }
5905
+ if (bestPair) {
5906
+ snapDx = bestPair.delta;
5907
+ const newLeft = moving.left + snapDx;
5908
+ const newRight = moving.right + snapDx;
5909
+ const equalGap = Math.round(newLeft - bestPair.A.right);
5910
+ const bracketY = (Math.max(bestPair.A.top, projectedTop, bestPair.B.top) + Math.min(bestPair.A.bottom, projectedBottom, bestPair.B.bottom)) / 2;
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 };
5914
+ newGuides.push({
5915
+ type: "horizontal",
5916
+ position: bracketY,
5917
+ kind: "gap",
5918
+ gap: equalGap,
5919
+ start: bestPair.A.right,
5920
+ end: newLeft,
5921
+ bracketAt: bracketY,
5922
+ targetBoundsList: [aTb, bTb]
5923
+ });
5924
+ newGuides.push({
5925
+ type: "horizontal",
5926
+ position: bracketY,
5927
+ kind: "gap",
5928
+ gap: equalGap,
5929
+ start: newRight,
5930
+ end: bestPair.B.left,
5931
+ bracketAt: bracketY,
5932
+ targetBoundsList: [aTb, bTb]
5933
+ });
5934
+ }
5935
+ }
5936
+ }
5937
+ if (snapDy === 0) {
5938
+ let bestPair = null;
5939
+ for (const a of allObjects) {
5940
+ const A = getObjectSnapPoints(a);
5941
+ if (A.bottom > projectedTop) continue;
5942
+ if (A.right < projectedLeft || A.left > projectedRight) continue;
5943
+ for (const b of allObjects) {
5944
+ if (b === a) continue;
5945
+ const B = getObjectSnapPoints(b);
5946
+ if (B.top < projectedBottom) continue;
5947
+ if (B.right < projectedLeft || B.left > projectedRight) continue;
5948
+ const gapT = projectedTop - A.bottom;
5949
+ const gapB = B.top - projectedBottom;
5950
+ const diff = gapB - gapT;
5951
+ const absDiff = Math.abs(diff);
5952
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5953
+ bestPair = { A, B, delta: diff / 2, absDiff };
5954
+ }
5955
+ }
5956
+ }
5957
+ if (bestPair) {
5958
+ snapDy = bestPair.delta;
5959
+ const newTop = moving.top + snapDy;
5960
+ const newBottom = moving.bottom + snapDy;
5961
+ const equalGap = Math.round(newTop - bestPair.A.bottom);
5962
+ const bracketX = (Math.max(bestPair.A.left, projectedLeft, bestPair.B.left) + Math.min(bestPair.A.right, projectedRight, bestPair.B.right)) / 2;
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 };
5966
+ newGuides.push({
5967
+ type: "vertical",
5968
+ position: bracketX,
5969
+ kind: "gap",
5970
+ gap: equalGap,
5971
+ start: bestPair.A.bottom,
5972
+ end: newTop,
5973
+ bracketAt: bracketX,
5974
+ targetBoundsList: [aTb, bTb]
5975
+ });
5976
+ newGuides.push({
5977
+ type: "vertical",
5978
+ position: bracketX,
5979
+ kind: "gap",
5980
+ gap: equalGap,
5981
+ start: newBottom,
5982
+ end: bestPair.B.top,
5983
+ bracketAt: bracketX,
5984
+ targetBoundsList: [aTb, bTb]
5985
+ });
5986
+ }
5987
+ }
5827
5988
  }
5828
5989
  return { guides: newGuides, snapDx, snapDy };
5829
5990
  }
5830
- function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5991
+ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold, additionalBounds = []) {
5831
5992
  if (!snapToGuides) return [];
5832
- const threshold = snapThreshold || 5;
5993
+ const threshold = snapThreshold;
5833
5994
  const newGuides = [];
5834
5995
  const scaling = getObjectSnapPoints(scalingObj);
5835
5996
  const scalingId = getObjectId(scalingObj);
@@ -5842,7 +6003,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5842
6003
  const checkVerticalSnap = (edgePosition, targetPosition, guide) => {
5843
6004
  const dist = Math.abs(edgePosition - targetPosition);
5844
6005
  if (dist < threshold) {
5845
- newGuides.push({ ...guide, distance: Math.round(dist) });
6006
+ newGuides.push({ ...guide, kind: "alignment" });
5846
6007
  return true;
5847
6008
  }
5848
6009
  return false;
@@ -5850,7 +6011,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5850
6011
  const checkHorizontalSnap = (edgePosition, targetPosition, guide) => {
5851
6012
  const dist = Math.abs(edgePosition - targetPosition);
5852
6013
  if (dist < threshold) {
5853
- newGuides.push({ ...guide, distance: Math.round(dist) });
6014
+ newGuides.push({ ...guide, kind: "alignment" });
5854
6015
  return true;
5855
6016
  }
5856
6017
  return false;
@@ -5872,12 +6033,39 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5872
6033
  checkHorizontalSnap(scaling.bottom, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5873
6034
  }
5874
6035
  const rawObjects = canvas.getObjects();
6036
+ const objectBounds = [];
5875
6037
  for (const obj of rawObjects) {
5876
6038
  if (obj === scalingObj) continue;
5877
6039
  const objId = getObjectId(obj);
5878
6040
  if (objId === "__background__") continue;
5879
6041
  if (objId && objId === scalingId) continue;
5880
- 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
+ }
5881
6069
  if (resizingLeft) {
5882
6070
  checkVerticalSnap(scaling.left, other.left, {
5883
6071
  type: "vertical",
@@ -5959,6 +6147,18 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5959
6147
  });
5960
6148
  }
5961
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
+ }
5962
6162
  const seen = /* @__PURE__ */ new Set();
5963
6163
  return newGuides.filter((guide) => {
5964
6164
  const key = `${guide.type}-${guide.position.toFixed(1)}`;
@@ -5967,6 +6167,349 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5967
6167
  return true;
5968
6168
  });
5969
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
+ }
5970
6513
  const clamp01$1 = (v) => Math.max(0, Math.min(1, Number.isFinite(v) ? v : 0));
5971
6514
  function arcPath(w, sag, up) {
5972
6515
  if (sag <= 0.5) return `M 0 0 L ${w} 0`;
@@ -6137,12 +6680,87 @@ if (Array.isArray(stateProps)) {
6137
6680
  if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
6138
6681
  if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
6139
6682
  if (!stateProps.includes("textPath")) stateProps.push("textPath");
6683
+ if (!stateProps.includes("smartWrap")) stateProps.push("smartWrap");
6140
6684
  }
6141
6685
  const cacheProps = fabric__namespace.Textbox.prototype.cacheProperties;
6142
6686
  if (Array.isArray(cacheProps)) {
6143
6687
  if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
6144
6688
  if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
6145
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
+ };
6146
6764
  }
6147
6765
  const hasActiveTextPath = (obj) => {
6148
6766
  const tp = obj.textPath;
@@ -6299,14 +6917,14 @@ function scaleLocalToScreen(target, p) {
6299
6917
  return new fabric__namespace.Point(p.x * sx * zx, p.y * sy * zy);
6300
6918
  }
6301
6919
  function applyTextPathControls(textbox) {
6302
- 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;
6303
6921
  const obj = textbox;
6304
6922
  if (!hasActiveTextPath(obj)) {
6305
6923
  obj.__pdTextPathHovered = false;
6306
6924
  if (obj.__pdTextPathControls) {
6307
6925
  try {
6308
6926
  const cu2 = fabric__namespace.controlsUtils;
6309
- 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));
6310
6928
  if (defaults) obj.controls = defaults;
6311
6929
  } catch {
6312
6930
  }
@@ -6570,7 +7188,7 @@ function applyTextPathControls(textbox) {
6570
7188
  actionName: "tpPivot",
6571
7189
  positionHandler: (_d2, finalMatrix, fabricObject) => scaleLocalToScreen(fabricObject, getPivotLocalCentered(fabricObject)).transform(finalMatrix),
6572
7190
  actionHandler: (_e2, transform, x, y) => {
6573
- var _a3, _b2;
7191
+ var _a3, _b3;
6574
7192
  const target = transform.target;
6575
7193
  const state = transform.__pdCirclePivotDrag || (transform.__pdCirclePivotDrag = {
6576
7194
  startMatrix: target.calcTransformMatrix(),
@@ -6587,7 +7205,7 @@ function applyTextPathControls(textbox) {
6587
7205
  y: (state.startOffset.y || 0) + dy
6588
7206
  };
6589
7207
  target.setCoords();
6590
- (_b2 = target.canvas) == null ? void 0 : _b2.requestRenderAll();
7208
+ (_b3 = target.canvas) == null ? void 0 : _b3.requestRenderAll();
6591
7209
  return true;
6592
7210
  },
6593
7211
  render: renderPivot
@@ -6638,13 +7256,13 @@ function applyTextPathControls(textbox) {
6638
7256
  }
6639
7257
  if (!obj.__pdCirclePivotDblWired) {
6640
7258
  obj.on("mousedblclick", () => {
6641
- var _a3, _b2;
7259
+ var _a3, _b3;
6642
7260
  if (((_a3 = obj.textPath) == null ? void 0 : _a3.preset) !== "circle") return;
6643
7261
  if (obj.__corner !== "tpPivot") return;
6644
7262
  if (!obj.textPath.pivot) return;
6645
7263
  obj.textPath.pivot = { x: 0, y: 0 };
6646
7264
  obj.setCoords();
6647
- (_b2 = obj.canvas) == null ? void 0 : _b2.requestRenderAll();
7265
+ (_b3 = obj.canvas) == null ? void 0 : _b3.requestRenderAll();
6648
7266
  });
6649
7267
  obj.__pdCirclePivotDblWired = true;
6650
7268
  }
@@ -7012,10 +7630,10 @@ function textPathBoundsContainScenePoint(obj, point) {
7012
7630
  }
7013
7631
  }
7014
7632
  function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7015
- var _a2, _b, _c;
7633
+ var _a2, _b2, _c;
7016
7634
  const host = hostCanvas || obj.canvas || ((_a2 = obj.group) == null ? void 0 : _a2.canvas);
7017
7635
  if (!host) return;
7018
- 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;
7019
7637
  const matrix = fabric__namespace.util.multiplyTransformMatrices(host.viewportTransform || [1, 0, 0, 1, 0, 0], obj.calcTransformMatrix());
7020
7638
  const corners = [
7021
7639
  new fabric__namespace.Point(bounds.minX, bounds.minY),
@@ -7037,8 +7655,8 @@ function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7037
7655
  }
7038
7656
  if (typeof TextboxProto._renderControls === "function" && !TextboxProto.__pixldocsOrigRenderControls) {
7039
7657
  let drawWarpGuides = function(ctx) {
7040
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7041
- 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);
7042
7660
  if (!hostCanvas) return;
7043
7661
  const hoverBounds = getTextPathHitBounds(this);
7044
7662
  const active = (_d = hostCanvas.getActiveObject) == null ? void 0 : _d.call(hostCanvas);
@@ -7461,12 +8079,12 @@ function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
7461
8079
  ctx.closePath();
7462
8080
  }
7463
8081
  function measureLineGlyphWidth(obj, lineIndex) {
7464
- var _a2, _b, _c, _d, _e, _f;
8082
+ var _a2, _b2, _c, _d, _e, _f;
7465
8083
  try {
7466
8084
  const rawLine = (_a2 = obj == null ? void 0 : obj._textLines) == null ? void 0 : _a2[lineIndex];
7467
8085
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
7468
8086
  if (!lineText) return 0;
7469
- 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);
7470
8088
  if (!fontSize) return 0;
7471
8089
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
7472
8090
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
@@ -7735,7 +8353,7 @@ function applyTextBackground(obj, cfg) {
7735
8353
  const originalToSVG = (_a2 = obj.toSVG) == null ? void 0 : _a2.bind(obj);
7736
8354
  if (typeof originalToSVG === "function") {
7737
8355
  obj.toSVG = function(reviver) {
7738
- var _a3, _b;
8356
+ var _a3, _b2;
7739
8357
  let svg = originalToSVG(reviver);
7740
8358
  const bg = this[PD_BG_KEY];
7741
8359
  const shadow = this.shadow;
@@ -7784,7 +8402,7 @@ function applyTextBackground(obj, cfg) {
7784
8402
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7785
8403
  let bgGradDefs = "";
7786
8404
  let bgFillAttr = escapeXmlAttr(bgFill);
7787
- 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) {
7788
8406
  const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7789
8407
  const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7790
8408
  const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
@@ -8813,9 +9431,9 @@ function createShape(element) {
8813
9431
  });
8814
9432
  }
8815
9433
  case "circle": {
8816
- const radius = Math.min(w, h) / 2;
8817
- return new fabric__namespace.Circle({
8818
- radius,
9434
+ return new fabric__namespace.Ellipse({
9435
+ rx: w / 2,
9436
+ ry: h / 2,
8819
9437
  fill,
8820
9438
  stroke,
8821
9439
  strokeWidth,
@@ -8824,8 +9442,7 @@ function createShape(element) {
8824
9442
  objectCaching: true,
8825
9443
  strokeUniform: true,
8826
9444
  strokeLineJoin: "round",
8827
- strokeLineCap: "round",
8828
- lockUniScaling: true
9445
+ strokeLineCap: "round"
8829
9446
  });
8830
9447
  }
8831
9448
  case "triangle": {
@@ -8860,7 +9477,7 @@ function createShape(element) {
8860
9477
  }
8861
9478
  }
8862
9479
  function createText(element) {
8863
- var _a2, _b, _c, _d, _e;
9480
+ var _a2, _b2, _c, _d, _e;
8864
9481
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
8865
9482
  let text = element.text || "Text";
8866
9483
  let fontSize = element.fontSize || 16;
@@ -9061,7 +9678,7 @@ function createText(element) {
9061
9678
  textbox.setCoords();
9062
9679
  const widthAfterSet = textbox.width ?? 0;
9063
9680
  try {
9064
- (_b = textbox.setControlsVisibility) == null ? void 0 : _b.call(textbox, {
9681
+ (_b2 = textbox.setControlsVisibility) == null ? void 0 : _b2.call(textbox, {
9065
9682
  tl: true,
9066
9683
  tr: true,
9067
9684
  bl: true,
@@ -9860,34 +10477,595 @@ function bakeEdgeFade(source, fade) {
9860
10477
  return canvas;
9861
10478
  }
9862
10479
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10480
+ const SELECTION_BORDER_SCALE = 2;
10481
+ let ensureCanvaControlRenders = () => {
10482
+ };
9863
10483
  try {
9864
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
+ }
9865
10494
  if (InteractiveBase == null ? void 0 : InteractiveBase.ownDefaults) {
9866
10495
  Object.assign(InteractiveBase.ownDefaults, {
9867
10496
  borderColor: SELECTION_PRIMARY,
9868
- borderScaleFactor: 1.25,
10497
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9869
10498
  cornerColor: SELECTION_PRIMARY,
9870
10499
  cornerStrokeColor: "#ffffff",
9871
- cornerStyle: "rect",
10500
+ cornerStyle: "circle",
9872
10501
  transparentCorners: false,
9873
- cornerSize: 8,
10502
+ cornerSize: 10,
9874
10503
  borderOpacityWhenMoving: 0.9
9875
10504
  });
9876
10505
  } else if (InteractiveBase == null ? void 0 : InteractiveBase.prototype) {
9877
10506
  Object.assign(InteractiveBase.prototype, {
9878
10507
  borderColor: SELECTION_PRIMARY,
9879
- borderScaleFactor: 1.25,
10508
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9880
10509
  cornerColor: SELECTION_PRIMARY,
9881
10510
  cornerStrokeColor: "#ffffff",
9882
- cornerStyle: "rect",
10511
+ cornerStyle: "circle",
9883
10512
  transparentCorners: false,
9884
- cornerSize: 8,
10513
+ cornerSize: 10,
9885
10514
  borderOpacityWhenMoving: 0.9
9886
10515
  });
9887
10516
  }
9888
10517
  } catch (e) {
9889
10518
  console.warn("[PageCanvas] Failed to apply global selection defaults:", e);
9890
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
+ };
9891
11069
  function applyWarpAwareSelectionBorders(selection) {
9892
11070
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
9893
11071
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -9955,8 +11133,11 @@ const PageCanvas = react.forwardRef(
9955
11133
  const hasRunPostReadyReflowForPageRef = react.useRef(null);
9956
11134
  const hasNotifiedReadyForPageRef = react.useRef(null);
9957
11135
  const hasClearedCachesBeforeFirstSyncRef = react.useRef(false);
11136
+ const projectSettingsRef = react.useRef(projectSettings);
11137
+ projectSettingsRef.current = projectSettings;
9958
11138
  const [guides, setGuides] = react.useState([]);
9959
11139
  const [gridResizeLabel, setGridResizeLabel] = react.useState(null);
11140
+ const [hoverBounds, setHoverBounds] = react.useState(null);
9960
11141
  const [rotationLabel, setRotationLabel] = react.useState(null);
9961
11142
  const [sizeLabel, setSizeLabel] = react.useState(null);
9962
11143
  const [ready, setReady] = react.useState(false);
@@ -10023,7 +11204,8 @@ const PageCanvas = react.forwardRef(
10023
11204
  react.useRef(null);
10024
11205
  react.useRef(null);
10025
11206
  react.useRef(/* @__PURE__ */ new Map());
10026
- react.useRef(null);
11207
+ const groupResizeActiveSnapRef = react.useRef(null);
11208
+ const objectResizeActiveSnapRef = react.useRef(null);
10027
11209
  react.useRef(null);
10028
11210
  react.useRef(null);
10029
11211
  react.useRef(null);
@@ -10165,33 +11347,358 @@ const PageCanvas = react.forwardRef(
10165
11347
  (movingObj) => {
10166
11348
  const fabricCanvas = fabricRef.current;
10167
11349
  if (!fabricCanvas) return { guides: [], snapDx: 0, snapDy: 0 };
11350
+ const ps = projectSettingsRef.current;
10168
11351
  return calculateSnapGuides(
10169
11352
  movingObj,
10170
11353
  fabricCanvas,
10171
11354
  canvasWidth,
10172
11355
  canvasHeight,
10173
- projectSettings.snapToGuides,
10174
- projectSettings.snapThreshold
11356
+ ps.snapToGuides,
11357
+ ps.snapThreshold
10175
11358
  );
10176
11359
  },
10177
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11360
+ [canvasWidth, canvasHeight]
10178
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]);
10179
11430
  const calculateScaleSnapGuidesCallback = react.useCallback(
10180
11431
  (scalingObj, corner) => {
10181
11432
  const fabricCanvas = fabricRef.current;
10182
11433
  if (!fabricCanvas) return [];
11434
+ const ps = projectSettingsRef.current;
11435
+ const excludeIds = getResizeExcludeIdsCallback(scalingObj);
10183
11436
  return calculateScaleSnapGuides(
10184
11437
  scalingObj,
10185
11438
  corner,
10186
- fabricCanvas,
11439
+ fabricCanvas,
11440
+ canvasWidth,
11441
+ canvasHeight,
11442
+ ps.snapToGuides,
11443
+ ps.snapThreshold || 4,
11444
+ getLogicalGroupSnapBoundsCallback(excludeIds)
11445
+ );
11446
+ },
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,
10187
11470
  canvasWidth,
10188
11471
  canvasHeight,
10189
- projectSettings.snapToGuides,
10190
- projectSettings.snapThreshold
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
+ }
10191
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();
10192
11532
  },
10193
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11533
+ [canvasWidth, canvasHeight, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback]
10194
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]);
10195
11702
  const isTransforming = react.useCallback((canvas2) => {
10196
11703
  if (!canvas2) return false;
10197
11704
  return !!canvas2._currentTransform || !!canvas2.__isUserTransforming;
@@ -10317,6 +11824,8 @@ const PageCanvas = react.forwardRef(
10317
11824
  // Transparent so underlay (page bg + group bgs) shows through
10318
11825
  backgroundColor: "transparent"
10319
11826
  });
11827
+ fabricCanvas.hoverCursor = "default";
11828
+ fabricCanvas.moveCursor = "move";
10320
11829
  if (!allowSelection) {
10321
11830
  fabricCanvas.selection = false;
10322
11831
  fabricCanvas.on("selection:created", () => {
@@ -10338,6 +11847,61 @@ const PageCanvas = react.forwardRef(
10338
11847
  fabricRef.current = fabricCanvas;
10339
11848
  const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
10340
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
+ }
10341
11905
  const initFonts = async () => {
10342
11906
  try {
10343
11907
  await preloadAllFonts();
@@ -10427,6 +11991,17 @@ const PageCanvas = react.forwardRef(
10427
11991
  });
10428
11992
  fabricCanvas.on("mouse:up", () => {
10429
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
+ }
10430
12005
  });
10431
12006
  fabricCanvas.on("object:scaling", () => {
10432
12007
  fabricCanvas.__isUserTransforming = true;
@@ -10473,7 +12048,7 @@ const PageCanvas = react.forwardRef(
10473
12048
  didTransformRef.current = true;
10474
12049
  });
10475
12050
  const syncSelectionToStore = () => {
10476
- var _a2, _b, _c, _d;
12051
+ var _a2, _b2, _c, _d;
10477
12052
  if (!isActiveRef.current || isRebuildingRef.current || isSyncingSelectionToFabricRef.current || !allowSelection) return;
10478
12053
  const walkToTopmostGroup = (childId, children, activeEditingGroupId) => {
10479
12054
  let topmost = null;
@@ -10512,7 +12087,7 @@ const PageCanvas = react.forwardRef(
10512
12087
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
10513
12088
  const clickedId = ids[0];
10514
12089
  const state = useEditorStore.getState();
10515
- 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);
10516
12091
  const children = (currentPage2 == null ? void 0 : currentPage2.children) ?? [];
10517
12092
  const parent = walkToTopmostGroup(clickedId, children, activeEditingGroupId);
10518
12093
  const targetIsInCrop = !!(active instanceof fabric__namespace.Group && isCropGroupInCropMode(active) || (active == null ? void 0 : active.group) instanceof fabric__namespace.Group && isCropGroupInCropMode(active.group));
@@ -10659,8 +12234,10 @@ const PageCanvas = react.forwardRef(
10659
12234
  const activeObj = fabricCanvas.getActiveObject();
10660
12235
  if (activeObj instanceof fabric__namespace.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10661
12236
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12237
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10662
12238
  if (activeObj && !(activeObj instanceof fabric__namespace.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10663
- installCanvaMaskControls(activeObj);
12239
+ installImageResizeControlsWithSnap(activeObj);
12240
+ ensureCanvaControlRenders(activeObj);
10664
12241
  }
10665
12242
  });
10666
12243
  fabricCanvas.on("selection:updated", () => {
@@ -10672,12 +12249,14 @@ const PageCanvas = react.forwardRef(
10672
12249
  const activeObj = fabricCanvas.getActiveObject();
10673
12250
  if (activeObj instanceof fabric__namespace.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10674
12251
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12252
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10675
12253
  if (activeObj && !(activeObj instanceof fabric__namespace.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10676
- installCanvaMaskControls(activeObj);
12254
+ installImageResizeControlsWithSnap(activeObj);
12255
+ ensureCanvaControlRenders(activeObj);
10677
12256
  }
10678
12257
  });
10679
12258
  fabricCanvas.on("mouse:dblclick", (opt) => {
10680
- var _a2, _b;
12259
+ var _a2, _b2;
10681
12260
  const target = opt == null ? void 0 : opt.target;
10682
12261
  if (!target) return;
10683
12262
  if (target.isEditing) return;
@@ -10701,7 +12280,7 @@ const PageCanvas = react.forwardRef(
10701
12280
  const childId = getObjectId(hitChild);
10702
12281
  if (!childId) return;
10703
12282
  const stateNow = useEditorStore.getState();
10704
- 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);
10705
12284
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
10706
12285
  const chain = [];
10707
12286
  {
@@ -10828,7 +12407,7 @@ const PageCanvas = react.forwardRef(
10828
12407
  transformingIdsRef.current.clear();
10829
12408
  };
10830
12409
  const prepareGroupSelectionTransformStart = (target) => {
10831
- var _a2, _b;
12410
+ var _a2, _b2;
10832
12411
  const active = target instanceof fabric__namespace.ActiveSelection ? target : fabricCanvas.getActiveObject();
10833
12412
  if (!(active instanceof fabric__namespace.ActiveSelection)) return;
10834
12413
  if (!activeSelectionMoveStartRef.current || activeSelectionMoveStartRef.current.selection !== active) {
@@ -10842,7 +12421,7 @@ const PageCanvas = react.forwardRef(
10842
12421
  const groupId = active.__pixldocsGroupSelection;
10843
12422
  if (!groupId) return;
10844
12423
  if (((_a2 = groupSelectionTransformStartRef.current) == null ? void 0 : _a2.groupId) === groupId && groupSelectionTransformStartRef.current.selection === active) return;
10845
- 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) ?? [];
10846
12425
  const groupNode = findNodeById(pageChildren2, groupId);
10847
12426
  if (!groupNode) return;
10848
12427
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
@@ -10926,7 +12505,7 @@ const PageCanvas = react.forwardRef(
10926
12505
  };
10927
12506
  let pendingShiftMultiSelect = null;
10928
12507
  const applyShiftMultiSelect = (target, event, baselineActive = fabricCanvas.getActiveObject(), baselineObjects = fabricCanvas.getActiveObjects()) => {
10929
- var _a2, _b, _c;
12508
+ var _a2, _b2, _c;
10930
12509
  if (!target || !target.selectable) return false;
10931
12510
  const active = baselineActive;
10932
12511
  if (!active || ((_a2 = active.getActiveControl) == null ? void 0 : _a2.call(active))) return false;
@@ -10960,7 +12539,7 @@ const PageCanvas = react.forwardRef(
10960
12539
  isSyncingSelectionToFabricRef.current = false;
10961
12540
  });
10962
12541
  }
10963
- (_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);
10964
12543
  (_c = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _c.call(event);
10965
12544
  return true;
10966
12545
  };
@@ -11011,9 +12590,9 @@ const PageCanvas = react.forwardRef(
11011
12590
  return !!(((_a2 = o == null ? void 0 : o._ct) == null ? void 0 : _a2.isCropGroup) || (o == null ? void 0 : o.__cropGroup));
11012
12591
  };
11013
12592
  const promoteToCropGroup = (opt) => {
11014
- var _a2, _b, _c, _d, _e, _f;
12593
+ var _a2, _b2, _c, _d, _e, _f;
11015
12594
  const t = opt.target;
11016
- 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") {
11017
12596
  if (t && isCropGroup2(t)) {
11018
12597
  fabricCanvas._hoveredTarget = t;
11019
12598
  } else if ((t == null ? void 0 : t.group) && isCropGroup2(t.group)) {
@@ -11029,6 +12608,8 @@ const PageCanvas = react.forwardRef(
11029
12608
  const objects = fabricCanvas.getObjects();
11030
12609
  for (const obj of objects) {
11031
12610
  if (isCropGroup2(obj) && obj.containsPoint(pointer)) {
12611
+ installImageResizeControlsWithSnap(obj);
12612
+ ensureCanvaControlRenders(obj);
11032
12613
  fabricCanvas.setActiveObject(obj);
11033
12614
  opt.target = obj;
11034
12615
  fabricCanvas._hoveredTarget = obj;
@@ -11039,12 +12620,16 @@ const PageCanvas = react.forwardRef(
11039
12620
  }
11040
12621
  const g = t.group;
11041
12622
  if (g && isCropGroup2(g)) {
12623
+ installImageResizeControlsWithSnap(g);
12624
+ ensureCanvaControlRenders(g);
11042
12625
  fabricCanvas.setActiveObject(g);
11043
12626
  opt.target = g;
11044
12627
  fabricCanvas._hoveredTarget = g;
11045
12628
  return;
11046
12629
  }
11047
12630
  if (isCropGroup2(t)) {
12631
+ installImageResizeControlsWithSnap(t);
12632
+ ensureCanvaControlRenders(t);
11048
12633
  fabricCanvas.setActiveObject(t);
11049
12634
  t.set({
11050
12635
  selectable: true,
@@ -11097,7 +12682,7 @@ const PageCanvas = react.forwardRef(
11097
12682
  });
11098
12683
  }
11099
12684
  fabricCanvas.on("mouse:down", (opt) => {
11100
- var _a2, _b;
12685
+ var _a2, _b2;
11101
12686
  if (pendingShiftMultiSelect) {
11102
12687
  const pending = pendingShiftMultiSelect;
11103
12688
  pendingShiftMultiSelect = null;
@@ -11105,17 +12690,19 @@ const PageCanvas = react.forwardRef(
11105
12690
  return;
11106
12691
  }
11107
12692
  const target = opt.target;
11108
- 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;
11109
12694
  if (cropGroup) {
11110
12695
  lockEdits();
11111
12696
  didTransformRef.current = false;
12697
+ installImageResizeControlsWithSnap(cropGroup);
12698
+ ensureCanvaControlRenders(cropGroup);
11112
12699
  fabricCanvas.setActiveObject(cropGroup);
11113
12700
  cropGroup.setCoords();
11114
12701
  fabricCanvas.requestRenderAll();
11115
12702
  }
11116
12703
  });
11117
12704
  const groupFabricUnionBBox = (g) => {
11118
- var _a2, _b;
12705
+ var _a2, _b2;
11119
12706
  const memberIds = new Set(getAllElementIds(g.children ?? []));
11120
12707
  if (memberIds.size === 0) return null;
11121
12708
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -11123,7 +12710,7 @@ const PageCanvas = react.forwardRef(
11123
12710
  for (const o of fabricCanvas.getObjects()) {
11124
12711
  const oid = getObjectId(o);
11125
12712
  if (!oid || !memberIds.has(oid)) continue;
11126
- 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));
11127
12714
  if (!br) continue;
11128
12715
  minX = Math.min(minX, br.left);
11129
12716
  minY = Math.min(minY, br.top);
@@ -11149,13 +12736,13 @@ const PageCanvas = react.forwardRef(
11149
12736
  return pick;
11150
12737
  };
11151
12738
  fabricCanvas.on("mouse:down:before", (opt) => {
11152
- 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;
11153
12740
  if (editLockRef.current) {
11154
12741
  const active = fabricCanvas.getActiveObject();
11155
12742
  if (active && (((_a2 = active._ct) == null ? void 0 : _a2.isCropGroup) || active.__cropGroup)) {
11156
12743
  opt.target = active;
11157
12744
  if (opt.e) {
11158
- (_c = (_b = opt.e).preventDefault) == null ? void 0 : _c.call(_b);
12745
+ (_c = (_b2 = opt.e).preventDefault) == null ? void 0 : _c.call(_b2);
11159
12746
  (_e = (_d = opt.e).stopPropagation) == null ? void 0 : _e.call(_d);
11160
12747
  }
11161
12748
  }
@@ -11170,6 +12757,10 @@ const PageCanvas = react.forwardRef(
11170
12757
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11171
12758
  const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11172
12759
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12760
+ if (target instanceof fabric__namespace.Textbox && target.__corner && (target.__corner === "ml" || target.__corner === "mr")) {
12761
+ const sourceEl = targetId ? elementsRef.current.find((el) => el.id === targetId) : void 0;
12762
+ bakeTextboxScaleIntoTypography(target, sourceEl);
12763
+ }
11173
12764
  if (isMultiSelectModifier(opt.e)) {
11174
12765
  const manualTarget = target && !(target instanceof fabric__namespace.ActiveSelection) && targetId && targetId !== "__background__" ? target : pickSelectableObjectAtPointer(opt.e);
11175
12766
  if (manualTarget) {
@@ -11196,11 +12787,39 @@ const PageCanvas = react.forwardRef(
11196
12787
  return topmost;
11197
12788
  };
11198
12789
  if (target && targetId && targetId !== "__background__") {
11199
- const parent = findTopmostPromotableGroup(targetId);
11200
- const targetIsInCrop = !!(target instanceof fabric__namespace.Group && isCropGroupInCropMode(target) || (target == null ? void 0 : target.group) instanceof fabric__namespace.Group && isCropGroupInCropMode(target.group));
12790
+ let effectiveTarget = target;
12791
+ let effectiveTargetId = targetId;
12792
+ const activeNowEarly = fabricCanvas.getActiveObject();
12793
+ const asGroupId = target instanceof fabric__namespace.ActiveSelection ? target.__pixldocsGroupSelection : void 0;
12794
+ if (target instanceof fabric__namespace.ActiveSelection && asGroupId && target === activeNowEarly) {
12795
+ try {
12796
+ const pointer = fabricCanvas.getViewportPoint(opt.e);
12797
+ const members = target.getObjects();
12798
+ for (let i = members.length - 1; i >= 0; i--) {
12799
+ const m = members[i];
12800
+ if (m.visible === false) continue;
12801
+ if (typeof m.containsPoint === "function" && m.containsPoint(pointer)) {
12802
+ const mid = getObjectId(m);
12803
+ if (mid) {
12804
+ effectiveTarget = m;
12805
+ effectiveTargetId = mid;
12806
+ }
12807
+ break;
12808
+ }
12809
+ }
12810
+ } catch {
12811
+ }
12812
+ }
12813
+ const parent = findTopmostPromotableGroup(effectiveTargetId);
12814
+ const targetIsInCrop = !!(effectiveTarget instanceof fabric__namespace.Group && isCropGroupInCropMode(effectiveTarget) || (effectiveTarget == null ? void 0 : effectiveTarget.group) instanceof fabric__namespace.Group && isCropGroupInCropMode(effectiveTarget.group));
11201
12815
  const activeNow = fabricCanvas.getActiveObject();
11202
12816
  const alreadyThisGroup = activeNow instanceof fabric__namespace.ActiveSelection && activeNow.__pixldocsGroupSelection === (parent == null ? void 0 : parent.id);
11203
12817
  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));
12818
+ const isDeepSelectKey = !!((_i = opt.e) == null ? void 0 : _i.altKey);
12819
+ if (isDeepSelectKey) {
12820
+ pendingGroupPromotionRef.current = null;
12821
+ return;
12822
+ }
11204
12823
  if (parent && !parent.backgroundColor && !targetIsInCrop && activeEditingGroupId !== parent.id && !alreadyThisGroup && !isMultiSelectKey) {
11205
12824
  const memberIds = new Set(getAllElementIds(parent.children ?? []));
11206
12825
  const memberObjs = fabricCanvas.getObjects().filter((o) => {
@@ -11224,10 +12843,33 @@ const PageCanvas = react.forwardRef(
11224
12843
  opt.target = only;
11225
12844
  pendingGroupPromotionRef.current = { groupId: parent.id, selection: only };
11226
12845
  }
12846
+ } else if (parent && !parent.backgroundColor && !targetIsInCrop && alreadyThisGroup && !isMultiSelectKey && effectiveTarget !== activeNow) {
12847
+ try {
12848
+ skipSelectionClearOnDiscardRef.current = true;
12849
+ preserveEditingScopeOnSelectionClearRef.current = true;
12850
+ restoreSuppressedGroupBorders();
12851
+ fabricCanvas.discardActiveObject();
12852
+ } finally {
12853
+ skipSelectionClearOnDiscardRef.current = false;
12854
+ preserveEditingScopeOnSelectionClearRef.current = false;
12855
+ }
12856
+ fabricCanvas.__activeEditingGroupId = parent.id;
12857
+ delete effectiveTarget.__pixldocsGroupSelection;
12858
+ delete effectiveTarget.__pixldocsLogicalGroupIds;
12859
+ try {
12860
+ (_j = effectiveTarget.set) == null ? void 0 : _j.call(effectiveTarget, { selectable: true, evented: true, hasBorders: true, hasControls: true });
12861
+ } catch {
12862
+ }
12863
+ fabricCanvas.setActiveObject(effectiveTarget);
12864
+ effectiveTarget.setCoords();
12865
+ fabricCanvas._target = effectiveTarget;
12866
+ opt.target = effectiveTarget;
12867
+ pendingGroupPromotionRef.current = null;
11227
12868
  }
11228
12869
  } else if (!target || targetId === "__background__") {
11229
- 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));
12870
+ 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));
11230
12871
  if (isMultiSelectKey) return;
12872
+ if ((_n = opt.e) == null ? void 0 : _n.altKey) return;
11231
12873
  try {
11232
12874
  const pointer = fabricCanvas.getPointer(opt.e);
11233
12875
  const px = pointer.x;
@@ -11298,6 +12940,43 @@ const PageCanvas = react.forwardRef(
11298
12940
  if (editLockRef.current) return;
11299
12941
  const t = opt.target;
11300
12942
  const tid = t ? getObjectId(t) : null;
12943
+ try {
12944
+ const activeIds = new Set(selectedIdsRef.current);
12945
+ const pointer = fabricCanvas.getPointer(opt.e);
12946
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12947
+ const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12948
+ const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
12949
+ const groupPick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
12950
+ if (groupPick && !activeIds.has(groupPick.group.id)) {
12951
+ const b = groupFabricUnionBBox(groupPick.group);
12952
+ if (b) {
12953
+ setHoverBounds({
12954
+ left: b.left,
12955
+ top: b.top,
12956
+ width: b.right - b.left,
12957
+ height: b.bottom - b.top,
12958
+ angle: 0
12959
+ });
12960
+ return;
12961
+ }
12962
+ }
12963
+ const isHoveringSelected = !!(tid && activeIds.has(tid));
12964
+ if (t && tid && tid !== "__background__" && !isHoveringSelected) {
12965
+ t.setCoords();
12966
+ const br = t.getBoundingRect();
12967
+ setHoverBounds({
12968
+ left: br.left,
12969
+ top: br.top,
12970
+ width: br.width,
12971
+ height: br.height,
12972
+ angle: 0
12973
+ });
12974
+ } else {
12975
+ setHoverBounds(null);
12976
+ }
12977
+ } catch {
12978
+ setHoverBounds(null);
12979
+ }
11301
12980
  if (t && tid && tid !== "__background__") return;
11302
12981
  try {
11303
12982
  const pointer = fabricCanvas.getPointer(opt.e);
@@ -11305,11 +12984,19 @@ const PageCanvas = react.forwardRef(
11305
12984
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
11306
12985
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11307
12986
  const pick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
11308
- fabricCanvas.defaultCursor = pick ? "move" : "default";
12987
+ fabricCanvas.defaultCursor = "default";
11309
12988
  } catch {
11310
12989
  fabricCanvas.defaultCursor = "default";
11311
12990
  }
11312
12991
  });
12992
+ fabricCanvas.on("mouse:out", () => {
12993
+ setHoverBounds(null);
12994
+ });
12995
+ fabricCanvas.on("mouse:down", () => {
12996
+ setHoverBounds(null);
12997
+ });
12998
+ fabricCanvas.on("selection:created", () => setHoverBounds(null));
12999
+ fabricCanvas.on("selection:updated", () => setHoverBounds(null));
11313
13000
  fabricCanvas.on("mouse:down", (ev) => {
11314
13001
  if (fabricCanvas._currentTransform) {
11315
13002
  lockEdits();
@@ -11325,6 +13012,8 @@ const PageCanvas = react.forwardRef(
11325
13012
  setGuides([]);
11326
13013
  setRotationLabel(null);
11327
13014
  setSizeLabel(null);
13015
+ objectResizeActiveSnapRef.current = null;
13016
+ groupResizeActiveSnapRef.current = null;
11328
13017
  dragStarted = false;
11329
13018
  const pendingPromotion = pendingGroupPromotionRef.current;
11330
13019
  pendingGroupPromotionRef.current = null;
@@ -11479,7 +13168,9 @@ const PageCanvas = react.forwardRef(
11479
13168
  const intrH = obj.height ?? 1;
11480
13169
  let newW = Math.max(1, intrW * Math.abs(sx));
11481
13170
  let newH = Math.max(1, intrH * Math.abs(sy));
11482
- if (obj instanceof fabric__namespace.Circle) {
13171
+ if (obj instanceof fabric__namespace.Ellipse) {
13172
+ obj.set({ rx: newW / 2, ry: newH / 2 });
13173
+ } else if (obj instanceof fabric__namespace.Circle) {
11483
13174
  const diameter = Math.max(1, Math.min(newW, newH));
11484
13175
  newW = diameter;
11485
13176
  newH = diameter;
@@ -11596,8 +13287,128 @@ const PageCanvas = react.forwardRef(
11596
13287
  }
11597
13288
  const transform = e.transform;
11598
13289
  const corner = (transform == null ? void 0 : transform.corner) || "";
13290
+ if (obj instanceof fabric__namespace.ActiveSelection && (corner === "ml" || corner === "mr" || corner === "mt" || corner === "mb")) {
13291
+ const isXSide = corner === "ml" || corner === "mr";
13292
+ const sAxis = isXSide ? Math.abs(obj.scaleX ?? 1) : Math.abs(obj.scaleY ?? 1);
13293
+ if (sAxis > 1e-3) {
13294
+ for (const child of obj.getObjects()) {
13295
+ if (!(child instanceof fabric__namespace.Textbox)) continue;
13296
+ if (isXSide) {
13297
+ if (child.__asLiveOrigW == null) {
13298
+ child.__asLiveOrigW = (child.width ?? 0) * (child.scaleX ?? 1);
13299
+ }
13300
+ const origW = child.__asLiveOrigW;
13301
+ const newW = Math.max(20, origW * sAxis);
13302
+ if (Math.abs((child.width ?? 0) - newW) > 0.5) {
13303
+ child.set({ width: newW, scaleX: 1 / sAxis });
13304
+ try {
13305
+ child.initDimensions();
13306
+ } catch {
13307
+ }
13308
+ child.dirty = true;
13309
+ }
13310
+ } else {
13311
+ if (child.__asLiveOrigH == null) {
13312
+ child.__asLiveOrigH = (child.height ?? 0) * (child.scaleY ?? 1);
13313
+ }
13314
+ const origH = child.__asLiveOrigH;
13315
+ const newH = Math.max(20, origH * sAxis);
13316
+ child.minBoxHeight = newH;
13317
+ child.set({ scaleY: 1 / sAxis });
13318
+ try {
13319
+ child.initDimensions();
13320
+ } catch {
13321
+ }
13322
+ child.dirty = true;
13323
+ }
13324
+ }
13325
+ }
13326
+ }
13327
+ snapDuringScaleCallback(obj, corner);
11599
13328
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11600
- setGuides(scaleGuides);
13329
+ const gridGuidesForScale = [];
13330
+ try {
13331
+ const psGrid = projectSettingsRef.current;
13332
+ const canApplyGridSnap = psGrid.snapToGrid && corner && !obj.__cropGroup && !obj.__resizeSnapHandler;
13333
+ if (canApplyGridSnap) {
13334
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13335
+ const gridY = psGrid.gridSizeY ?? psGrid.gridSize ?? 0;
13336
+ if (gridX > 0 && gridY > 0) {
13337
+ obj.setCoords();
13338
+ const br = obj.getBoundingRect();
13339
+ if (br.width > 1 && br.height > 1) {
13340
+ const hasL = corner.includes("l");
13341
+ const hasR = corner.includes("r");
13342
+ const hasT = corner.includes("t");
13343
+ const hasB = corner.includes("b");
13344
+ const anchorRight = br.left + br.width;
13345
+ const anchorBottom = br.top + br.height;
13346
+ let newLeft = br.left;
13347
+ let newTop = br.top;
13348
+ let newWidth = br.width;
13349
+ let newHeight = br.height;
13350
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13351
+ const yAlreadySnapped = scaleGuides.some((g) => g.type === "horizontal");
13352
+ if (!xAlreadySnapped) {
13353
+ if (hasL) {
13354
+ const nL = Math.round(br.left / gridX) * gridX;
13355
+ newWidth = Math.max(20, anchorRight - nL);
13356
+ newLeft = anchorRight - newWidth;
13357
+ } else if (hasR) {
13358
+ const nR = Math.round(anchorRight / gridX) * gridX;
13359
+ newWidth = Math.max(20, nR - br.left);
13360
+ }
13361
+ }
13362
+ if (!yAlreadySnapped) {
13363
+ if (hasT) {
13364
+ const nT = Math.round(br.top / gridY) * gridY;
13365
+ newHeight = Math.max(20, anchorBottom - nT);
13366
+ newTop = anchorBottom - newHeight;
13367
+ } else if (hasB) {
13368
+ const nB = Math.round(anchorBottom / gridY) * gridY;
13369
+ newHeight = Math.max(20, nB - br.top);
13370
+ }
13371
+ }
13372
+ const widthChanged = Math.abs(newWidth - br.width) > 0.5;
13373
+ const heightChanged = Math.abs(newHeight - br.height) > 0.5;
13374
+ if (widthChanged || heightChanged) {
13375
+ const isTextWidth = obj instanceof fabric__namespace.Textbox && widthChanged && !heightChanged;
13376
+ if (isTextWidth) {
13377
+ obj.set({ width: Math.max(20, newWidth) });
13378
+ try {
13379
+ obj.initDimensions();
13380
+ } catch {
13381
+ }
13382
+ } else {
13383
+ const ratioX = widthChanged ? newWidth / br.width : 1;
13384
+ const ratioY = heightChanged ? newHeight / br.height : 1;
13385
+ const curSx = obj.scaleX ?? 1;
13386
+ const curSy = obj.scaleY ?? 1;
13387
+ obj.set({ scaleX: curSx * ratioX, scaleY: curSy * ratioY });
13388
+ }
13389
+ obj.setCoords();
13390
+ const after = obj.getBoundingRect();
13391
+ const dx = newLeft - after.left;
13392
+ const dy = newTop - after.top;
13393
+ if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
13394
+ obj.set({ left: (obj.left ?? 0) + dx, top: (obj.top ?? 0) + dy });
13395
+ obj.setCoords();
13396
+ }
13397
+ if (!xAlreadySnapped) {
13398
+ if (hasL) gridGuidesForScale.push({ type: "vertical", position: newLeft, kind: "grid" });
13399
+ else if (hasR) gridGuidesForScale.push({ type: "vertical", position: newLeft + newWidth, kind: "grid" });
13400
+ }
13401
+ if (!yAlreadySnapped) {
13402
+ if (hasT) gridGuidesForScale.push({ type: "horizontal", position: newTop, kind: "grid" });
13403
+ else if (hasB) gridGuidesForScale.push({ type: "horizontal", position: newTop + newHeight, kind: "grid" });
13404
+ }
13405
+ }
13406
+ }
13407
+ }
13408
+ }
13409
+ } catch {
13410
+ }
13411
+ setGuides(gridGuidesForScale.length ? [...scaleGuides, ...gridGuidesForScale] : scaleGuides);
11601
13412
  });
11602
13413
  fabricCanvas.on("object:resizing", (e) => {
11603
13414
  if (!isActiveRef.current) return;
@@ -11623,13 +13434,70 @@ const PageCanvas = react.forwardRef(
11623
13434
  }
11624
13435
  const transform = e.transform;
11625
13436
  const corner = (transform == null ? void 0 : transform.corner) || "";
13437
+ if (obj instanceof fabric__namespace.Textbox && (corner === "ml" || corner === "mr")) {
13438
+ const objId = getObjectId(obj);
13439
+ const sourceEl = objId ? elementsRef.current.find((el) => el.id === objId) : void 0;
13440
+ bakeTextboxScaleIntoTypography(obj, sourceEl);
13441
+ }
13442
+ snapDuringScaleCallback(obj, corner);
11626
13443
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11627
- setGuides(scaleGuides);
13444
+ const gridGuidesForTextResize = [];
13445
+ try {
13446
+ const psGrid = projectSettingsRef.current;
13447
+ if (psGrid.snapToGrid && corner && obj instanceof fabric__namespace.Textbox && (corner === "ml" || corner === "mr")) {
13448
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13449
+ if (gridX > 0) {
13450
+ obj.setCoords();
13451
+ const br = obj.getBoundingRect();
13452
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13453
+ if (!xAlreadySnapped && br.width > 1) {
13454
+ const anchorRight = br.left + br.width;
13455
+ let newLeft = br.left;
13456
+ let newWidth = br.width;
13457
+ if (corner === "ml") {
13458
+ const nL = Math.round(br.left / gridX) * gridX;
13459
+ newWidth = Math.max(20, anchorRight - nL);
13460
+ newLeft = anchorRight - newWidth;
13461
+ } else {
13462
+ const nR = Math.round(anchorRight / gridX) * gridX;
13463
+ newWidth = Math.max(20, nR - br.left);
13464
+ }
13465
+ if (Math.abs(newWidth - br.width) > 0.5) {
13466
+ obj.set({ width: Math.max(20, newWidth) });
13467
+ try {
13468
+ obj.initDimensions();
13469
+ } catch {
13470
+ }
13471
+ obj.setCoords();
13472
+ const after = obj.getBoundingRect();
13473
+ const dx = newLeft - after.left;
13474
+ if (Math.abs(dx) > 0.01) {
13475
+ obj.set({ left: (obj.left ?? 0) + dx });
13476
+ obj.setCoords();
13477
+ }
13478
+ gridGuidesForTextResize.push({
13479
+ type: "vertical",
13480
+ position: corner === "ml" ? newLeft : newLeft + newWidth,
13481
+ kind: "grid"
13482
+ });
13483
+ }
13484
+ }
13485
+ }
13486
+ }
13487
+ } catch {
13488
+ }
13489
+ setGuides(gridGuidesForTextResize.length ? [...scaleGuides, ...gridGuidesForTextResize] : scaleGuides);
11628
13490
  });
11629
13491
  fabricCanvas.on("object:rotating", (e) => {
11630
13492
  markSimpleTransform(e);
11631
13493
  didTransformRef.current = true;
11632
13494
  const tr = e.target;
13495
+ try {
13496
+ const getCursor = fabricCanvas.__pixldocsGetRotateCursor;
13497
+ const upper = fabricCanvas.upperCanvasEl;
13498
+ if (typeof getCursor === "function" && upper && tr) upper.style.cursor = getCursor(tr);
13499
+ } catch {
13500
+ }
11633
13501
  const rotateTargetId = tr ? getObjectId(tr) : null;
11634
13502
  if (rotateTargetId && rotateTargetId !== "__background__") {
11635
13503
  preserveSelectionAfterTransformIdRef.current = rotateTargetId;
@@ -11656,6 +13524,7 @@ const PageCanvas = react.forwardRef(
11656
13524
  prepareGroupSelectionTransformStart(e.target);
11657
13525
  markTransforming(e.target);
11658
13526
  didTransformRef.current = true;
13527
+ if (e.target) e.target.__pixldocsDragMoved = true;
11659
13528
  const moveTargetId = e.target ? getObjectId(e.target) : null;
11660
13529
  if (moveTargetId && moveTargetId !== "__background__") {
11661
13530
  preserveSelectionAfterTransformIdRef.current = moveTargetId;
@@ -11672,19 +13541,57 @@ const PageCanvas = react.forwardRef(
11672
13541
  if (!obj) return;
11673
13542
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11674
13543
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11675
- setGuides(newGuides);
11676
- if (snapDx !== 0 || snapDy !== 0) {
11677
- snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
13544
+ let finalDx = snapDx;
13545
+ let finalDy = snapDy;
13546
+ let mergedGuides = newGuides;
13547
+ const psLive = projectSettingsRef.current;
13548
+ const gridX = psLive.gridSizeX ?? psLive.gridSize ?? 0;
13549
+ const gridY = psLive.gridSizeY ?? psLive.gridSize ?? 0;
13550
+ if (psLive.snapToGrid && gridX > 0 && gridY > 0) {
13551
+ try {
13552
+ snapTarget.setCoords();
13553
+ } catch {
13554
+ }
13555
+ const br = snapTarget.getBoundingRect();
13556
+ const gridGuides = [];
13557
+ if (finalDx === 0) {
13558
+ const newLeft = Math.round(br.left / gridX) * gridX;
13559
+ finalDx = newLeft - br.left;
13560
+ gridGuides.push({ type: "vertical", position: newLeft, kind: "grid" });
13561
+ }
13562
+ if (finalDy === 0) {
13563
+ const newTop = Math.round(br.top / gridY) * gridY;
13564
+ finalDy = newTop - br.top;
13565
+ gridGuides.push({ type: "horizontal", position: newTop, kind: "grid" });
13566
+ }
13567
+ if (gridGuides.length) mergedGuides = [...newGuides, ...gridGuides];
13568
+ }
13569
+ setGuides(mergedGuides);
13570
+ setHoverBounds(null);
13571
+ if (finalDx !== 0 || finalDy !== 0) {
13572
+ snapTarget.set({ left: (snapTarget.left ?? 0) + finalDx, top: (snapTarget.top ?? 0) + finalDy });
11678
13573
  }
11679
13574
  });
11680
13575
  let cropGroupSaveTimer = null;
11681
13576
  fabricCanvas.on("object:modified", (e) => {
11682
- var _a2, _b, _c, _d, _e, _f, _g;
13577
+ var _a2, _b2, _c, _d, _e, _f, _g;
11683
13578
  try {
11684
13579
  dragStarted = false;
11685
13580
  setGuides([]);
11686
13581
  setGroupOverlayLiveBoundsRef.current(null);
13582
+ objectResizeActiveSnapRef.current = null;
13583
+ groupResizeActiveSnapRef.current = null;
11687
13584
  onDragEnd == null ? void 0 : onDragEnd();
13585
+ try {
13586
+ const t = e.target;
13587
+ if (t instanceof fabric__namespace.ActiveSelection) {
13588
+ for (const child of t.getObjects()) {
13589
+ delete child.__asLiveOrigW;
13590
+ delete child.__asLiveOrigH;
13591
+ }
13592
+ }
13593
+ } catch {
13594
+ }
11688
13595
  lockEdits();
11689
13596
  const modifiedTarget = e.target;
11690
13597
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -11793,7 +13700,7 @@ const PageCanvas = react.forwardRef(
11793
13700
  useEditorStore.getState().reflowStackGroupInPage(pageId, groupId);
11794
13701
  }
11795
13702
  const stateAfter = useEditorStore.getState();
11796
- const pageAfter = ((_b = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b.children) ?? [];
13703
+ const pageAfter = ((_b2 = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b2.children) ?? [];
11797
13704
  const groupNodeAfter = findNodeById(pageAfter, groupId);
11798
13705
  if (groupNodeAfter) {
11799
13706
  const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);
@@ -11820,11 +13727,11 @@ const PageCanvas = react.forwardRef(
11820
13727
  clearTimeout(cropGroupSaveTimer);
11821
13728
  }
11822
13729
  cropGroupSaveTimer = setTimeout(() => {
11823
- var _a3, _b2, _c2;
13730
+ var _a3, _b3, _c2;
11824
13731
  const { updateElement: updateElement2 } = useEditorStore.getState();
11825
13732
  const img = ct._img;
11826
13733
  const zoom3 = ((_a3 = img == null ? void 0 : img._ct) == null ? void 0 : _a3.zoom) ?? 1;
11827
- const panX = ((_b2 = img == null ? void 0 : img._ct) == null ? void 0 : _b2.panX) ?? 0.5;
13734
+ const panX = ((_b3 = img == null ? void 0 : img._ct) == null ? void 0 : _b3.panX) ?? 0.5;
11828
13735
  const panY = ((_c2 = img == null ? void 0 : img._ct) == null ? void 0 : _c2.panY) ?? 0.5;
11829
13736
  const stateCrop = useEditorStore.getState();
11830
13737
  const pageCrop = stateCrop.canvas.pages.find((p) => p.id === pageId);
@@ -11843,6 +13750,8 @@ const PageCanvas = react.forwardRef(
11843
13750
  cropZoom: zoom3
11844
13751
  }, { recordHistory: false });
11845
13752
  active.__isInternalCropUpdate = false;
13753
+ installImageResizeControlsWithSnap(active);
13754
+ ensureCanvaControlRenders(active);
11846
13755
  fabricCanvas.setActiveObject(active);
11847
13756
  setTimeout(() => justModifiedIdsRef.current.delete(objId), 150);
11848
13757
  }, 0);
@@ -12096,6 +14005,10 @@ const PageCanvas = react.forwardRef(
12096
14005
  for (const obj of activeObjects) {
12097
14006
  const objId = getObjectId(obj);
12098
14007
  if (!objId || objId === "__background__") continue;
14008
+ const sourceElement = elementsRef.current.find((el) => el.id === objId);
14009
+ if (obj instanceof fabric__namespace.Textbox && !isActiveSelection) {
14010
+ bakeTextboxScaleIntoTypography(obj, sourceElement);
14011
+ }
12099
14012
  let intrinsicWidth;
12100
14013
  let intrinsicHeight;
12101
14014
  if (obj instanceof fabric__namespace.Circle) {
@@ -12162,7 +14075,6 @@ const PageCanvas = react.forwardRef(
12162
14075
  absoluteLeft = (absoluteLeft ?? 0) - w / 2;
12163
14076
  absoluteTop = (absoluteTop ?? 0) - h / 2;
12164
14077
  }
12165
- const sourceElement = elementsRef.current.find((el) => el.id === objId);
12166
14078
  const preserveCornerGeometry = (sourceElement == null ? void 0 : sourceElement.type) === "shape" && (sourceElement.shapeType === "circle" || sourceElement.shapeType === "rounded-rect" || sourceElement.shapeType === "triangle");
12167
14079
  let finalWidth = intrinsicWidth;
12168
14080
  let finalHeight = intrinsicHeight;
@@ -12248,17 +14160,35 @@ const PageCanvas = react.forwardRef(
12248
14160
  finalHeight = 0;
12249
14161
  finalScaleX = 1;
12250
14162
  finalScaleY = 1;
14163
+ } else if (obj instanceof fabric__namespace.Textbox && isActiveSelection && (Math.abs((decomposed.scaleX ?? 1) - 1) > 1e-3 || Math.abs((decomposed.scaleY ?? 1) - 1) > 1e-3)) {
14164
+ const sx = Math.abs(decomposed.scaleX || 1);
14165
+ const sy = Math.abs(decomposed.scaleY || 1);
14166
+ const bakedWidth = Math.max(20, intrinsicWidth * sx);
14167
+ const bakedHeight = Math.max(1, intrinsicHeight * sy);
14168
+ finalWidth = bakedWidth;
14169
+ finalHeight = bakedHeight;
14170
+ finalScaleX = 1;
14171
+ finalScaleY = 1;
14172
+ try {
14173
+ obj.set({ width: bakedWidth, scaleX: 1, scaleY: 1 });
14174
+ obj.initDimensions();
14175
+ obj.setCoords();
14176
+ } catch {
14177
+ }
14178
+ finalAbsoluteMatrix = fabric__namespace.util.composeMatrix({
14179
+ translateX: decomposed.translateX,
14180
+ translateY: decomposed.translateY,
14181
+ angle: decomposed.angle ?? 0,
14182
+ scaleX: 1,
14183
+ scaleY: 1,
14184
+ skewX: 0,
14185
+ skewY: 0
14186
+ });
12251
14187
  } else if (preserveCornerGeometry) {
12252
14188
  const scaledW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));
12253
14189
  const scaledH = Math.max(1, intrinsicHeight * Math.abs(decomposed.scaleY || 1));
12254
- if ((sourceElement == null ? void 0 : sourceElement.shapeType) === "circle") {
12255
- const diameter = Math.max(1, Math.min(scaledW, scaledH));
12256
- finalWidth = diameter;
12257
- finalHeight = diameter;
12258
- } else {
12259
- finalWidth = scaledW;
12260
- finalHeight = scaledH;
12261
- }
14190
+ finalWidth = scaledW;
14191
+ finalHeight = scaledH;
12262
14192
  finalScaleX = 1;
12263
14193
  finalScaleY = 1;
12264
14194
  obj.set({ scaleX: 1, scaleY: 1 });
@@ -12297,6 +14227,11 @@ const PageCanvas = react.forwardRef(
12297
14227
  transformMatrix: finalAbsoluteMatrix
12298
14228
  };
12299
14229
  if (obj instanceof fabric__namespace.Textbox) {
14230
+ const bakedTextScaleUpdates = obj.__pixldocsBakedTextScaleUpdates;
14231
+ if (bakedTextScaleUpdates && typeof bakedTextScaleUpdates === "object") {
14232
+ Object.assign(elementUpdate, bakedTextScaleUpdates);
14233
+ delete obj.__pixldocsBakedTextScaleUpdates;
14234
+ }
12300
14235
  const baked = obj.minBoxHeight;
12301
14236
  if (typeof baked === "number" && baked > 0) {
12302
14237
  elementUpdate.minBoxHeight = baked;
@@ -12405,7 +14340,7 @@ const PageCanvas = react.forwardRef(
12405
14340
  }
12406
14341
  });
12407
14342
  fabricCanvas.on("mouse:dblclick", (e) => {
12408
- var _a2, _b;
14343
+ var _a2, _b2;
12409
14344
  if (!isActiveRef.current || !allowEditing) return;
12410
14345
  let target = e.target;
12411
14346
  if (!target) {
@@ -12415,7 +14350,7 @@ const PageCanvas = react.forwardRef(
12415
14350
  if (target && target instanceof fabric__namespace.Group && target.__cropGroup) {
12416
14351
  const ct = target.__cropData;
12417
14352
  const innerImg = ct == null ? void 0 : ct._img;
12418
- 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) || "";
14353
+ 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) || "";
12419
14354
  const isPlaceholder = !innerSrc || innerSrc === EMPTY_IMAGE_PLACEHOLDER_DATA_URL;
12420
14355
  if (innerImg && !isPlaceholder && !isCropGroupInCropMode(target)) {
12421
14356
  enterCropMode(target);
@@ -12594,13 +14529,13 @@ const PageCanvas = react.forwardRef(
12594
14529
  visibilityUpdateInProgressRef.current = false;
12595
14530
  }
12596
14531
  doSyncRef.current = () => {
12597
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
14532
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
12598
14533
  const shouldSkipUpdates2 = syncLockedRef.current || editLockRef.current;
12599
14534
  const state = useEditorStore.getState();
12600
14535
  const elementsToSync = elements;
12601
14536
  elementsRef.current = elementsToSync;
12602
14537
  const pageTree = isPreviewMode && (pageChildren == null ? void 0 : pageChildren.length) ? pageChildren ?? [] : ((_a2 = state.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _a2.children) ?? [];
12603
- const selectedIdsFromStore = new Set(((_b = state.canvas) == null ? void 0 : _b.selectedIds) ?? []);
14538
+ const selectedIdsFromStore = new Set(((_b2 = state.canvas) == null ? void 0 : _b2.selectedIds) ?? []);
12604
14539
  isRebuildingRef.current = true;
12605
14540
  const allElementIds = new Set(elementsToSync.map((el) => el.id));
12606
14541
  const sectionGroups = pageTree.filter(
@@ -12627,7 +14562,8 @@ const PageCanvas = react.forwardRef(
12627
14562
  const activeObj = fc.getActiveObject();
12628
14563
  const activeObjId = activeObj ? getObjectId(activeObj) : null;
12629
14564
  const isTextBeingEdited = activeObjId && editingTextIdRef.current === activeObjId;
12630
- if (activeObj && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
14565
+ const isMultiSelect = activeObj instanceof fabric__namespace.ActiveSelection;
14566
+ if (activeObj && isMultiSelect && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
12631
14567
  fc.discardActiveObject();
12632
14568
  }
12633
14569
  }
@@ -12947,7 +14883,7 @@ const PageCanvas = react.forwardRef(
12947
14883
  updateCoverLayout(existingObj);
12948
14884
  applyEdgeFadeFrameClipPath(existingObj, element, ct.frameW, ct.frameH, ct.shape || "rect", ct.rx || 0);
12949
14885
  if (allowEditing) {
12950
- installCanvaMaskControls(existingObj);
14886
+ installImageResizeControlsWithSnap(existingObj);
12951
14887
  } else {
12952
14888
  existingObj.set({
12953
14889
  hasControls: false,
@@ -13029,7 +14965,7 @@ const PageCanvas = react.forwardRef(
13029
14965
  hoverCursor: isDynamicField && isPreviewMode ? "pointer" : void 0
13030
14966
  });
13031
14967
  if (allowEditing) {
13032
- installCanvaMaskControls(existingObj);
14968
+ installImageResizeControlsWithSnap(existingObj);
13033
14969
  }
13034
14970
  existingObj.setCoords();
13035
14971
  fc.requestRenderAll();
@@ -13755,7 +15691,7 @@ const PageCanvas = react.forwardRef(
13755
15691
  return unsub;
13756
15692
  }, []);
13757
15693
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
13758
- var _a2, _b, _c;
15694
+ var _a2, _b2, _c;
13759
15695
  const fc = fabricRef.current;
13760
15696
  if (fc && isTransforming(fc)) {
13761
15697
  return;
@@ -13823,11 +15759,12 @@ const PageCanvas = react.forwardRef(
13823
15759
  // Disable rotation for crop groups (simplifies resize math)
13824
15760
  hasRotatingPoint: false,
13825
15761
  // Hide rotation handle
13826
- // Scale with zoom so handles stay same visual size (controls drawn in canvas pixel space)
13827
- cornerSize: Math.max(6, Math.round(8 * (fc.getZoom() || 1))),
13828
- borderScaleFactor: Math.max(1.25, 1.25 * (fc.getZoom() || 1)),
15762
+ // Handles are drawn in screen-space keep them constant on-screen.
15763
+ // Match the global Canva-style defaults (circular dots, pill sides).
15764
+ cornerSize: 10,
15765
+ borderScaleFactor: SELECTION_BORDER_SCALE,
13829
15766
  transparentCorners: false,
13830
- cornerStyle: "rect",
15767
+ cornerStyle: "circle",
13831
15768
  cornerColor: SELECTION_PRIMARY,
13832
15769
  cornerStrokeColor: "#ffffff",
13833
15770
  borderColor: SELECTION_PRIMARY,
@@ -13937,7 +15874,8 @@ const PageCanvas = react.forwardRef(
13937
15874
  obj.clipPath.dirty = true;
13938
15875
  obj.clipPath.setCoords();
13939
15876
  }
13940
- installCanvaMaskControls(obj);
15877
+ installImageResizeControlsWithSnap(obj);
15878
+ ensureCanvaControlRenders(obj);
13941
15879
  obj.set({
13942
15880
  selectable: true,
13943
15881
  evented: true,
@@ -14032,7 +15970,7 @@ const PageCanvas = react.forwardRef(
14032
15970
  obj.setCoords();
14033
15971
  }
14034
15972
  if (!isLine) {
14035
- const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
15973
+ const angleTextPathActive = isTextbox && ((_b2 = element.textPath) == null ? void 0 : _b2.preset) === "rise";
14036
15974
  const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
14037
15975
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
14038
15976
  if (!skipPositionUpdate && (obj instanceof fabric__namespace.FabricImage && obj.originX === "center" || obj instanceof fabric__namespace.Group && obj.__cropGroup)) {
@@ -14109,7 +16047,17 @@ const PageCanvas = react.forwardRef(
14109
16047
  objectCaching: true
14110
16048
  });
14111
16049
  } else if (obj instanceof fabric__namespace.Ellipse) {
14112
- obj.set({ rx: rW / 2, ry: rH / 2 });
16050
+ obj.set({
16051
+ rx: cornerSafeW / 2,
16052
+ ry: cornerSafeH / 2,
16053
+ fill: element.fill || "transparent",
16054
+ stroke: element.stroke || "transparent",
16055
+ strokeWidth: element.strokeWidth || 0,
16056
+ strokeUniform: true,
16057
+ strokeLineJoin: "round",
16058
+ strokeLineCap: "round",
16059
+ objectCaching: true
16060
+ });
14113
16061
  } else if (obj instanceof fabric__namespace.Textbox) {
14114
16062
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
14115
16063
  let text = element.text || "Text";
@@ -14303,6 +16251,16 @@ const PageCanvas = react.forwardRef(
14303
16251
  strokeUniform: true,
14304
16252
  objectCaching: true
14305
16253
  });
16254
+ } else if (obj instanceof fabric__namespace.Ellipse) {
16255
+ obj.set({
16256
+ rx: cornerSafeW / 2,
16257
+ ry: cornerSafeH / 2,
16258
+ fill: element.fill || "transparent",
16259
+ stroke: element.stroke || "transparent",
16260
+ strokeWidth: element.strokeWidth || 0,
16261
+ strokeUniform: true,
16262
+ objectCaching: true
16263
+ });
14306
16264
  } else if (obj instanceof fabric__namespace.Rect && element.shapeType === "rounded-rect") {
14307
16265
  const toRadius = (value, fallback) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
14308
16266
  const baseRx = Math.max(0, Number(element.rx ?? 0));
@@ -14562,7 +16520,7 @@ const PageCanvas = react.forwardRef(
14562
16520
  return Math.max(min, Math.min(max, v));
14563
16521
  };
14564
16522
  const applyEdgeFadeFrameClipPath = (group, element, frameW, frameH, shape, rxRatio) => {
14565
- var _a2, _b, _c;
16523
+ var _a2, _b2, _c;
14566
16524
  const fadeElement = element;
14567
16525
  const fadeGroup = group;
14568
16526
  const inputKey = edgeFadeKey(fadeElement);
@@ -14683,7 +16641,7 @@ const PageCanvas = react.forwardRef(
14683
16641
  delete fadeGroup.__edgeFadeRenderConfig;
14684
16642
  delete fadeGroup.__edgeFadeKey;
14685
16643
  delete fadeGroup.__edgeFadeInputKey;
14686
- if ((_b = group.clipPath) == null ? void 0 : _b.__edgeFadeMask) {
16644
+ if ((_b2 = group.clipPath) == null ? void 0 : _b2.__edgeFadeMask) {
14687
16645
  group.clipPath = void 0;
14688
16646
  updateCoverLayout(group);
14689
16647
  }
@@ -14700,7 +16658,7 @@ const PageCanvas = react.forwardRef(
14700
16658
  (_c = group.canvas) == null ? void 0 : _c.requestRenderAll();
14701
16659
  };
14702
16660
  const loadImageAsync2 = async (element, placeholder, fc) => {
14703
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
16661
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
14704
16662
  const imageUrl = element.src || element.imageUrl;
14705
16663
  if (!imageUrl) return;
14706
16664
  const elementId = element.id;
@@ -14731,7 +16689,7 @@ const PageCanvas = react.forwardRef(
14731
16689
  await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);
14732
16690
  if (!isLatestRequest()) return;
14733
16691
  const imageFitForFade = element.imageFit || ((_a2 = element.style) == null ? void 0 : _a2.imageFit) || "cover";
14734
- const clipShapeForFade = element.clipShape ?? ((_b = element.style) == null ? void 0 : _b.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
16692
+ const clipShapeForFade = element.clipShape ?? ((_b2 = element.style) == null ? void 0 : _b2.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
14735
16693
  const willUseCropGroupForFade = imageFitForFade !== "fill" || clipShapeForFade && clipShapeForFade !== "none";
14736
16694
  try {
14737
16695
  if (hasEdgeFade(element) && !willUseCropGroupForFade) {
@@ -14949,7 +16907,7 @@ const PageCanvas = react.forwardRef(
14949
16907
  evented: canBeEvented && !isHidden
14950
16908
  });
14951
16909
  } else {
14952
- installCanvaMaskControls(cropGroup);
16910
+ installImageResizeControlsWithSnap(cropGroup);
14953
16911
  }
14954
16912
  const cropImg = (_o = cropGroup.__cropData) == null ? void 0 : _o._img;
14955
16913
  if (cropImg) {
@@ -15160,65 +17118,191 @@ const PageCanvas = react.forwardRef(
15160
17118
  ),
15161
17119
  sectionsOverlay,
15162
17120
  groupBoundsOverlay,
15163
- guides.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
17121
+ hoverBounds && !guides.length && !(selectedIdsRef.current && selectedIdsRef.current.length) && /* @__PURE__ */ jsxRuntime.jsx(
15164
17122
  "svg",
15165
17123
  {
15166
17124
  className: "absolute inset-0 pointer-events-none",
15167
17125
  style: { width: scaledWidth, height: scaledHeight },
15168
17126
  viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15169
- children: (() => {
15170
- const seen = /* @__PURE__ */ new Set();
15171
- return guides.filter((guide) => {
15172
- const key = `${guide.type}-${guide.position.toFixed(1)}`;
15173
- if (seen.has(key)) return false;
15174
- seen.add(key);
15175
- return true;
15176
- }).map((guide, i) => {
15177
- const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
15178
- const isActive2 = guide.active === true;
15179
- const strokeColor = isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#3b82f6";
15180
- const strokeWidth = isActive2 ? 2.5 : 1;
15181
- const strokeDasharray = isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
15182
- const showDistance = typeof guide.distance === "number";
15183
- const labelText = showDistance ? String(Math.round(guide.distance)) : "";
15184
- const labelW = Math.max(24, labelText.length * 7);
15185
- if (guide.type === "vertical") {
15186
- const padding = isElementRelative || isActive2 ? 20 : 0;
15187
- const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15188
- const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
15189
- const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
15190
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15191
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray }),
15192
- isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
15193
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15194
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
15195
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
15196
- ] }),
15197
- showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
15198
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15199
- /* @__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 })
15200
- ] })
15201
- ] }, i);
15202
- } else {
15203
- const padding = isElementRelative || isActive2 ? 20 : 0;
15204
- const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15205
- const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
15206
- const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
15207
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15208
- /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray }),
15209
- isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
15210
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
15211
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
15212
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
15213
- ] }),
15214
- showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
15215
- /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15216
- /* @__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 })
15217
- ] })
15218
- ] }, i);
17127
+ children: /* @__PURE__ */ jsxRuntime.jsx(
17128
+ "rect",
17129
+ {
17130
+ x: hoverBounds.left,
17131
+ y: hoverBounds.top,
17132
+ width: hoverBounds.width,
17133
+ height: hoverBounds.height,
17134
+ fill: "none",
17135
+ stroke: SELECTION_PRIMARY,
17136
+ strokeWidth: 2,
17137
+ vectorEffect: "non-scaling-stroke",
17138
+ strokeDasharray: "0",
17139
+ opacity: 1
17140
+ }
17141
+ )
17142
+ }
17143
+ ),
17144
+ canvas.projectSettings.showGrid && (() => {
17145
+ const ps = canvas.projectSettings;
17146
+ const gx = Math.max(1, ps.gridSizeX ?? ps.gridSize ?? 0);
17147
+ const gy = Math.max(1, ps.gridSizeY ?? ps.gridSize ?? 0);
17148
+ if (gx <= 0 || gy <= 0) return null;
17149
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17150
+ "svg",
17151
+ {
17152
+ className: "absolute inset-0 pointer-events-none",
17153
+ style: { width: scaledWidth, height: scaledHeight },
17154
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
17155
+ children: [
17156
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
17157
+ "pattern",
17158
+ {
17159
+ id: `pixldocs-grid-${pageId}`,
17160
+ width: gx,
17161
+ height: gy,
17162
+ patternUnits: "userSpaceOnUse",
17163
+ children: /* @__PURE__ */ jsxRuntime.jsx(
17164
+ "path",
17165
+ {
17166
+ d: `M ${gx} 0 L 0 0 0 ${gy}`,
17167
+ fill: "none",
17168
+ stroke: ps.gridColor || "#0f172a",
17169
+ strokeWidth: 0.5,
17170
+ opacity: 0.22
17171
+ }
17172
+ )
17173
+ }
17174
+ ) }),
17175
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
17176
+ ]
17177
+ }
17178
+ );
17179
+ })(),
17180
+ guides.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
17181
+ "svg",
17182
+ {
17183
+ className: "absolute inset-0 pointer-events-none",
17184
+ style: { width: scaledWidth, height: scaledHeight },
17185
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
17186
+ children: [
17187
+ (() => {
17188
+ const seenBoxes = /* @__PURE__ */ new Set();
17189
+ const boxes = [];
17190
+ for (const g of guides) {
17191
+ const list = g.targetBoundsList ?? (g.targetBounds ? [g.targetBounds] : []);
17192
+ for (const b of list) {
17193
+ const key = `${b.left.toFixed(1)}-${b.top.toFixed(1)}-${b.width.toFixed(1)}-${b.height.toFixed(1)}`;
17194
+ if (seenBoxes.has(key)) continue;
17195
+ seenBoxes.add(key);
17196
+ boxes.push(b);
17197
+ }
15219
17198
  }
15220
- });
15221
- })()
17199
+ return boxes.map((b, i) => /* @__PURE__ */ jsxRuntime.jsx(
17200
+ "rect",
17201
+ {
17202
+ x: b.left,
17203
+ y: b.top,
17204
+ width: b.width,
17205
+ height: b.height,
17206
+ fill: "none",
17207
+ stroke: "#f43f5e",
17208
+ strokeWidth: 1.5,
17209
+ strokeDasharray: "4,3",
17210
+ opacity: 0.9,
17211
+ vectorEffect: "non-scaling-stroke"
17212
+ },
17213
+ `tb-${i}`
17214
+ ));
17215
+ })(),
17216
+ (() => {
17217
+ const seen = /* @__PURE__ */ new Set();
17218
+ return guides.filter((guide) => {
17219
+ if (guide.kind === "gap") return true;
17220
+ const key = `${guide.type}-${guide.position.toFixed(1)}`;
17221
+ if (seen.has(key)) return false;
17222
+ seen.add(key);
17223
+ return true;
17224
+ }).map((guide, i) => {
17225
+ if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
17226
+ const gapColor = "#ec4899";
17227
+ const label = `${guide.gap}`;
17228
+ const labelW2 = Math.max(22, label.length * 7 + 8);
17229
+ if (guide.type === "horizontal") {
17230
+ const y = guide.bracketAt ?? guide.position;
17231
+ const x1 = guide.start;
17232
+ const x2 = guide.end;
17233
+ const mid = (x1 + x2) / 2;
17234
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17235
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17236
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17237
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17238
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
17239
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17240
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17241
+ ] })
17242
+ ] }, i);
17243
+ } else {
17244
+ const x = guide.bracketAt ?? guide.position;
17245
+ const y1 = guide.start;
17246
+ const y2 = guide.end;
17247
+ const mid = (y1 + y2) / 2;
17248
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17249
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17250
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17251
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17252
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
17253
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17254
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17255
+ ] })
17256
+ ] }, i);
17257
+ }
17258
+ }
17259
+ const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
17260
+ const isActive2 = guide.active === true;
17261
+ const isGrid = guide.kind === "grid";
17262
+ const strokeColor = isGrid ? "#f59e0b" : isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#ec4899";
17263
+ const strokeWidth = isGrid ? 1.75 : 1.75;
17264
+ const strokeDasharray = isGrid ? "2,3" : isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
17265
+ const showDistance = typeof guide.distance === "number";
17266
+ const labelText = showDistance ? String(Math.round(guide.distance)) : "";
17267
+ const labelW = Math.max(24, labelText.length * 7);
17268
+ if (guide.type === "vertical") {
17269
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17270
+ const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17271
+ const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
17272
+ const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
17273
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17274
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17275
+ isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
17276
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
17277
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
17278
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
17279
+ ] }),
17280
+ showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
17281
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17282
+ /* @__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 })
17283
+ ] })
17284
+ ] }, i);
17285
+ } else {
17286
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17287
+ const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17288
+ const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
17289
+ const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
17290
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17291
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17292
+ isActive2 && /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
17293
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
17294
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
17295
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
17296
+ ] }),
17297
+ showDistance && /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
17298
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17299
+ /* @__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 })
17300
+ ] })
17301
+ ] }, i);
17302
+ }
17303
+ });
17304
+ })()
17305
+ ]
15222
17306
  }
15223
17307
  ),
15224
17308
  gridResizeLabel && /* @__PURE__ */ jsxRuntime.jsx(
@@ -15354,13 +17438,13 @@ function PreviewCanvas({
15354
17438
  onDynamicFieldClick,
15355
17439
  onReady
15356
17440
  }) {
15357
- var _a2, _b, _c, _d, _e;
17441
+ var _a2, _b2, _c, _d, _e;
15358
17442
  const canvasRef = react.useRef(null);
15359
17443
  const containerRef = react.useRef(null);
15360
17444
  const [containerWidth, setContainerWidth] = react.useState(0);
15361
17445
  const [hoveredFieldId, setHoveredFieldId] = react.useState(null);
15362
17446
  const page = (_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2[pageIndex];
15363
- const canvasWidth = ((_b = config == null ? void 0 : config.canvas) == null ? void 0 : _b.width) || 612;
17447
+ const canvasWidth = ((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.width) || 612;
15364
17448
  const canvasHeight = ((_c = config == null ? void 0 : config.canvas) == null ? void 0 : _c.height) || 792;
15365
17449
  const elementToFieldMap = react.useMemo(
15366
17450
  () => buildElementToFieldMap(config == null ? void 0 : config.dynamicFields),
@@ -15410,14 +17494,14 @@ function PreviewCanvas({
15410
17494
  }
15411
17495
  }, [elements]);
15412
17496
  const pageSettings = react.useMemo(() => {
15413
- var _a3, _b2;
17497
+ var _a3, _b3;
15414
17498
  return {
15415
17499
  backgroundColor: ((_a3 = page == null ? void 0 : page.settings) == null ? void 0 : _a3.backgroundColor) || "#ffffff",
15416
- backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
17500
+ backgroundGradient: (_b3 = page == null ? void 0 : page.settings) == null ? void 0 : _b3.backgroundGradient
15417
17501
  };
15418
17502
  }, [(_d = page == null ? void 0 : page.settings) == null ? void 0 : _d.backgroundColor, (_e = page == null ? void 0 : page.settings) == null ? void 0 : _e.backgroundGradient]);
15419
17503
  const projectSettings = react.useMemo(() => {
15420
- var _a3, _b2, _c2;
17504
+ var _a3, _b3, _c2;
15421
17505
  const vars = ((_a3 = config.themeConfig) == null ? void 0 : _a3.variables) || {};
15422
17506
  return {
15423
17507
  showGrid: false,
@@ -15425,7 +17509,7 @@ function PreviewCanvas({
15425
17509
  gridSize: 10,
15426
17510
  snapToGuides: false,
15427
17511
  snapThreshold: 5,
15428
- primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
17512
+ primaryColor: (_b3 = vars.primary) == null ? void 0 : _b3.value,
15429
17513
  secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
15430
17514
  };
15431
17515
  }, [config.themeConfig]);
@@ -15577,7 +17661,7 @@ const PreviewCanvas$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
15577
17661
  PreviewCanvas
15578
17662
  }, Symbol.toStringTag, { value: "Module" }));
15579
17663
  function applyThemeToConfig(config, themeOverrides) {
15580
- var _a2, _b, _c;
17664
+ var _a2, _b2, _c;
15581
17665
  if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;
15582
17666
  const cloned = JSON.parse(JSON.stringify(config));
15583
17667
  if ((_a2 = cloned.themeConfig) == null ? void 0 : _a2.variables) {
@@ -15588,7 +17672,7 @@ function applyThemeToConfig(config, themeOverrides) {
15588
17672
  }
15589
17673
  }
15590
17674
  const varMap = /* @__PURE__ */ new Map();
15591
- if ((_b = cloned.themeConfig) == null ? void 0 : _b.variables) {
17675
+ if ((_b2 = cloned.themeConfig) == null ? void 0 : _b2.variables) {
15592
17676
  for (const [key, def] of Object.entries(cloned.themeConfig.variables)) {
15593
17677
  varMap.set(key, themeOverrides[key] ?? def.value);
15594
17678
  }
@@ -15625,7 +17709,7 @@ function mapFormDefFieldType(t) {
15625
17709
  function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15626
17710
  const sections = [];
15627
17711
  function convert(defs, parentId) {
15628
- var _a2, _b;
17712
+ var _a2, _b2;
15629
17713
  for (const def of defs) {
15630
17714
  const isRepeatable = def.repeatable === true || def.type === "repeatable";
15631
17715
  const defFields = def.fields ?? def.entryFields ?? [];
@@ -15677,7 +17761,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15677
17761
  placeholder: f.placeholder
15678
17762
  }))
15679
17763
  });
15680
- if ((_b = def.children) == null ? void 0 : _b.length) {
17764
+ if ((_b2 = def.children) == null ? void 0 : _b2.length) {
15681
17765
  convert(def.children, def.id);
15682
17766
  }
15683
17767
  }
@@ -16306,7 +18390,7 @@ function findAllRepeatableElementsBySourceId(pages, baseNodeId, oneBasedIndex, s
16306
18390
  return out;
16307
18391
  }
16308
18392
  function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi, childBaseNodeId, childCi, sourceElementId) {
16309
- var _a2, _b;
18393
+ var _a2, _b2;
16310
18394
  const parentGroups = collectGroupsByBaseId(pages, parentBaseNodeId);
16311
18395
  const parentGroup = parentGroups[parentPi - 1];
16312
18396
  if (!parentGroup || !isGroup(parentGroup) || !((_a2 = parentGroup.children) == null ? void 0 : _a2.length)) return void 0;
@@ -16321,7 +18405,7 @@ function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi
16321
18405
  }
16322
18406
  collectChild(parentGroup.children);
16323
18407
  const childGroup = childGroups[childCi - 1];
16324
- if (!childGroup || !isGroup(childGroup) || !((_b = childGroup.children) == null ? void 0 : _b.length)) return void 0;
18408
+ if (!childGroup || !isGroup(childGroup) || !((_b2 = childGroup.children) == null ? void 0 : _b2.length)) return void 0;
16325
18409
  return findElementBySourceIdInSubtree(childGroup.children, sourceElementId) ?? (childGroup.__sourceId === sourceElementId ? childGroup.id : void 0);
16326
18410
  }
16327
18411
  function repeatableLabelKey(label) {
@@ -16371,7 +18455,7 @@ function getRepeatableFromConfig(pages) {
16371
18455
  var _a2;
16372
18456
  const result = [];
16373
18457
  function walk(children) {
16374
- var _a3, _b;
18458
+ var _a3, _b2;
16375
18459
  if (!children) return;
16376
18460
  for (const node of children) {
16377
18461
  if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && ((_a3 = node.children) == null ? void 0 : _a3.length)) {
@@ -16380,7 +18464,7 @@ function getRepeatableFromConfig(pages) {
16380
18464
  if (rep == null ? void 0 : rep.label) result.push({ nodeId: child.id, label: rep.label });
16381
18465
  }
16382
18466
  }
16383
- if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) walk(node.children);
18467
+ if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) walk(node.children);
16384
18468
  }
16385
18469
  }
16386
18470
  for (const page of pages) if ((_a2 = page.children) == null ? void 0 : _a2.length) walk(page.children);
@@ -16396,7 +18480,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16396
18480
  return !!((_a3 = n.repeatableSection) == null ? void 0 : _a3.label);
16397
18481
  };
16398
18482
  function walk(children, parentRepeatableBaseId, parentInfo, parentRepeatableEntryIndex) {
16399
- var _a3, _b;
18483
+ var _a3, _b2;
16400
18484
  if (!Array.isArray(children)) return;
16401
18485
  for (let i = 0; i < children.length; i++) {
16402
18486
  const node = children[i];
@@ -16439,7 +18523,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16439
18523
  if (isGroup(child) && Array.isArray(child.children)) walk(child.children, effectiveChildBase, void 0, childEntryIndex);
16440
18524
  j += count;
16441
18525
  }
16442
- } else if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) {
18526
+ } else if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) {
16443
18527
  const nodeBase = baseId(node.id);
16444
18528
  const selfMatch = schemaBaseIds.has(node.id) || schemaBaseIds.has(nodeBase) || node.__baseNodeId != null && schemaBaseIds.has(node.__baseNodeId);
16445
18529
  const isSelfRepeatable = selfMatch && hasRepeatableSection(node);
@@ -16528,7 +18612,7 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
16528
18612
  return Math.max(1, maxIndex);
16529
18613
  }
16530
18614
  function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
16531
- var _a2, _b, _c;
18615
+ var _a2, _b2, _c;
16532
18616
  const cloned = JSON.parse(JSON.stringify(config));
16533
18617
  if (!cloned.pages) return cloned;
16534
18618
  const dynamicFields = cloned.dynamicFields;
@@ -16709,7 +18793,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
16709
18793
  const { node, baseNodeId, startIndex, count } = block;
16710
18794
  const occIdx = block.__occurrenceIndex ?? 1;
16711
18795
  const N = computeN(baseNodeId, node.id);
16712
- const entryFilter = ((_b = node.repeatableSection) == null ? void 0 : _b.entryFilter) ?? entryFilterFromList(baseNodeId);
18796
+ const entryFilter = ((_b2 = node.repeatableSection) == null ? void 0 : _b2.entryFilter) ?? entryFilterFromList(baseNodeId);
16713
18797
  let entryIndices;
16714
18798
  if (entryFilter && entryFilter.mode === "range" && entryFilter.range) {
16715
18799
  const parsed = parseEntryRange(entryFilter.range, N, entryMetaFromList(baseNodeId));
@@ -17166,7 +19250,7 @@ function findAllFlowStacks(pageChildren) {
17166
19250
  return result;
17167
19251
  }
17168
19252
  function findNestedFlowStack(entry) {
17169
- var _a2, _b;
19253
+ var _a2, _b2;
17170
19254
  const queue = [...entry.children ?? []];
17171
19255
  while (queue.length > 0) {
17172
19256
  const node = queue.shift();
@@ -17174,7 +19258,7 @@ function findNestedFlowStack(entry) {
17174
19258
  const g = node;
17175
19259
  const hasRepeatableChildren = ((_a2 = g.children) == null ? void 0 : _a2.length) && (g.children.some(hasBaseNodeId) || g.children.some((c) => isGroup(c)));
17176
19260
  if (isVerticalStackLayoutMode(g.layoutMode) && hasRepeatableChildren) return g;
17177
- if ((_b = g.children) == null ? void 0 : _b.length) queue.push(...g.children);
19261
+ if ((_b2 = g.children) == null ? void 0 : _b2.length) queue.push(...g.children);
17178
19262
  }
17179
19263
  return null;
17180
19264
  }
@@ -17305,9 +19389,9 @@ function splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overf
17305
19389
  return { stayChildren: newStay, overflowChildren: newOverflow };
17306
19390
  }
17307
19391
  function paginateSinglePage(sourcePage, pageOffsetIndex) {
17308
- var _a2, _b, _c, _d, _e, _f;
19392
+ var _a2, _b2, _c, _d, _e, _f;
17309
19393
  const contentTop = (_a2 = sourcePage.settings) == null ? void 0 : _a2.contentTop;
17310
- const contentBottom = (_b = sourcePage.settings) == null ? void 0 : _b.contentBottom;
19394
+ const contentBottom = (_b2 = sourcePage.settings) == null ? void 0 : _b2.contentBottom;
17311
19395
  if (contentTop == null || contentBottom == null || contentBottom <= contentTop) {
17312
19396
  return [sourcePage];
17313
19397
  }
@@ -18497,7 +20581,7 @@ function splitIntoRuns(text, mainSupportsChar) {
18497
20581
  return runs;
18498
20582
  }
18499
20583
  function rewriteSvgFontsForJsPDF(svgStr) {
18500
- var _a2, _b;
20584
+ var _a2, _b2;
18501
20585
  const parser = new DOMParser();
18502
20586
  const doc = parser.parseFromString(svgStr, "image/svg+xml");
18503
20587
  const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
@@ -18602,7 +20686,7 @@ function rewriteSvgFontsForJsPDF(svgStr) {
18602
20686
  for (const node of childNodes) {
18603
20687
  if (node.nodeType !== 3 || !node.textContent) continue;
18604
20688
  const runs = splitIntoRuns(node.textContent, mainSupportsChar);
18605
- if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) === "main") continue;
20689
+ if (runs.length <= 1 && ((_b2 = runs[0]) == null ? void 0 : _b2.runType) === "main") continue;
18606
20690
  const fragment = doc.createDocumentFragment();
18607
20691
  for (const run of runs) {
18608
20692
  const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
@@ -18912,9 +20996,9 @@ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
18912
20996
  styleEl.appendChild(document.createTextNode(cssText));
18913
20997
  }
18914
20998
  function resolveHarnessFontProxyUrl() {
18915
- var _a2, _b;
20999
+ var _a2, _b2;
18916
21000
  try {
18917
- 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)) || "";
21001
+ 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)) || "";
18918
21002
  const base = String(runtimeBase || "").replace(/\/$/, "");
18919
21003
  return base ? `${base}/functions/v1/font-proxy` : "";
18920
21004
  } catch {
@@ -19576,7 +21660,7 @@ async function resolveTemplateData(options) {
19576
21660
  };
19577
21661
  }
19578
21662
  async function resolveFromForm(options) {
19579
- var _a2, _b, _c;
21663
+ var _a2, _b2, _c;
19580
21664
  const { templateId, formSchemaId, sectionState, flatFormData: directFlatFormData, themeId, supabaseUrl, supabaseAnonKey, prefetched } = options;
19581
21665
  const hasSectionStateInput = !!sectionState && Object.keys(sectionState).length > 0;
19582
21666
  if (!formSchemaId && !hasSectionStateInput) {
@@ -19626,7 +21710,7 @@ async function resolveFromForm(options) {
19626
21710
  inferredSections = inferFormSchemaFromTemplate(
19627
21711
  templateConfig.dynamicFields,
19628
21712
  groups,
19629
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
21713
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
19630
21714
  );
19631
21715
  } else {
19632
21716
  inferredSections = [];
@@ -19813,10 +21897,10 @@ function themeBaseId(id) {
19813
21897
  return out;
19814
21898
  }
19815
21899
  function applyThemeVariantToConfig(config, themeConfig, themeId) {
19816
- var _a2, _b, _c, _d;
21900
+ var _a2, _b2, _c, _d;
19817
21901
  if (!themeConfig) return config;
19818
21902
  const variant = themeId && themeId !== "default" ? (_a2 = themeConfig.variants) == null ? void 0 : _a2.find((v) => v.id === themeId) : null;
19819
- const shouldApplyDefaults = !variant && ((_b = themeConfig.properties) == null ? void 0 : _b.length);
21903
+ const shouldApplyDefaults = !variant && ((_b2 = themeConfig.properties) == null ? void 0 : _b2.length);
19820
21904
  if (!variant && !shouldApplyDefaults) return config;
19821
21905
  if (!((_c = themeConfig.properties) == null ? void 0 : _c.length)) return config;
19822
21906
  const result = JSON.parse(JSON.stringify(config));
@@ -19837,8 +21921,8 @@ function applyThemeVariantToConfig(config, themeConfig, themeId) {
19837
21921
  if (stopMatch) {
19838
21922
  const stopIndex = parseInt(stopMatch[1], 10);
19839
21923
  result.pages.forEach((p) => {
19840
- var _a3, _b2;
19841
- if ((_b2 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b2[stopIndex]) {
21924
+ var _a3, _b3;
21925
+ if ((_b3 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b3[stopIndex]) {
19842
21926
  p.settings.backgroundGradient = {
19843
21927
  ...p.settings.backgroundGradient,
19844
21928
  stops: p.settings.backgroundGradient.stops.map(
@@ -19915,7 +21999,7 @@ function normalizeLayoutModes(config) {
19915
21999
  }
19916
22000
  }
19917
22001
  function paintRepeatableSections(config, repeatableSections, formSchema) {
19918
- var _a2, _b;
22002
+ var _a2, _b2;
19919
22003
  const pages = config.pages ?? [];
19920
22004
  const entryFilters = (formSchema == null ? void 0 : formSchema.entryFilters) ?? [];
19921
22005
  const entryFilterBases = new Set(entryFilters.map((f) => baseId(f.nodeId)));
@@ -19943,7 +22027,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
19943
22027
  return painted;
19944
22028
  }
19945
22029
  for (const section of repeatableSections) {
19946
- const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b = section.entryFilter.range) == null ? void 0 : _b.trim() : void 0;
22030
+ const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b2 = section.entryFilter.range) == null ? void 0 : _b2.trim() : void 0;
19947
22031
  const shouldUseInlineFilter = !!inlineRange && !entryFilterBases.has(baseId(section.nodeId));
19948
22032
  const payload = { label: section.label };
19949
22033
  if (section.minEntries !== void 0) payload.minEntries = section.minEntries;
@@ -19979,7 +22063,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
19979
22063
  }
19980
22064
  }
19981
22065
  async function getTemplateForm(options) {
19982
- var _a2, _b;
22066
+ var _a2, _b2;
19983
22067
  const { templateId, supabaseUrl, supabaseAnonKey } = options;
19984
22068
  if (!supabaseUrl || !supabaseAnonKey) {
19985
22069
  throw new Error("[getTemplateForm] supabaseUrl and supabaseAnonKey are required");
@@ -20023,7 +22107,7 @@ async function getTemplateForm(options) {
20023
22107
  sections = inferFormSchemaFromTemplate(
20024
22108
  templateConfig.dynamicFields,
20025
22109
  templateConfig.fieldGroups || [],
20026
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
22110
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
20027
22111
  );
20028
22112
  } else {
20029
22113
  sections = [];
@@ -20407,7 +22491,7 @@ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExact
20407
22491
  return out;
20408
22492
  }
20409
22493
  function PixldocsPreview(props) {
20410
- var _a2, _b;
22494
+ var _a2, _b2;
20411
22495
  const {
20412
22496
  pageIndex = 0,
20413
22497
  zoom = 1,
@@ -20475,10 +22559,10 @@ function PixldocsPreview(props) {
20475
22559
  supabaseUrl: p.supabaseUrl,
20476
22560
  supabaseAnonKey: p.supabaseAnonKey
20477
22561
  }).then((resolved) => {
20478
- var _a3, _b2;
22562
+ var _a3, _b3;
20479
22563
  if (!cancelled) {
20480
22564
  console.log(PREVIEW_DEBUG_PREFIX, "resolve-done", {
20481
- pages: ((_b2 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b2.length) ?? 0,
22565
+ pages: ((_b3 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b3.length) ?? 0,
20482
22566
  underlinedNodes: countUnderlinedNodes(resolved.config)
20483
22567
  });
20484
22568
  setResolvedConfig(resolved.config);
@@ -20588,7 +22672,7 @@ function PixldocsPreview(props) {
20588
22672
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
20589
22673
  const bgTint = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.background) ?? "rgba(255,255,255,0.12)";
20590
22674
  const canvasW = getNum((_a2 = config == null ? void 0 : config.canvas) == null ? void 0 : _a2.width, 0);
20591
- const canvasH = getNum((_b = config == null ? void 0 : config.canvas) == null ? void 0 : _b.height, 0);
22675
+ const canvasH = getNum((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.height, 0);
20592
22676
  const hasOverlays = frostedBounds.length > 0 && canvasW > 0 && canvasH > 0;
20593
22677
  if (isLoading) {
20594
22678
  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..." }) });
@@ -20729,7 +22813,7 @@ function ensureFabricGradientDef(doc, obj, width, height) {
20729
22813
  return `url(#${id})`;
20730
22814
  }
20731
22815
  function warpTextboxSvgAlongPath(svg, obj) {
20732
- var _a2, _b, _c, _d, _e;
22816
+ var _a2, _b2, _c, _d, _e;
20733
22817
  const tp = obj == null ? void 0 : obj.textPath;
20734
22818
  if (!tp || !tp.preset || tp.preset === "none") return svg;
20735
22819
  if (tp.preset === "rise" || tp.preset === "angle") {
@@ -20793,7 +22877,7 @@ function warpTextboxSvgAlongPath(svg, obj) {
20793
22877
  }
20794
22878
  for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
20795
22879
  try {
20796
- (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
22880
+ (_b2 = clone.parentNode) == null ? void 0 : _b2.removeChild(clone);
20797
22881
  } catch {
20798
22882
  }
20799
22883
  }
@@ -20813,9 +22897,9 @@ function warpTextboxSvgAlongPath(svg, obj) {
20813
22897
  const fw = obj.fontWeight ?? 400;
20814
22898
  const fst = obj.fontStyle || "normal";
20815
22899
  const readFill = (el) => {
20816
- var _a3, _b2;
22900
+ var _a3, _b3;
20817
22901
  if (!el) return "";
20818
- 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()) || "";
22902
+ 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()) || "";
20819
22903
  };
20820
22904
  const w = Number(obj.width) || 0;
20821
22905
  const h = Number(obj.height) || 0;
@@ -21168,9 +23252,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21168
23252
  }
21169
23253
  return svgString;
21170
23254
  }
21171
- const resolvedPackageVersion = "0.5.240";
23255
+ const resolvedPackageVersion = "0.5.242";
21172
23256
  const PACKAGE_VERSION = resolvedPackageVersion;
21173
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.240";
23257
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.242";
21174
23258
  const roundParityValue = (value) => {
21175
23259
  if (typeof value !== "number") return value;
21176
23260
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21347,11 +23431,11 @@ function installUnderlineFix(fab) {
21347
23431
  if (!TextProto || typeof TextProto._renderTextDecoration !== "function") return;
21348
23432
  const original = TextProto._renderTextDecoration;
21349
23433
  const measureLineTextWidth = (obj, ctx, lineIndex) => {
21350
- var _a3, _b, _c, _d, _e, _f;
23434
+ var _a3, _b2, _c, _d, _e, _f;
21351
23435
  const rawLine = (_a3 = obj._textLines) == null ? void 0 : _a3[lineIndex];
21352
23436
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
21353
23437
  if (!lineText) return 0;
21354
- const fontSize = Number(((_b = obj.getValueOfPropertyAt) == null ? void 0 : _b.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
23438
+ const fontSize = Number(((_b2 = obj.getValueOfPropertyAt) == null ? void 0 : _b2.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
21355
23439
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
21356
23440
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
21357
23441
  const fontFamily = String(((_e = obj.getValueOfPropertyAt) == null ? void 0 : _e.call(obj, lineIndex, 0, "fontFamily")) ?? obj.fontFamily ?? "sans-serif");
@@ -21491,11 +23575,78 @@ function installTextboxBoxExtensions(fab) {
21491
23575
  if (Array.isArray(stateProps2)) {
21492
23576
  if (!stateProps2.includes("minBoxHeight")) stateProps2.push("minBoxHeight");
21493
23577
  if (!stateProps2.includes("verticalAlign")) stateProps2.push("verticalAlign");
23578
+ if (!stateProps2.includes("smartWrap")) stateProps2.push("smartWrap");
21494
23579
  }
21495
23580
  const cacheProps2 = TextboxProto2.cacheProperties;
21496
23581
  if (Array.isArray(cacheProps2)) {
21497
23582
  if (!cacheProps2.includes("minBoxHeight")) cacheProps2.push("minBoxHeight");
21498
23583
  if (!cacheProps2.includes("verticalAlign")) cacheProps2.push("verticalAlign");
23584
+ if (!cacheProps2.includes("smartWrap")) cacheProps2.push("smartWrap");
23585
+ }
23586
+ TextboxProto2.smartWrap = true;
23587
+ if (typeof TextboxProto2._wrapLine === "function" && !TextboxProto2.__pixldocsOrigWrapLine) {
23588
+ const origWrapLine = TextboxProto2._wrapLine;
23589
+ TextboxProto2.__pixldocsOrigWrapLine = origWrapLine;
23590
+ TextboxProto2._wrapLine = function(lineIndex, desiredWidth, ref, reservedSpace = 0) {
23591
+ var _a3;
23592
+ if (!this.smartWrap || this.splitByGrapheme) {
23593
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23594
+ }
23595
+ try {
23596
+ const additionalSpace = this._getWidthOfCharSpacing();
23597
+ const data = ((_a3 = ref == null ? void 0 : ref.wordsData) == null ? void 0 : _a3[lineIndex]) || [];
23598
+ const effective = Math.max(1, desiredWidth - reservedSpace);
23599
+ const infix = " ";
23600
+ const infixWidth = this._measureWord([infix], lineIndex, 0);
23601
+ const graphemeLines = [];
23602
+ let line = [];
23603
+ let lineWidth = 0;
23604
+ let lineJustStarted = true;
23605
+ let largestWordWidth = 0;
23606
+ let offset = 0;
23607
+ const pushLine = () => {
23608
+ graphemeLines.push(line);
23609
+ line = [];
23610
+ lineWidth = 0;
23611
+ lineJustStarted = true;
23612
+ };
23613
+ for (let i = 0; i < data.length; i++) {
23614
+ const { word, width: wordWidth } = data[i];
23615
+ if (wordWidth <= effective) {
23616
+ if (wordWidth > largestWordWidth) largestWordWidth = wordWidth;
23617
+ const projected = lineJustStarted ? wordWidth : lineWidth + infixWidth + wordWidth - additionalSpace;
23618
+ if (!lineJustStarted && projected > effective) pushLine();
23619
+ if (!lineJustStarted) {
23620
+ line.push(infix);
23621
+ lineWidth += infixWidth;
23622
+ }
23623
+ line = line.concat(word);
23624
+ lineWidth += wordWidth;
23625
+ lineJustStarted = false;
23626
+ } else {
23627
+ if (!lineJustStarted) pushLine();
23628
+ for (const g of word) {
23629
+ const gw = this._measureWord([g], lineIndex, offset);
23630
+ if (gw > largestWordWidth) largestWordWidth = gw;
23631
+ const gProj = lineJustStarted ? gw : lineWidth + gw - additionalSpace;
23632
+ if (!lineJustStarted && gProj > effective) pushLine();
23633
+ line.push(g);
23634
+ lineWidth += gw;
23635
+ lineJustStarted = false;
23636
+ offset++;
23637
+ }
23638
+ offset -= word.length;
23639
+ }
23640
+ offset += word.length + 1;
23641
+ }
23642
+ if (line.length) graphemeLines.push(line);
23643
+ const minNeeded = Math.max(0, Math.min(largestWordWidth, effective) - additionalSpace + reservedSpace);
23644
+ if (minNeeded > this.dynamicMinWidth) this.dynamicMinWidth = minNeeded;
23645
+ return graphemeLines;
23646
+ } catch (e) {
23647
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23648
+ }
23649
+ };
21499
23650
  }
21500
23651
  TextboxProto2.__pixldocsTextboxExtended = true;
21501
23652
  __textboxBoxExtensionsInstalled = true;
@@ -21917,7 +24068,7 @@ class PixldocsRenderer {
21917
24068
  await this.waitForCanvasScene(container, cloned, i);
21918
24069
  }
21919
24070
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
21920
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-D1yCN3sm.cjs"));
24071
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-De9vylBF.cjs"));
21921
24072
  const prepared = preparePagesForExport(
21922
24073
  cloned.pages,
21923
24074
  canvasWidth,
@@ -22134,9 +24285,9 @@ class PixldocsRenderer {
22134
24285
  }
22135
24286
  waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
22136
24287
  return new Promise((resolve) => {
22137
- var _a2, _b;
24288
+ var _a2, _b2;
22138
24289
  const start = Date.now();
22139
- const pageHasContent = (((_b = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b.length) ?? 0) > 0;
24290
+ const pageHasContent = (((_b2 = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) ?? 0) > 0;
22140
24291
  const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
22141
24292
  const check = () => {
22142
24293
  const fabricCanvas = this.getFabricCanvasFromContainer(container);
@@ -22211,9 +24362,9 @@ class PixldocsRenderer {
22211
24362
  return normalized;
22212
24363
  }
22213
24364
  paintPageBackground(ctx, page, width, height) {
22214
- var _a2, _b;
24365
+ var _a2, _b2;
22215
24366
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22216
- const gradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
24367
+ const gradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22217
24368
  ctx.clearRect(0, 0, width, height);
22218
24369
  ctx.fillStyle = backgroundColor;
22219
24370
  ctx.fillRect(0, 0, width, height);
@@ -22461,7 +24612,7 @@ class PixldocsRenderer {
22461
24612
  };
22462
24613
  const onReady = () => {
22463
24614
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
22464
- var _a2, _b;
24615
+ var _a2, _b2;
22465
24616
  try {
22466
24617
  const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
22467
24618
  await this.waitForCanvasImages(container, expectedImageIds);
@@ -22480,7 +24631,7 @@ class PixldocsRenderer {
22480
24631
  );
22481
24632
  const page = renderConfig.pages[pageIndex];
22482
24633
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22483
- const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
24634
+ const backgroundGradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22484
24635
  cleanup();
22485
24636
  resolve({
22486
24637
  svg: svgString,
@@ -22601,7 +24752,7 @@ class PixldocsRenderer {
22601
24752
  logJsonLine("[canvas-renderer][fabric-text-parity]", { stage, textboxes: sample.length, sample });
22602
24753
  }
22603
24754
  async waitForStableTextMetrics(container, config, options = {}) {
22604
- var _a2, _b, _c;
24755
+ var _a2, _b2, _c;
22605
24756
  if (typeof document !== "undefined") {
22606
24757
  void ensureFontsForResolvedConfig(config);
22607
24758
  await this.waitForRelevantFonts(config);
@@ -22643,7 +24794,7 @@ class PixldocsRenderer {
22643
24794
  }
22644
24795
  fabricInstance.getObjects().forEach(primeCharBounds);
22645
24796
  (_a2 = fabricInstance.calcOffset) == null ? void 0 : _a2.call(fabricInstance);
22646
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
24797
+ (_b2 = fabricInstance.renderAll) == null ? void 0 : _b2.call(fabricInstance);
22647
24798
  await waitForPaint();
22648
24799
  (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
22649
24800
  await waitForPaint();
@@ -22684,7 +24835,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22684
24835
  };
22685
24836
  logParityJson(tag, stage, { kind: "summary", ...summary });
22686
24837
  const sample = texts.slice(0, maxItems).map((t, idx) => {
22687
- var _a2, _b;
24838
+ var _a2, _b2;
22688
24839
  const tspans = Array.from(t.querySelectorAll("tspan"));
22689
24840
  const tspanInfo = tspans.slice(0, 8).map((s) => ({
22690
24841
  x: s.getAttribute("x"),
@@ -22701,7 +24852,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22701
24852
  while (cursor && !containerWidth) {
22702
24853
  containerWidth = (_a2 = cursor.getAttribute) == null ? void 0 : _a2.call(cursor, "width");
22703
24854
  cursor = cursor.parentElement;
22704
- if (cursor && ((_b = cursor.tagName) == null ? void 0 : _b.toLowerCase()) === "svg") break;
24855
+ if (cursor && ((_b2 = cursor.tagName) == null ? void 0 : _b2.toLowerCase()) === "svg") break;
22705
24856
  }
22706
24857
  return {
22707
24858
  idx,
@@ -23089,10 +25240,10 @@ function normalizeSvgExplicitColors(svg) {
23089
25240
  return normalizeGradientPaintRef(v);
23090
25241
  };
23091
25242
  const hasExplicitNonePaint = (el, attr) => {
23092
- var _a2, _b;
25243
+ var _a2, _b2;
23093
25244
  const raw = (el.getAttribute(attr) ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue(attr)) ?? "").trim().toLowerCase();
23094
25245
  if (raw === "none" || raw === "transparent") return true;
23095
- const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b = el.style) == null ? void 0 : _b.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
25246
+ const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
23096
25247
  return rawOpacity === "0" || rawOpacity === "0%" || rawOpacity === "0.0";
23097
25248
  };
23098
25249
  function walk(el, parentFill, parentStroke, parentColor) {
@@ -23159,14 +25310,14 @@ function normalizeSvgExplicitColors(svg) {
23159
25310
  function bakeGroupOpacityIntoChildren(svg) {
23160
25311
  const DRAWABLE = /* @__PURE__ */ new Set(["path", "rect", "circle", "ellipse", "polygon", "polyline", "line", "text", "tspan"]);
23161
25312
  function walkAndBake(el, inheritedOpacity) {
23162
- var _a2, _b, _c, _d;
25313
+ var _a2, _b2, _c, _d;
23163
25314
  if (isInSvgDefinitionSubtree(el)) {
23164
25315
  for (let i = 0; i < el.children.length; i++) walkAndBake(el.children[i], 1);
23165
25316
  return;
23166
25317
  }
23167
25318
  const tag = ((_a2 = el.tagName) == null ? void 0 : _a2.toLowerCase()) ?? "";
23168
25319
  const opacityAttr = parseSvgOpacity(el.getAttribute("opacity"));
23169
- const styleOpacity = parseSvgOpacity(((_b = el.style) == null ? void 0 : _b.getPropertyValue("opacity")) || null);
25320
+ const styleOpacity = parseSvgOpacity(((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("opacity")) || null);
23170
25321
  const ownOpacity = opacityAttr ?? styleOpacity ?? 1;
23171
25322
  const combinedOpacity = inheritedOpacity * ownOpacity;
23172
25323
  if (ownOpacity < 0.999 && tag !== "image") {
@@ -23689,10 +25840,10 @@ function getFirstExplicitColorFromSvg(svg) {
23689
25840
  return getGradientStopColorAsHex(svg, gradientId);
23690
25841
  };
23691
25842
  function walk(el) {
23692
- var _a2, _b;
25843
+ var _a2, _b2;
23693
25844
  if (fill && stroke) return;
23694
25845
  const f = el.getAttribute("fill") ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue("fill"));
23695
- const s = el.getAttribute("stroke") ?? ((_b = el.style) == null ? void 0 : _b.getPropertyValue("stroke"));
25846
+ const s = el.getAttribute("stroke") ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("stroke"));
23696
25847
  if (!fill) {
23697
25848
  if (isRealColor(f)) fill = f;
23698
25849
  else if (f) {
@@ -23720,8 +25871,8 @@ function isNearWhite(hex) {
23720
25871
  return r >= 250 && g >= 250 && b >= 250;
23721
25872
  }
23722
25873
  function getStopColorRaw(stop) {
23723
- var _a2, _b;
23724
- 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");
25874
+ var _a2, _b2;
25875
+ 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");
23725
25876
  }
23726
25877
  function getGradientStopColorAsHex(svgRoot, gradientId, visited = /* @__PURE__ */ new Set()) {
23727
25878
  var _a2;
@@ -23785,12 +25936,12 @@ async function convertTextDecorationsToLines(svg) {
23785
25936
  else el.removeAttribute("style");
23786
25937
  };
23787
25938
  const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
23788
- var _a2, _b;
25939
+ var _a2, _b2;
23789
25940
  let current = el;
23790
25941
  while (current) {
23791
25942
  const attrValue = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
23792
25943
  if (attrValue) return attrValue;
23793
- const styleValue = (_b = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b.trim();
25944
+ const styleValue = (_b2 = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b2.trim();
23794
25945
  if (styleValue) return styleValue;
23795
25946
  current = current.parentElement;
23796
25947
  }
@@ -23968,7 +26119,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
23968
26119
  }
23969
26120
  }
23970
26121
  async function rasterizeShadowMarkers(svg) {
23971
- var _a2, _b, _c, _d, _e, _f;
26122
+ var _a2, _b2, _c, _d, _e, _f;
23972
26123
  if (typeof window === "undefined" || typeof document === "undefined") return;
23973
26124
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
23974
26125
  if (markers.length === 0) return;
@@ -23992,7 +26143,7 @@ async function rasterizeShadowMarkers(svg) {
23992
26143
  const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
23993
26144
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
23994
26145
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
23995
- (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
26146
+ (_b2 = marker.parentNode) == null ? void 0 : _b2.removeChild(marker);
23996
26147
  continue;
23997
26148
  }
23998
26149
  const innerXml = restoreSourceFontsForShadowRaster(
@@ -24237,7 +26388,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24237
26388
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24238
26389
  sanitizeSvgTreeForPdf(svgToDraw);
24239
26390
  try {
24240
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-D1yCN3sm.cjs"));
26391
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-De9vylBF.cjs"));
24241
26392
  try {
24242
26393
  await logTextMeasurementDiagnostic(svgToDraw);
24243
26394
  } catch {
@@ -24253,7 +26404,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24253
26404
  }
24254
26405
  }
24255
26406
  function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundColor, backgroundGradient) {
24256
- var _a2, _b;
26407
+ var _a2, _b2;
24257
26408
  if (backgroundGradient && ((_a2 = backgroundGradient.stops) == null ? void 0 : _a2.length) >= 2) {
24258
26409
  const grad = backgroundGradient;
24259
26410
  const colorStops = grad.stops.map((s) => {
@@ -24308,7 +26459,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24308
26459
  doc.fill({ key: patternKey, matrix: doc.Matrix(1, 0, 0, 1, 0, 0) });
24309
26460
  });
24310
26461
  } catch {
24311
- const fallback = ((_b = colorStops[0]) == null ? void 0 : _b.color) || [255, 255, 255];
26462
+ const fallback = ((_b2 = colorStops[0]) == null ? void 0 : _b2.color) || [255, 255, 255];
24312
26463
  pdf.setFillColor(fallback[0], fallback[1], fallback[2]);
24313
26464
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
24314
26465
  }
@@ -24321,7 +26472,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24321
26472
  }
24322
26473
  }
24323
26474
  async function assemblePdfFromSvgs(svgResults, options = {}) {
24324
- var _a2, _b;
26475
+ var _a2, _b2;
24325
26476
  if (svgResults.length === 0) throw new Error("No pages to export");
24326
26477
  const { title, stripPageBackground } = options;
24327
26478
  const firstPage = svgResults[0];
@@ -24354,7 +26505,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
24354
26505
  const pageOrientation = page.width > page.height ? "landscape" : "portrait";
24355
26506
  pdf.addPage([page.width, page.height], pageOrientation);
24356
26507
  }
24357
- const hasGradient = !!((_b = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b.length);
26508
+ const hasGradient = !!((_b2 = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b2.length);
24358
26509
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
24359
26510
  const shouldStripBg = stripPageBackground ?? hasGradient;
24360
26511
  const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
@@ -24634,4 +26785,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
24634
26785
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
24635
26786
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
24636
26787
  exports.warmTemplateFromForm = warmTemplateFromForm;
24637
- //# sourceMappingURL=index-DBOS1ouN.cjs.map
26788
+ //# sourceMappingURL=index-YCh9sTg1.cjs.map