@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.
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- var _a;
4
+ var _a, _b;
5
5
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
6
6
  import { forwardRef, useRef, useState, useCallback, useMemo, useEffect, useImperativeHandle, createElement } from "react";
7
7
  import { flushSync } from "react-dom";
@@ -273,13 +273,13 @@ function lineToString(line) {
273
273
  return Array.isArray(line) ? line.join("") : String(line ?? "");
274
274
  }
275
275
  function measureTextLineWithCanvas(textbox, lineText, lineIndex) {
276
- var _a2, _b, _c, _d, _e;
276
+ var _a2, _b2, _c, _d, _e;
277
277
  if (!lineText) return 0;
278
278
  const ctx = getMeasureContext();
279
279
  if (!ctx) return null;
280
280
  const tb = textbox;
281
281
  const fontSize = Number(((_a2 = tb.getValueOfPropertyAt) == null ? void 0 : _a2.call(tb, lineIndex, 0, "fontSize")) ?? textbox.fontSize ?? 16);
282
- const fontStyle = String(((_b = tb.getValueOfPropertyAt) == null ? void 0 : _b.call(tb, lineIndex, 0, "fontStyle")) ?? textbox.fontStyle ?? "normal");
282
+ const fontStyle = String(((_b2 = tb.getValueOfPropertyAt) == null ? void 0 : _b2.call(tb, lineIndex, 0, "fontStyle")) ?? textbox.fontStyle ?? "normal");
283
283
  const fontWeight = String(((_c = tb.getValueOfPropertyAt) == null ? void 0 : _c.call(tb, lineIndex, 0, "fontWeight")) ?? textbox.fontWeight ?? "400");
284
284
  const fontFamily = String(((_d = tb.getValueOfPropertyAt) == null ? void 0 : _d.call(tb, lineIndex, 0, "fontFamily")) ?? textbox.fontFamily ?? "Open Sans");
285
285
  const charSpacing = Number(((_e = tb.getValueOfPropertyAt) == null ? void 0 : _e.call(tb, lineIndex, 0, "charSpacing")) ?? textbox.charSpacing ?? 0);
@@ -768,7 +768,7 @@ function resolveStackGroupEffectivePositions(group, pageChildren, options) {
768
768
  return out;
769
769
  }
770
770
  function groupBoundsFromChildren(group, pageChildren, options) {
771
- var _a2, _b;
771
+ var _a2, _b2;
772
772
  const kids = group.children ?? [];
773
773
  if (kids.length === 0) {
774
774
  const w = group.width;
@@ -784,7 +784,7 @@ function groupBoundsFromChildren(group, pageChildren, options) {
784
784
  return { width: b.width, height: b.height };
785
785
  })();
786
786
  const cl = positions ? ((_a2 = positions.get(child.id)) == null ? void 0 : _a2.left) ?? getNodeLeft(child) : getNodeLeft(child);
787
- const ct = positions ? ((_b = positions.get(child.id)) == null ? void 0 : _b.top) ?? getNodeTop(child) : getNodeTop(child);
787
+ const ct = positions ? ((_b2 = positions.get(child.id)) == null ? void 0 : _b2.top) ?? getNodeTop(child) : getNodeTop(child);
788
788
  minX = Math.min(minX, cl);
789
789
  minY = Math.min(minY, ct);
790
790
  maxX = Math.max(maxX, cl + sz.width);
@@ -822,14 +822,14 @@ function getNodeBoundsFromChildren(node) {
822
822
  return getNodeBounds(node);
823
823
  }
824
824
  function absoluteBoundsRecur(node, pageChildren, options) {
825
- var _a2, _b;
825
+ var _a2, _b2;
826
826
  const parent = pageChildren ? findParentGroup(pageChildren, node.id) : null;
827
827
  const b = getNodeBounds(node, pageChildren);
828
828
  if (!parent) return b;
829
829
  const parentAbs = absoluteBoundsRecur(parent, pageChildren);
830
830
  const isStackParent = isStackLayoutMode(parent.layoutMode);
831
831
  const inParentLeft = isStackParent ? ((_a2 = resolveStackGroupEffectivePositions(parent, pageChildren).get(node.id)) == null ? void 0 : _a2.left) ?? b.left : b.left;
832
- const inParentTop = isStackParent ? ((_b = resolveStackGroupEffectivePositions(parent, pageChildren).get(node.id)) == null ? void 0 : _b.top) ?? b.top : b.top;
832
+ const inParentTop = isStackParent ? ((_b2 = resolveStackGroupEffectivePositions(parent, pageChildren).get(node.id)) == null ? void 0 : _b2.top) ?? b.top : b.top;
833
833
  return {
834
834
  left: parentAbs.left + inParentLeft,
835
835
  top: parentAbs.top + inParentTop,
@@ -1009,7 +1009,7 @@ const defaultProjectSettings = {
1009
1009
  snapToGrid: false,
1010
1010
  gridSize: 20,
1011
1011
  snapToGuides: true,
1012
- snapThreshold: 5
1012
+ snapThreshold: 8
1013
1013
  };
1014
1014
  const defaultPageSettings = {
1015
1015
  backgroundColor: "#ffffff"
@@ -3547,7 +3547,7 @@ const clearFontCacheAndRerender = (canvas, options = {}) => {
3547
3547
  }
3548
3548
  };
3549
3549
  const collectUnderlineMetrics = (obj) => {
3550
- var _a2, _b;
3550
+ var _a2, _b2;
3551
3551
  if (obj instanceof fabric.Textbox) {
3552
3552
  if (!obj.underline) return [];
3553
3553
  const lineWidths = obj.__lineWidths;
@@ -3562,7 +3562,7 @@ const clearFontCacheAndRerender = (canvas, options = {}) => {
3562
3562
  height: obj.height ?? null,
3563
3563
  scaleX: obj.scaleX,
3564
3564
  scaleY: obj.scaleY,
3565
- lineCount: ((_b = obj.textLines) == null ? void 0 : _b.length) ?? 0,
3565
+ lineCount: ((_b2 = obj.textLines) == null ? void 0 : _b2.length) ?? 0,
3566
3566
  maxLineWidth: lineWidths && lineWidths.length > 0 ? Math.max(...lineWidths) : null
3567
3567
  }];
3568
3568
  }
@@ -3592,13 +3592,13 @@ const clearFontCacheAndRerender = (canvas, options = {}) => {
3592
3592
  canvas.requestRenderAll();
3593
3593
  };
3594
3594
  const ensureFontLoaded = async (fontFamily) => {
3595
- var _a2, _b, _c;
3595
+ var _a2, _b2, _c;
3596
3596
  if (!fontFamily) return;
3597
3597
  if (LOCAL_FONTS.has(fontFamily)) {
3598
3598
  try {
3599
3599
  const isLoaded = (_a2 = document.fonts) == null ? void 0 : _a2.check(`16px "${fontFamily}"`);
3600
3600
  if (isLoaded) return;
3601
- await ((_b = document.fonts) == null ? void 0 : _b.load(`16px "${fontFamily}"`));
3601
+ await ((_b2 = document.fonts) == null ? void 0 : _b2.load(`16px "${fontFamily}"`));
3602
3602
  await ((_c = document.fonts) == null ? void 0 : _c.load(`bold 16px "${fontFamily}"`));
3603
3603
  } catch (e) {
3604
3604
  console.warn(`Failed to ensure local font loaded: ${fontFamily}`, e);
@@ -4093,7 +4093,7 @@ function createImageClipPath(element, imgWidth, imgHeight) {
4093
4093
  }
4094
4094
  }
4095
4095
  async function loadImageAsync(element, placeholder, fc, fabricRef, syncLockedRef, isTransforming) {
4096
- var _a2, _b, _c, _d;
4096
+ var _a2, _b2, _c, _d;
4097
4097
  const imageUrl = element.src || element.imageUrl;
4098
4098
  if (!imageUrl) return;
4099
4099
  const nextSvgColorMap = element.svgColorMap ? JSON.stringify(element.svgColorMap) : "";
@@ -4257,7 +4257,7 @@ async function loadImageAsync(element, placeholder, fc, fabricRef, syncLockedRef
4257
4257
  if (existingCropGroup) {
4258
4258
  const existingImg = (_a2 = existingCropGroup.__cropData) == null ? void 0 : _a2._img;
4259
4259
  if (existingImg) {
4260
- panX = ((_b = existingImg._ct) == null ? void 0 : _b.panX) ?? existingImg.__panX ?? 0.5;
4260
+ panX = ((_b2 = existingImg._ct) == null ? void 0 : _b2.panX) ?? existingImg.__panX ?? 0.5;
4261
4261
  panY = ((_c = existingImg._ct) == null ? void 0 : _c.panY) ?? existingImg.__panY ?? 0.5;
4262
4262
  zoom = ((_d = existingImg._ct) == null ? void 0 : _d.zoom) ?? 1;
4263
4263
  }
@@ -4465,11 +4465,11 @@ function isLuminanceMaskClipPath(clipPath) {
4465
4465
  return Boolean(clipPath && clipPath.__svgMaskType === "luminance");
4466
4466
  }
4467
4467
  function syncSvgMaskClipPath(cropGroup) {
4468
- var _a2, _b;
4468
+ var _a2, _b2;
4469
4469
  const clipPath = cropGroup.clipPath;
4470
4470
  if (!isCropGroup(cropGroup)) return;
4471
4471
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4472
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4472
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4473
4473
  if (frameW <= 0 || frameH <= 0) return;
4474
4474
  if (isSvgMaskClipPath(clipPath)) {
4475
4475
  fitMaskGroupToFrame(clipPath, frameW, frameH);
@@ -4493,12 +4493,12 @@ async function detectMaskType(svgUrl) {
4493
4493
  }
4494
4494
  }
4495
4495
  async function applySvgMaskToCropGroup(cropGroup, svgUrl) {
4496
- var _a2, _b, _c;
4496
+ var _a2, _b2, _c;
4497
4497
  if (!isCropGroup(cropGroup)) {
4498
4498
  throw new Error("Selected object is not a crop group / image");
4499
4499
  }
4500
4500
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4501
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4501
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4502
4502
  if (frameW <= 0 || frameH <= 0) {
4503
4503
  throw new Error("Crop group has no frame dimensions");
4504
4504
  }
@@ -4584,12 +4584,12 @@ async function buildLuminanceAlphaCanvas(svgUrl, frameW, frameH) {
4584
4584
  return canvas;
4585
4585
  }
4586
4586
  async function applyLuminanceMaskToCropGroup(cropGroup, svgUrl) {
4587
- var _a2, _b, _c;
4587
+ var _a2, _b2, _c;
4588
4588
  if (!isCropGroup(cropGroup)) {
4589
4589
  throw new Error("Selected object is not a crop group / image");
4590
4590
  }
4591
4591
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4592
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4592
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4593
4593
  if (frameW <= 0 || frameH <= 0) {
4594
4594
  throw new Error("Crop group has no frame dimensions");
4595
4595
  }
@@ -4638,10 +4638,10 @@ function getAppliedSvgMaskType(obj) {
4638
4638
  return t === "luminance" || t === "shape" ? t : null;
4639
4639
  }
4640
4640
  function clearSvgMaskFromCropGroup(cropGroup) {
4641
- var _a2, _b, _c;
4641
+ var _a2, _b2, _c;
4642
4642
  if (!isCropGroup(cropGroup)) return;
4643
4643
  const frameW = ((_a2 = cropGroup._ct) == null ? void 0 : _a2.frameW) ?? cropGroup.width ?? 0;
4644
- const frameH = ((_b = cropGroup._ct) == null ? void 0 : _b.frameH) ?? cropGroup.height ?? 0;
4644
+ const frameH = ((_b2 = cropGroup._ct) == null ? void 0 : _b2.frameH) ?? cropGroup.height ?? 0;
4645
4645
  const rect = new fabric.Rect({
4646
4646
  width: frameW,
4647
4647
  height: frameH,
@@ -4675,17 +4675,16 @@ const svgMaskApply = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
4675
4675
  isSvgMaskClipPath,
4676
4676
  syncSvgMaskClipPath
4677
4677
  }, Symbol.toStringTag, { value: "Module" }));
4678
+ const SELECTION_BORDER_SCALE$1 = 2;
4678
4679
  function clamp$1(v, min, max) {
4679
4680
  return Math.max(min, Math.min(max, v));
4680
4681
  }
4681
4682
  function applyControlSizeForZoom(canvas, obj) {
4682
4683
  if (!canvas || !obj) return;
4683
- const z = canvas.getZoom() || 1;
4684
- const size = Math.max(6, Math.round(8 * z));
4685
4684
  obj.set({
4686
- cornerSize: size,
4687
- // Subtle but visible border at any zoom (min ~1.25px).
4688
- borderScaleFactor: Math.max(1.25, 1.25 * z)
4685
+ cornerSize: 10,
4686
+ touchCornerSize: 20,
4687
+ borderScaleFactor: SELECTION_BORDER_SCALE$1
4689
4688
  });
4690
4689
  obj.setCoords();
4691
4690
  }
@@ -5062,6 +5061,21 @@ function resizeFrameFromSide(g, side, localDx, localDy) {
5062
5061
  function installCanvaMaskControls(g) {
5063
5062
  const ct = g.__cropData;
5064
5063
  if (ct) ct.fit = ct.fit ?? "cover";
5064
+ const controlStyle = {
5065
+ cornerSize: 10,
5066
+ touchCornerSize: 20,
5067
+ borderScaleFactor: SELECTION_BORDER_SCALE$1,
5068
+ transparentCorners: false,
5069
+ cornerStyle: "circle",
5070
+ cornerColor: "hsl(217, 91%, 60%)",
5071
+ cornerStrokeColor: "#ffffff",
5072
+ borderColor: "hsl(217, 91%, 60%)"
5073
+ };
5074
+ g.set(controlStyle);
5075
+ const notifyResizeSnap = (target, corner) => {
5076
+ const handler = target.__resizeSnapHandler;
5077
+ if (typeof handler === "function") handler(target, corner);
5078
+ };
5065
5079
  g.setControlsVisibility({
5066
5080
  mt: true,
5067
5081
  mb: true,
@@ -5091,6 +5105,7 @@ function installCanvaMaskControls(g) {
5091
5105
  const { localDx, localDy } = getLocalDeltaStable(t, eventData);
5092
5106
  t.__lockScaleDuringCrop = true;
5093
5107
  resizeFrameFromSide(t, side, localDx, localDy);
5108
+ notifyResizeSnap(t, side);
5094
5109
  (_a2 = t.canvas) == null ? void 0 : _a2.requestRenderAll();
5095
5110
  return true;
5096
5111
  }
@@ -5115,7 +5130,9 @@ function installCanvaMaskControls(g) {
5115
5130
  if (canvas && canvas.__editLockRef) {
5116
5131
  canvas.__editLockRef.current = true;
5117
5132
  }
5118
- return resizeFrameFromCornerUniform(eventData, transform);
5133
+ const resized = resizeFrameFromCornerUniform(eventData, transform);
5134
+ if (resized) notifyResizeSnap(t, key);
5135
+ return resized;
5119
5136
  }
5120
5137
  });
5121
5138
  };
@@ -5124,6 +5141,8 @@ function installCanvaMaskControls(g) {
5124
5141
  g.controls.bl = makeCornerControl("bl", -0.5, 0.5, "nesw-resize");
5125
5142
  g.controls.br = makeCornerControl("br", 0.5, 0.5, "nwse-resize");
5126
5143
  g.__hasCustomControls = true;
5144
+ g.set(controlStyle);
5145
+ g.setCoords();
5127
5146
  }
5128
5147
  async function createMaskedImageElement({
5129
5148
  url,
@@ -5694,12 +5713,15 @@ function exitCropMode(g, commit = true) {
5694
5713
  g.hasControls = true;
5695
5714
  g.borderDashArray = void 0;
5696
5715
  installCanvaMaskControls(g);
5697
- g.borderColor = "#4f46e5";
5716
+ g.borderColor = "hsl(217, 91%, 60%)";
5698
5717
  g.borderDashArray = void 0;
5699
- g.cornerColor = "#ffffff";
5700
- g.cornerStrokeColor = "#4f46e5";
5701
- g.cornerStyle = "rect";
5718
+ g.cornerColor = "hsl(217, 91%, 60%)";
5719
+ g.cornerStrokeColor = "#ffffff";
5720
+ g.cornerStyle = "circle";
5702
5721
  g.transparentCorners = false;
5722
+ g.cornerSize = 10;
5723
+ g.touchCornerSize = 20;
5724
+ g.borderScaleFactor = 2;
5703
5725
  g[CROP_MODE_FLAG] = false;
5704
5726
  g.__cropZoomLastPointer = void 0;
5705
5727
  g.__lastPointerForCrop = void 0;
@@ -5708,6 +5730,16 @@ function exitCropMode(g, commit = true) {
5708
5730
  }
5709
5731
  canvas == null ? void 0 : canvas.requestRenderAll();
5710
5732
  }
5733
+ function boundsToSnapPoints(bounds) {
5734
+ return {
5735
+ left: bounds.left,
5736
+ right: bounds.left + bounds.width,
5737
+ top: bounds.top,
5738
+ bottom: bounds.top + bounds.height,
5739
+ centerX: bounds.left + bounds.width / 2,
5740
+ centerY: bounds.top + bounds.height / 2
5741
+ };
5742
+ }
5711
5743
  function getObjectSnapPoints(obj) {
5712
5744
  try {
5713
5745
  obj.setCoords();
@@ -5737,7 +5769,8 @@ function getObjectSnapPoints(obj) {
5737
5769
  }
5738
5770
  function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5739
5771
  if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };
5740
- const threshold = snapThreshold || 5;
5772
+ const threshold = snapThreshold || 8;
5773
+ const centerThreshold = threshold * 1.75;
5741
5774
  const newGuides = [];
5742
5775
  const horizontalSnaps = [];
5743
5776
  const verticalSnaps = [];
@@ -5757,12 +5790,24 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5757
5790
  horizontalSnaps.push({ delta: targetPoint - objPoint, distance, guide });
5758
5791
  }
5759
5792
  };
5793
+ const addCanvasCenterVerticalSnap = (objPoint, targetPoint, guide) => {
5794
+ const distance = Math.abs(objPoint - targetPoint);
5795
+ if (distance < centerThreshold) {
5796
+ verticalSnaps.push({ delta: targetPoint - objPoint, distance: distance * 0.85, guide });
5797
+ }
5798
+ };
5799
+ const addCanvasCenterHorizontalSnap = (objPoint, targetPoint, guide) => {
5800
+ const distance = Math.abs(objPoint - targetPoint);
5801
+ if (distance < centerThreshold) {
5802
+ horizontalSnaps.push({ delta: targetPoint - objPoint, distance: distance * 0.85, guide });
5803
+ }
5804
+ };
5760
5805
  addVerticalSnap(moving.left, 0, { type: "vertical", position: 0 });
5761
5806
  addVerticalSnap(moving.right, canvasWidth, { type: "vertical", position: canvasWidth });
5762
5807
  addHorizontalSnap(moving.top, 0, { type: "horizontal", position: 0 });
5763
5808
  addHorizontalSnap(moving.bottom, canvasHeight, { type: "horizontal", position: canvasHeight });
5764
- addVerticalSnap(moving.centerX, canvasCenterX, { type: "vertical", position: canvasCenterX });
5765
- addHorizontalSnap(moving.centerY, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5809
+ addCanvasCenterVerticalSnap(moving.centerX, canvasCenterX, { type: "vertical", position: canvasCenterX });
5810
+ addCanvasCenterHorizontalSnap(moving.centerY, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5766
5811
  const rawObjects = canvas.getObjects();
5767
5812
  const allObjects = [];
5768
5813
  for (const obj of rawObjects) {
@@ -5782,16 +5827,22 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5782
5827
  }
5783
5828
  for (const otherObj of allObjects) {
5784
5829
  const other = getObjectSnapPoints(otherObj);
5785
- addVerticalSnap(moving.left, other.left, { type: "vertical", position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5786
- addVerticalSnap(moving.right, other.right, { type: "vertical", position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5787
- addVerticalSnap(moving.left, other.right, { type: "vertical", position: other.right, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5788
- addVerticalSnap(moving.right, other.left, { type: "vertical", position: other.left, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5789
- addVerticalSnap(moving.centerX, other.centerX, { type: "vertical", position: other.centerX, start: Math.min(moving.top, other.top), end: Math.max(moving.bottom, other.bottom) });
5790
- addHorizontalSnap(moving.top, other.top, { type: "horizontal", position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5791
- addHorizontalSnap(moving.bottom, other.bottom, { type: "horizontal", position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5792
- addHorizontalSnap(moving.top, other.bottom, { type: "horizontal", position: other.bottom, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5793
- addHorizontalSnap(moving.bottom, other.top, { type: "horizontal", position: other.top, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5794
- addHorizontalSnap(moving.centerY, other.centerY, { type: "horizontal", position: other.centerY, start: Math.min(moving.left, other.left), end: Math.max(moving.right, other.right) });
5830
+ const tb = {
5831
+ left: other.left,
5832
+ top: other.top,
5833
+ width: other.right - other.left,
5834
+ height: other.bottom - other.top
5835
+ };
5836
+ 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 });
5837
+ 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 });
5838
+ 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 });
5839
+ 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 });
5840
+ 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 });
5841
+ 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 });
5842
+ 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 });
5843
+ 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 });
5844
+ 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 });
5845
+ 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 });
5795
5846
  }
5796
5847
  let snapDx = 0;
5797
5848
  let snapDy = 0;
@@ -5799,19 +5850,129 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5799
5850
  verticalSnaps.sort((a, b) => a.distance - b.distance);
5800
5851
  const bestSnap = verticalSnaps[0];
5801
5852
  snapDx = bestSnap.delta;
5802
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5853
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5803
5854
  }
5804
5855
  if (horizontalSnaps.length > 0) {
5805
5856
  horizontalSnaps.sort((a, b) => a.distance - b.distance);
5806
5857
  const bestSnap = horizontalSnaps[0];
5807
5858
  snapDy = bestSnap.delta;
5808
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5859
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5860
+ }
5861
+ const projectedLeft = moving.left + snapDx;
5862
+ const projectedRight = moving.right + snapDx;
5863
+ moving.centerX + snapDx;
5864
+ const projectedTop = moving.top + snapDy;
5865
+ const projectedBottom = moving.bottom + snapDy;
5866
+ moving.centerY + snapDy;
5867
+ if (snapDx === 0) {
5868
+ let bestPair = null;
5869
+ for (const a of allObjects) {
5870
+ const A = getObjectSnapPoints(a);
5871
+ if (A.right > projectedLeft) continue;
5872
+ if (A.bottom < projectedTop || A.top > projectedBottom) continue;
5873
+ for (const b of allObjects) {
5874
+ if (b === a) continue;
5875
+ const B = getObjectSnapPoints(b);
5876
+ if (B.left < projectedRight) continue;
5877
+ if (B.bottom < projectedTop || B.top > projectedBottom) continue;
5878
+ const gapL = projectedLeft - A.right;
5879
+ const gapR = B.left - projectedRight;
5880
+ const diff = gapR - gapL;
5881
+ const absDiff = Math.abs(diff);
5882
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5883
+ bestPair = { A, B, delta: diff / 2, absDiff };
5884
+ }
5885
+ }
5886
+ }
5887
+ if (bestPair) {
5888
+ snapDx = bestPair.delta;
5889
+ const newLeft = moving.left + snapDx;
5890
+ const newRight = moving.right + snapDx;
5891
+ const equalGap = Math.round(newLeft - bestPair.A.right);
5892
+ const bracketY = (Math.max(bestPair.A.top, projectedTop, bestPair.B.top) + Math.min(bestPair.A.bottom, projectedBottom, bestPair.B.bottom)) / 2;
5893
+ if (equalGap > 0) {
5894
+ const aTb = { left: bestPair.A.left, top: bestPair.A.top, width: bestPair.A.right - bestPair.A.left, height: bestPair.A.bottom - bestPair.A.top };
5895
+ const bTb = { left: bestPair.B.left, top: bestPair.B.top, width: bestPair.B.right - bestPair.B.left, height: bestPair.B.bottom - bestPair.B.top };
5896
+ newGuides.push({
5897
+ type: "horizontal",
5898
+ position: bracketY,
5899
+ kind: "gap",
5900
+ gap: equalGap,
5901
+ start: bestPair.A.right,
5902
+ end: newLeft,
5903
+ bracketAt: bracketY,
5904
+ targetBoundsList: [aTb, bTb]
5905
+ });
5906
+ newGuides.push({
5907
+ type: "horizontal",
5908
+ position: bracketY,
5909
+ kind: "gap",
5910
+ gap: equalGap,
5911
+ start: newRight,
5912
+ end: bestPair.B.left,
5913
+ bracketAt: bracketY,
5914
+ targetBoundsList: [aTb, bTb]
5915
+ });
5916
+ }
5917
+ }
5918
+ }
5919
+ if (snapDy === 0) {
5920
+ let bestPair = null;
5921
+ for (const a of allObjects) {
5922
+ const A = getObjectSnapPoints(a);
5923
+ if (A.bottom > projectedTop) continue;
5924
+ if (A.right < projectedLeft || A.left > projectedRight) continue;
5925
+ for (const b of allObjects) {
5926
+ if (b === a) continue;
5927
+ const B = getObjectSnapPoints(b);
5928
+ if (B.top < projectedBottom) continue;
5929
+ if (B.right < projectedLeft || B.left > projectedRight) continue;
5930
+ const gapT = projectedTop - A.bottom;
5931
+ const gapB = B.top - projectedBottom;
5932
+ const diff = gapB - gapT;
5933
+ const absDiff = Math.abs(diff);
5934
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5935
+ bestPair = { A, B, delta: diff / 2, absDiff };
5936
+ }
5937
+ }
5938
+ }
5939
+ if (bestPair) {
5940
+ snapDy = bestPair.delta;
5941
+ const newTop = moving.top + snapDy;
5942
+ const newBottom = moving.bottom + snapDy;
5943
+ const equalGap = Math.round(newTop - bestPair.A.bottom);
5944
+ const bracketX = (Math.max(bestPair.A.left, projectedLeft, bestPair.B.left) + Math.min(bestPair.A.right, projectedRight, bestPair.B.right)) / 2;
5945
+ if (equalGap > 0) {
5946
+ const aTb = { left: bestPair.A.left, top: bestPair.A.top, width: bestPair.A.right - bestPair.A.left, height: bestPair.A.bottom - bestPair.A.top };
5947
+ const bTb = { left: bestPair.B.left, top: bestPair.B.top, width: bestPair.B.right - bestPair.B.left, height: bestPair.B.bottom - bestPair.B.top };
5948
+ newGuides.push({
5949
+ type: "vertical",
5950
+ position: bracketX,
5951
+ kind: "gap",
5952
+ gap: equalGap,
5953
+ start: bestPair.A.bottom,
5954
+ end: newTop,
5955
+ bracketAt: bracketX,
5956
+ targetBoundsList: [aTb, bTb]
5957
+ });
5958
+ newGuides.push({
5959
+ type: "vertical",
5960
+ position: bracketX,
5961
+ kind: "gap",
5962
+ gap: equalGap,
5963
+ start: newBottom,
5964
+ end: bestPair.B.top,
5965
+ bracketAt: bracketX,
5966
+ targetBoundsList: [aTb, bTb]
5967
+ });
5968
+ }
5969
+ }
5809
5970
  }
5810
5971
  return { guides: newGuides, snapDx, snapDy };
5811
5972
  }
5812
- function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5973
+ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold, additionalBounds = []) {
5813
5974
  if (!snapToGuides) return [];
5814
- const threshold = snapThreshold || 5;
5975
+ const threshold = snapThreshold;
5815
5976
  const newGuides = [];
5816
5977
  const scaling = getObjectSnapPoints(scalingObj);
5817
5978
  const scalingId = getObjectId(scalingObj);
@@ -5824,7 +5985,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5824
5985
  const checkVerticalSnap = (edgePosition, targetPosition, guide) => {
5825
5986
  const dist = Math.abs(edgePosition - targetPosition);
5826
5987
  if (dist < threshold) {
5827
- newGuides.push({ ...guide, distance: Math.round(dist) });
5988
+ newGuides.push({ ...guide, kind: "alignment" });
5828
5989
  return true;
5829
5990
  }
5830
5991
  return false;
@@ -5832,7 +5993,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5832
5993
  const checkHorizontalSnap = (edgePosition, targetPosition, guide) => {
5833
5994
  const dist = Math.abs(edgePosition - targetPosition);
5834
5995
  if (dist < threshold) {
5835
- newGuides.push({ ...guide, distance: Math.round(dist) });
5996
+ newGuides.push({ ...guide, kind: "alignment" });
5836
5997
  return true;
5837
5998
  }
5838
5999
  return false;
@@ -5854,12 +6015,39 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5854
6015
  checkHorizontalSnap(scaling.bottom, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5855
6016
  }
5856
6017
  const rawObjects = canvas.getObjects();
6018
+ const objectBounds = [];
5857
6019
  for (const obj of rawObjects) {
5858
6020
  if (obj === scalingObj) continue;
5859
6021
  const objId = getObjectId(obj);
5860
6022
  if (objId === "__background__") continue;
5861
6023
  if (objId && objId === scalingId) continue;
5862
- const other = getObjectSnapPoints(obj);
6024
+ if (scalingObj instanceof fabric.ActiveSelection && scalingObj.contains(obj)) continue;
6025
+ if (scalingObj instanceof fabric.Group && typeof scalingObj.getObjects === "function") {
6026
+ const inner = scalingObj.getObjects();
6027
+ if (inner.includes(obj)) continue;
6028
+ }
6029
+ objectBounds.push(getObjectSnapPoints(obj));
6030
+ }
6031
+ for (const bounds of additionalBounds) {
6032
+ objectBounds.push(boundsToSnapPoints(bounds));
6033
+ }
6034
+ let bestWidthMatch = null;
6035
+ let bestWidthDiff = threshold + 1;
6036
+ let bestHeightMatch = null;
6037
+ let bestHeightDiff = threshold + 1;
6038
+ for (const other of objectBounds) {
6039
+ const otherWidth = other.right - other.left;
6040
+ const otherHeight = other.bottom - other.top;
6041
+ const widthDiff = Math.abs(scaling.right - scaling.left - otherWidth);
6042
+ const heightDiff = Math.abs(scaling.bottom - scaling.top - otherHeight);
6043
+ if (resizingLeft !== resizingRight && widthDiff < bestWidthDiff) {
6044
+ bestWidthDiff = widthDiff;
6045
+ bestWidthMatch = other;
6046
+ }
6047
+ if (resizingTop !== resizingBottom && heightDiff < bestHeightDiff) {
6048
+ bestHeightDiff = heightDiff;
6049
+ bestHeightMatch = other;
6050
+ }
5863
6051
  if (resizingLeft) {
5864
6052
  checkVerticalSnap(scaling.left, other.left, {
5865
6053
  type: "vertical",
@@ -5941,6 +6129,18 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5941
6129
  });
5942
6130
  }
5943
6131
  }
6132
+ if (bestWidthMatch && bestWidthDiff < threshold) {
6133
+ newGuides.push(
6134
+ { type: "horizontal", position: scaling.top, start: scaling.left, end: scaling.right, kind: "alignment" },
6135
+ { type: "horizontal", position: bestWidthMatch.top, start: bestWidthMatch.left, end: bestWidthMatch.right, kind: "alignment" }
6136
+ );
6137
+ }
6138
+ if (bestHeightMatch && bestHeightDiff < threshold) {
6139
+ newGuides.push(
6140
+ { type: "vertical", position: scaling.left, start: scaling.top, end: scaling.bottom, kind: "alignment" },
6141
+ { type: "vertical", position: bestHeightMatch.left, start: bestHeightMatch.top, end: bestHeightMatch.bottom, kind: "alignment" }
6142
+ );
6143
+ }
5944
6144
  const seen = /* @__PURE__ */ new Set();
5945
6145
  return newGuides.filter((guide) => {
5946
6146
  const key = `${guide.type}-${guide.position.toFixed(1)}`;
@@ -5949,6 +6149,349 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5949
6149
  return true;
5950
6150
  });
5951
6151
  }
6152
+ const MIN_SNAP_BOX = 20;
6153
+ const SNAP_HYSTERESIS_PX = 6;
6154
+ function applyScaleSnapToBox(box, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold, excludeObjectId, options) {
6155
+ var _a2, _b2, _c, _d, _e, _f, _g, _h;
6156
+ const hysteresis = (options == null ? void 0 : options.hysteresis) ?? SNAP_HYSTERESIS_PX;
6157
+ const activeSnapRef = options == null ? void 0 : options.activeSnapRef;
6158
+ const roundSnappedOnly = (options == null ? void 0 : options.roundSnappedOnly) ?? false;
6159
+ if (!snapToGuides || !canvas) {
6160
+ if (roundSnappedOnly) return { ...box };
6161
+ return {
6162
+ left: Math.round(box.left),
6163
+ top: Math.round(box.top),
6164
+ width: Math.round(box.width),
6165
+ height: Math.round(box.height)
6166
+ };
6167
+ }
6168
+ const threshold = snapThreshold || 5;
6169
+ const releaseDist = threshold + hysteresis;
6170
+ const matchDimensions = (options == null ? void 0 : options.matchDimensions) ?? true;
6171
+ const excludedIds = new Set((options == null ? void 0 : options.excludeObjectIds) ?? []);
6172
+ if (excludeObjectId) excludedIds.add(excludeObjectId);
6173
+ const right = box.left + box.width;
6174
+ const bottom = box.top + box.height;
6175
+ const canvasCenterX = canvasWidth / 2;
6176
+ const canvasCenterY = canvasHeight / 2;
6177
+ const resizingLeft = corner.includes("l");
6178
+ const resizingRight = corner.includes("r");
6179
+ const resizingTop = corner.includes("t");
6180
+ const resizingBottom = corner.includes("b");
6181
+ const verticalTargets = [0, canvasWidth, canvasCenterX];
6182
+ const horizontalTargets = [0, canvasHeight, canvasCenterY];
6183
+ const widthTargets = [];
6184
+ const heightTargets = [];
6185
+ if ((_a2 = options == null ? void 0 : options.verticalTargetsExtra) == null ? void 0 : _a2.length) verticalTargets.push(...options.verticalTargetsExtra);
6186
+ if ((_b2 = options == null ? void 0 : options.horizontalTargetsExtra) == null ? void 0 : _b2.length) horizontalTargets.push(...options.horizontalTargetsExtra);
6187
+ const rawObjects = canvas.getObjects();
6188
+ for (const obj of rawObjects) {
6189
+ const objId = getObjectId(obj);
6190
+ if (objId === "__background__") continue;
6191
+ if (objId && excludedIds.has(objId)) continue;
6192
+ const pts = getObjectSnapPoints(obj);
6193
+ verticalTargets.push(pts.left, pts.right, pts.centerX);
6194
+ horizontalTargets.push(pts.top, pts.bottom, pts.centerY);
6195
+ widthTargets.push(Math.max(1, pts.right - pts.left));
6196
+ heightTargets.push(Math.max(1, pts.bottom - pts.top));
6197
+ }
6198
+ for (const bounds of (options == null ? void 0 : options.additionalBounds) ?? []) {
6199
+ const pts = boundsToSnapPoints(bounds);
6200
+ verticalTargets.push(pts.left, pts.right, pts.centerX);
6201
+ horizontalTargets.push(pts.top, pts.bottom, pts.centerY);
6202
+ widthTargets.push(Math.max(1, bounds.width));
6203
+ heightTargets.push(Math.max(1, bounds.height));
6204
+ }
6205
+ const matchAsEdgeEnabled = (options == null ? void 0 : options.matchDimensions) ?? true;
6206
+ if (matchAsEdgeEnabled) {
6207
+ if (resizingLeft && !resizingRight) {
6208
+ for (const w of widthTargets) verticalTargets.push(right - w);
6209
+ } else if (resizingRight && !resizingLeft) {
6210
+ for (const w of widthTargets) verticalTargets.push(box.left + w);
6211
+ }
6212
+ if (resizingTop && !resizingBottom) {
6213
+ for (const h of heightTargets) horizontalTargets.push(bottom - h);
6214
+ } else if (resizingBottom && !resizingTop) {
6215
+ for (const h of heightTargets) horizontalTargets.push(box.top + h);
6216
+ }
6217
+ }
6218
+ const findBestSnap = (edgePos, targets) => {
6219
+ let best = edgePos;
6220
+ let bestDist = threshold + 1;
6221
+ for (const t of targets) {
6222
+ const d = Math.abs(edgePos - t);
6223
+ if (d < bestDist) {
6224
+ bestDist = d;
6225
+ best = t;
6226
+ }
6227
+ }
6228
+ return { value: best, dist: bestDist };
6229
+ };
6230
+ const findBestDimensionSnap = (size, targets) => {
6231
+ let best = size;
6232
+ let bestDist = threshold + 1;
6233
+ for (const t of targets) {
6234
+ const d = Math.abs(size - t);
6235
+ if (d < bestDist) {
6236
+ bestDist = d;
6237
+ best = t;
6238
+ }
6239
+ }
6240
+ return { value: best, dist: bestDist };
6241
+ };
6242
+ let left = box.left;
6243
+ let top = box.top;
6244
+ let width = box.width;
6245
+ let height = box.height;
6246
+ let snappedLeft = false;
6247
+ let snappedRight = false;
6248
+ let snappedTop = false;
6249
+ let snappedBottom = false;
6250
+ if (resizingLeft) {
6251
+ const stick = (_c = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _c.left;
6252
+ if (stick !== void 0) {
6253
+ const dist = Math.abs(box.left - stick);
6254
+ if (dist <= releaseDist) {
6255
+ left = stick;
6256
+ width = right - left;
6257
+ snappedLeft = true;
6258
+ } else if (dist > releaseDist) {
6259
+ if (activeSnapRef.current) delete activeSnapRef.current.left;
6260
+ const { value, dist: d } = findBestSnap(box.left, verticalTargets);
6261
+ if (d <= threshold) {
6262
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6263
+ activeSnapRef.current.left = value;
6264
+ left = value;
6265
+ width = right - left;
6266
+ snappedLeft = true;
6267
+ } else {
6268
+ left = box.left;
6269
+ width = right - left;
6270
+ }
6271
+ } else {
6272
+ left = box.left;
6273
+ width = right - left;
6274
+ }
6275
+ } else {
6276
+ const { value, dist } = findBestSnap(box.left, verticalTargets);
6277
+ if (dist <= threshold) {
6278
+ if (activeSnapRef) {
6279
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6280
+ activeSnapRef.current.left = value;
6281
+ }
6282
+ left = value;
6283
+ width = right - left;
6284
+ snappedLeft = true;
6285
+ } else {
6286
+ left = box.left;
6287
+ width = right - left;
6288
+ }
6289
+ }
6290
+ }
6291
+ if (resizingRight) {
6292
+ const stick = (_d = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _d.right;
6293
+ if (stick !== void 0) {
6294
+ const dist = Math.abs(right - stick);
6295
+ if (dist <= releaseDist) {
6296
+ width = stick - left;
6297
+ snappedRight = true;
6298
+ } else if (dist > releaseDist) {
6299
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.right;
6300
+ const { value, dist: d } = findBestSnap(right, verticalTargets);
6301
+ if (d <= threshold) {
6302
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6303
+ activeSnapRef.current.right = value;
6304
+ width = value - left;
6305
+ snappedRight = true;
6306
+ } else {
6307
+ width = box.width;
6308
+ }
6309
+ } else {
6310
+ width = box.width;
6311
+ }
6312
+ } else {
6313
+ const { value, dist } = findBestSnap(right, verticalTargets);
6314
+ if (dist <= threshold) {
6315
+ if (activeSnapRef) {
6316
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6317
+ activeSnapRef.current.right = value;
6318
+ }
6319
+ width = value - left;
6320
+ snappedRight = true;
6321
+ } else {
6322
+ width = box.width;
6323
+ }
6324
+ }
6325
+ }
6326
+ if (resizingTop) {
6327
+ const stick = (_e = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _e.top;
6328
+ if (stick !== void 0) {
6329
+ const dist = Math.abs(box.top - stick);
6330
+ if (dist <= releaseDist) {
6331
+ top = stick;
6332
+ height = bottom - top;
6333
+ snappedTop = true;
6334
+ } else if (dist > releaseDist) {
6335
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.top;
6336
+ const { value, dist: d } = findBestSnap(box.top, horizontalTargets);
6337
+ if (d <= threshold) {
6338
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6339
+ activeSnapRef.current.top = value;
6340
+ top = value;
6341
+ height = bottom - top;
6342
+ snappedTop = true;
6343
+ } else {
6344
+ top = box.top;
6345
+ height = bottom - top;
6346
+ }
6347
+ } else {
6348
+ top = box.top;
6349
+ height = bottom - top;
6350
+ }
6351
+ } else {
6352
+ const { value, dist } = findBestSnap(box.top, horizontalTargets);
6353
+ if (dist <= threshold) {
6354
+ if (activeSnapRef) {
6355
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6356
+ activeSnapRef.current.top = value;
6357
+ }
6358
+ top = value;
6359
+ height = bottom - top;
6360
+ snappedTop = true;
6361
+ } else {
6362
+ top = box.top;
6363
+ height = bottom - top;
6364
+ }
6365
+ }
6366
+ }
6367
+ if (resizingBottom) {
6368
+ const stick = (_f = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _f.bottom;
6369
+ if (stick !== void 0) {
6370
+ const dist = Math.abs(bottom - stick);
6371
+ if (dist <= releaseDist) {
6372
+ height = stick - top;
6373
+ snappedBottom = true;
6374
+ } else if (dist > releaseDist) {
6375
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.bottom;
6376
+ const { value, dist: d } = findBestSnap(bottom, horizontalTargets);
6377
+ if (d <= threshold) {
6378
+ if (!(activeSnapRef == null ? void 0 : activeSnapRef.current)) activeSnapRef.current = {};
6379
+ activeSnapRef.current.bottom = value;
6380
+ height = value - top;
6381
+ snappedBottom = true;
6382
+ } else {
6383
+ height = box.height;
6384
+ }
6385
+ } else {
6386
+ height = box.height;
6387
+ }
6388
+ } else {
6389
+ const { value, dist } = findBestSnap(bottom, horizontalTargets);
6390
+ if (dist <= threshold) {
6391
+ if (activeSnapRef) {
6392
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6393
+ activeSnapRef.current.bottom = value;
6394
+ }
6395
+ height = value - top;
6396
+ snappedBottom = true;
6397
+ } else {
6398
+ height = box.height;
6399
+ }
6400
+ }
6401
+ }
6402
+ if (matchDimensions && resizingLeft !== resizingRight && !snappedLeft && !snappedRight && widthTargets.length > 0) {
6403
+ const stick = (_g = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _g.width;
6404
+ const applyWidth = (targetWidth) => {
6405
+ width = Math.max(MIN_SNAP_BOX, targetWidth);
6406
+ if (resizingLeft) {
6407
+ left = right - width;
6408
+ snappedLeft = true;
6409
+ } else {
6410
+ snappedRight = true;
6411
+ }
6412
+ };
6413
+ if (stick !== void 0) {
6414
+ const dist = Math.abs(box.width - stick);
6415
+ if (dist <= releaseDist) {
6416
+ applyWidth(stick);
6417
+ } else if (dist > releaseDist) {
6418
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.width;
6419
+ const { value, dist: d } = findBestDimensionSnap(box.width, widthTargets);
6420
+ if (d <= threshold) {
6421
+ if (activeSnapRef) {
6422
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6423
+ activeSnapRef.current.width = value;
6424
+ }
6425
+ applyWidth(value);
6426
+ }
6427
+ }
6428
+ } else {
6429
+ const { value, dist } = findBestDimensionSnap(box.width, widthTargets);
6430
+ if (dist <= threshold) {
6431
+ if (activeSnapRef) {
6432
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6433
+ activeSnapRef.current.width = value;
6434
+ }
6435
+ applyWidth(value);
6436
+ }
6437
+ }
6438
+ }
6439
+ if (matchDimensions && resizingTop !== resizingBottom && !snappedTop && !snappedBottom && heightTargets.length > 0) {
6440
+ const stick = (_h = activeSnapRef == null ? void 0 : activeSnapRef.current) == null ? void 0 : _h.height;
6441
+ const applyHeight = (targetHeight) => {
6442
+ height = Math.max(MIN_SNAP_BOX, targetHeight);
6443
+ if (resizingTop) {
6444
+ top = bottom - height;
6445
+ snappedTop = true;
6446
+ } else {
6447
+ snappedBottom = true;
6448
+ }
6449
+ };
6450
+ if (stick !== void 0) {
6451
+ const dist = Math.abs(box.height - stick);
6452
+ if (dist <= releaseDist) {
6453
+ applyHeight(stick);
6454
+ } else if (dist > releaseDist) {
6455
+ if (activeSnapRef == null ? void 0 : activeSnapRef.current) delete activeSnapRef.current.height;
6456
+ const { value, dist: d } = findBestDimensionSnap(box.height, heightTargets);
6457
+ if (d <= threshold) {
6458
+ if (activeSnapRef) {
6459
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6460
+ activeSnapRef.current.height = value;
6461
+ }
6462
+ applyHeight(value);
6463
+ }
6464
+ }
6465
+ } else {
6466
+ const { value, dist } = findBestDimensionSnap(box.height, heightTargets);
6467
+ if (dist <= threshold) {
6468
+ if (activeSnapRef) {
6469
+ if (!activeSnapRef.current) activeSnapRef.current = {};
6470
+ activeSnapRef.current.height = value;
6471
+ }
6472
+ applyHeight(value);
6473
+ }
6474
+ }
6475
+ }
6476
+ width = Math.max(MIN_SNAP_BOX, width);
6477
+ height = Math.max(MIN_SNAP_BOX, height);
6478
+ if (resizingLeft && !resizingRight) left = right - width;
6479
+ if (resizingTop && !resizingBottom) top = bottom - height;
6480
+ if (roundSnappedOnly) {
6481
+ return {
6482
+ left: snappedLeft ? Math.round(left) : left,
6483
+ top: snappedTop ? Math.round(top) : top,
6484
+ width: snappedLeft || snappedRight ? Math.round(width) : width,
6485
+ height: snappedTop || snappedBottom ? Math.round(height) : height
6486
+ };
6487
+ }
6488
+ return {
6489
+ left: Math.round(left),
6490
+ top: Math.round(top),
6491
+ width: Math.round(width),
6492
+ height: Math.round(height)
6493
+ };
6494
+ }
5952
6495
  const clamp01$1 = (v) => Math.max(0, Math.min(1, Number.isFinite(v) ? v : 0));
5953
6496
  function arcPath(w, sag, up) {
5954
6497
  if (sag <= 0.5) return `M 0 0 L ${w} 0`;
@@ -6119,12 +6662,87 @@ if (Array.isArray(stateProps)) {
6119
6662
  if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
6120
6663
  if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
6121
6664
  if (!stateProps.includes("textPath")) stateProps.push("textPath");
6665
+ if (!stateProps.includes("smartWrap")) stateProps.push("smartWrap");
6122
6666
  }
6123
6667
  const cacheProps = fabric.Textbox.prototype.cacheProperties;
6124
6668
  if (Array.isArray(cacheProps)) {
6125
6669
  if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
6126
6670
  if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
6127
6671
  if (!cacheProps.includes("textPath")) cacheProps.push("textPath");
6672
+ if (!cacheProps.includes("smartWrap")) cacheProps.push("smartWrap");
6673
+ }
6674
+ fabric.Textbox.prototype.smartWrap = true;
6675
+ if (TextboxProto._wrapLine && !TextboxProto.__pixldocsOrigWrapLine) {
6676
+ TextboxProto.__pixldocsOrigWrapLine = TextboxProto._wrapLine;
6677
+ TextboxProto._wrapLine = function(lineIndex, desiredWidth, ref, reservedSpace = 0) {
6678
+ var _a2;
6679
+ const orig = TextboxProto.__pixldocsOrigWrapLine;
6680
+ if (!this.smartWrap || this.splitByGrapheme) {
6681
+ return orig.call(this, lineIndex, desiredWidth, ref, reservedSpace);
6682
+ }
6683
+ try {
6684
+ const additionalSpace = TextboxProto._getWidthOfCharSpacing.call(this);
6685
+ const data = ((_a2 = ref == null ? void 0 : ref.wordsData) == null ? void 0 : _a2[lineIndex]) ?? [];
6686
+ const effective = Math.max(1, desiredWidth - reservedSpace);
6687
+ const infix = " ";
6688
+ const measureWord = TextboxProto._measureWord;
6689
+ const infixWidth = measureWord.call(this, [infix], lineIndex, 0);
6690
+ const graphemeLines = [];
6691
+ let line = [];
6692
+ let lineWidth = 0;
6693
+ let lineJustStarted = true;
6694
+ let largestWordWidth = 0;
6695
+ let offset = 0;
6696
+ const pushLine = () => {
6697
+ graphemeLines.push(line);
6698
+ line = [];
6699
+ lineWidth = 0;
6700
+ lineJustStarted = true;
6701
+ };
6702
+ for (let i = 0; i < data.length; i++) {
6703
+ const { word, width: wordWidth } = data[i];
6704
+ if (wordWidth <= effective) {
6705
+ if (wordWidth > largestWordWidth) largestWordWidth = wordWidth;
6706
+ const projected = lineJustStarted ? wordWidth : lineWidth + infixWidth + wordWidth - additionalSpace;
6707
+ if (!lineJustStarted && projected > effective) {
6708
+ pushLine();
6709
+ }
6710
+ if (!lineJustStarted) {
6711
+ line.push(infix);
6712
+ lineWidth += infixWidth;
6713
+ }
6714
+ line = line.concat(word);
6715
+ lineWidth += wordWidth;
6716
+ lineJustStarted = false;
6717
+ } else {
6718
+ if (!lineJustStarted) pushLine();
6719
+ for (const g of word) {
6720
+ const gw = measureWord.call(this, [g], lineIndex, offset);
6721
+ if (gw > largestWordWidth) largestWordWidth = gw;
6722
+ const projected = lineJustStarted ? gw : lineWidth + gw - additionalSpace;
6723
+ if (!lineJustStarted && projected > effective) {
6724
+ pushLine();
6725
+ }
6726
+ line.push(g);
6727
+ lineWidth += gw;
6728
+ lineJustStarted = false;
6729
+ offset++;
6730
+ }
6731
+ offset -= word.length;
6732
+ }
6733
+ offset += word.length + 1;
6734
+ }
6735
+ if (line.length) graphemeLines.push(line);
6736
+ const minNeeded = Math.max(0, Math.min(largestWordWidth, effective) - additionalSpace + reservedSpace);
6737
+ if (minNeeded > this.dynamicMinWidth) {
6738
+ this.dynamicMinWidth = minNeeded;
6739
+ }
6740
+ return graphemeLines;
6741
+ } catch (e) {
6742
+ console.warn("[smartWrap] fell back to default wrap:", e);
6743
+ return orig.call(this, lineIndex, desiredWidth, ref, reservedSpace);
6744
+ }
6745
+ };
6128
6746
  }
6129
6747
  const hasActiveTextPath = (obj) => {
6130
6748
  const tp = obj.textPath;
@@ -6281,14 +6899,14 @@ function scaleLocalToScreen(target, p) {
6281
6899
  return new fabric.Point(p.x * sx * zx, p.y * sy * zy);
6282
6900
  }
6283
6901
  function applyTextPathControls(textbox) {
6284
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
6902
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
6285
6903
  const obj = textbox;
6286
6904
  if (!hasActiveTextPath(obj)) {
6287
6905
  obj.__pdTextPathHovered = false;
6288
6906
  if (obj.__pdTextPathControls) {
6289
6907
  try {
6290
6908
  const cu2 = fabric.controlsUtils;
6291
- 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));
6909
+ 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));
6292
6910
  if (defaults) obj.controls = defaults;
6293
6911
  } catch {
6294
6912
  }
@@ -6552,7 +7170,7 @@ function applyTextPathControls(textbox) {
6552
7170
  actionName: "tpPivot",
6553
7171
  positionHandler: (_d2, finalMatrix, fabricObject) => scaleLocalToScreen(fabricObject, getPivotLocalCentered(fabricObject)).transform(finalMatrix),
6554
7172
  actionHandler: (_e2, transform, x, y) => {
6555
- var _a3, _b2;
7173
+ var _a3, _b3;
6556
7174
  const target = transform.target;
6557
7175
  const state = transform.__pdCirclePivotDrag || (transform.__pdCirclePivotDrag = {
6558
7176
  startMatrix: target.calcTransformMatrix(),
@@ -6569,7 +7187,7 @@ function applyTextPathControls(textbox) {
6569
7187
  y: (state.startOffset.y || 0) + dy
6570
7188
  };
6571
7189
  target.setCoords();
6572
- (_b2 = target.canvas) == null ? void 0 : _b2.requestRenderAll();
7190
+ (_b3 = target.canvas) == null ? void 0 : _b3.requestRenderAll();
6573
7191
  return true;
6574
7192
  },
6575
7193
  render: renderPivot
@@ -6620,13 +7238,13 @@ function applyTextPathControls(textbox) {
6620
7238
  }
6621
7239
  if (!obj.__pdCirclePivotDblWired) {
6622
7240
  obj.on("mousedblclick", () => {
6623
- var _a3, _b2;
7241
+ var _a3, _b3;
6624
7242
  if (((_a3 = obj.textPath) == null ? void 0 : _a3.preset) !== "circle") return;
6625
7243
  if (obj.__corner !== "tpPivot") return;
6626
7244
  if (!obj.textPath.pivot) return;
6627
7245
  obj.textPath.pivot = { x: 0, y: 0 };
6628
7246
  obj.setCoords();
6629
- (_b2 = obj.canvas) == null ? void 0 : _b2.requestRenderAll();
7247
+ (_b3 = obj.canvas) == null ? void 0 : _b3.requestRenderAll();
6630
7248
  });
6631
7249
  obj.__pdCirclePivotDblWired = true;
6632
7250
  }
@@ -6994,10 +7612,10 @@ function textPathBoundsContainScenePoint(obj, point) {
6994
7612
  }
6995
7613
  }
6996
7614
  function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
6997
- var _a2, _b, _c;
7615
+ var _a2, _b2, _c;
6998
7616
  const host = hostCanvas || obj.canvas || ((_a2 = obj.group) == null ? void 0 : _a2.canvas);
6999
7617
  if (!host) return;
7000
- const retina = ((_b = host.getRetinaScaling) == null ? void 0 : _b.call(host)) || ((_c = obj.getCanvasRetinaScaling) == null ? void 0 : _c.call(obj)) || 1;
7618
+ const retina = ((_b2 = host.getRetinaScaling) == null ? void 0 : _b2.call(host)) || ((_c = obj.getCanvasRetinaScaling) == null ? void 0 : _c.call(obj)) || 1;
7001
7619
  const matrix = fabric.util.multiplyTransformMatrices(host.viewportTransform || [1, 0, 0, 1, 0, 0], obj.calcTransformMatrix());
7002
7620
  const corners = [
7003
7621
  new fabric.Point(bounds.minX, bounds.minY),
@@ -7019,8 +7637,8 @@ function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7019
7637
  }
7020
7638
  if (typeof TextboxProto._renderControls === "function" && !TextboxProto.__pixldocsOrigRenderControls) {
7021
7639
  let drawWarpGuides = function(ctx) {
7022
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7023
- 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);
7640
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
7641
+ 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);
7024
7642
  if (!hostCanvas) return;
7025
7643
  const hoverBounds = getTextPathHitBounds(this);
7026
7644
  const active = (_d = hostCanvas.getActiveObject) == null ? void 0 : _d.call(hostCanvas);
@@ -7443,12 +8061,12 @@ function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
7443
8061
  ctx.closePath();
7444
8062
  }
7445
8063
  function measureLineGlyphWidth(obj, lineIndex) {
7446
- var _a2, _b, _c, _d, _e, _f;
8064
+ var _a2, _b2, _c, _d, _e, _f;
7447
8065
  try {
7448
8066
  const rawLine = (_a2 = obj == null ? void 0 : obj._textLines) == null ? void 0 : _a2[lineIndex];
7449
8067
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
7450
8068
  if (!lineText) return 0;
7451
- const fontSize = Number(((_b = obj.getValueOfPropertyAt) == null ? void 0 : _b.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
8069
+ const fontSize = Number(((_b2 = obj.getValueOfPropertyAt) == null ? void 0 : _b2.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
7452
8070
  if (!fontSize) return 0;
7453
8071
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
7454
8072
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
@@ -7717,7 +8335,7 @@ function applyTextBackground(obj, cfg) {
7717
8335
  const originalToSVG = (_a2 = obj.toSVG) == null ? void 0 : _a2.bind(obj);
7718
8336
  if (typeof originalToSVG === "function") {
7719
8337
  obj.toSVG = function(reviver) {
7720
- var _a3, _b;
8338
+ var _a3, _b2;
7721
8339
  let svg = originalToSVG(reviver);
7722
8340
  const bg = this[PD_BG_KEY];
7723
8341
  const shadow = this.shadow;
@@ -7766,7 +8384,7 @@ function applyTextBackground(obj, cfg) {
7766
8384
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7767
8385
  let bgGradDefs = "";
7768
8386
  let bgFillAttr = escapeXmlAttr(bgFill);
7769
- if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b = bg.gradient.stops) == null ? void 0 : _b.length) >= 2) {
8387
+ if (hasBg && (bg == null ? void 0 : bg.gradient) && ((_b2 = bg.gradient.stops) == null ? void 0 : _b2.length) >= 2) {
7770
8388
  const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7771
8389
  const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7772
8390
  const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
@@ -8795,9 +9413,9 @@ function createShape(element) {
8795
9413
  });
8796
9414
  }
8797
9415
  case "circle": {
8798
- const radius = Math.min(w, h) / 2;
8799
- return new fabric.Circle({
8800
- radius,
9416
+ return new fabric.Ellipse({
9417
+ rx: w / 2,
9418
+ ry: h / 2,
8801
9419
  fill,
8802
9420
  stroke,
8803
9421
  strokeWidth,
@@ -8806,8 +9424,7 @@ function createShape(element) {
8806
9424
  objectCaching: true,
8807
9425
  strokeUniform: true,
8808
9426
  strokeLineJoin: "round",
8809
- strokeLineCap: "round",
8810
- lockUniScaling: true
9427
+ strokeLineCap: "round"
8811
9428
  });
8812
9429
  }
8813
9430
  case "triangle": {
@@ -8842,7 +9459,7 @@ function createShape(element) {
8842
9459
  }
8843
9460
  }
8844
9461
  function createText(element) {
8845
- var _a2, _b, _c, _d, _e;
9462
+ var _a2, _b2, _c, _d, _e;
8846
9463
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
8847
9464
  let text = element.text || "Text";
8848
9465
  let fontSize = element.fontSize || 16;
@@ -9043,7 +9660,7 @@ function createText(element) {
9043
9660
  textbox.setCoords();
9044
9661
  const widthAfterSet = textbox.width ?? 0;
9045
9662
  try {
9046
- (_b = textbox.setControlsVisibility) == null ? void 0 : _b.call(textbox, {
9663
+ (_b2 = textbox.setControlsVisibility) == null ? void 0 : _b2.call(textbox, {
9047
9664
  tl: true,
9048
9665
  tr: true,
9049
9666
  bl: true,
@@ -9842,34 +10459,595 @@ function bakeEdgeFade(source, fade) {
9842
10459
  return canvas;
9843
10460
  }
9844
10461
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10462
+ const SELECTION_BORDER_SCALE = 2;
10463
+ let ensureCanvaControlRenders = () => {
10464
+ };
9845
10465
  try {
9846
10466
  const InteractiveBase = fabric.InteractiveFabricObject ?? fabric.Object;
10467
+ if ((InteractiveBase == null ? void 0 : InteractiveBase.prototype) && !InteractiveBase.prototype.__pixldocsCenteredSelectionBorder) {
10468
+ InteractiveBase.prototype.__pixldocsCenteredSelectionBorder = true;
10469
+ InteractiveBase.prototype.strokeBorders = function(ctx, size) {
10470
+ const border = Number(this.borderScaleFactor ?? SELECTION_BORDER_SCALE) || SELECTION_BORDER_SCALE;
10471
+ const width = Math.max(0, ((size == null ? void 0 : size.x) ?? 0) - border);
10472
+ const height = Math.max(0, ((size == null ? void 0 : size.y) ?? 0) - border);
10473
+ ctx.strokeRect(-width / 2, -height / 2, width, height);
10474
+ };
10475
+ }
9847
10476
  if (InteractiveBase == null ? void 0 : InteractiveBase.ownDefaults) {
9848
10477
  Object.assign(InteractiveBase.ownDefaults, {
9849
10478
  borderColor: SELECTION_PRIMARY,
9850
- borderScaleFactor: 1.25,
10479
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9851
10480
  cornerColor: SELECTION_PRIMARY,
9852
10481
  cornerStrokeColor: "#ffffff",
9853
- cornerStyle: "rect",
10482
+ cornerStyle: "circle",
9854
10483
  transparentCorners: false,
9855
- cornerSize: 8,
10484
+ cornerSize: 10,
9856
10485
  borderOpacityWhenMoving: 0.9
9857
10486
  });
9858
10487
  } else if (InteractiveBase == null ? void 0 : InteractiveBase.prototype) {
9859
10488
  Object.assign(InteractiveBase.prototype, {
9860
10489
  borderColor: SELECTION_PRIMARY,
9861
- borderScaleFactor: 1.25,
10490
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9862
10491
  cornerColor: SELECTION_PRIMARY,
9863
10492
  cornerStrokeColor: "#ffffff",
9864
- cornerStyle: "rect",
10493
+ cornerStyle: "circle",
9865
10494
  transparentCorners: false,
9866
- cornerSize: 8,
10495
+ cornerSize: 10,
9867
10496
  borderOpacityWhenMoving: 0.9
9868
10497
  });
9869
10498
  }
9870
10499
  } catch (e) {
9871
10500
  console.warn("[PageCanvas] Failed to apply global selection defaults:", e);
9872
10501
  }
10502
+ try {
10503
+ const cu = fabric.controlsUtils;
10504
+ if (cu && typeof cu.createObjectDefaultControls === "function") {
10505
+ const PILL_LEN = 20;
10506
+ const PILL_THICK = 6;
10507
+ const PILL_RADIUS = 3;
10508
+ const CORNER_RADIUS = 6;
10509
+ const EDGE_HIT_PERP = 22;
10510
+ const EDGE_HIT_ALONG = 44;
10511
+ const HOVER_FILL = SELECTION_PRIMARY;
10512
+ const HOVER_STROKE = "#ffffff";
10513
+ const IDLE_FILL = "#ffffff";
10514
+ const IDLE_STROKE = "rgba(15, 23, 42, 0.18)";
10515
+ const isHovered = (fabricObject, controlKey) => fabricObject && fabricObject.__corner === controlKey;
10516
+ const getHoverProgress = (fabricObject, controlKey) => {
10517
+ const map = fabricObject == null ? void 0 : fabricObject.__handleHoverProgress;
10518
+ const p = map ? map[controlKey] : void 0;
10519
+ if (typeof p === "number") return Math.max(0, Math.min(1, p));
10520
+ return isHovered(fabricObject, controlKey) ? 1 : 0;
10521
+ };
10522
+ const HOVER_RGB = [59, 130, 246];
10523
+ const IDLE_RGB = [255, 255, 255];
10524
+ const lerpFill = (p) => {
10525
+ const r = Math.round(IDLE_RGB[0] + (HOVER_RGB[0] - IDLE_RGB[0]) * p);
10526
+ const g = Math.round(IDLE_RGB[1] + (HOVER_RGB[1] - IDLE_RGB[1]) * p);
10527
+ const b = Math.round(IDLE_RGB[2] + (HOVER_RGB[2] - IDLE_RGB[2]) * p);
10528
+ return `rgb(${r}, ${g}, ${b})`;
10529
+ };
10530
+ const getVisualScale = (_fabricObject) => {
10531
+ return 1;
10532
+ };
10533
+ const COLLAPSE_THRESHOLD_PX = 32;
10534
+ const shouldCollapseHandles = (fabricObject) => {
10535
+ var _a2, _b2, _c;
10536
+ try {
10537
+ const canvas = fabricObject == null ? void 0 : fabricObject.canvas;
10538
+ if (!canvas) return false;
10539
+ const zoom = ((_a2 = canvas.getZoom) == null ? void 0 : _a2.call(canvas)) ?? 1;
10540
+ const w = (((_b2 = fabricObject.getScaledWidth) == null ? void 0 : _b2.call(fabricObject)) ?? fabricObject.width ?? 0) * zoom;
10541
+ const h = (((_c = fabricObject.getScaledHeight) == null ? void 0 : _c.call(fabricObject)) ?? fabricObject.height ?? 0) * zoom;
10542
+ return Math.min(w, h) < COLLAPSE_THRESHOLD_PX;
10543
+ } catch {
10544
+ return false;
10545
+ }
10546
+ };
10547
+ const isVisibleControlWhenCollapsed = (controlKey) => controlKey === "tl" || controlKey === "mr" || controlKey === "mtr" || controlKey === "mvh";
10548
+ const wrapCollapseVisibility = (control, controlKey) => {
10549
+ if (!control || control.__pixldocsCollapseVisibilityWrapped) return;
10550
+ const originalGetVisibility = typeof control.getVisibility === "function" ? control.getVisibility.bind(control) : null;
10551
+ control.getVisibility = (fabricObject, key) => {
10552
+ const baseVisible = originalGetVisibility ? originalGetVisibility(fabricObject, key) : control.visible !== false;
10553
+ if (!baseVisible) return false;
10554
+ return !shouldCollapseHandles(fabricObject) || isVisibleControlWhenCollapsed(controlKey);
10555
+ };
10556
+ control.__pixldocsCollapseVisibilityWrapped = true;
10557
+ };
10558
+ const renderPill = (ctx, left, top, _styleOverride, fabricObject, orientation, controlKey) => {
10559
+ var _a2, _b2, _c;
10560
+ const action = (_b2 = (_a2 = fabricObject.canvas) == null ? void 0 : _a2._currentTransform) == null ? void 0 : _b2.action;
10561
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10562
+ if (shouldCollapseHandles(fabricObject) && !isVisibleControlWhenCollapsed(controlKey)) return;
10563
+ const angle = ((_c = fabricObject.getTotalAngle) == null ? void 0 : _c.call(fabricObject)) ?? (fabricObject.angle ?? 0);
10564
+ const p = getHoverProgress(fabricObject, controlKey);
10565
+ const scale = getVisualScale(fabricObject);
10566
+ const pillLen = PILL_LEN * scale;
10567
+ const pillThick = PILL_THICK * scale;
10568
+ const w = orientation === "horizontal" ? pillLen : pillThick;
10569
+ const h = orientation === "horizontal" ? pillThick : pillLen;
10570
+ ctx.save();
10571
+ ctx.translate(left, top);
10572
+ ctx.rotate(angle * Math.PI / 180);
10573
+ ctx.beginPath();
10574
+ const x = -w / 2;
10575
+ const y = -h / 2;
10576
+ const r = Math.min(PILL_RADIUS * scale, Math.min(w, h) / 2);
10577
+ ctx.moveTo(x + r, y);
10578
+ ctx.lineTo(x + w - r, y);
10579
+ ctx.quadraticCurveTo(x + w, y, x + w, y + r);
10580
+ ctx.lineTo(x + w, y + h - r);
10581
+ ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
10582
+ ctx.lineTo(x + r, y + h);
10583
+ ctx.quadraticCurveTo(x, y + h, x, y + h - r);
10584
+ ctx.lineTo(x, y + r);
10585
+ ctx.quadraticCurveTo(x, y, x + r, y);
10586
+ ctx.closePath();
10587
+ ctx.fillStyle = lerpFill(p);
10588
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10589
+ ctx.lineWidth = 0.75 * (1 - p);
10590
+ ctx.shadowColor = "rgba(15, 23, 42, 0.38)";
10591
+ ctx.shadowBlur = 8;
10592
+ ctx.shadowOffsetY = 2;
10593
+ ctx.fill();
10594
+ if (p < 1) ctx.stroke();
10595
+ ctx.restore();
10596
+ };
10597
+ const renderCornerDot = (ctx, left, top, _styleOverride, fabricObject, controlKey) => {
10598
+ var _a2, _b2;
10599
+ const action = (_b2 = (_a2 = fabricObject.canvas) == null ? void 0 : _a2._currentTransform) == null ? void 0 : _b2.action;
10600
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10601
+ if (shouldCollapseHandles(fabricObject) && !isVisibleControlWhenCollapsed(controlKey)) return;
10602
+ const p = getHoverProgress(fabricObject, controlKey);
10603
+ const scale = getVisualScale(fabricObject);
10604
+ const r = CORNER_RADIUS * scale;
10605
+ ctx.save();
10606
+ ctx.beginPath();
10607
+ ctx.arc(left, top, r, 0, Math.PI * 2);
10608
+ ctx.closePath();
10609
+ ctx.fillStyle = lerpFill(p);
10610
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10611
+ ctx.lineWidth = 0.75 * (1 - p);
10612
+ ctx.shadowColor = "rgba(15, 23, 42, 0.38)";
10613
+ ctx.shadowBlur = 8;
10614
+ ctx.shadowOffsetY = 2;
10615
+ ctx.fill();
10616
+ if (p < 1) ctx.stroke();
10617
+ ctx.restore();
10618
+ };
10619
+ const installPillRenders = (controls) => {
10620
+ var _a2;
10621
+ const CUR_SIZE = 22;
10622
+ const HOT = Math.round(CUR_SIZE / 2);
10623
+ const makeCursor = (angleDeg) => {
10624
+ const svg = `<?xml version="1.0" encoding="UTF-8"?>
10625
+ <svg xmlns="http://www.w3.org/2000/svg" width="${CUR_SIZE}" height="${CUR_SIZE}" viewBox="0 0 22 22">
10626
+ <g transform="rotate(${angleDeg} 11 11)" fill="none" stroke="#000" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
10627
+ <path d="M2.5 11 L19.5 11" stroke="#fff" stroke-width="3.2"/>
10628
+ <path d="M5 8.5 L2.5 11 L5 13.5" stroke="#fff" stroke-width="3.2"/>
10629
+ <path d="M17 8.5 L19.5 11 L17 13.5" stroke="#fff" stroke-width="3.2"/>
10630
+ <path d="M2.5 11 L19.5 11"/>
10631
+ <path d="M5 8.5 L2.5 11 L5 13.5"/>
10632
+ <path d="M17 8.5 L19.5 11 L17 13.5"/>
10633
+ </g>
10634
+ </svg>`;
10635
+ const encoded = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
10636
+ const fallback = angleDeg % 180 === 0 ? "ew-resize" : angleDeg % 180 === 90 ? "ns-resize" : angleDeg === 45 || angleDeg === 225 ? "nwse-resize" : "nesw-resize";
10637
+ return `url("data:image/svg+xml;utf8,${encoded}") ${HOT} ${HOT}, ${fallback}`;
10638
+ };
10639
+ const cursorFor = {
10640
+ ml: makeCursor(0),
10641
+ mr: makeCursor(0),
10642
+ mt: makeCursor(90),
10643
+ mb: makeCursor(90),
10644
+ tl: makeCursor(45),
10645
+ br: makeCursor(45),
10646
+ tr: makeCursor(135),
10647
+ bl: makeCursor(135)
10648
+ };
10649
+ 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";
10650
+ const CUR_R_SIZE = 18;
10651
+ const HOT_R = Math.round(CUR_R_SIZE / 2);
10652
+ const makeRotateCursor = (angleDeg) => {
10653
+ const a = ((angleDeg + 215) % 360 + 360) % 360;
10654
+ const halo = `<path d="${ROTATE_ICON_PATH}" fill="#fff" stroke="#fff" stroke-width="40" stroke-linejoin="round"/>`;
10655
+ const fg = `<path d="${ROTATE_ICON_PATH}" fill="#0f172a" stroke="#0f172a" stroke-width="6" stroke-linejoin="round"/>`;
10656
+ const svg = `<?xml version="1.0" encoding="UTF-8"?>
10657
+ <svg xmlns="http://www.w3.org/2000/svg" width="${CUR_R_SIZE}" height="${CUR_R_SIZE}" viewBox="0 0 512 512">
10658
+ <g transform="rotate(${a} 256 256)">${halo}${fg}</g>
10659
+ </svg>`;
10660
+ const encoded = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
10661
+ return `url("data:image/svg+xml;utf8,${encoded}") ${HOT_R} ${HOT_R}, crosshair`;
10662
+ };
10663
+ const rotateCursorCache = {};
10664
+ const getRotateCursor = (fabricObject) => {
10665
+ let objAngle = 0;
10666
+ try {
10667
+ objAngle = typeof (fabricObject == null ? void 0 : fabricObject.getTotalAngle) === "function" ? fabricObject.getTotalAngle() : (fabricObject == null ? void 0 : fabricObject.angle) ?? 0;
10668
+ } catch {
10669
+ objAngle = 0;
10670
+ }
10671
+ let a = (objAngle % 360 + 360) % 360;
10672
+ a = Math.round(a / 15) * 15;
10673
+ if (a === 360) a = 0;
10674
+ if (!rotateCursorCache[a]) rotateCursorCache[a] = makeRotateCursor(a);
10675
+ return rotateCursorCache[a];
10676
+ };
10677
+ const MOVE_PATHS_2D = [
10678
+ 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"),
10679
+ 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"),
10680
+ 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"),
10681
+ 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")
10682
+ ];
10683
+ const ROTATE_PATHS_2D = [
10684
+ new Path2D("M22 12l-3 3-3-3"),
10685
+ new Path2D("M2 12l3-3 3 3"),
10686
+ new Path2D("M19.016 14v-1.95A7.05 7.05 0 0 0 8 6.22"),
10687
+ new Path2D("M16.016 17.845A7.05 7.05 0 0 1 5 12.015V10"),
10688
+ new Path2D("M5 10V9"),
10689
+ new Path2D("M19 15v-1")
10690
+ ];
10691
+ const baseAngleFor = {
10692
+ ml: 0,
10693
+ mr: 0,
10694
+ mt: 90,
10695
+ mb: 90,
10696
+ tl: 45,
10697
+ br: 45,
10698
+ tr: 135,
10699
+ bl: 135
10700
+ };
10701
+ const cursorCache = {};
10702
+ const getRotatedCursor = (key, fabricObject) => {
10703
+ const base = baseAngleFor[key];
10704
+ if (base === void 0) return cursorFor[key];
10705
+ let objAngle = 0;
10706
+ try {
10707
+ objAngle = typeof (fabricObject == null ? void 0 : fabricObject.getTotalAngle) === "function" ? fabricObject.getTotalAngle() : (fabricObject == null ? void 0 : fabricObject.angle) ?? 0;
10708
+ } catch {
10709
+ objAngle = 0;
10710
+ }
10711
+ let a = ((base + objAngle) % 180 + 180) % 180;
10712
+ a = Math.round(a / 15) * 15;
10713
+ if (a === 180) a = 0;
10714
+ if (!cursorCache[a]) cursorCache[a] = makeCursor(a);
10715
+ return cursorCache[a];
10716
+ };
10717
+ const sides = [
10718
+ ["ml", "vertical"],
10719
+ ["mr", "vertical"],
10720
+ ["mt", "horizontal"],
10721
+ ["mb", "horizontal"]
10722
+ ];
10723
+ for (const [key, orient] of sides) {
10724
+ const c = controls[key];
10725
+ if (!c) continue;
10726
+ c.sizeX = orient === "horizontal" ? EDGE_HIT_ALONG : EDGE_HIT_PERP;
10727
+ c.sizeY = orient === "horizontal" ? EDGE_HIT_PERP : EDGE_HIT_ALONG;
10728
+ c.touchSizeX = c.sizeX;
10729
+ c.touchSizeY = c.sizeY;
10730
+ wrapCollapseVisibility(c, key);
10731
+ c.cursorStyle = cursorFor[key];
10732
+ c.cursorStyleHandler = (_eventData, _control, fabricObject) => getRotatedCursor(key, fabricObject);
10733
+ c.render = (ctx, left, top, styleOverride, fabricObject) => renderPill(ctx, left, top, styleOverride, fabricObject, orient, key);
10734
+ }
10735
+ const corners = ["tl", "tr", "bl", "br"];
10736
+ for (const key of corners) {
10737
+ const c = controls[key];
10738
+ if (!c) continue;
10739
+ c.sizeX = 16;
10740
+ c.sizeY = 16;
10741
+ c.touchSizeX = 24;
10742
+ c.touchSizeY = 24;
10743
+ wrapCollapseVisibility(c, key);
10744
+ c.cursorStyle = cursorFor[key];
10745
+ c.cursorStyleHandler = (_eventData, _control, fabricObject) => getRotatedCursor(key, fabricObject);
10746
+ c.render = (ctx, left, top, styleOverride, fabricObject) => renderCornerDot(ctx, left, top, styleOverride, fabricObject, key);
10747
+ }
10748
+ const mtr = controls.mtr;
10749
+ if (mtr) {
10750
+ mtr.sizeX = 22;
10751
+ mtr.sizeY = 22;
10752
+ mtr.touchSizeX = 32;
10753
+ mtr.touchSizeY = 32;
10754
+ wrapCollapseVisibility(mtr, "mtr");
10755
+ mtr.offsetY = -28;
10756
+ mtr.withConnection = false;
10757
+ mtr.x = 0;
10758
+ mtr.y = -0.5;
10759
+ mtr.cursorStyle = getRotateCursor(null);
10760
+ mtr.cursorStyleHandler = (_e, _c, fabricObject) => {
10761
+ const cursor = getRotateCursor(fabricObject);
10762
+ try {
10763
+ const canvas = fabricObject == null ? void 0 : fabricObject.canvas;
10764
+ if (canvas) canvas.__pixldocsGetRotateCursor = getRotateCursor;
10765
+ const upper = canvas == null ? void 0 : canvas.upperCanvasEl;
10766
+ if (upper) upper.style.cursor = cursor;
10767
+ } catch {
10768
+ }
10769
+ return cursor;
10770
+ };
10771
+ mtr.render = (ctx, left, top, _styleOverride, fabricObject) => {
10772
+ var _a3, _b2;
10773
+ const action = (_b2 = (_a3 = fabricObject.canvas) == null ? void 0 : _a3._currentTransform) == null ? void 0 : _b2.action;
10774
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10775
+ const scale = getVisualScale(fabricObject);
10776
+ const p = getHoverProgress(fabricObject, "mtr");
10777
+ const r = 11 * scale;
10778
+ ctx.save();
10779
+ ctx.beginPath();
10780
+ ctx.arc(left, top, r, 0, Math.PI * 2);
10781
+ ctx.closePath();
10782
+ ctx.fillStyle = lerpFill(p);
10783
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10784
+ ctx.lineWidth = 0.75 * (1 - p);
10785
+ ctx.shadowColor = "rgba(15, 23, 42, 0.4)";
10786
+ ctx.shadowBlur = 10;
10787
+ ctx.shadowOffsetY = 2;
10788
+ ctx.fill();
10789
+ if (p < 1) ctx.stroke();
10790
+ ctx.shadowColor = "transparent";
10791
+ ctx.shadowBlur = 0;
10792
+ ctx.shadowOffsetY = 0;
10793
+ const iconColor = p > 0.5 ? "#ffffff" : "rgba(15, 23, 42, 0.85)";
10794
+ ctx.strokeStyle = iconColor;
10795
+ ctx.fillStyle = iconColor;
10796
+ ctx.lineJoin = "miter";
10797
+ ctx.miterLimit = 4;
10798
+ {
10799
+ const target = r * 1.55;
10800
+ const s = target / 24;
10801
+ ctx.translate(left, top);
10802
+ ctx.scale(s, s);
10803
+ ctx.translate(-12, -12);
10804
+ ctx.strokeStyle = iconColor;
10805
+ ctx.lineWidth = 1 / s;
10806
+ ROTATE_PATHS_2D.forEach((p2d, index) => {
10807
+ ctx.lineCap = index >= 4 ? "round" : "square";
10808
+ ctx.stroke(p2d);
10809
+ });
10810
+ }
10811
+ ctx.restore();
10812
+ };
10813
+ }
10814
+ const moveActionHandler = (eventData, transform, x, y) => {
10815
+ const target = transform.target;
10816
+ if (!target) return false;
10817
+ if (!transform.__pixldocsMoveStart) {
10818
+ transform.__pixldocsMoveStart = {
10819
+ x,
10820
+ y,
10821
+ left: target.left ?? 0,
10822
+ top: target.top ?? 0
10823
+ };
10824
+ }
10825
+ const s = transform.__pixldocsMoveStart;
10826
+ target.set({ left: s.left + (x - s.x), top: s.top + (y - s.y) });
10827
+ target.setCoords();
10828
+ try {
10829
+ const canvas = target.canvas;
10830
+ if (canvas) {
10831
+ canvas.fire("object:moving", { target, e: eventData == null ? void 0 : eventData.e, transform, pointer: { x, y } });
10832
+ target.fire("moving", { e: eventData == null ? void 0 : eventData.e, transform, pointer: { x, y } });
10833
+ }
10834
+ } catch {
10835
+ }
10836
+ return true;
10837
+ };
10838
+ const renderMoveHandle = (ctx, left, top, _styleOverride, fabricObject) => {
10839
+ var _a3, _b2;
10840
+ if (!shouldCollapseHandles(fabricObject)) return;
10841
+ const action = (_b2 = (_a3 = fabricObject.canvas) == null ? void 0 : _a3._currentTransform) == null ? void 0 : _b2.action;
10842
+ if (action === "drag" && fabricObject.__pixldocsDragMoved) return;
10843
+ const scale = getVisualScale(fabricObject);
10844
+ const p = getHoverProgress(fabricObject, "mvh");
10845
+ const r = 11 * scale;
10846
+ ctx.save();
10847
+ ctx.beginPath();
10848
+ ctx.arc(left, top, r, 0, Math.PI * 2);
10849
+ ctx.closePath();
10850
+ ctx.fillStyle = lerpFill(p);
10851
+ ctx.strokeStyle = `rgba(15, 23, 42, ${0.18 * (1 - p)})`;
10852
+ ctx.lineWidth = 0.75 * (1 - p);
10853
+ ctx.shadowColor = "rgba(15, 23, 42, 0.4)";
10854
+ ctx.shadowBlur = 10;
10855
+ ctx.shadowOffsetY = 2;
10856
+ ctx.fill();
10857
+ if (p < 1) ctx.stroke();
10858
+ ctx.shadowColor = "transparent";
10859
+ ctx.shadowBlur = 0;
10860
+ ctx.shadowOffsetY = 0;
10861
+ const iconColor = p > 0.5 ? "#ffffff" : "rgba(15, 23, 42, 0.85)";
10862
+ ctx.fillStyle = iconColor;
10863
+ const target = r * 1.5;
10864
+ const s = target / 24;
10865
+ ctx.translate(left, top);
10866
+ ctx.scale(s, s);
10867
+ ctx.translate(-12, -12);
10868
+ for (const p2d of MOVE_PATHS_2D) ctx.fill(p2d);
10869
+ ctx.restore();
10870
+ };
10871
+ if (!controls.mvh) {
10872
+ const mvh = new fabric.Control({
10873
+ x: 0,
10874
+ y: 0.5,
10875
+ offsetY: 28,
10876
+ sizeX: 22,
10877
+ sizeY: 22,
10878
+ touchSizeX: 32,
10879
+ touchSizeY: 32,
10880
+ cursorStyle: "move",
10881
+ actionName: "drag",
10882
+ actionHandler: moveActionHandler,
10883
+ render: renderMoveHandle
10884
+ });
10885
+ mvh.withConnection = false;
10886
+ controls.mvh = mvh;
10887
+ } else {
10888
+ controls.mvh.render = renderMoveHandle;
10889
+ controls.mvh.actionHandler = moveActionHandler;
10890
+ controls.mvh.cursorStyle = "move";
10891
+ }
10892
+ wrapCollapseVisibility(controls.mvh, "mvh");
10893
+ const baseGetVisibility = (_a2 = controls.mvh.getVisibility) == null ? void 0 : _a2.bind(controls.mvh);
10894
+ if (baseGetVisibility && !controls.mvh.__pixldocsMvhVisibilityWrapped) {
10895
+ controls.mvh.getVisibility = (fabricObject, key) => {
10896
+ if (!shouldCollapseHandles(fabricObject)) return false;
10897
+ return baseGetVisibility(fabricObject, key);
10898
+ };
10899
+ controls.mvh.__pixldocsMvhVisibilityWrapped = true;
10900
+ }
10901
+ return controls;
10902
+ };
10903
+ ensureCanvaControlRenders = (obj) => {
10904
+ try {
10905
+ if (obj && obj.controls) installPillRenders(obj.controls);
10906
+ if (obj && Array.isArray(obj._objects)) {
10907
+ for (const child of obj._objects) {
10908
+ if (child && child.controls) installPillRenders(child.controls);
10909
+ }
10910
+ }
10911
+ } catch (e) {
10912
+ }
10913
+ };
10914
+ const origObj = cu.createObjectDefaultControls.bind(cu);
10915
+ cu.createObjectDefaultControls = () => installPillRenders(origObj());
10916
+ if (typeof cu.createTextboxDefaultControls === "function") {
10917
+ const origTb = cu.createTextboxDefaultControls.bind(cu);
10918
+ cu.createTextboxDefaultControls = () => installPillRenders(origTb());
10919
+ }
10920
+ const wrapClassCreateControls = (Klass) => {
10921
+ if (!Klass || typeof Klass.createControls !== "function") return;
10922
+ const orig = Klass.createControls.bind(Klass);
10923
+ Klass.createControls = () => {
10924
+ const res = orig();
10925
+ if (res && res.controls) installPillRenders(res.controls);
10926
+ return res;
10927
+ };
10928
+ };
10929
+ wrapClassCreateControls(fabric.InteractiveFabricObject);
10930
+ wrapClassCreateControls(fabric.FabricObject);
10931
+ wrapClassCreateControls(fabric.Textbox);
10932
+ wrapClassCreateControls(fabric.IText);
10933
+ const CanvasProto = (_b = fabric.Canvas) == null ? void 0 : _b.prototype;
10934
+ if (CanvasProto && typeof CanvasProto._setCursorFromEvent === "function") {
10935
+ const origSet = CanvasProto._setCursorFromEvent;
10936
+ CanvasProto._setCursorFromEvent = function(e, target) {
10937
+ const prev = target && target.__corner;
10938
+ const res = origSet.call(this, e, target);
10939
+ const next = target && target.__corner;
10940
+ if (prev !== next) {
10941
+ try {
10942
+ this.requestRenderAll();
10943
+ } catch {
10944
+ }
10945
+ }
10946
+ return res;
10947
+ };
10948
+ }
10949
+ }
10950
+ } catch (e) {
10951
+ console.warn("[PageCanvas] Failed to install Canva-style control handles:", e);
10952
+ }
10953
+ const scaleTextPathConfig = (textPath, sx, sy, uniform) => {
10954
+ if (!textPath || typeof textPath !== "object") return textPath;
10955
+ const next = JSON.parse(JSON.stringify(textPath));
10956
+ if (typeof next.radius === "number") next.radius *= uniform;
10957
+ if (next.bbox) {
10958
+ if (typeof next.bbox.width === "number") next.bbox.width *= sx;
10959
+ if (typeof next.bbox.height === "number") next.bbox.height *= sy;
10960
+ }
10961
+ if (next.endpoints) {
10962
+ if (typeof next.endpoints.leftY === "number") next.endpoints.leftY *= sy;
10963
+ if (typeof next.endpoints.rightY === "number") next.endpoints.rightY *= sy;
10964
+ if (typeof next.endpoints.centerY === "number") next.endpoints.centerY *= sy;
10965
+ }
10966
+ if (next.pivot) {
10967
+ if (typeof next.pivot.x === "number") next.pivot.x *= sx;
10968
+ if (typeof next.pivot.y === "number") next.pivot.y *= sy;
10969
+ }
10970
+ if (next.bezier) {
10971
+ ["p0", "c0", "c1", "p1"].forEach((key) => {
10972
+ if (Array.isArray(next.bezier[key])) {
10973
+ next.bezier[key][0] *= sx;
10974
+ next.bezier[key][1] *= sy;
10975
+ }
10976
+ });
10977
+ }
10978
+ return next;
10979
+ };
10980
+ const scaleUpdateNumber = (updates, source, key, factor) => {
10981
+ const value = Number(source == null ? void 0 : source[key]);
10982
+ if (Number.isFinite(value)) updates[key] = value * factor;
10983
+ };
10984
+ const bakeTextboxScaleIntoTypography = (obj, sourceElement) => {
10985
+ const sx = Math.abs(obj.scaleX ?? 1) || 1;
10986
+ const sy = Math.abs(obj.scaleY ?? 1) || 1;
10987
+ if (Math.abs(sx - 1) < 1e-3 && Math.abs(sy - 1) < 1e-3) return null;
10988
+ const isUniform = Math.abs(sx - sy) < 0.01;
10989
+ const fontScale = isUniform ? (sx + sy) / 2 : Math.abs(sy - 1) > 1e-3 ? sy : 1;
10990
+ const effectScale = isUniform ? fontScale : Math.max(1e-3, Math.sqrt(sx * sy));
10991
+ const updates = {
10992
+ width: Math.max(20, (obj.width ?? (sourceElement == null ? void 0 : sourceElement.width) ?? 20) * sx),
10993
+ scaleX: 1,
10994
+ scaleY: 1
10995
+ };
10996
+ if (fontScale !== 1) {
10997
+ updates.fontSize = Math.max(1, Number(obj.fontSize || (sourceElement == null ? void 0 : sourceElement.fontSize) || 16) * fontScale);
10998
+ const minBoxHeight = Number(obj.minBoxHeight ?? (sourceElement == null ? void 0 : sourceElement.minBoxHeight));
10999
+ if (Number.isFinite(minBoxHeight) && minBoxHeight > 0) updates.minBoxHeight = minBoxHeight * sy;
11000
+ }
11001
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "strokeWidth", effectScale);
11002
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowBlur", effectScale);
11003
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowDistance", effectScale);
11004
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowOffsetX", sx);
11005
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textShadowOffsetY", sy);
11006
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingTop", sy);
11007
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingBottom", sy);
11008
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingLeft", sx);
11009
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPaddingRight", sx);
11010
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgPadding", effectScale);
11011
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxTL", effectScale);
11012
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxTR", effectScale);
11013
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxBR", effectScale);
11014
+ scaleUpdateNumber(updates, sourceElement ?? void 0, "textBgRxBL", effectScale);
11015
+ const textPath = obj.textPath ?? (sourceElement == null ? void 0 : sourceElement.textPath);
11016
+ if (textPath) updates.textPath = scaleTextPathConfig(textPath, sx, sy, effectScale);
11017
+ const center = obj.getCenterPoint();
11018
+ obj.set({
11019
+ width: updates.width,
11020
+ scaleX: 1,
11021
+ scaleY: 1,
11022
+ ...updates.fontSize ? { fontSize: updates.fontSize } : {},
11023
+ ...updates.strokeWidth !== void 0 ? { strokeWidth: updates.strokeWidth } : {}
11024
+ });
11025
+ if (updates.minBoxHeight !== void 0) obj.minBoxHeight = updates.minBoxHeight;
11026
+ if (updates.textPath) obj.textPath = updates.textPath;
11027
+ const shadow = obj.shadow;
11028
+ if (shadow) {
11029
+ shadow.blur = updates.textShadowBlur ?? shadow.blur;
11030
+ shadow.offsetX = updates.textShadowOffsetX ?? shadow.offsetX;
11031
+ shadow.offsetY = updates.textShadowOffsetY ?? shadow.offsetY;
11032
+ }
11033
+ if ((sourceElement == null ? void 0 : sourceElement.type) === "text") {
11034
+ const bakedElement = { ...sourceElement, ...updates };
11035
+ applyTextBackground(obj, extractTextBgConfig(bakedElement));
11036
+ applyTextShadow(obj, bakedElement);
11037
+ }
11038
+ try {
11039
+ obj.initDimensions();
11040
+ } catch {
11041
+ }
11042
+ obj.setPositionByOrigin(center, "center", "center");
11043
+ obj.setCoords();
11044
+ obj.dirty = true;
11045
+ obj.__pixldocsBakedTextScaleUpdates = {
11046
+ ...obj.__pixldocsBakedTextScaleUpdates || {},
11047
+ ...updates
11048
+ };
11049
+ return updates;
11050
+ };
9873
11051
  function applyWarpAwareSelectionBorders(selection) {
9874
11052
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
9875
11053
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -9937,8 +11115,11 @@ const PageCanvas = forwardRef(
9937
11115
  const hasRunPostReadyReflowForPageRef = useRef(null);
9938
11116
  const hasNotifiedReadyForPageRef = useRef(null);
9939
11117
  const hasClearedCachesBeforeFirstSyncRef = useRef(false);
11118
+ const projectSettingsRef = useRef(projectSettings);
11119
+ projectSettingsRef.current = projectSettings;
9940
11120
  const [guides, setGuides] = useState([]);
9941
11121
  const [gridResizeLabel, setGridResizeLabel] = useState(null);
11122
+ const [hoverBounds, setHoverBounds] = useState(null);
9942
11123
  const [rotationLabel, setRotationLabel] = useState(null);
9943
11124
  const [sizeLabel, setSizeLabel] = useState(null);
9944
11125
  const [ready, setReady] = useState(false);
@@ -10005,7 +11186,8 @@ const PageCanvas = forwardRef(
10005
11186
  useRef(null);
10006
11187
  useRef(null);
10007
11188
  useRef(/* @__PURE__ */ new Map());
10008
- useRef(null);
11189
+ const groupResizeActiveSnapRef = useRef(null);
11190
+ const objectResizeActiveSnapRef = useRef(null);
10009
11191
  useRef(null);
10010
11192
  useRef(null);
10011
11193
  useRef(null);
@@ -10147,33 +11329,358 @@ const PageCanvas = forwardRef(
10147
11329
  (movingObj) => {
10148
11330
  const fabricCanvas = fabricRef.current;
10149
11331
  if (!fabricCanvas) return { guides: [], snapDx: 0, snapDy: 0 };
11332
+ const ps = projectSettingsRef.current;
10150
11333
  return calculateSnapGuides(
10151
11334
  movingObj,
10152
11335
  fabricCanvas,
10153
11336
  canvasWidth,
10154
11337
  canvasHeight,
10155
- projectSettings.snapToGuides,
10156
- projectSettings.snapThreshold
11338
+ ps.snapToGuides,
11339
+ ps.snapThreshold
10157
11340
  );
10158
11341
  },
10159
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11342
+ [canvasWidth, canvasHeight]
10160
11343
  );
11344
+ const getResizeExcludeIdsCallback = useCallback((obj) => {
11345
+ const ids = /* @__PURE__ */ new Set();
11346
+ const ownId = getObjectId(obj);
11347
+ if (ownId) ids.add(ownId);
11348
+ if (obj instanceof fabric.ActiveSelection) {
11349
+ obj.getObjects().forEach((member) => {
11350
+ const id = getObjectId(member);
11351
+ if (id) ids.add(id);
11352
+ });
11353
+ } else if (obj instanceof fabric.Group && typeof obj.getObjects === "function") {
11354
+ obj.getObjects().forEach((member) => {
11355
+ const id = getObjectId(member);
11356
+ if (id) ids.add(id);
11357
+ });
11358
+ }
11359
+ return ids;
11360
+ }, []);
11361
+ const getLogicalGroupSnapBoundsCallback = useCallback((excludeIds = []) => {
11362
+ const fabricCanvas = fabricRef.current;
11363
+ if (!fabricCanvas) return [];
11364
+ const excluded = new Set(excludeIds);
11365
+ const page = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11366
+ const children = (page == null ? void 0 : page.children) ?? pageChildren ?? [];
11367
+ const objectBounds = /* @__PURE__ */ new Map();
11368
+ for (const object of fabricCanvas.getObjects()) {
11369
+ const id = getObjectId(object);
11370
+ if (!id || id === "__background__") continue;
11371
+ try {
11372
+ object.setCoords();
11373
+ } catch {
11374
+ }
11375
+ const bounds2 = object.getBoundingRect();
11376
+ objectBounds.set(id, { left: bounds2.left, top: bounds2.top, width: bounds2.width, height: bounds2.height });
11377
+ }
11378
+ const bounds = [];
11379
+ const visit = (nodes) => {
11380
+ for (const node of nodes) {
11381
+ if (!isGroup(node)) continue;
11382
+ const memberIds = getAllElementIds(node.children ?? []);
11383
+ if (memberIds.length === 0) continue;
11384
+ if (memberIds.some((id) => excluded.has(id)) || excluded.has(node.id)) {
11385
+ visit(node.children ?? []);
11386
+ continue;
11387
+ }
11388
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
11389
+ for (const memberId of memberIds) {
11390
+ const b = objectBounds.get(memberId);
11391
+ if (!b) continue;
11392
+ minX = Math.min(minX, b.left);
11393
+ minY = Math.min(minY, b.top);
11394
+ maxX = Math.max(maxX, b.left + b.width);
11395
+ maxY = Math.max(maxY, b.top + b.height);
11396
+ }
11397
+ if (Number.isFinite(minX) && Number.isFinite(minY) && maxX > minX && maxY > minY) {
11398
+ bounds.push({ left: minX, top: minY, width: maxX - minX, height: maxY - minY });
11399
+ }
11400
+ visit(node.children ?? []);
11401
+ }
11402
+ };
11403
+ visit(children);
11404
+ const seen = /* @__PURE__ */ new Set();
11405
+ return bounds.filter((b) => {
11406
+ const key = `${Math.round(b.left)}:${Math.round(b.top)}:${Math.round(b.width)}:${Math.round(b.height)}`;
11407
+ if (seen.has(key)) return false;
11408
+ seen.add(key);
11409
+ return true;
11410
+ });
11411
+ }, [pageId, pageChildren]);
10161
11412
  const calculateScaleSnapGuidesCallback = useCallback(
10162
11413
  (scalingObj, corner) => {
10163
11414
  const fabricCanvas = fabricRef.current;
10164
11415
  if (!fabricCanvas) return [];
11416
+ const ps = projectSettingsRef.current;
11417
+ const excludeIds = getResizeExcludeIdsCallback(scalingObj);
10165
11418
  return calculateScaleSnapGuides(
10166
11419
  scalingObj,
10167
11420
  corner,
10168
- fabricCanvas,
11421
+ fabricCanvas,
11422
+ canvasWidth,
11423
+ canvasHeight,
11424
+ ps.snapToGuides,
11425
+ ps.snapThreshold || 4,
11426
+ getLogicalGroupSnapBoundsCallback(excludeIds)
11427
+ );
11428
+ },
11429
+ [canvasWidth, canvasHeight, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback]
11430
+ );
11431
+ const snapDuringScaleCallback = useCallback(
11432
+ (obj, corner) => {
11433
+ const fc = fabricRef.current;
11434
+ if (!fc || !obj || !corner) return;
11435
+ const ps = projectSettingsRef.current;
11436
+ if (!ps.snapToGuides) return;
11437
+ if (obj instanceof fabric.Textbox && (corner === "ml" || corner === "mr")) {
11438
+ const sourceEl = getObjectId(obj) ? elementsRef.current.find((el) => el.id === getObjectId(obj)) : void 0;
11439
+ bakeTextboxScaleIntoTypography(obj, sourceEl);
11440
+ }
11441
+ try {
11442
+ obj.setCoords();
11443
+ } catch {
11444
+ }
11445
+ const br = obj.getBoundingRect();
11446
+ const excludeIds = getResizeExcludeIdsCallback(obj);
11447
+ const snapThreshold = ps.snapThreshold || 4;
11448
+ const snapped = applyScaleSnapToBox(
11449
+ { left: br.left, top: br.top, width: br.width, height: br.height },
11450
+ corner,
11451
+ fc,
10169
11452
  canvasWidth,
10170
11453
  canvasHeight,
10171
- projectSettings.snapToGuides,
10172
- projectSettings.snapThreshold
11454
+ true,
11455
+ snapThreshold,
11456
+ getObjectId(obj),
11457
+ {
11458
+ hysteresis: 3,
11459
+ activeSnapRef: objectResizeActiveSnapRef,
11460
+ roundSnappedOnly: true,
11461
+ additionalBounds: getLogicalGroupSnapBoundsCallback(excludeIds),
11462
+ excludeObjectIds: excludeIds,
11463
+ matchDimensions: true
11464
+ }
10173
11465
  );
11466
+ const maxSnapShift = Math.max(snapThreshold + 3, 10);
11467
+ const brRight = br.left + br.width;
11468
+ const brBottom = br.top + br.height;
11469
+ const snappedRight = snapped.left + snapped.width;
11470
+ const snappedBottom = snapped.top + snapped.height;
11471
+ const xJump = Math.max(Math.abs(snapped.left - br.left), Math.abs(snappedRight - brRight));
11472
+ const yJump = Math.max(Math.abs(snapped.top - br.top), Math.abs(snappedBottom - brBottom));
11473
+ if (xJump > maxSnapShift) {
11474
+ snapped.left = br.left;
11475
+ snapped.width = br.width;
11476
+ if (objectResizeActiveSnapRef.current) {
11477
+ delete objectResizeActiveSnapRef.current.left;
11478
+ delete objectResizeActiveSnapRef.current.right;
11479
+ delete objectResizeActiveSnapRef.current.width;
11480
+ }
11481
+ }
11482
+ if (yJump > maxSnapShift) {
11483
+ snapped.top = br.top;
11484
+ snapped.height = br.height;
11485
+ if (objectResizeActiveSnapRef.current) {
11486
+ delete objectResizeActiveSnapRef.current.top;
11487
+ delete objectResizeActiveSnapRef.current.bottom;
11488
+ delete objectResizeActiveSnapRef.current.height;
11489
+ }
11490
+ }
11491
+ 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;
11492
+ if (obj instanceof fabric.Textbox && (corner === "ml" || corner === "mr")) {
11493
+ if (corner.includes("l") !== corner.includes("r")) {
11494
+ obj.set({ width: Math.max(20, snapped.width) });
11495
+ obj.initDimensions();
11496
+ obj.setCoords();
11497
+ const after2 = obj.getBoundingRect();
11498
+ obj.set({ left: (obj.left ?? 0) + (snapped.left - after2.left), top: (obj.top ?? 0) + (snapped.top - after2.top) });
11499
+ obj.setCoords();
11500
+ }
11501
+ return;
11502
+ }
11503
+ const baseW = obj.width ?? 1;
11504
+ const baseH = obj.height ?? 1;
11505
+ const signX = (obj.scaleX ?? 1) < 0 ? -1 : 1;
11506
+ const signY = (obj.scaleY ?? 1) < 0 ? -1 : 1;
11507
+ const newScaleX = snapped.width / Math.max(1, baseW) * signX;
11508
+ const newScaleY = snapped.height / Math.max(1, baseH) * signY;
11509
+ obj.set({ scaleX: newScaleX, scaleY: newScaleY });
11510
+ obj.setCoords();
11511
+ const after = obj.getBoundingRect();
11512
+ obj.set({ left: (obj.left ?? 0) + (snapped.left - after.left), top: (obj.top ?? 0) + (snapped.top - after.top) });
11513
+ obj.setCoords();
10174
11514
  },
10175
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11515
+ [canvasWidth, canvasHeight, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback]
10176
11516
  );
11517
+ const installImageResizeControlsWithSnap = useCallback((group) => {
11518
+ group.__resizeSnapHandler = (target, corner) => {
11519
+ const fc = fabricRef.current ?? target.canvas;
11520
+ if (!fc || !isActiveRef.current || !corner) return;
11521
+ fc.__isUserTransforming = true;
11522
+ didTransformRef.current = true;
11523
+ lastResizeScaleTargetRef.current = target;
11524
+ const targetId = getObjectId(target);
11525
+ if (targetId && targetId !== "__background__") {
11526
+ preserveSelectionAfterTransformIdRef.current = targetId;
11527
+ transformingIdsRef.current.add(targetId);
11528
+ }
11529
+ target.getObjects().forEach((member) => {
11530
+ const memberId = getObjectId(member);
11531
+ if (memberId) transformingIdsRef.current.add(memberId);
11532
+ });
11533
+ const gridGuidesForResize = [];
11534
+ try {
11535
+ target.setCoords();
11536
+ const br = target.getBoundingRect();
11537
+ const excludeIds = getResizeExcludeIdsCallback(target);
11538
+ const ps = projectSettingsRef.current;
11539
+ const snapThreshold = ps.snapThreshold || 4;
11540
+ const snapped = applyScaleSnapToBox(
11541
+ { left: br.left, top: br.top, width: br.width, height: br.height },
11542
+ corner,
11543
+ fc,
11544
+ canvasWidth,
11545
+ canvasHeight,
11546
+ ps.snapToGuides,
11547
+ snapThreshold,
11548
+ targetId ?? void 0,
11549
+ {
11550
+ hysteresis: 3,
11551
+ activeSnapRef: objectResizeActiveSnapRef,
11552
+ roundSnappedOnly: true,
11553
+ additionalBounds: getLogicalGroupSnapBoundsCallback(excludeIds),
11554
+ excludeObjectIds: excludeIds,
11555
+ matchDimensions: true
11556
+ }
11557
+ );
11558
+ const maxSnapShift = Math.max(snapThreshold + 3, 10);
11559
+ const brRight = br.left + br.width;
11560
+ const brBottom = br.top + br.height;
11561
+ const snappedRight = snapped.left + snapped.width;
11562
+ const snappedBottom = snapped.top + snapped.height;
11563
+ const xJump = Math.max(Math.abs(snapped.left - br.left), Math.abs(snappedRight - brRight));
11564
+ const yJump = Math.max(Math.abs(snapped.top - br.top), Math.abs(snappedBottom - brBottom));
11565
+ if (xJump > maxSnapShift) {
11566
+ snapped.left = br.left;
11567
+ snapped.width = br.width;
11568
+ if (objectResizeActiveSnapRef.current) {
11569
+ delete objectResizeActiveSnapRef.current.left;
11570
+ delete objectResizeActiveSnapRef.current.right;
11571
+ delete objectResizeActiveSnapRef.current.width;
11572
+ }
11573
+ }
11574
+ if (yJump > maxSnapShift) {
11575
+ snapped.top = br.top;
11576
+ snapped.height = br.height;
11577
+ if (objectResizeActiveSnapRef.current) {
11578
+ delete objectResizeActiveSnapRef.current.top;
11579
+ delete objectResizeActiveSnapRef.current.bottom;
11580
+ delete objectResizeActiveSnapRef.current.height;
11581
+ }
11582
+ }
11583
+ const gridX = ps.gridSizeX ?? ps.gridSize ?? 0;
11584
+ const gridY = ps.gridSizeY ?? ps.gridSize ?? 0;
11585
+ if (ps.snapToGrid && gridX > 0 && gridY > 0) {
11586
+ const hasL = corner.includes("l");
11587
+ const hasR = corner.includes("r");
11588
+ const hasT = corner.includes("t");
11589
+ const hasB = corner.includes("b");
11590
+ const anchorRight = snapped.left + snapped.width;
11591
+ const anchorBottom = snapped.top + snapped.height;
11592
+ if (hasL) {
11593
+ const newLeft = Math.round(snapped.left / gridX) * gridX;
11594
+ const newWidth = Math.max(20, anchorRight - newLeft);
11595
+ snapped.left = anchorRight - newWidth;
11596
+ snapped.width = newWidth;
11597
+ gridGuidesForResize.push({ type: "vertical", position: snapped.left, kind: "grid" });
11598
+ } else if (hasR) {
11599
+ const newRight = Math.round(anchorRight / gridX) * gridX;
11600
+ snapped.width = Math.max(20, newRight - snapped.left);
11601
+ gridGuidesForResize.push({ type: "vertical", position: snapped.left + snapped.width, kind: "grid" });
11602
+ }
11603
+ if (hasT) {
11604
+ const newTop = Math.round(snapped.top / gridY) * gridY;
11605
+ const newHeight = Math.max(20, anchorBottom - newTop);
11606
+ snapped.top = anchorBottom - newHeight;
11607
+ snapped.height = newHeight;
11608
+ gridGuidesForResize.push({ type: "horizontal", position: snapped.top, kind: "grid" });
11609
+ } else if (hasB) {
11610
+ const newBottom = Math.round(anchorBottom / gridY) * gridY;
11611
+ snapped.height = Math.max(20, newBottom - snapped.top);
11612
+ gridGuidesForResize.push({ type: "horizontal", position: snapped.top + snapped.height, kind: "grid" });
11613
+ }
11614
+ }
11615
+ 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;
11616
+ if (changed) {
11617
+ const ct = target.__cropData;
11618
+ if (ct) {
11619
+ const next = { ...snapped };
11620
+ const isCornerHandle = corner.includes("l") !== corner.includes("r") && corner.includes("t") !== corner.includes("b");
11621
+ const widthChanged = Math.abs(snapped.width - br.width) > 0.01;
11622
+ const heightChanged = Math.abs(snapped.height - br.height) > 0.01;
11623
+ if (isCornerHandle && br.width > 0 && br.height > 0 && widthChanged !== heightChanged) {
11624
+ const aspect = br.width / br.height;
11625
+ if (widthChanged) {
11626
+ next.height = Math.max(20, next.width / aspect);
11627
+ if (corner.includes("t")) next.top = br.top + br.height - next.height;
11628
+ } else {
11629
+ next.width = Math.max(20, next.height * aspect);
11630
+ if (corner.includes("l")) next.left = br.left + br.width - next.width;
11631
+ }
11632
+ } else if (isCornerHandle && br.width > 0 && br.height > 0) {
11633
+ const aspect = br.width / br.height;
11634
+ const nextAspect = next.width / Math.max(1, next.height);
11635
+ if (Math.abs(nextAspect - aspect) > 0.01) {
11636
+ const widthDelta = Math.abs(next.width - br.width);
11637
+ const heightDelta = Math.abs(next.height - br.height);
11638
+ if (widthDelta <= heightDelta) {
11639
+ next.height = Math.max(20, next.width / aspect);
11640
+ if (corner.includes("t")) next.top = br.top + br.height - next.height;
11641
+ } else {
11642
+ next.width = Math.max(20, next.height * aspect);
11643
+ if (corner.includes("l")) next.left = br.left + br.width - next.width;
11644
+ }
11645
+ }
11646
+ }
11647
+ ct.frameW = Math.max(20, next.width);
11648
+ ct.frameH = Math.max(20, next.height);
11649
+ target.set({
11650
+ left: next.left + ct.frameW / 2,
11651
+ top: next.top + ct.frameH / 2,
11652
+ width: ct.frameW,
11653
+ height: ct.frameH,
11654
+ scaleX: 1,
11655
+ scaleY: 1,
11656
+ originX: "center",
11657
+ originY: "center"
11658
+ });
11659
+ updateCoverLayout(target);
11660
+ }
11661
+ }
11662
+ } catch {
11663
+ snapDuringScaleCallback(target, corner);
11664
+ }
11665
+ try {
11666
+ target.setCoords();
11667
+ const br = target.getBoundingRect();
11668
+ setSizeLabel({
11669
+ width: Math.round(br.width),
11670
+ height: Math.round(br.height),
11671
+ x: br.left + br.width / 2,
11672
+ y: br.top + br.height + 18
11673
+ });
11674
+ } catch {
11675
+ }
11676
+ const smartGuides = calculateScaleSnapGuidesCallback(target, corner);
11677
+ setGuides(gridGuidesForResize.length ? [...smartGuides, ...gridGuidesForResize] : smartGuides);
11678
+ setHoverBounds(null);
11679
+ };
11680
+ installCanvaMaskControls(group);
11681
+ applyControlSizeForZoom(group.canvas ?? fabricRef.current, group);
11682
+ ensureCanvaControlRenders(group);
11683
+ }, [calculateScaleSnapGuidesCallback, canvasHeight, canvasWidth, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback, snapDuringScaleCallback]);
10177
11684
  const isTransforming = useCallback((canvas2) => {
10178
11685
  if (!canvas2) return false;
10179
11686
  return !!canvas2._currentTransform || !!canvas2.__isUserTransforming;
@@ -10299,6 +11806,8 @@ const PageCanvas = forwardRef(
10299
11806
  // Transparent so underlay (page bg + group bgs) shows through
10300
11807
  backgroundColor: "transparent"
10301
11808
  });
11809
+ fabricCanvas.hoverCursor = "default";
11810
+ fabricCanvas.moveCursor = "move";
10302
11811
  if (!allowSelection) {
10303
11812
  fabricCanvas.selection = false;
10304
11813
  fabricCanvas.on("selection:created", () => {
@@ -10320,6 +11829,61 @@ const PageCanvas = forwardRef(
10320
11829
  fabricRef.current = fabricCanvas;
10321
11830
  const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
10322
11831
  fabricCanvas.__storeRegistryKey = storeRegistryKey;
11832
+ {
11833
+ const TWEEN_MS = 130;
11834
+ const active = /* @__PURE__ */ new Map();
11835
+ const ensureMap = (obj) => {
11836
+ if (!obj.__handleHoverProgress) obj.__handleHoverProgress = {};
11837
+ return obj.__handleHoverProgress;
11838
+ };
11839
+ const stateKey = (obj, key) => `${obj.__docuforgeId || ""}:${key}`;
11840
+ const step = (sk) => {
11841
+ const s = active.get(sk);
11842
+ if (!s) return;
11843
+ const t = Math.min(1, (performance.now() - s.start) / TWEEN_MS);
11844
+ const eased = 1 - Math.pow(1 - t, 3);
11845
+ const value = s.from + (s.to - s.from) * eased;
11846
+ const map = ensureMap(s.obj);
11847
+ map[s.key] = value;
11848
+ try {
11849
+ fabricCanvas.requestRenderAll();
11850
+ } catch {
11851
+ }
11852
+ if (t < 1) {
11853
+ s.raf = requestAnimationFrame(() => step(sk));
11854
+ } else {
11855
+ map[s.key] = s.to;
11856
+ active.delete(sk);
11857
+ }
11858
+ };
11859
+ const startTween = (obj, key, to) => {
11860
+ const sk = stateKey(obj, key);
11861
+ const map = ensureMap(obj);
11862
+ const from = map[key] ?? (to === 1 ? 0 : 1);
11863
+ if (from === to) return;
11864
+ const existing = active.get(sk);
11865
+ if (existing && existing.raf != null) cancelAnimationFrame(existing.raf);
11866
+ const s = { obj, key, start: performance.now(), from, to, raf: null };
11867
+ active.set(sk, s);
11868
+ s.raf = requestAnimationFrame(() => step(sk));
11869
+ };
11870
+ let prevTarget = null;
11871
+ let prevKey = null;
11872
+ fabricCanvas.on("mouse:move", (opt) => {
11873
+ const t = opt == null ? void 0 : opt.target;
11874
+ const key = t && t.__corner ? String(t.__corner) : null;
11875
+ if (t === prevTarget && key === prevKey) return;
11876
+ if (prevTarget && prevKey) startTween(prevTarget, prevKey, 0);
11877
+ if (t && key) startTween(t, key, 1);
11878
+ prevTarget = t;
11879
+ prevKey = key;
11880
+ });
11881
+ fabricCanvas.on("mouse:out", () => {
11882
+ if (prevTarget && prevKey) startTween(prevTarget, prevKey, 0);
11883
+ prevTarget = null;
11884
+ prevKey = null;
11885
+ });
11886
+ }
10323
11887
  const initFonts = async () => {
10324
11888
  try {
10325
11889
  await preloadAllFonts();
@@ -10409,6 +11973,17 @@ const PageCanvas = forwardRef(
10409
11973
  });
10410
11974
  fabricCanvas.on("mouse:up", () => {
10411
11975
  fabricCanvas.__isUserTransforming = false;
11976
+ objectResizeActiveSnapRef.current = null;
11977
+ groupResizeActiveSnapRef.current = null;
11978
+ try {
11979
+ for (const o of fabricCanvas.getObjects()) {
11980
+ if (o.__pixldocsDragMoved) o.__pixldocsDragMoved = false;
11981
+ }
11982
+ const active = fabricCanvas.getActiveObject();
11983
+ if (active == null ? void 0 : active.__pixldocsDragMoved) active.__pixldocsDragMoved = false;
11984
+ fabricCanvas.requestRenderAll();
11985
+ } catch {
11986
+ }
10412
11987
  });
10413
11988
  fabricCanvas.on("object:scaling", () => {
10414
11989
  fabricCanvas.__isUserTransforming = true;
@@ -10455,7 +12030,7 @@ const PageCanvas = forwardRef(
10455
12030
  didTransformRef.current = true;
10456
12031
  });
10457
12032
  const syncSelectionToStore = () => {
10458
- var _a2, _b, _c, _d;
12033
+ var _a2, _b2, _c, _d;
10459
12034
  if (!isActiveRef.current || isRebuildingRef.current || isSyncingSelectionToFabricRef.current || !allowSelection) return;
10460
12035
  const walkToTopmostGroup = (childId, children, activeEditingGroupId) => {
10461
12036
  let topmost = null;
@@ -10494,7 +12069,7 @@ const PageCanvas = forwardRef(
10494
12069
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
10495
12070
  const clickedId = ids[0];
10496
12071
  const state = useEditorStore.getState();
10497
- const currentPage2 = (_b = state.canvas.pages) == null ? void 0 : _b.find((p) => p.id === pageId);
12072
+ const currentPage2 = (_b2 = state.canvas.pages) == null ? void 0 : _b2.find((p) => p.id === pageId);
10498
12073
  const children = (currentPage2 == null ? void 0 : currentPage2.children) ?? [];
10499
12074
  const parent = walkToTopmostGroup(clickedId, children, activeEditingGroupId);
10500
12075
  const targetIsInCrop = !!(active instanceof fabric.Group && isCropGroupInCropMode(active) || (active == null ? void 0 : active.group) instanceof fabric.Group && isCropGroupInCropMode(active.group));
@@ -10641,8 +12216,10 @@ const PageCanvas = forwardRef(
10641
12216
  const activeObj = fabricCanvas.getActiveObject();
10642
12217
  if (activeObj instanceof fabric.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10643
12218
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12219
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10644
12220
  if (activeObj && !(activeObj instanceof fabric.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10645
- installCanvaMaskControls(activeObj);
12221
+ installImageResizeControlsWithSnap(activeObj);
12222
+ ensureCanvaControlRenders(activeObj);
10646
12223
  }
10647
12224
  });
10648
12225
  fabricCanvas.on("selection:updated", () => {
@@ -10654,12 +12231,14 @@ const PageCanvas = forwardRef(
10654
12231
  const activeObj = fabricCanvas.getActiveObject();
10655
12232
  if (activeObj instanceof fabric.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10656
12233
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12234
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10657
12235
  if (activeObj && !(activeObj instanceof fabric.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10658
- installCanvaMaskControls(activeObj);
12236
+ installImageResizeControlsWithSnap(activeObj);
12237
+ ensureCanvaControlRenders(activeObj);
10659
12238
  }
10660
12239
  });
10661
12240
  fabricCanvas.on("mouse:dblclick", (opt) => {
10662
- var _a2, _b;
12241
+ var _a2, _b2;
10663
12242
  const target = opt == null ? void 0 : opt.target;
10664
12243
  if (!target) return;
10665
12244
  if (target.isEditing) return;
@@ -10683,7 +12262,7 @@ const PageCanvas = forwardRef(
10683
12262
  const childId = getObjectId(hitChild);
10684
12263
  if (!childId) return;
10685
12264
  const stateNow = useEditorStore.getState();
10686
- const pageNow = (_b = stateNow.canvas.pages) == null ? void 0 : _b.find((p) => p.id === pageId);
12265
+ const pageNow = (_b2 = stateNow.canvas.pages) == null ? void 0 : _b2.find((p) => p.id === pageId);
10687
12266
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
10688
12267
  const chain = [];
10689
12268
  {
@@ -10810,7 +12389,7 @@ const PageCanvas = forwardRef(
10810
12389
  transformingIdsRef.current.clear();
10811
12390
  };
10812
12391
  const prepareGroupSelectionTransformStart = (target) => {
10813
- var _a2, _b;
12392
+ var _a2, _b2;
10814
12393
  const active = target instanceof fabric.ActiveSelection ? target : fabricCanvas.getActiveObject();
10815
12394
  if (!(active instanceof fabric.ActiveSelection)) return;
10816
12395
  if (!activeSelectionMoveStartRef.current || activeSelectionMoveStartRef.current.selection !== active) {
@@ -10824,7 +12403,7 @@ const PageCanvas = forwardRef(
10824
12403
  const groupId = active.__pixldocsGroupSelection;
10825
12404
  if (!groupId) return;
10826
12405
  if (((_a2 = groupSelectionTransformStartRef.current) == null ? void 0 : _a2.groupId) === groupId && groupSelectionTransformStartRef.current.selection === active) return;
10827
- const pageChildren2 = ((_b = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b.children) ?? [];
12406
+ const pageChildren2 = ((_b2 = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b2.children) ?? [];
10828
12407
  const groupNode = findNodeById(pageChildren2, groupId);
10829
12408
  if (!groupNode) return;
10830
12409
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
@@ -10908,7 +12487,7 @@ const PageCanvas = forwardRef(
10908
12487
  };
10909
12488
  let pendingShiftMultiSelect = null;
10910
12489
  const applyShiftMultiSelect = (target, event, baselineActive = fabricCanvas.getActiveObject(), baselineObjects = fabricCanvas.getActiveObjects()) => {
10911
- var _a2, _b, _c;
12490
+ var _a2, _b2, _c;
10912
12491
  if (!target || !target.selectable) return false;
10913
12492
  const active = baselineActive;
10914
12493
  if (!active || ((_a2 = active.getActiveControl) == null ? void 0 : _a2.call(active))) return false;
@@ -10942,7 +12521,7 @@ const PageCanvas = forwardRef(
10942
12521
  isSyncingSelectionToFabricRef.current = false;
10943
12522
  });
10944
12523
  }
10945
- (_b = event == null ? void 0 : event.preventDefault) == null ? void 0 : _b.call(event);
12524
+ (_b2 = event == null ? void 0 : event.preventDefault) == null ? void 0 : _b2.call(event);
10946
12525
  (_c = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _c.call(event);
10947
12526
  return true;
10948
12527
  };
@@ -10993,9 +12572,9 @@ const PageCanvas = forwardRef(
10993
12572
  return !!(((_a2 = o == null ? void 0 : o._ct) == null ? void 0 : _a2.isCropGroup) || (o == null ? void 0 : o.__cropGroup));
10994
12573
  };
10995
12574
  const promoteToCropGroup = (opt) => {
10996
- var _a2, _b, _c, _d, _e, _f;
12575
+ var _a2, _b2, _c, _d, _e, _f;
10997
12576
  const t = opt.target;
10998
- 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") {
12577
+ 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") {
10999
12578
  if (t && isCropGroup2(t)) {
11000
12579
  fabricCanvas._hoveredTarget = t;
11001
12580
  } else if ((t == null ? void 0 : t.group) && isCropGroup2(t.group)) {
@@ -11011,6 +12590,8 @@ const PageCanvas = forwardRef(
11011
12590
  const objects = fabricCanvas.getObjects();
11012
12591
  for (const obj of objects) {
11013
12592
  if (isCropGroup2(obj) && obj.containsPoint(pointer)) {
12593
+ installImageResizeControlsWithSnap(obj);
12594
+ ensureCanvaControlRenders(obj);
11014
12595
  fabricCanvas.setActiveObject(obj);
11015
12596
  opt.target = obj;
11016
12597
  fabricCanvas._hoveredTarget = obj;
@@ -11021,12 +12602,16 @@ const PageCanvas = forwardRef(
11021
12602
  }
11022
12603
  const g = t.group;
11023
12604
  if (g && isCropGroup2(g)) {
12605
+ installImageResizeControlsWithSnap(g);
12606
+ ensureCanvaControlRenders(g);
11024
12607
  fabricCanvas.setActiveObject(g);
11025
12608
  opt.target = g;
11026
12609
  fabricCanvas._hoveredTarget = g;
11027
12610
  return;
11028
12611
  }
11029
12612
  if (isCropGroup2(t)) {
12613
+ installImageResizeControlsWithSnap(t);
12614
+ ensureCanvaControlRenders(t);
11030
12615
  fabricCanvas.setActiveObject(t);
11031
12616
  t.set({
11032
12617
  selectable: true,
@@ -11079,7 +12664,7 @@ const PageCanvas = forwardRef(
11079
12664
  });
11080
12665
  }
11081
12666
  fabricCanvas.on("mouse:down", (opt) => {
11082
- var _a2, _b;
12667
+ var _a2, _b2;
11083
12668
  if (pendingShiftMultiSelect) {
11084
12669
  const pending = pendingShiftMultiSelect;
11085
12670
  pendingShiftMultiSelect = null;
@@ -11087,17 +12672,19 @@ const PageCanvas = forwardRef(
11087
12672
  return;
11088
12673
  }
11089
12674
  const target = opt.target;
11090
- 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;
12675
+ 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;
11091
12676
  if (cropGroup) {
11092
12677
  lockEdits();
11093
12678
  didTransformRef.current = false;
12679
+ installImageResizeControlsWithSnap(cropGroup);
12680
+ ensureCanvaControlRenders(cropGroup);
11094
12681
  fabricCanvas.setActiveObject(cropGroup);
11095
12682
  cropGroup.setCoords();
11096
12683
  fabricCanvas.requestRenderAll();
11097
12684
  }
11098
12685
  });
11099
12686
  const groupFabricUnionBBox = (g) => {
11100
- var _a2, _b;
12687
+ var _a2, _b2;
11101
12688
  const memberIds = new Set(getAllElementIds(g.children ?? []));
11102
12689
  if (memberIds.size === 0) return null;
11103
12690
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -11105,7 +12692,7 @@ const PageCanvas = forwardRef(
11105
12692
  for (const o of fabricCanvas.getObjects()) {
11106
12693
  const oid = getObjectId(o);
11107
12694
  if (!oid || !memberIds.has(oid)) continue;
11108
- const br = ((_a2 = o.getBoundingRect) == null ? void 0 : _a2.call(o, true, true)) ?? ((_b = o.getBoundingRect) == null ? void 0 : _b.call(o));
12695
+ const br = ((_a2 = o.getBoundingRect) == null ? void 0 : _a2.call(o, true, true)) ?? ((_b2 = o.getBoundingRect) == null ? void 0 : _b2.call(o));
11109
12696
  if (!br) continue;
11110
12697
  minX = Math.min(minX, br.left);
11111
12698
  minY = Math.min(minY, br.top);
@@ -11131,13 +12718,13 @@ const PageCanvas = forwardRef(
11131
12718
  return pick;
11132
12719
  };
11133
12720
  fabricCanvas.on("mouse:down:before", (opt) => {
11134
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
12721
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
11135
12722
  if (editLockRef.current) {
11136
12723
  const active = fabricCanvas.getActiveObject();
11137
12724
  if (active && (((_a2 = active._ct) == null ? void 0 : _a2.isCropGroup) || active.__cropGroup)) {
11138
12725
  opt.target = active;
11139
12726
  if (opt.e) {
11140
- (_c = (_b = opt.e).preventDefault) == null ? void 0 : _c.call(_b);
12727
+ (_c = (_b2 = opt.e).preventDefault) == null ? void 0 : _c.call(_b2);
11141
12728
  (_e = (_d = opt.e).stopPropagation) == null ? void 0 : _e.call(_d);
11142
12729
  }
11143
12730
  }
@@ -11152,6 +12739,10 @@ const PageCanvas = forwardRef(
11152
12739
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11153
12740
  const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11154
12741
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12742
+ if (target instanceof fabric.Textbox && target.__corner && (target.__corner === "ml" || target.__corner === "mr")) {
12743
+ const sourceEl = targetId ? elementsRef.current.find((el) => el.id === targetId) : void 0;
12744
+ bakeTextboxScaleIntoTypography(target, sourceEl);
12745
+ }
11155
12746
  if (isMultiSelectModifier(opt.e)) {
11156
12747
  const manualTarget = target && !(target instanceof fabric.ActiveSelection) && targetId && targetId !== "__background__" ? target : pickSelectableObjectAtPointer(opt.e);
11157
12748
  if (manualTarget) {
@@ -11178,11 +12769,39 @@ const PageCanvas = forwardRef(
11178
12769
  return topmost;
11179
12770
  };
11180
12771
  if (target && targetId && targetId !== "__background__") {
11181
- const parent = findTopmostPromotableGroup(targetId);
11182
- const targetIsInCrop = !!(target instanceof fabric.Group && isCropGroupInCropMode(target) || (target == null ? void 0 : target.group) instanceof fabric.Group && isCropGroupInCropMode(target.group));
12772
+ let effectiveTarget = target;
12773
+ let effectiveTargetId = targetId;
12774
+ const activeNowEarly = fabricCanvas.getActiveObject();
12775
+ const asGroupId = target instanceof fabric.ActiveSelection ? target.__pixldocsGroupSelection : void 0;
12776
+ if (target instanceof fabric.ActiveSelection && asGroupId && target === activeNowEarly) {
12777
+ try {
12778
+ const pointer = fabricCanvas.getViewportPoint(opt.e);
12779
+ const members = target.getObjects();
12780
+ for (let i = members.length - 1; i >= 0; i--) {
12781
+ const m = members[i];
12782
+ if (m.visible === false) continue;
12783
+ if (typeof m.containsPoint === "function" && m.containsPoint(pointer)) {
12784
+ const mid = getObjectId(m);
12785
+ if (mid) {
12786
+ effectiveTarget = m;
12787
+ effectiveTargetId = mid;
12788
+ }
12789
+ break;
12790
+ }
12791
+ }
12792
+ } catch {
12793
+ }
12794
+ }
12795
+ const parent = findTopmostPromotableGroup(effectiveTargetId);
12796
+ const targetIsInCrop = !!(effectiveTarget instanceof fabric.Group && isCropGroupInCropMode(effectiveTarget) || (effectiveTarget == null ? void 0 : effectiveTarget.group) instanceof fabric.Group && isCropGroupInCropMode(effectiveTarget.group));
11183
12797
  const activeNow = fabricCanvas.getActiveObject();
11184
12798
  const alreadyThisGroup = activeNow instanceof fabric.ActiveSelection && activeNow.__pixldocsGroupSelection === (parent == null ? void 0 : parent.id);
11185
12799
  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));
12800
+ const isDeepSelectKey = !!((_i = opt.e) == null ? void 0 : _i.altKey);
12801
+ if (isDeepSelectKey) {
12802
+ pendingGroupPromotionRef.current = null;
12803
+ return;
12804
+ }
11186
12805
  if (parent && !parent.backgroundColor && !targetIsInCrop && activeEditingGroupId !== parent.id && !alreadyThisGroup && !isMultiSelectKey) {
11187
12806
  const memberIds = new Set(getAllElementIds(parent.children ?? []));
11188
12807
  const memberObjs = fabricCanvas.getObjects().filter((o) => {
@@ -11206,10 +12825,33 @@ const PageCanvas = forwardRef(
11206
12825
  opt.target = only;
11207
12826
  pendingGroupPromotionRef.current = { groupId: parent.id, selection: only };
11208
12827
  }
12828
+ } else if (parent && !parent.backgroundColor && !targetIsInCrop && alreadyThisGroup && !isMultiSelectKey && effectiveTarget !== activeNow) {
12829
+ try {
12830
+ skipSelectionClearOnDiscardRef.current = true;
12831
+ preserveEditingScopeOnSelectionClearRef.current = true;
12832
+ restoreSuppressedGroupBorders();
12833
+ fabricCanvas.discardActiveObject();
12834
+ } finally {
12835
+ skipSelectionClearOnDiscardRef.current = false;
12836
+ preserveEditingScopeOnSelectionClearRef.current = false;
12837
+ }
12838
+ fabricCanvas.__activeEditingGroupId = parent.id;
12839
+ delete effectiveTarget.__pixldocsGroupSelection;
12840
+ delete effectiveTarget.__pixldocsLogicalGroupIds;
12841
+ try {
12842
+ (_j = effectiveTarget.set) == null ? void 0 : _j.call(effectiveTarget, { selectable: true, evented: true, hasBorders: true, hasControls: true });
12843
+ } catch {
12844
+ }
12845
+ fabricCanvas.setActiveObject(effectiveTarget);
12846
+ effectiveTarget.setCoords();
12847
+ fabricCanvas._target = effectiveTarget;
12848
+ opt.target = effectiveTarget;
12849
+ pendingGroupPromotionRef.current = null;
11209
12850
  }
11210
12851
  } else if (!target || targetId === "__background__") {
11211
- 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));
12852
+ 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));
11212
12853
  if (isMultiSelectKey) return;
12854
+ if ((_n = opt.e) == null ? void 0 : _n.altKey) return;
11213
12855
  try {
11214
12856
  const pointer = fabricCanvas.getPointer(opt.e);
11215
12857
  const px = pointer.x;
@@ -11280,6 +12922,43 @@ const PageCanvas = forwardRef(
11280
12922
  if (editLockRef.current) return;
11281
12923
  const t = opt.target;
11282
12924
  const tid = t ? getObjectId(t) : null;
12925
+ try {
12926
+ const activeIds = new Set(selectedIdsRef.current);
12927
+ const pointer = fabricCanvas.getPointer(opt.e);
12928
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12929
+ const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12930
+ const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
12931
+ const groupPick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
12932
+ if (groupPick && !activeIds.has(groupPick.group.id)) {
12933
+ const b = groupFabricUnionBBox(groupPick.group);
12934
+ if (b) {
12935
+ setHoverBounds({
12936
+ left: b.left,
12937
+ top: b.top,
12938
+ width: b.right - b.left,
12939
+ height: b.bottom - b.top,
12940
+ angle: 0
12941
+ });
12942
+ return;
12943
+ }
12944
+ }
12945
+ const isHoveringSelected = !!(tid && activeIds.has(tid));
12946
+ if (t && tid && tid !== "__background__" && !isHoveringSelected) {
12947
+ t.setCoords();
12948
+ const br = t.getBoundingRect();
12949
+ setHoverBounds({
12950
+ left: br.left,
12951
+ top: br.top,
12952
+ width: br.width,
12953
+ height: br.height,
12954
+ angle: 0
12955
+ });
12956
+ } else {
12957
+ setHoverBounds(null);
12958
+ }
12959
+ } catch {
12960
+ setHoverBounds(null);
12961
+ }
11283
12962
  if (t && tid && tid !== "__background__") return;
11284
12963
  try {
11285
12964
  const pointer = fabricCanvas.getPointer(opt.e);
@@ -11287,11 +12966,19 @@ const PageCanvas = forwardRef(
11287
12966
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
11288
12967
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11289
12968
  const pick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
11290
- fabricCanvas.defaultCursor = pick ? "move" : "default";
12969
+ fabricCanvas.defaultCursor = "default";
11291
12970
  } catch {
11292
12971
  fabricCanvas.defaultCursor = "default";
11293
12972
  }
11294
12973
  });
12974
+ fabricCanvas.on("mouse:out", () => {
12975
+ setHoverBounds(null);
12976
+ });
12977
+ fabricCanvas.on("mouse:down", () => {
12978
+ setHoverBounds(null);
12979
+ });
12980
+ fabricCanvas.on("selection:created", () => setHoverBounds(null));
12981
+ fabricCanvas.on("selection:updated", () => setHoverBounds(null));
11295
12982
  fabricCanvas.on("mouse:down", (ev) => {
11296
12983
  if (fabricCanvas._currentTransform) {
11297
12984
  lockEdits();
@@ -11307,6 +12994,8 @@ const PageCanvas = forwardRef(
11307
12994
  setGuides([]);
11308
12995
  setRotationLabel(null);
11309
12996
  setSizeLabel(null);
12997
+ objectResizeActiveSnapRef.current = null;
12998
+ groupResizeActiveSnapRef.current = null;
11310
12999
  dragStarted = false;
11311
13000
  const pendingPromotion = pendingGroupPromotionRef.current;
11312
13001
  pendingGroupPromotionRef.current = null;
@@ -11461,7 +13150,9 @@ const PageCanvas = forwardRef(
11461
13150
  const intrH = obj.height ?? 1;
11462
13151
  let newW = Math.max(1, intrW * Math.abs(sx));
11463
13152
  let newH = Math.max(1, intrH * Math.abs(sy));
11464
- if (obj instanceof fabric.Circle) {
13153
+ if (obj instanceof fabric.Ellipse) {
13154
+ obj.set({ rx: newW / 2, ry: newH / 2 });
13155
+ } else if (obj instanceof fabric.Circle) {
11465
13156
  const diameter = Math.max(1, Math.min(newW, newH));
11466
13157
  newW = diameter;
11467
13158
  newH = diameter;
@@ -11578,8 +13269,128 @@ const PageCanvas = forwardRef(
11578
13269
  }
11579
13270
  const transform = e.transform;
11580
13271
  const corner = (transform == null ? void 0 : transform.corner) || "";
13272
+ if (obj instanceof fabric.ActiveSelection && (corner === "ml" || corner === "mr" || corner === "mt" || corner === "mb")) {
13273
+ const isXSide = corner === "ml" || corner === "mr";
13274
+ const sAxis = isXSide ? Math.abs(obj.scaleX ?? 1) : Math.abs(obj.scaleY ?? 1);
13275
+ if (sAxis > 1e-3) {
13276
+ for (const child of obj.getObjects()) {
13277
+ if (!(child instanceof fabric.Textbox)) continue;
13278
+ if (isXSide) {
13279
+ if (child.__asLiveOrigW == null) {
13280
+ child.__asLiveOrigW = (child.width ?? 0) * (child.scaleX ?? 1);
13281
+ }
13282
+ const origW = child.__asLiveOrigW;
13283
+ const newW = Math.max(20, origW * sAxis);
13284
+ if (Math.abs((child.width ?? 0) - newW) > 0.5) {
13285
+ child.set({ width: newW, scaleX: 1 / sAxis });
13286
+ try {
13287
+ child.initDimensions();
13288
+ } catch {
13289
+ }
13290
+ child.dirty = true;
13291
+ }
13292
+ } else {
13293
+ if (child.__asLiveOrigH == null) {
13294
+ child.__asLiveOrigH = (child.height ?? 0) * (child.scaleY ?? 1);
13295
+ }
13296
+ const origH = child.__asLiveOrigH;
13297
+ const newH = Math.max(20, origH * sAxis);
13298
+ child.minBoxHeight = newH;
13299
+ child.set({ scaleY: 1 / sAxis });
13300
+ try {
13301
+ child.initDimensions();
13302
+ } catch {
13303
+ }
13304
+ child.dirty = true;
13305
+ }
13306
+ }
13307
+ }
13308
+ }
13309
+ snapDuringScaleCallback(obj, corner);
11581
13310
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11582
- setGuides(scaleGuides);
13311
+ const gridGuidesForScale = [];
13312
+ try {
13313
+ const psGrid = projectSettingsRef.current;
13314
+ const canApplyGridSnap = psGrid.snapToGrid && corner && !obj.__cropGroup && !obj.__resizeSnapHandler;
13315
+ if (canApplyGridSnap) {
13316
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13317
+ const gridY = psGrid.gridSizeY ?? psGrid.gridSize ?? 0;
13318
+ if (gridX > 0 && gridY > 0) {
13319
+ obj.setCoords();
13320
+ const br = obj.getBoundingRect();
13321
+ if (br.width > 1 && br.height > 1) {
13322
+ const hasL = corner.includes("l");
13323
+ const hasR = corner.includes("r");
13324
+ const hasT = corner.includes("t");
13325
+ const hasB = corner.includes("b");
13326
+ const anchorRight = br.left + br.width;
13327
+ const anchorBottom = br.top + br.height;
13328
+ let newLeft = br.left;
13329
+ let newTop = br.top;
13330
+ let newWidth = br.width;
13331
+ let newHeight = br.height;
13332
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13333
+ const yAlreadySnapped = scaleGuides.some((g) => g.type === "horizontal");
13334
+ if (!xAlreadySnapped) {
13335
+ if (hasL) {
13336
+ const nL = Math.round(br.left / gridX) * gridX;
13337
+ newWidth = Math.max(20, anchorRight - nL);
13338
+ newLeft = anchorRight - newWidth;
13339
+ } else if (hasR) {
13340
+ const nR = Math.round(anchorRight / gridX) * gridX;
13341
+ newWidth = Math.max(20, nR - br.left);
13342
+ }
13343
+ }
13344
+ if (!yAlreadySnapped) {
13345
+ if (hasT) {
13346
+ const nT = Math.round(br.top / gridY) * gridY;
13347
+ newHeight = Math.max(20, anchorBottom - nT);
13348
+ newTop = anchorBottom - newHeight;
13349
+ } else if (hasB) {
13350
+ const nB = Math.round(anchorBottom / gridY) * gridY;
13351
+ newHeight = Math.max(20, nB - br.top);
13352
+ }
13353
+ }
13354
+ const widthChanged = Math.abs(newWidth - br.width) > 0.5;
13355
+ const heightChanged = Math.abs(newHeight - br.height) > 0.5;
13356
+ if (widthChanged || heightChanged) {
13357
+ const isTextWidth = obj instanceof fabric.Textbox && widthChanged && !heightChanged;
13358
+ if (isTextWidth) {
13359
+ obj.set({ width: Math.max(20, newWidth) });
13360
+ try {
13361
+ obj.initDimensions();
13362
+ } catch {
13363
+ }
13364
+ } else {
13365
+ const ratioX = widthChanged ? newWidth / br.width : 1;
13366
+ const ratioY = heightChanged ? newHeight / br.height : 1;
13367
+ const curSx = obj.scaleX ?? 1;
13368
+ const curSy = obj.scaleY ?? 1;
13369
+ obj.set({ scaleX: curSx * ratioX, scaleY: curSy * ratioY });
13370
+ }
13371
+ obj.setCoords();
13372
+ const after = obj.getBoundingRect();
13373
+ const dx = newLeft - after.left;
13374
+ const dy = newTop - after.top;
13375
+ if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
13376
+ obj.set({ left: (obj.left ?? 0) + dx, top: (obj.top ?? 0) + dy });
13377
+ obj.setCoords();
13378
+ }
13379
+ if (!xAlreadySnapped) {
13380
+ if (hasL) gridGuidesForScale.push({ type: "vertical", position: newLeft, kind: "grid" });
13381
+ else if (hasR) gridGuidesForScale.push({ type: "vertical", position: newLeft + newWidth, kind: "grid" });
13382
+ }
13383
+ if (!yAlreadySnapped) {
13384
+ if (hasT) gridGuidesForScale.push({ type: "horizontal", position: newTop, kind: "grid" });
13385
+ else if (hasB) gridGuidesForScale.push({ type: "horizontal", position: newTop + newHeight, kind: "grid" });
13386
+ }
13387
+ }
13388
+ }
13389
+ }
13390
+ }
13391
+ } catch {
13392
+ }
13393
+ setGuides(gridGuidesForScale.length ? [...scaleGuides, ...gridGuidesForScale] : scaleGuides);
11583
13394
  });
11584
13395
  fabricCanvas.on("object:resizing", (e) => {
11585
13396
  if (!isActiveRef.current) return;
@@ -11605,13 +13416,70 @@ const PageCanvas = forwardRef(
11605
13416
  }
11606
13417
  const transform = e.transform;
11607
13418
  const corner = (transform == null ? void 0 : transform.corner) || "";
13419
+ if (obj instanceof fabric.Textbox && (corner === "ml" || corner === "mr")) {
13420
+ const objId = getObjectId(obj);
13421
+ const sourceEl = objId ? elementsRef.current.find((el) => el.id === objId) : void 0;
13422
+ bakeTextboxScaleIntoTypography(obj, sourceEl);
13423
+ }
13424
+ snapDuringScaleCallback(obj, corner);
11608
13425
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11609
- setGuides(scaleGuides);
13426
+ const gridGuidesForTextResize = [];
13427
+ try {
13428
+ const psGrid = projectSettingsRef.current;
13429
+ if (psGrid.snapToGrid && corner && obj instanceof fabric.Textbox && (corner === "ml" || corner === "mr")) {
13430
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13431
+ if (gridX > 0) {
13432
+ obj.setCoords();
13433
+ const br = obj.getBoundingRect();
13434
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13435
+ if (!xAlreadySnapped && br.width > 1) {
13436
+ const anchorRight = br.left + br.width;
13437
+ let newLeft = br.left;
13438
+ let newWidth = br.width;
13439
+ if (corner === "ml") {
13440
+ const nL = Math.round(br.left / gridX) * gridX;
13441
+ newWidth = Math.max(20, anchorRight - nL);
13442
+ newLeft = anchorRight - newWidth;
13443
+ } else {
13444
+ const nR = Math.round(anchorRight / gridX) * gridX;
13445
+ newWidth = Math.max(20, nR - br.left);
13446
+ }
13447
+ if (Math.abs(newWidth - br.width) > 0.5) {
13448
+ obj.set({ width: Math.max(20, newWidth) });
13449
+ try {
13450
+ obj.initDimensions();
13451
+ } catch {
13452
+ }
13453
+ obj.setCoords();
13454
+ const after = obj.getBoundingRect();
13455
+ const dx = newLeft - after.left;
13456
+ if (Math.abs(dx) > 0.01) {
13457
+ obj.set({ left: (obj.left ?? 0) + dx });
13458
+ obj.setCoords();
13459
+ }
13460
+ gridGuidesForTextResize.push({
13461
+ type: "vertical",
13462
+ position: corner === "ml" ? newLeft : newLeft + newWidth,
13463
+ kind: "grid"
13464
+ });
13465
+ }
13466
+ }
13467
+ }
13468
+ }
13469
+ } catch {
13470
+ }
13471
+ setGuides(gridGuidesForTextResize.length ? [...scaleGuides, ...gridGuidesForTextResize] : scaleGuides);
11610
13472
  });
11611
13473
  fabricCanvas.on("object:rotating", (e) => {
11612
13474
  markSimpleTransform(e);
11613
13475
  didTransformRef.current = true;
11614
13476
  const tr = e.target;
13477
+ try {
13478
+ const getCursor = fabricCanvas.__pixldocsGetRotateCursor;
13479
+ const upper = fabricCanvas.upperCanvasEl;
13480
+ if (typeof getCursor === "function" && upper && tr) upper.style.cursor = getCursor(tr);
13481
+ } catch {
13482
+ }
11615
13483
  const rotateTargetId = tr ? getObjectId(tr) : null;
11616
13484
  if (rotateTargetId && rotateTargetId !== "__background__") {
11617
13485
  preserveSelectionAfterTransformIdRef.current = rotateTargetId;
@@ -11638,6 +13506,7 @@ const PageCanvas = forwardRef(
11638
13506
  prepareGroupSelectionTransformStart(e.target);
11639
13507
  markTransforming(e.target);
11640
13508
  didTransformRef.current = true;
13509
+ if (e.target) e.target.__pixldocsDragMoved = true;
11641
13510
  const moveTargetId = e.target ? getObjectId(e.target) : null;
11642
13511
  if (moveTargetId && moveTargetId !== "__background__") {
11643
13512
  preserveSelectionAfterTransformIdRef.current = moveTargetId;
@@ -11654,19 +13523,57 @@ const PageCanvas = forwardRef(
11654
13523
  if (!obj) return;
11655
13524
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11656
13525
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11657
- setGuides(newGuides);
11658
- if (snapDx !== 0 || snapDy !== 0) {
11659
- snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
13526
+ let finalDx = snapDx;
13527
+ let finalDy = snapDy;
13528
+ let mergedGuides = newGuides;
13529
+ const psLive = projectSettingsRef.current;
13530
+ const gridX = psLive.gridSizeX ?? psLive.gridSize ?? 0;
13531
+ const gridY = psLive.gridSizeY ?? psLive.gridSize ?? 0;
13532
+ if (psLive.snapToGrid && gridX > 0 && gridY > 0) {
13533
+ try {
13534
+ snapTarget.setCoords();
13535
+ } catch {
13536
+ }
13537
+ const br = snapTarget.getBoundingRect();
13538
+ const gridGuides = [];
13539
+ if (finalDx === 0) {
13540
+ const newLeft = Math.round(br.left / gridX) * gridX;
13541
+ finalDx = newLeft - br.left;
13542
+ gridGuides.push({ type: "vertical", position: newLeft, kind: "grid" });
13543
+ }
13544
+ if (finalDy === 0) {
13545
+ const newTop = Math.round(br.top / gridY) * gridY;
13546
+ finalDy = newTop - br.top;
13547
+ gridGuides.push({ type: "horizontal", position: newTop, kind: "grid" });
13548
+ }
13549
+ if (gridGuides.length) mergedGuides = [...newGuides, ...gridGuides];
13550
+ }
13551
+ setGuides(mergedGuides);
13552
+ setHoverBounds(null);
13553
+ if (finalDx !== 0 || finalDy !== 0) {
13554
+ snapTarget.set({ left: (snapTarget.left ?? 0) + finalDx, top: (snapTarget.top ?? 0) + finalDy });
11660
13555
  }
11661
13556
  });
11662
13557
  let cropGroupSaveTimer = null;
11663
13558
  fabricCanvas.on("object:modified", (e) => {
11664
- var _a2, _b, _c, _d, _e, _f, _g;
13559
+ var _a2, _b2, _c, _d, _e, _f, _g;
11665
13560
  try {
11666
13561
  dragStarted = false;
11667
13562
  setGuides([]);
11668
13563
  setGroupOverlayLiveBoundsRef.current(null);
13564
+ objectResizeActiveSnapRef.current = null;
13565
+ groupResizeActiveSnapRef.current = null;
11669
13566
  onDragEnd == null ? void 0 : onDragEnd();
13567
+ try {
13568
+ const t = e.target;
13569
+ if (t instanceof fabric.ActiveSelection) {
13570
+ for (const child of t.getObjects()) {
13571
+ delete child.__asLiveOrigW;
13572
+ delete child.__asLiveOrigH;
13573
+ }
13574
+ }
13575
+ } catch {
13576
+ }
11670
13577
  lockEdits();
11671
13578
  const modifiedTarget = e.target;
11672
13579
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -11775,7 +13682,7 @@ const PageCanvas = forwardRef(
11775
13682
  useEditorStore.getState().reflowStackGroupInPage(pageId, groupId);
11776
13683
  }
11777
13684
  const stateAfter = useEditorStore.getState();
11778
- const pageAfter = ((_b = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b.children) ?? [];
13685
+ const pageAfter = ((_b2 = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b2.children) ?? [];
11779
13686
  const groupNodeAfter = findNodeById(pageAfter, groupId);
11780
13687
  if (groupNodeAfter) {
11781
13688
  const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);
@@ -11802,11 +13709,11 @@ const PageCanvas = forwardRef(
11802
13709
  clearTimeout(cropGroupSaveTimer);
11803
13710
  }
11804
13711
  cropGroupSaveTimer = setTimeout(() => {
11805
- var _a3, _b2, _c2;
13712
+ var _a3, _b3, _c2;
11806
13713
  const { updateElement: updateElement2 } = useEditorStore.getState();
11807
13714
  const img = ct._img;
11808
13715
  const zoom3 = ((_a3 = img == null ? void 0 : img._ct) == null ? void 0 : _a3.zoom) ?? 1;
11809
- const panX = ((_b2 = img == null ? void 0 : img._ct) == null ? void 0 : _b2.panX) ?? 0.5;
13716
+ const panX = ((_b3 = img == null ? void 0 : img._ct) == null ? void 0 : _b3.panX) ?? 0.5;
11810
13717
  const panY = ((_c2 = img == null ? void 0 : img._ct) == null ? void 0 : _c2.panY) ?? 0.5;
11811
13718
  const stateCrop = useEditorStore.getState();
11812
13719
  const pageCrop = stateCrop.canvas.pages.find((p) => p.id === pageId);
@@ -11825,6 +13732,8 @@ const PageCanvas = forwardRef(
11825
13732
  cropZoom: zoom3
11826
13733
  }, { recordHistory: false });
11827
13734
  active.__isInternalCropUpdate = false;
13735
+ installImageResizeControlsWithSnap(active);
13736
+ ensureCanvaControlRenders(active);
11828
13737
  fabricCanvas.setActiveObject(active);
11829
13738
  setTimeout(() => justModifiedIdsRef.current.delete(objId), 150);
11830
13739
  }, 0);
@@ -12078,6 +13987,10 @@ const PageCanvas = forwardRef(
12078
13987
  for (const obj of activeObjects) {
12079
13988
  const objId = getObjectId(obj);
12080
13989
  if (!objId || objId === "__background__") continue;
13990
+ const sourceElement = elementsRef.current.find((el) => el.id === objId);
13991
+ if (obj instanceof fabric.Textbox && !isActiveSelection) {
13992
+ bakeTextboxScaleIntoTypography(obj, sourceElement);
13993
+ }
12081
13994
  let intrinsicWidth;
12082
13995
  let intrinsicHeight;
12083
13996
  if (obj instanceof fabric.Circle) {
@@ -12144,7 +14057,6 @@ const PageCanvas = forwardRef(
12144
14057
  absoluteLeft = (absoluteLeft ?? 0) - w / 2;
12145
14058
  absoluteTop = (absoluteTop ?? 0) - h / 2;
12146
14059
  }
12147
- const sourceElement = elementsRef.current.find((el) => el.id === objId);
12148
14060
  const preserveCornerGeometry = (sourceElement == null ? void 0 : sourceElement.type) === "shape" && (sourceElement.shapeType === "circle" || sourceElement.shapeType === "rounded-rect" || sourceElement.shapeType === "triangle");
12149
14061
  let finalWidth = intrinsicWidth;
12150
14062
  let finalHeight = intrinsicHeight;
@@ -12230,17 +14142,35 @@ const PageCanvas = forwardRef(
12230
14142
  finalHeight = 0;
12231
14143
  finalScaleX = 1;
12232
14144
  finalScaleY = 1;
14145
+ } else if (obj instanceof fabric.Textbox && isActiveSelection && (Math.abs((decomposed.scaleX ?? 1) - 1) > 1e-3 || Math.abs((decomposed.scaleY ?? 1) - 1) > 1e-3)) {
14146
+ const sx = Math.abs(decomposed.scaleX || 1);
14147
+ const sy = Math.abs(decomposed.scaleY || 1);
14148
+ const bakedWidth = Math.max(20, intrinsicWidth * sx);
14149
+ const bakedHeight = Math.max(1, intrinsicHeight * sy);
14150
+ finalWidth = bakedWidth;
14151
+ finalHeight = bakedHeight;
14152
+ finalScaleX = 1;
14153
+ finalScaleY = 1;
14154
+ try {
14155
+ obj.set({ width: bakedWidth, scaleX: 1, scaleY: 1 });
14156
+ obj.initDimensions();
14157
+ obj.setCoords();
14158
+ } catch {
14159
+ }
14160
+ finalAbsoluteMatrix = fabric.util.composeMatrix({
14161
+ translateX: decomposed.translateX,
14162
+ translateY: decomposed.translateY,
14163
+ angle: decomposed.angle ?? 0,
14164
+ scaleX: 1,
14165
+ scaleY: 1,
14166
+ skewX: 0,
14167
+ skewY: 0
14168
+ });
12233
14169
  } else if (preserveCornerGeometry) {
12234
14170
  const scaledW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));
12235
14171
  const scaledH = Math.max(1, intrinsicHeight * Math.abs(decomposed.scaleY || 1));
12236
- if ((sourceElement == null ? void 0 : sourceElement.shapeType) === "circle") {
12237
- const diameter = Math.max(1, Math.min(scaledW, scaledH));
12238
- finalWidth = diameter;
12239
- finalHeight = diameter;
12240
- } else {
12241
- finalWidth = scaledW;
12242
- finalHeight = scaledH;
12243
- }
14172
+ finalWidth = scaledW;
14173
+ finalHeight = scaledH;
12244
14174
  finalScaleX = 1;
12245
14175
  finalScaleY = 1;
12246
14176
  obj.set({ scaleX: 1, scaleY: 1 });
@@ -12279,6 +14209,11 @@ const PageCanvas = forwardRef(
12279
14209
  transformMatrix: finalAbsoluteMatrix
12280
14210
  };
12281
14211
  if (obj instanceof fabric.Textbox) {
14212
+ const bakedTextScaleUpdates = obj.__pixldocsBakedTextScaleUpdates;
14213
+ if (bakedTextScaleUpdates && typeof bakedTextScaleUpdates === "object") {
14214
+ Object.assign(elementUpdate, bakedTextScaleUpdates);
14215
+ delete obj.__pixldocsBakedTextScaleUpdates;
14216
+ }
12282
14217
  const baked = obj.minBoxHeight;
12283
14218
  if (typeof baked === "number" && baked > 0) {
12284
14219
  elementUpdate.minBoxHeight = baked;
@@ -12387,7 +14322,7 @@ const PageCanvas = forwardRef(
12387
14322
  }
12388
14323
  });
12389
14324
  fabricCanvas.on("mouse:dblclick", (e) => {
12390
- var _a2, _b;
14325
+ var _a2, _b2;
12391
14326
  if (!isActiveRef.current || !allowEditing) return;
12392
14327
  let target = e.target;
12393
14328
  if (!target) {
@@ -12397,7 +14332,7 @@ const PageCanvas = forwardRef(
12397
14332
  if (target && target instanceof fabric.Group && target.__cropGroup) {
12398
14333
  const ct = target.__cropData;
12399
14334
  const innerImg = ct == null ? void 0 : ct._img;
12400
- 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) || "";
14335
+ 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) || "";
12401
14336
  const isPlaceholder = !innerSrc || innerSrc === EMPTY_IMAGE_PLACEHOLDER_DATA_URL;
12402
14337
  if (innerImg && !isPlaceholder && !isCropGroupInCropMode(target)) {
12403
14338
  enterCropMode(target);
@@ -12576,13 +14511,13 @@ const PageCanvas = forwardRef(
12576
14511
  visibilityUpdateInProgressRef.current = false;
12577
14512
  }
12578
14513
  doSyncRef.current = () => {
12579
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
14514
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
12580
14515
  const shouldSkipUpdates2 = syncLockedRef.current || editLockRef.current;
12581
14516
  const state = useEditorStore.getState();
12582
14517
  const elementsToSync = elements;
12583
14518
  elementsRef.current = elementsToSync;
12584
14519
  const pageTree = isPreviewMode && (pageChildren == null ? void 0 : pageChildren.length) ? pageChildren ?? [] : ((_a2 = state.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _a2.children) ?? [];
12585
- const selectedIdsFromStore = new Set(((_b = state.canvas) == null ? void 0 : _b.selectedIds) ?? []);
14520
+ const selectedIdsFromStore = new Set(((_b2 = state.canvas) == null ? void 0 : _b2.selectedIds) ?? []);
12586
14521
  isRebuildingRef.current = true;
12587
14522
  const allElementIds = new Set(elementsToSync.map((el) => el.id));
12588
14523
  const sectionGroups = pageTree.filter(
@@ -12609,7 +14544,8 @@ const PageCanvas = forwardRef(
12609
14544
  const activeObj = fc.getActiveObject();
12610
14545
  const activeObjId = activeObj ? getObjectId(activeObj) : null;
12611
14546
  const isTextBeingEdited = activeObjId && editingTextIdRef.current === activeObjId;
12612
- if (activeObj && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
14547
+ const isMultiSelect = activeObj instanceof fabric.ActiveSelection;
14548
+ if (activeObj && isMultiSelect && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
12613
14549
  fc.discardActiveObject();
12614
14550
  }
12615
14551
  }
@@ -12929,7 +14865,7 @@ const PageCanvas = forwardRef(
12929
14865
  updateCoverLayout(existingObj);
12930
14866
  applyEdgeFadeFrameClipPath(existingObj, element, ct.frameW, ct.frameH, ct.shape || "rect", ct.rx || 0);
12931
14867
  if (allowEditing) {
12932
- installCanvaMaskControls(existingObj);
14868
+ installImageResizeControlsWithSnap(existingObj);
12933
14869
  } else {
12934
14870
  existingObj.set({
12935
14871
  hasControls: false,
@@ -13011,7 +14947,7 @@ const PageCanvas = forwardRef(
13011
14947
  hoverCursor: isDynamicField && isPreviewMode ? "pointer" : void 0
13012
14948
  });
13013
14949
  if (allowEditing) {
13014
- installCanvaMaskControls(existingObj);
14950
+ installImageResizeControlsWithSnap(existingObj);
13015
14951
  }
13016
14952
  existingObj.setCoords();
13017
14953
  fc.requestRenderAll();
@@ -13737,7 +15673,7 @@ const PageCanvas = forwardRef(
13737
15673
  return unsub;
13738
15674
  }, []);
13739
15675
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
13740
- var _a2, _b, _c;
15676
+ var _a2, _b2, _c;
13741
15677
  const fc = fabricRef.current;
13742
15678
  if (fc && isTransforming(fc)) {
13743
15679
  return;
@@ -13805,11 +15741,12 @@ const PageCanvas = forwardRef(
13805
15741
  // Disable rotation for crop groups (simplifies resize math)
13806
15742
  hasRotatingPoint: false,
13807
15743
  // Hide rotation handle
13808
- // Scale with zoom so handles stay same visual size (controls drawn in canvas pixel space)
13809
- cornerSize: Math.max(6, Math.round(8 * (fc.getZoom() || 1))),
13810
- borderScaleFactor: Math.max(1.25, 1.25 * (fc.getZoom() || 1)),
15744
+ // Handles are drawn in screen-space keep them constant on-screen.
15745
+ // Match the global Canva-style defaults (circular dots, pill sides).
15746
+ cornerSize: 10,
15747
+ borderScaleFactor: SELECTION_BORDER_SCALE,
13811
15748
  transparentCorners: false,
13812
- cornerStyle: "rect",
15749
+ cornerStyle: "circle",
13813
15750
  cornerColor: SELECTION_PRIMARY,
13814
15751
  cornerStrokeColor: "#ffffff",
13815
15752
  borderColor: SELECTION_PRIMARY,
@@ -13919,7 +15856,8 @@ const PageCanvas = forwardRef(
13919
15856
  obj.clipPath.dirty = true;
13920
15857
  obj.clipPath.setCoords();
13921
15858
  }
13922
- installCanvaMaskControls(obj);
15859
+ installImageResizeControlsWithSnap(obj);
15860
+ ensureCanvaControlRenders(obj);
13923
15861
  obj.set({
13924
15862
  selectable: true,
13925
15863
  evented: true,
@@ -14014,7 +15952,7 @@ const PageCanvas = forwardRef(
14014
15952
  obj.setCoords();
14015
15953
  }
14016
15954
  if (!isLine) {
14017
- const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
15955
+ const angleTextPathActive = isTextbox && ((_b2 = element.textPath) == null ? void 0 : _b2.preset) === "rise";
14018
15956
  const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
14019
15957
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
14020
15958
  if (!skipPositionUpdate && (obj instanceof fabric.FabricImage && obj.originX === "center" || obj instanceof fabric.Group && obj.__cropGroup)) {
@@ -14091,7 +16029,17 @@ const PageCanvas = forwardRef(
14091
16029
  objectCaching: true
14092
16030
  });
14093
16031
  } else if (obj instanceof fabric.Ellipse) {
14094
- obj.set({ rx: rW / 2, ry: rH / 2 });
16032
+ obj.set({
16033
+ rx: cornerSafeW / 2,
16034
+ ry: cornerSafeH / 2,
16035
+ fill: element.fill || "transparent",
16036
+ stroke: element.stroke || "transparent",
16037
+ strokeWidth: element.strokeWidth || 0,
16038
+ strokeUniform: true,
16039
+ strokeLineJoin: "round",
16040
+ strokeLineCap: "round",
16041
+ objectCaching: true
16042
+ });
14095
16043
  } else if (obj instanceof fabric.Textbox) {
14096
16044
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
14097
16045
  let text = element.text || "Text";
@@ -14285,6 +16233,16 @@ const PageCanvas = forwardRef(
14285
16233
  strokeUniform: true,
14286
16234
  objectCaching: true
14287
16235
  });
16236
+ } else if (obj instanceof fabric.Ellipse) {
16237
+ obj.set({
16238
+ rx: cornerSafeW / 2,
16239
+ ry: cornerSafeH / 2,
16240
+ fill: element.fill || "transparent",
16241
+ stroke: element.stroke || "transparent",
16242
+ strokeWidth: element.strokeWidth || 0,
16243
+ strokeUniform: true,
16244
+ objectCaching: true
16245
+ });
14288
16246
  } else if (obj instanceof fabric.Rect && element.shapeType === "rounded-rect") {
14289
16247
  const toRadius = (value, fallback) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
14290
16248
  const baseRx = Math.max(0, Number(element.rx ?? 0));
@@ -14544,7 +16502,7 @@ const PageCanvas = forwardRef(
14544
16502
  return Math.max(min, Math.min(max, v));
14545
16503
  };
14546
16504
  const applyEdgeFadeFrameClipPath = (group, element, frameW, frameH, shape, rxRatio) => {
14547
- var _a2, _b, _c;
16505
+ var _a2, _b2, _c;
14548
16506
  const fadeElement = element;
14549
16507
  const fadeGroup = group;
14550
16508
  const inputKey = edgeFadeKey(fadeElement);
@@ -14665,7 +16623,7 @@ const PageCanvas = forwardRef(
14665
16623
  delete fadeGroup.__edgeFadeRenderConfig;
14666
16624
  delete fadeGroup.__edgeFadeKey;
14667
16625
  delete fadeGroup.__edgeFadeInputKey;
14668
- if ((_b = group.clipPath) == null ? void 0 : _b.__edgeFadeMask) {
16626
+ if ((_b2 = group.clipPath) == null ? void 0 : _b2.__edgeFadeMask) {
14669
16627
  group.clipPath = void 0;
14670
16628
  updateCoverLayout(group);
14671
16629
  }
@@ -14682,7 +16640,7 @@ const PageCanvas = forwardRef(
14682
16640
  (_c = group.canvas) == null ? void 0 : _c.requestRenderAll();
14683
16641
  };
14684
16642
  const loadImageAsync2 = async (element, placeholder, fc) => {
14685
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
16643
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
14686
16644
  const imageUrl = element.src || element.imageUrl;
14687
16645
  if (!imageUrl) return;
14688
16646
  const elementId = element.id;
@@ -14713,7 +16671,7 @@ const PageCanvas = forwardRef(
14713
16671
  await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);
14714
16672
  if (!isLatestRequest()) return;
14715
16673
  const imageFitForFade = element.imageFit || ((_a2 = element.style) == null ? void 0 : _a2.imageFit) || "cover";
14716
- const clipShapeForFade = element.clipShape ?? ((_b = element.style) == null ? void 0 : _b.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
16674
+ const clipShapeForFade = element.clipShape ?? ((_b2 = element.style) == null ? void 0 : _b2.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
14717
16675
  const willUseCropGroupForFade = imageFitForFade !== "fill" || clipShapeForFade && clipShapeForFade !== "none";
14718
16676
  try {
14719
16677
  if (hasEdgeFade(element) && !willUseCropGroupForFade) {
@@ -14931,7 +16889,7 @@ const PageCanvas = forwardRef(
14931
16889
  evented: canBeEvented && !isHidden
14932
16890
  });
14933
16891
  } else {
14934
- installCanvaMaskControls(cropGroup);
16892
+ installImageResizeControlsWithSnap(cropGroup);
14935
16893
  }
14936
16894
  const cropImg = (_o = cropGroup.__cropData) == null ? void 0 : _o._img;
14937
16895
  if (cropImg) {
@@ -15142,65 +17100,191 @@ const PageCanvas = forwardRef(
15142
17100
  ),
15143
17101
  sectionsOverlay,
15144
17102
  groupBoundsOverlay,
15145
- guides.length > 0 && /* @__PURE__ */ jsx(
17103
+ hoverBounds && !guides.length && !(selectedIdsRef.current && selectedIdsRef.current.length) && /* @__PURE__ */ jsx(
15146
17104
  "svg",
15147
17105
  {
15148
17106
  className: "absolute inset-0 pointer-events-none",
15149
17107
  style: { width: scaledWidth, height: scaledHeight },
15150
17108
  viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15151
- children: (() => {
15152
- const seen = /* @__PURE__ */ new Set();
15153
- return guides.filter((guide) => {
15154
- const key = `${guide.type}-${guide.position.toFixed(1)}`;
15155
- if (seen.has(key)) return false;
15156
- seen.add(key);
15157
- return true;
15158
- }).map((guide, i) => {
15159
- const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
15160
- const isActive2 = guide.active === true;
15161
- const strokeColor = isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#3b82f6";
15162
- const strokeWidth = isActive2 ? 2.5 : 1;
15163
- const strokeDasharray = isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
15164
- const showDistance = typeof guide.distance === "number";
15165
- const labelText = showDistance ? String(Math.round(guide.distance)) : "";
15166
- const labelW = Math.max(24, labelText.length * 7);
15167
- if (guide.type === "vertical") {
15168
- const padding = isElementRelative || isActive2 ? 20 : 0;
15169
- const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15170
- const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
15171
- const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
15172
- return /* @__PURE__ */ jsxs("g", { children: [
15173
- /* @__PURE__ */ jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray }),
15174
- isActive2 && /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
15175
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
15176
- /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
15177
- /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
15178
- ] }),
15179
- showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
15180
- /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15181
- /* @__PURE__ */ jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15182
- ] })
15183
- ] }, i);
15184
- } else {
15185
- const padding = isElementRelative || isActive2 ? 20 : 0;
15186
- const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15187
- const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
15188
- const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
15189
- return /* @__PURE__ */ jsxs("g", { children: [
15190
- /* @__PURE__ */ jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray }),
15191
- isActive2 && /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
15192
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
15193
- /* @__PURE__ */ jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
15194
- /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
15195
- ] }),
15196
- showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
15197
- /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15198
- /* @__PURE__ */ jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15199
- ] })
15200
- ] }, i);
17109
+ children: /* @__PURE__ */ jsx(
17110
+ "rect",
17111
+ {
17112
+ x: hoverBounds.left,
17113
+ y: hoverBounds.top,
17114
+ width: hoverBounds.width,
17115
+ height: hoverBounds.height,
17116
+ fill: "none",
17117
+ stroke: SELECTION_PRIMARY,
17118
+ strokeWidth: 2,
17119
+ vectorEffect: "non-scaling-stroke",
17120
+ strokeDasharray: "0",
17121
+ opacity: 1
17122
+ }
17123
+ )
17124
+ }
17125
+ ),
17126
+ canvas.projectSettings.showGrid && (() => {
17127
+ const ps = canvas.projectSettings;
17128
+ const gx = Math.max(1, ps.gridSizeX ?? ps.gridSize ?? 0);
17129
+ const gy = Math.max(1, ps.gridSizeY ?? ps.gridSize ?? 0);
17130
+ if (gx <= 0 || gy <= 0) return null;
17131
+ return /* @__PURE__ */ jsxs(
17132
+ "svg",
17133
+ {
17134
+ className: "absolute inset-0 pointer-events-none",
17135
+ style: { width: scaledWidth, height: scaledHeight },
17136
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
17137
+ children: [
17138
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
17139
+ "pattern",
17140
+ {
17141
+ id: `pixldocs-grid-${pageId}`,
17142
+ width: gx,
17143
+ height: gy,
17144
+ patternUnits: "userSpaceOnUse",
17145
+ children: /* @__PURE__ */ jsx(
17146
+ "path",
17147
+ {
17148
+ d: `M ${gx} 0 L 0 0 0 ${gy}`,
17149
+ fill: "none",
17150
+ stroke: ps.gridColor || "#0f172a",
17151
+ strokeWidth: 0.5,
17152
+ opacity: 0.22
17153
+ }
17154
+ )
17155
+ }
17156
+ ) }),
17157
+ /* @__PURE__ */ jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
17158
+ ]
17159
+ }
17160
+ );
17161
+ })(),
17162
+ guides.length > 0 && /* @__PURE__ */ jsxs(
17163
+ "svg",
17164
+ {
17165
+ className: "absolute inset-0 pointer-events-none",
17166
+ style: { width: scaledWidth, height: scaledHeight },
17167
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
17168
+ children: [
17169
+ (() => {
17170
+ const seenBoxes = /* @__PURE__ */ new Set();
17171
+ const boxes = [];
17172
+ for (const g of guides) {
17173
+ const list = g.targetBoundsList ?? (g.targetBounds ? [g.targetBounds] : []);
17174
+ for (const b of list) {
17175
+ const key = `${b.left.toFixed(1)}-${b.top.toFixed(1)}-${b.width.toFixed(1)}-${b.height.toFixed(1)}`;
17176
+ if (seenBoxes.has(key)) continue;
17177
+ seenBoxes.add(key);
17178
+ boxes.push(b);
17179
+ }
15201
17180
  }
15202
- });
15203
- })()
17181
+ return boxes.map((b, i) => /* @__PURE__ */ jsx(
17182
+ "rect",
17183
+ {
17184
+ x: b.left,
17185
+ y: b.top,
17186
+ width: b.width,
17187
+ height: b.height,
17188
+ fill: "none",
17189
+ stroke: "#f43f5e",
17190
+ strokeWidth: 1.5,
17191
+ strokeDasharray: "4,3",
17192
+ opacity: 0.9,
17193
+ vectorEffect: "non-scaling-stroke"
17194
+ },
17195
+ `tb-${i}`
17196
+ ));
17197
+ })(),
17198
+ (() => {
17199
+ const seen = /* @__PURE__ */ new Set();
17200
+ return guides.filter((guide) => {
17201
+ if (guide.kind === "gap") return true;
17202
+ const key = `${guide.type}-${guide.position.toFixed(1)}`;
17203
+ if (seen.has(key)) return false;
17204
+ seen.add(key);
17205
+ return true;
17206
+ }).map((guide, i) => {
17207
+ if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
17208
+ const gapColor = "#ec4899";
17209
+ const label = `${guide.gap}`;
17210
+ const labelW2 = Math.max(22, label.length * 7 + 8);
17211
+ if (guide.type === "horizontal") {
17212
+ const y = guide.bracketAt ?? guide.position;
17213
+ const x1 = guide.start;
17214
+ const x2 = guide.end;
17215
+ const mid = (x1 + x2) / 2;
17216
+ return /* @__PURE__ */ jsxs("g", { children: [
17217
+ /* @__PURE__ */ jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17218
+ /* @__PURE__ */ jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17219
+ /* @__PURE__ */ jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17220
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
17221
+ /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17222
+ /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17223
+ ] })
17224
+ ] }, i);
17225
+ } else {
17226
+ const x = guide.bracketAt ?? guide.position;
17227
+ const y1 = guide.start;
17228
+ const y2 = guide.end;
17229
+ const mid = (y1 + y2) / 2;
17230
+ return /* @__PURE__ */ jsxs("g", { children: [
17231
+ /* @__PURE__ */ jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17232
+ /* @__PURE__ */ jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17233
+ /* @__PURE__ */ jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17234
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
17235
+ /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17236
+ /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17237
+ ] })
17238
+ ] }, i);
17239
+ }
17240
+ }
17241
+ const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
17242
+ const isActive2 = guide.active === true;
17243
+ const isGrid = guide.kind === "grid";
17244
+ const strokeColor = isGrid ? "#f59e0b" : isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#ec4899";
17245
+ const strokeWidth = isGrid ? 1.75 : 1.75;
17246
+ const strokeDasharray = isGrid ? "2,3" : isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
17247
+ const showDistance = typeof guide.distance === "number";
17248
+ const labelText = showDistance ? String(Math.round(guide.distance)) : "";
17249
+ const labelW = Math.max(24, labelText.length * 7);
17250
+ if (guide.type === "vertical") {
17251
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17252
+ const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17253
+ const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
17254
+ const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
17255
+ return /* @__PURE__ */ jsxs("g", { children: [
17256
+ /* @__PURE__ */ jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17257
+ isActive2 && /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
17258
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
17259
+ /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
17260
+ /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
17261
+ ] }),
17262
+ showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
17263
+ /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17264
+ /* @__PURE__ */ jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
17265
+ ] })
17266
+ ] }, i);
17267
+ } else {
17268
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17269
+ const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17270
+ const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
17271
+ const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
17272
+ return /* @__PURE__ */ jsxs("g", { children: [
17273
+ /* @__PURE__ */ jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17274
+ isActive2 && /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
17275
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
17276
+ /* @__PURE__ */ jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
17277
+ /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
17278
+ ] }),
17279
+ showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
17280
+ /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17281
+ /* @__PURE__ */ jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
17282
+ ] })
17283
+ ] }, i);
17284
+ }
17285
+ });
17286
+ })()
17287
+ ]
15204
17288
  }
15205
17289
  ),
15206
17290
  gridResizeLabel && /* @__PURE__ */ jsx(
@@ -15336,13 +17420,13 @@ function PreviewCanvas({
15336
17420
  onDynamicFieldClick,
15337
17421
  onReady
15338
17422
  }) {
15339
- var _a2, _b, _c, _d, _e;
17423
+ var _a2, _b2, _c, _d, _e;
15340
17424
  const canvasRef = useRef(null);
15341
17425
  const containerRef = useRef(null);
15342
17426
  const [containerWidth, setContainerWidth] = useState(0);
15343
17427
  const [hoveredFieldId, setHoveredFieldId] = useState(null);
15344
17428
  const page = (_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2[pageIndex];
15345
- const canvasWidth = ((_b = config == null ? void 0 : config.canvas) == null ? void 0 : _b.width) || 612;
17429
+ const canvasWidth = ((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.width) || 612;
15346
17430
  const canvasHeight = ((_c = config == null ? void 0 : config.canvas) == null ? void 0 : _c.height) || 792;
15347
17431
  const elementToFieldMap = useMemo(
15348
17432
  () => buildElementToFieldMap(config == null ? void 0 : config.dynamicFields),
@@ -15392,14 +17476,14 @@ function PreviewCanvas({
15392
17476
  }
15393
17477
  }, [elements]);
15394
17478
  const pageSettings = useMemo(() => {
15395
- var _a3, _b2;
17479
+ var _a3, _b3;
15396
17480
  return {
15397
17481
  backgroundColor: ((_a3 = page == null ? void 0 : page.settings) == null ? void 0 : _a3.backgroundColor) || "#ffffff",
15398
- backgroundGradient: (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient
17482
+ backgroundGradient: (_b3 = page == null ? void 0 : page.settings) == null ? void 0 : _b3.backgroundGradient
15399
17483
  };
15400
17484
  }, [(_d = page == null ? void 0 : page.settings) == null ? void 0 : _d.backgroundColor, (_e = page == null ? void 0 : page.settings) == null ? void 0 : _e.backgroundGradient]);
15401
17485
  const projectSettings = useMemo(() => {
15402
- var _a3, _b2, _c2;
17486
+ var _a3, _b3, _c2;
15403
17487
  const vars = ((_a3 = config.themeConfig) == null ? void 0 : _a3.variables) || {};
15404
17488
  return {
15405
17489
  showGrid: false,
@@ -15407,7 +17491,7 @@ function PreviewCanvas({
15407
17491
  gridSize: 10,
15408
17492
  snapToGuides: false,
15409
17493
  snapThreshold: 5,
15410
- primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
17494
+ primaryColor: (_b3 = vars.primary) == null ? void 0 : _b3.value,
15411
17495
  secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
15412
17496
  };
15413
17497
  }, [config.themeConfig]);
@@ -15559,7 +17643,7 @@ const PreviewCanvas$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
15559
17643
  PreviewCanvas
15560
17644
  }, Symbol.toStringTag, { value: "Module" }));
15561
17645
  function applyThemeToConfig(config, themeOverrides) {
15562
- var _a2, _b, _c;
17646
+ var _a2, _b2, _c;
15563
17647
  if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;
15564
17648
  const cloned = JSON.parse(JSON.stringify(config));
15565
17649
  if ((_a2 = cloned.themeConfig) == null ? void 0 : _a2.variables) {
@@ -15570,7 +17654,7 @@ function applyThemeToConfig(config, themeOverrides) {
15570
17654
  }
15571
17655
  }
15572
17656
  const varMap = /* @__PURE__ */ new Map();
15573
- if ((_b = cloned.themeConfig) == null ? void 0 : _b.variables) {
17657
+ if ((_b2 = cloned.themeConfig) == null ? void 0 : _b2.variables) {
15574
17658
  for (const [key, def] of Object.entries(cloned.themeConfig.variables)) {
15575
17659
  varMap.set(key, themeOverrides[key] ?? def.value);
15576
17660
  }
@@ -15607,7 +17691,7 @@ function mapFormDefFieldType(t) {
15607
17691
  function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15608
17692
  const sections = [];
15609
17693
  function convert(defs, parentId) {
15610
- var _a2, _b;
17694
+ var _a2, _b2;
15611
17695
  for (const def of defs) {
15612
17696
  const isRepeatable = def.repeatable === true || def.type === "repeatable";
15613
17697
  const defFields = def.fields ?? def.entryFields ?? [];
@@ -15659,7 +17743,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15659
17743
  placeholder: f.placeholder
15660
17744
  }))
15661
17745
  });
15662
- if ((_b = def.children) == null ? void 0 : _b.length) {
17746
+ if ((_b2 = def.children) == null ? void 0 : _b2.length) {
15663
17747
  convert(def.children, def.id);
15664
17748
  }
15665
17749
  }
@@ -16288,7 +18372,7 @@ function findAllRepeatableElementsBySourceId(pages, baseNodeId, oneBasedIndex, s
16288
18372
  return out;
16289
18373
  }
16290
18374
  function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi, childBaseNodeId, childCi, sourceElementId) {
16291
- var _a2, _b;
18375
+ var _a2, _b2;
16292
18376
  const parentGroups = collectGroupsByBaseId(pages, parentBaseNodeId);
16293
18377
  const parentGroup = parentGroups[parentPi - 1];
16294
18378
  if (!parentGroup || !isGroup(parentGroup) || !((_a2 = parentGroup.children) == null ? void 0 : _a2.length)) return void 0;
@@ -16303,7 +18387,7 @@ function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi
16303
18387
  }
16304
18388
  collectChild(parentGroup.children);
16305
18389
  const childGroup = childGroups[childCi - 1];
16306
- if (!childGroup || !isGroup(childGroup) || !((_b = childGroup.children) == null ? void 0 : _b.length)) return void 0;
18390
+ if (!childGroup || !isGroup(childGroup) || !((_b2 = childGroup.children) == null ? void 0 : _b2.length)) return void 0;
16307
18391
  return findElementBySourceIdInSubtree(childGroup.children, sourceElementId) ?? (childGroup.__sourceId === sourceElementId ? childGroup.id : void 0);
16308
18392
  }
16309
18393
  function repeatableLabelKey(label) {
@@ -16353,7 +18437,7 @@ function getRepeatableFromConfig(pages) {
16353
18437
  var _a2;
16354
18438
  const result = [];
16355
18439
  function walk(children) {
16356
- var _a3, _b;
18440
+ var _a3, _b2;
16357
18441
  if (!children) return;
16358
18442
  for (const node of children) {
16359
18443
  if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && ((_a3 = node.children) == null ? void 0 : _a3.length)) {
@@ -16362,7 +18446,7 @@ function getRepeatableFromConfig(pages) {
16362
18446
  if (rep == null ? void 0 : rep.label) result.push({ nodeId: child.id, label: rep.label });
16363
18447
  }
16364
18448
  }
16365
- if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) walk(node.children);
18449
+ if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) walk(node.children);
16366
18450
  }
16367
18451
  }
16368
18452
  for (const page of pages) if ((_a2 = page.children) == null ? void 0 : _a2.length) walk(page.children);
@@ -16378,7 +18462,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16378
18462
  return !!((_a3 = n.repeatableSection) == null ? void 0 : _a3.label);
16379
18463
  };
16380
18464
  function walk(children, parentRepeatableBaseId, parentInfo, parentRepeatableEntryIndex) {
16381
- var _a3, _b;
18465
+ var _a3, _b2;
16382
18466
  if (!Array.isArray(children)) return;
16383
18467
  for (let i = 0; i < children.length; i++) {
16384
18468
  const node = children[i];
@@ -16421,7 +18505,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16421
18505
  if (isGroup(child) && Array.isArray(child.children)) walk(child.children, effectiveChildBase, void 0, childEntryIndex);
16422
18506
  j += count;
16423
18507
  }
16424
- } else if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) {
18508
+ } else if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) {
16425
18509
  const nodeBase = baseId(node.id);
16426
18510
  const selfMatch = schemaBaseIds.has(node.id) || schemaBaseIds.has(nodeBase) || node.__baseNodeId != null && schemaBaseIds.has(node.__baseNodeId);
16427
18511
  const isSelfRepeatable = selfMatch && hasRepeatableSection(node);
@@ -16510,7 +18594,7 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
16510
18594
  return Math.max(1, maxIndex);
16511
18595
  }
16512
18596
  function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
16513
- var _a2, _b, _c;
18597
+ var _a2, _b2, _c;
16514
18598
  const cloned = JSON.parse(JSON.stringify(config));
16515
18599
  if (!cloned.pages) return cloned;
16516
18600
  const dynamicFields = cloned.dynamicFields;
@@ -16691,7 +18775,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
16691
18775
  const { node, baseNodeId, startIndex, count } = block;
16692
18776
  const occIdx = block.__occurrenceIndex ?? 1;
16693
18777
  const N = computeN(baseNodeId, node.id);
16694
- const entryFilter = ((_b = node.repeatableSection) == null ? void 0 : _b.entryFilter) ?? entryFilterFromList(baseNodeId);
18778
+ const entryFilter = ((_b2 = node.repeatableSection) == null ? void 0 : _b2.entryFilter) ?? entryFilterFromList(baseNodeId);
16695
18779
  let entryIndices;
16696
18780
  if (entryFilter && entryFilter.mode === "range" && entryFilter.range) {
16697
18781
  const parsed = parseEntryRange(entryFilter.range, N, entryMetaFromList(baseNodeId));
@@ -17148,7 +19232,7 @@ function findAllFlowStacks(pageChildren) {
17148
19232
  return result;
17149
19233
  }
17150
19234
  function findNestedFlowStack(entry) {
17151
- var _a2, _b;
19235
+ var _a2, _b2;
17152
19236
  const queue = [...entry.children ?? []];
17153
19237
  while (queue.length > 0) {
17154
19238
  const node = queue.shift();
@@ -17156,7 +19240,7 @@ function findNestedFlowStack(entry) {
17156
19240
  const g = node;
17157
19241
  const hasRepeatableChildren = ((_a2 = g.children) == null ? void 0 : _a2.length) && (g.children.some(hasBaseNodeId) || g.children.some((c) => isGroup(c)));
17158
19242
  if (isVerticalStackLayoutMode(g.layoutMode) && hasRepeatableChildren) return g;
17159
- if ((_b = g.children) == null ? void 0 : _b.length) queue.push(...g.children);
19243
+ if ((_b2 = g.children) == null ? void 0 : _b2.length) queue.push(...g.children);
17160
19244
  }
17161
19245
  return null;
17162
19246
  }
@@ -17287,9 +19371,9 @@ function splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overf
17287
19371
  return { stayChildren: newStay, overflowChildren: newOverflow };
17288
19372
  }
17289
19373
  function paginateSinglePage(sourcePage, pageOffsetIndex) {
17290
- var _a2, _b, _c, _d, _e, _f;
19374
+ var _a2, _b2, _c, _d, _e, _f;
17291
19375
  const contentTop = (_a2 = sourcePage.settings) == null ? void 0 : _a2.contentTop;
17292
- const contentBottom = (_b = sourcePage.settings) == null ? void 0 : _b.contentBottom;
19376
+ const contentBottom = (_b2 = sourcePage.settings) == null ? void 0 : _b2.contentBottom;
17293
19377
  if (contentTop == null || contentBottom == null || contentBottom <= contentTop) {
17294
19378
  return [sourcePage];
17295
19379
  }
@@ -18479,7 +20563,7 @@ function splitIntoRuns(text, mainSupportsChar) {
18479
20563
  return runs;
18480
20564
  }
18481
20565
  function rewriteSvgFontsForJsPDF(svgStr) {
18482
- var _a2, _b;
20566
+ var _a2, _b2;
18483
20567
  const parser = new DOMParser();
18484
20568
  const doc = parser.parseFromString(svgStr, "image/svg+xml");
18485
20569
  const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
@@ -18584,7 +20668,7 @@ function rewriteSvgFontsForJsPDF(svgStr) {
18584
20668
  for (const node of childNodes) {
18585
20669
  if (node.nodeType !== 3 || !node.textContent) continue;
18586
20670
  const runs = splitIntoRuns(node.textContent, mainSupportsChar);
18587
- if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) === "main") continue;
20671
+ if (runs.length <= 1 && ((_b2 = runs[0]) == null ? void 0 : _b2.runType) === "main") continue;
18588
20672
  const fragment = doc.createDocumentFragment();
18589
20673
  for (const run of runs) {
18590
20674
  const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
@@ -18894,9 +20978,9 @@ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
18894
20978
  styleEl.appendChild(document.createTextNode(cssText));
18895
20979
  }
18896
20980
  function resolveHarnessFontProxyUrl() {
18897
- var _a2, _b;
20981
+ var _a2, _b2;
18898
20982
  try {
18899
- 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)) || "";
20983
+ 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)) || "";
18900
20984
  const base = String(runtimeBase || "").replace(/\/$/, "");
18901
20985
  return base ? `${base}/functions/v1/font-proxy` : "";
18902
20986
  } catch {
@@ -19558,7 +21642,7 @@ async function resolveTemplateData(options) {
19558
21642
  };
19559
21643
  }
19560
21644
  async function resolveFromForm(options) {
19561
- var _a2, _b, _c;
21645
+ var _a2, _b2, _c;
19562
21646
  const { templateId, formSchemaId, sectionState, flatFormData: directFlatFormData, themeId, supabaseUrl, supabaseAnonKey, prefetched } = options;
19563
21647
  const hasSectionStateInput = !!sectionState && Object.keys(sectionState).length > 0;
19564
21648
  if (!formSchemaId && !hasSectionStateInput) {
@@ -19608,7 +21692,7 @@ async function resolveFromForm(options) {
19608
21692
  inferredSections = inferFormSchemaFromTemplate(
19609
21693
  templateConfig.dynamicFields,
19610
21694
  groups,
19611
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
21695
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
19612
21696
  );
19613
21697
  } else {
19614
21698
  inferredSections = [];
@@ -19795,10 +21879,10 @@ function themeBaseId(id) {
19795
21879
  return out;
19796
21880
  }
19797
21881
  function applyThemeVariantToConfig(config, themeConfig, themeId) {
19798
- var _a2, _b, _c, _d;
21882
+ var _a2, _b2, _c, _d;
19799
21883
  if (!themeConfig) return config;
19800
21884
  const variant = themeId && themeId !== "default" ? (_a2 = themeConfig.variants) == null ? void 0 : _a2.find((v) => v.id === themeId) : null;
19801
- const shouldApplyDefaults = !variant && ((_b = themeConfig.properties) == null ? void 0 : _b.length);
21885
+ const shouldApplyDefaults = !variant && ((_b2 = themeConfig.properties) == null ? void 0 : _b2.length);
19802
21886
  if (!variant && !shouldApplyDefaults) return config;
19803
21887
  if (!((_c = themeConfig.properties) == null ? void 0 : _c.length)) return config;
19804
21888
  const result = JSON.parse(JSON.stringify(config));
@@ -19819,8 +21903,8 @@ function applyThemeVariantToConfig(config, themeConfig, themeId) {
19819
21903
  if (stopMatch) {
19820
21904
  const stopIndex = parseInt(stopMatch[1], 10);
19821
21905
  result.pages.forEach((p) => {
19822
- var _a3, _b2;
19823
- if ((_b2 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b2[stopIndex]) {
21906
+ var _a3, _b3;
21907
+ if ((_b3 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b3[stopIndex]) {
19824
21908
  p.settings.backgroundGradient = {
19825
21909
  ...p.settings.backgroundGradient,
19826
21910
  stops: p.settings.backgroundGradient.stops.map(
@@ -19897,7 +21981,7 @@ function normalizeLayoutModes(config) {
19897
21981
  }
19898
21982
  }
19899
21983
  function paintRepeatableSections(config, repeatableSections, formSchema) {
19900
- var _a2, _b;
21984
+ var _a2, _b2;
19901
21985
  const pages = config.pages ?? [];
19902
21986
  const entryFilters = (formSchema == null ? void 0 : formSchema.entryFilters) ?? [];
19903
21987
  const entryFilterBases = new Set(entryFilters.map((f) => baseId(f.nodeId)));
@@ -19925,7 +22009,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
19925
22009
  return painted;
19926
22010
  }
19927
22011
  for (const section of repeatableSections) {
19928
- const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b = section.entryFilter.range) == null ? void 0 : _b.trim() : void 0;
22012
+ const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b2 = section.entryFilter.range) == null ? void 0 : _b2.trim() : void 0;
19929
22013
  const shouldUseInlineFilter = !!inlineRange && !entryFilterBases.has(baseId(section.nodeId));
19930
22014
  const payload = { label: section.label };
19931
22015
  if (section.minEntries !== void 0) payload.minEntries = section.minEntries;
@@ -19961,7 +22045,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
19961
22045
  }
19962
22046
  }
19963
22047
  async function getTemplateForm(options) {
19964
- var _a2, _b;
22048
+ var _a2, _b2;
19965
22049
  const { templateId, supabaseUrl, supabaseAnonKey } = options;
19966
22050
  if (!supabaseUrl || !supabaseAnonKey) {
19967
22051
  throw new Error("[getTemplateForm] supabaseUrl and supabaseAnonKey are required");
@@ -20005,7 +22089,7 @@ async function getTemplateForm(options) {
20005
22089
  sections = inferFormSchemaFromTemplate(
20006
22090
  templateConfig.dynamicFields,
20007
22091
  templateConfig.fieldGroups || [],
20008
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
22092
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
20009
22093
  );
20010
22094
  } else {
20011
22095
  sections = [];
@@ -20389,7 +22473,7 @@ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExact
20389
22473
  return out;
20390
22474
  }
20391
22475
  function PixldocsPreview(props) {
20392
- var _a2, _b;
22476
+ var _a2, _b2;
20393
22477
  const {
20394
22478
  pageIndex = 0,
20395
22479
  zoom = 1,
@@ -20457,10 +22541,10 @@ function PixldocsPreview(props) {
20457
22541
  supabaseUrl: p.supabaseUrl,
20458
22542
  supabaseAnonKey: p.supabaseAnonKey
20459
22543
  }).then((resolved) => {
20460
- var _a3, _b2;
22544
+ var _a3, _b3;
20461
22545
  if (!cancelled) {
20462
22546
  console.log(PREVIEW_DEBUG_PREFIX, "resolve-done", {
20463
- pages: ((_b2 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b2.length) ?? 0,
22547
+ pages: ((_b3 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b3.length) ?? 0,
20464
22548
  underlinedNodes: countUnderlinedNodes(resolved.config)
20465
22549
  });
20466
22550
  setResolvedConfig(resolved.config);
@@ -20570,7 +22654,7 @@ function PixldocsPreview(props) {
20570
22654
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
20571
22655
  const bgTint = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.background) ?? "rgba(255,255,255,0.12)";
20572
22656
  const canvasW = getNum((_a2 = config == null ? void 0 : config.canvas) == null ? void 0 : _a2.width, 0);
20573
- const canvasH = getNum((_b = config == null ? void 0 : config.canvas) == null ? void 0 : _b.height, 0);
22657
+ const canvasH = getNum((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.height, 0);
20574
22658
  const hasOverlays = frostedBounds.length > 0 && canvasW > 0 && canvasH > 0;
20575
22659
  if (isLoading) {
20576
22660
  return /* @__PURE__ */ jsx("div", { className, style: { ...style, position: "relative", minHeight: 200, display: "flex", alignItems: "center", justifyContent: "center" }, children: loadingFallback ?? /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
@@ -20711,7 +22795,7 @@ function ensureFabricGradientDef(doc, obj, width, height) {
20711
22795
  return `url(#${id})`;
20712
22796
  }
20713
22797
  function warpTextboxSvgAlongPath(svg, obj) {
20714
- var _a2, _b, _c, _d, _e;
22798
+ var _a2, _b2, _c, _d, _e;
20715
22799
  const tp = obj == null ? void 0 : obj.textPath;
20716
22800
  if (!tp || !tp.preset || tp.preset === "none") return svg;
20717
22801
  if (tp.preset === "rise" || tp.preset === "angle") {
@@ -20775,7 +22859,7 @@ function warpTextboxSvgAlongPath(svg, obj) {
20775
22859
  }
20776
22860
  for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
20777
22861
  try {
20778
- (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
22862
+ (_b2 = clone.parentNode) == null ? void 0 : _b2.removeChild(clone);
20779
22863
  } catch {
20780
22864
  }
20781
22865
  }
@@ -20795,9 +22879,9 @@ function warpTextboxSvgAlongPath(svg, obj) {
20795
22879
  const fw = obj.fontWeight ?? 400;
20796
22880
  const fst = obj.fontStyle || "normal";
20797
22881
  const readFill = (el) => {
20798
- var _a3, _b2;
22882
+ var _a3, _b3;
20799
22883
  if (!el) return "";
20800
- 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()) || "";
22884
+ 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()) || "";
20801
22885
  };
20802
22886
  const w = Number(obj.width) || 0;
20803
22887
  const h = Number(obj.height) || 0;
@@ -21150,9 +23234,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21150
23234
  }
21151
23235
  return svgString;
21152
23236
  }
21153
- const resolvedPackageVersion = "0.5.240";
23237
+ const resolvedPackageVersion = "0.5.242";
21154
23238
  const PACKAGE_VERSION = resolvedPackageVersion;
21155
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.240";
23239
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.242";
21156
23240
  const roundParityValue = (value) => {
21157
23241
  if (typeof value !== "number") return value;
21158
23242
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21329,11 +23413,11 @@ function installUnderlineFix(fab) {
21329
23413
  if (!TextProto || typeof TextProto._renderTextDecoration !== "function") return;
21330
23414
  const original = TextProto._renderTextDecoration;
21331
23415
  const measureLineTextWidth = (obj, ctx, lineIndex) => {
21332
- var _a3, _b, _c, _d, _e, _f;
23416
+ var _a3, _b2, _c, _d, _e, _f;
21333
23417
  const rawLine = (_a3 = obj._textLines) == null ? void 0 : _a3[lineIndex];
21334
23418
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
21335
23419
  if (!lineText) return 0;
21336
- const fontSize = Number(((_b = obj.getValueOfPropertyAt) == null ? void 0 : _b.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
23420
+ const fontSize = Number(((_b2 = obj.getValueOfPropertyAt) == null ? void 0 : _b2.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
21337
23421
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
21338
23422
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
21339
23423
  const fontFamily = String(((_e = obj.getValueOfPropertyAt) == null ? void 0 : _e.call(obj, lineIndex, 0, "fontFamily")) ?? obj.fontFamily ?? "sans-serif");
@@ -21473,11 +23557,78 @@ function installTextboxBoxExtensions(fab) {
21473
23557
  if (Array.isArray(stateProps2)) {
21474
23558
  if (!stateProps2.includes("minBoxHeight")) stateProps2.push("minBoxHeight");
21475
23559
  if (!stateProps2.includes("verticalAlign")) stateProps2.push("verticalAlign");
23560
+ if (!stateProps2.includes("smartWrap")) stateProps2.push("smartWrap");
21476
23561
  }
21477
23562
  const cacheProps2 = TextboxProto2.cacheProperties;
21478
23563
  if (Array.isArray(cacheProps2)) {
21479
23564
  if (!cacheProps2.includes("minBoxHeight")) cacheProps2.push("minBoxHeight");
21480
23565
  if (!cacheProps2.includes("verticalAlign")) cacheProps2.push("verticalAlign");
23566
+ if (!cacheProps2.includes("smartWrap")) cacheProps2.push("smartWrap");
23567
+ }
23568
+ TextboxProto2.smartWrap = true;
23569
+ if (typeof TextboxProto2._wrapLine === "function" && !TextboxProto2.__pixldocsOrigWrapLine) {
23570
+ const origWrapLine = TextboxProto2._wrapLine;
23571
+ TextboxProto2.__pixldocsOrigWrapLine = origWrapLine;
23572
+ TextboxProto2._wrapLine = function(lineIndex, desiredWidth, ref, reservedSpace = 0) {
23573
+ var _a3;
23574
+ if (!this.smartWrap || this.splitByGrapheme) {
23575
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23576
+ }
23577
+ try {
23578
+ const additionalSpace = this._getWidthOfCharSpacing();
23579
+ const data = ((_a3 = ref == null ? void 0 : ref.wordsData) == null ? void 0 : _a3[lineIndex]) || [];
23580
+ const effective = Math.max(1, desiredWidth - reservedSpace);
23581
+ const infix = " ";
23582
+ const infixWidth = this._measureWord([infix], lineIndex, 0);
23583
+ const graphemeLines = [];
23584
+ let line = [];
23585
+ let lineWidth = 0;
23586
+ let lineJustStarted = true;
23587
+ let largestWordWidth = 0;
23588
+ let offset = 0;
23589
+ const pushLine = () => {
23590
+ graphemeLines.push(line);
23591
+ line = [];
23592
+ lineWidth = 0;
23593
+ lineJustStarted = true;
23594
+ };
23595
+ for (let i = 0; i < data.length; i++) {
23596
+ const { word, width: wordWidth } = data[i];
23597
+ if (wordWidth <= effective) {
23598
+ if (wordWidth > largestWordWidth) largestWordWidth = wordWidth;
23599
+ const projected = lineJustStarted ? wordWidth : lineWidth + infixWidth + wordWidth - additionalSpace;
23600
+ if (!lineJustStarted && projected > effective) pushLine();
23601
+ if (!lineJustStarted) {
23602
+ line.push(infix);
23603
+ lineWidth += infixWidth;
23604
+ }
23605
+ line = line.concat(word);
23606
+ lineWidth += wordWidth;
23607
+ lineJustStarted = false;
23608
+ } else {
23609
+ if (!lineJustStarted) pushLine();
23610
+ for (const g of word) {
23611
+ const gw = this._measureWord([g], lineIndex, offset);
23612
+ if (gw > largestWordWidth) largestWordWidth = gw;
23613
+ const gProj = lineJustStarted ? gw : lineWidth + gw - additionalSpace;
23614
+ if (!lineJustStarted && gProj > effective) pushLine();
23615
+ line.push(g);
23616
+ lineWidth += gw;
23617
+ lineJustStarted = false;
23618
+ offset++;
23619
+ }
23620
+ offset -= word.length;
23621
+ }
23622
+ offset += word.length + 1;
23623
+ }
23624
+ if (line.length) graphemeLines.push(line);
23625
+ const minNeeded = Math.max(0, Math.min(largestWordWidth, effective) - additionalSpace + reservedSpace);
23626
+ if (minNeeded > this.dynamicMinWidth) this.dynamicMinWidth = minNeeded;
23627
+ return graphemeLines;
23628
+ } catch (e) {
23629
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23630
+ }
23631
+ };
21481
23632
  }
21482
23633
  TextboxProto2.__pixldocsTextboxExtended = true;
21483
23634
  __textboxBoxExtensionsInstalled = true;
@@ -21899,7 +24050,7 @@ class PixldocsRenderer {
21899
24050
  await this.waitForCanvasScene(container, cloned, i);
21900
24051
  }
21901
24052
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
21902
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CHVrwSi7.js");
24053
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CsrQStE8.js");
21903
24054
  const prepared = preparePagesForExport(
21904
24055
  cloned.pages,
21905
24056
  canvasWidth,
@@ -22116,9 +24267,9 @@ class PixldocsRenderer {
22116
24267
  }
22117
24268
  waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
22118
24269
  return new Promise((resolve) => {
22119
- var _a2, _b;
24270
+ var _a2, _b2;
22120
24271
  const start = Date.now();
22121
- const pageHasContent = (((_b = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b.length) ?? 0) > 0;
24272
+ const pageHasContent = (((_b2 = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) ?? 0) > 0;
22122
24273
  const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
22123
24274
  const check = () => {
22124
24275
  const fabricCanvas = this.getFabricCanvasFromContainer(container);
@@ -22193,9 +24344,9 @@ class PixldocsRenderer {
22193
24344
  return normalized;
22194
24345
  }
22195
24346
  paintPageBackground(ctx, page, width, height) {
22196
- var _a2, _b;
24347
+ var _a2, _b2;
22197
24348
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22198
- const gradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
24349
+ const gradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22199
24350
  ctx.clearRect(0, 0, width, height);
22200
24351
  ctx.fillStyle = backgroundColor;
22201
24352
  ctx.fillRect(0, 0, width, height);
@@ -22443,7 +24594,7 @@ class PixldocsRenderer {
22443
24594
  };
22444
24595
  const onReady = () => {
22445
24596
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
22446
- var _a2, _b;
24597
+ var _a2, _b2;
22447
24598
  try {
22448
24599
  const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
22449
24600
  await this.waitForCanvasImages(container, expectedImageIds);
@@ -22462,7 +24613,7 @@ class PixldocsRenderer {
22462
24613
  );
22463
24614
  const page = renderConfig.pages[pageIndex];
22464
24615
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22465
- const backgroundGradient = (_b = page == null ? void 0 : page.settings) == null ? void 0 : _b.backgroundGradient;
24616
+ const backgroundGradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22466
24617
  cleanup();
22467
24618
  resolve({
22468
24619
  svg: svgString,
@@ -22583,7 +24734,7 @@ class PixldocsRenderer {
22583
24734
  logJsonLine("[canvas-renderer][fabric-text-parity]", { stage, textboxes: sample.length, sample });
22584
24735
  }
22585
24736
  async waitForStableTextMetrics(container, config, options = {}) {
22586
- var _a2, _b, _c;
24737
+ var _a2, _b2, _c;
22587
24738
  if (typeof document !== "undefined") {
22588
24739
  void ensureFontsForResolvedConfig(config);
22589
24740
  await this.waitForRelevantFonts(config);
@@ -22625,7 +24776,7 @@ class PixldocsRenderer {
22625
24776
  }
22626
24777
  fabricInstance.getObjects().forEach(primeCharBounds);
22627
24778
  (_a2 = fabricInstance.calcOffset) == null ? void 0 : _a2.call(fabricInstance);
22628
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
24779
+ (_b2 = fabricInstance.renderAll) == null ? void 0 : _b2.call(fabricInstance);
22629
24780
  await waitForPaint();
22630
24781
  (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
22631
24782
  await waitForPaint();
@@ -22666,7 +24817,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22666
24817
  };
22667
24818
  logParityJson(tag, stage, { kind: "summary", ...summary });
22668
24819
  const sample = texts.slice(0, maxItems).map((t, idx) => {
22669
- var _a2, _b;
24820
+ var _a2, _b2;
22670
24821
  const tspans = Array.from(t.querySelectorAll("tspan"));
22671
24822
  const tspanInfo = tspans.slice(0, 8).map((s) => ({
22672
24823
  x: s.getAttribute("x"),
@@ -22683,7 +24834,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22683
24834
  while (cursor && !containerWidth) {
22684
24835
  containerWidth = (_a2 = cursor.getAttribute) == null ? void 0 : _a2.call(cursor, "width");
22685
24836
  cursor = cursor.parentElement;
22686
- if (cursor && ((_b = cursor.tagName) == null ? void 0 : _b.toLowerCase()) === "svg") break;
24837
+ if (cursor && ((_b2 = cursor.tagName) == null ? void 0 : _b2.toLowerCase()) === "svg") break;
22687
24838
  }
22688
24839
  return {
22689
24840
  idx,
@@ -23071,10 +25222,10 @@ function normalizeSvgExplicitColors(svg) {
23071
25222
  return normalizeGradientPaintRef(v);
23072
25223
  };
23073
25224
  const hasExplicitNonePaint = (el, attr) => {
23074
- var _a2, _b;
25225
+ var _a2, _b2;
23075
25226
  const raw = (el.getAttribute(attr) ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue(attr)) ?? "").trim().toLowerCase();
23076
25227
  if (raw === "none" || raw === "transparent") return true;
23077
- const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b = el.style) == null ? void 0 : _b.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
25228
+ const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
23078
25229
  return rawOpacity === "0" || rawOpacity === "0%" || rawOpacity === "0.0";
23079
25230
  };
23080
25231
  function walk(el, parentFill, parentStroke, parentColor) {
@@ -23141,14 +25292,14 @@ function normalizeSvgExplicitColors(svg) {
23141
25292
  function bakeGroupOpacityIntoChildren(svg) {
23142
25293
  const DRAWABLE = /* @__PURE__ */ new Set(["path", "rect", "circle", "ellipse", "polygon", "polyline", "line", "text", "tspan"]);
23143
25294
  function walkAndBake(el, inheritedOpacity) {
23144
- var _a2, _b, _c, _d;
25295
+ var _a2, _b2, _c, _d;
23145
25296
  if (isInSvgDefinitionSubtree(el)) {
23146
25297
  for (let i = 0; i < el.children.length; i++) walkAndBake(el.children[i], 1);
23147
25298
  return;
23148
25299
  }
23149
25300
  const tag = ((_a2 = el.tagName) == null ? void 0 : _a2.toLowerCase()) ?? "";
23150
25301
  const opacityAttr = parseSvgOpacity(el.getAttribute("opacity"));
23151
- const styleOpacity = parseSvgOpacity(((_b = el.style) == null ? void 0 : _b.getPropertyValue("opacity")) || null);
25302
+ const styleOpacity = parseSvgOpacity(((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("opacity")) || null);
23152
25303
  const ownOpacity = opacityAttr ?? styleOpacity ?? 1;
23153
25304
  const combinedOpacity = inheritedOpacity * ownOpacity;
23154
25305
  if (ownOpacity < 0.999 && tag !== "image") {
@@ -23671,10 +25822,10 @@ function getFirstExplicitColorFromSvg(svg) {
23671
25822
  return getGradientStopColorAsHex(svg, gradientId);
23672
25823
  };
23673
25824
  function walk(el) {
23674
- var _a2, _b;
25825
+ var _a2, _b2;
23675
25826
  if (fill && stroke) return;
23676
25827
  const f = el.getAttribute("fill") ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue("fill"));
23677
- const s = el.getAttribute("stroke") ?? ((_b = el.style) == null ? void 0 : _b.getPropertyValue("stroke"));
25828
+ const s = el.getAttribute("stroke") ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("stroke"));
23678
25829
  if (!fill) {
23679
25830
  if (isRealColor(f)) fill = f;
23680
25831
  else if (f) {
@@ -23702,8 +25853,8 @@ function isNearWhite(hex) {
23702
25853
  return r >= 250 && g >= 250 && b >= 250;
23703
25854
  }
23704
25855
  function getStopColorRaw(stop) {
23705
- var _a2, _b;
23706
- 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");
25856
+ var _a2, _b2;
25857
+ 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");
23707
25858
  }
23708
25859
  function getGradientStopColorAsHex(svgRoot, gradientId, visited = /* @__PURE__ */ new Set()) {
23709
25860
  var _a2;
@@ -23767,12 +25918,12 @@ async function convertTextDecorationsToLines(svg) {
23767
25918
  else el.removeAttribute("style");
23768
25919
  };
23769
25920
  const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
23770
- var _a2, _b;
25921
+ var _a2, _b2;
23771
25922
  let current = el;
23772
25923
  while (current) {
23773
25924
  const attrValue = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
23774
25925
  if (attrValue) return attrValue;
23775
- const styleValue = (_b = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b.trim();
25926
+ const styleValue = (_b2 = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b2.trim();
23776
25927
  if (styleValue) return styleValue;
23777
25928
  current = current.parentElement;
23778
25929
  }
@@ -23950,7 +26101,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
23950
26101
  }
23951
26102
  }
23952
26103
  async function rasterizeShadowMarkers(svg) {
23953
- var _a2, _b, _c, _d, _e, _f;
26104
+ var _a2, _b2, _c, _d, _e, _f;
23954
26105
  if (typeof window === "undefined" || typeof document === "undefined") return;
23955
26106
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
23956
26107
  if (markers.length === 0) return;
@@ -23974,7 +26125,7 @@ async function rasterizeShadowMarkers(svg) {
23974
26125
  const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
23975
26126
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
23976
26127
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
23977
- (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
26128
+ (_b2 = marker.parentNode) == null ? void 0 : _b2.removeChild(marker);
23978
26129
  continue;
23979
26130
  }
23980
26131
  const innerXml = restoreSourceFontsForShadowRaster(
@@ -24219,7 +26370,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24219
26370
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24220
26371
  sanitizeSvgTreeForPdf(svgToDraw);
24221
26372
  try {
24222
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CHVrwSi7.js");
26373
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CsrQStE8.js");
24223
26374
  try {
24224
26375
  await logTextMeasurementDiagnostic(svgToDraw);
24225
26376
  } catch {
@@ -24235,7 +26386,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24235
26386
  }
24236
26387
  }
24237
26388
  function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundColor, backgroundGradient) {
24238
- var _a2, _b;
26389
+ var _a2, _b2;
24239
26390
  if (backgroundGradient && ((_a2 = backgroundGradient.stops) == null ? void 0 : _a2.length) >= 2) {
24240
26391
  const grad = backgroundGradient;
24241
26392
  const colorStops = grad.stops.map((s) => {
@@ -24290,7 +26441,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24290
26441
  doc.fill({ key: patternKey, matrix: doc.Matrix(1, 0, 0, 1, 0, 0) });
24291
26442
  });
24292
26443
  } catch {
24293
- const fallback = ((_b = colorStops[0]) == null ? void 0 : _b.color) || [255, 255, 255];
26444
+ const fallback = ((_b2 = colorStops[0]) == null ? void 0 : _b2.color) || [255, 255, 255];
24294
26445
  pdf.setFillColor(fallback[0], fallback[1], fallback[2]);
24295
26446
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
24296
26447
  }
@@ -24303,7 +26454,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24303
26454
  }
24304
26455
  }
24305
26456
  async function assemblePdfFromSvgs(svgResults, options = {}) {
24306
- var _a2, _b;
26457
+ var _a2, _b2;
24307
26458
  if (svgResults.length === 0) throw new Error("No pages to export");
24308
26459
  const { title, stripPageBackground } = options;
24309
26460
  const firstPage = svgResults[0];
@@ -24336,7 +26487,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
24336
26487
  const pageOrientation = page.width > page.height ? "landscape" : "portrait";
24337
26488
  pdf.addPage([page.width, page.height], pageOrientation);
24338
26489
  }
24339
- const hasGradient = !!((_b = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b.length);
26490
+ const hasGradient = !!((_b2 = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b2.length);
24340
26491
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
24341
26492
  const shouldStripBg = stripPageBackground ?? hasGradient;
24342
26493
  const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
@@ -24619,4 +26770,4 @@ export {
24619
26770
  buildTeaserBlurFlatKeys as y,
24620
26771
  collectFontDescriptorsFromConfig as z
24621
26772
  };
24622
- //# sourceMappingURL=index-B2H1tQ0S.js.map
26773
+ //# sourceMappingURL=index-BQ5V2uT8.js.map