@pixldocs/canvas-renderer 0.5.241 → 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,
@@ -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();
@@ -5738,6 +5770,7 @@ function getObjectSnapPoints(obj) {
5738
5770
  function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5739
5771
  if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };
5740
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;
@@ -5840,6 +5891,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5840
5891
  const equalGap = Math.round(newLeft - bestPair.A.right);
5841
5892
  const bracketY = (Math.max(bestPair.A.top, projectedTop, bestPair.B.top) + Math.min(bestPair.A.bottom, projectedBottom, bestPair.B.bottom)) / 2;
5842
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 };
5843
5896
  newGuides.push({
5844
5897
  type: "horizontal",
5845
5898
  position: bracketY,
@@ -5847,7 +5900,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5847
5900
  gap: equalGap,
5848
5901
  start: bestPair.A.right,
5849
5902
  end: newLeft,
5850
- bracketAt: bracketY
5903
+ bracketAt: bracketY,
5904
+ targetBoundsList: [aTb, bTb]
5851
5905
  });
5852
5906
  newGuides.push({
5853
5907
  type: "horizontal",
@@ -5856,7 +5910,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5856
5910
  gap: equalGap,
5857
5911
  start: newRight,
5858
5912
  end: bestPair.B.left,
5859
- bracketAt: bracketY
5913
+ bracketAt: bracketY,
5914
+ targetBoundsList: [aTb, bTb]
5860
5915
  });
5861
5916
  }
5862
5917
  }
@@ -5888,6 +5943,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5888
5943
  const equalGap = Math.round(newTop - bestPair.A.bottom);
5889
5944
  const bracketX = (Math.max(bestPair.A.left, projectedLeft, bestPair.B.left) + Math.min(bestPair.A.right, projectedRight, bestPair.B.right)) / 2;
5890
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 };
5891
5948
  newGuides.push({
5892
5949
  type: "vertical",
5893
5950
  position: bracketX,
@@ -5895,7 +5952,8 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5895
5952
  gap: equalGap,
5896
5953
  start: bestPair.A.bottom,
5897
5954
  end: newTop,
5898
- bracketAt: bracketX
5955
+ bracketAt: bracketX,
5956
+ targetBoundsList: [aTb, bTb]
5899
5957
  });
5900
5958
  newGuides.push({
5901
5959
  type: "vertical",
@@ -5904,16 +5962,17 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5904
5962
  gap: equalGap,
5905
5963
  start: newBottom,
5906
5964
  end: bestPair.B.top,
5907
- bracketAt: bracketX
5965
+ bracketAt: bracketX,
5966
+ targetBoundsList: [aTb, bTb]
5908
5967
  });
5909
5968
  }
5910
5969
  }
5911
5970
  }
5912
5971
  return { guides: newGuides, snapDx, snapDy };
5913
5972
  }
5914
- function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5973
+ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold, additionalBounds = []) {
5915
5974
  if (!snapToGuides) return [];
5916
- const threshold = snapThreshold || 5;
5975
+ const threshold = snapThreshold;
5917
5976
  const newGuides = [];
5918
5977
  const scaling = getObjectSnapPoints(scalingObj);
5919
5978
  const scalingId = getObjectId(scalingObj);
@@ -5926,7 +5985,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5926
5985
  const checkVerticalSnap = (edgePosition, targetPosition, guide) => {
5927
5986
  const dist = Math.abs(edgePosition - targetPosition);
5928
5987
  if (dist < threshold) {
5929
- newGuides.push({ ...guide, distance: Math.round(dist) });
5988
+ newGuides.push({ ...guide, kind: "alignment" });
5930
5989
  return true;
5931
5990
  }
5932
5991
  return false;
@@ -5934,7 +5993,7 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5934
5993
  const checkHorizontalSnap = (edgePosition, targetPosition, guide) => {
5935
5994
  const dist = Math.abs(edgePosition - targetPosition);
5936
5995
  if (dist < threshold) {
5937
- newGuides.push({ ...guide, distance: Math.round(dist) });
5996
+ newGuides.push({ ...guide, kind: "alignment" });
5938
5997
  return true;
5939
5998
  }
5940
5999
  return false;
@@ -5956,12 +6015,39 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
5956
6015
  checkHorizontalSnap(scaling.bottom, canvasCenterY, { type: "horizontal", position: canvasCenterY });
5957
6016
  }
5958
6017
  const rawObjects = canvas.getObjects();
6018
+ const objectBounds = [];
5959
6019
  for (const obj of rawObjects) {
5960
6020
  if (obj === scalingObj) continue;
5961
6021
  const objId = getObjectId(obj);
5962
6022
  if (objId === "__background__") continue;
5963
6023
  if (objId && objId === scalingId) continue;
5964
- 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
+ }
5965
6051
  if (resizingLeft) {
5966
6052
  checkVerticalSnap(scaling.left, other.left, {
5967
6053
  type: "vertical",
@@ -6043,6 +6129,18 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
6043
6129
  });
6044
6130
  }
6045
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
+ }
6046
6144
  const seen = /* @__PURE__ */ new Set();
6047
6145
  return newGuides.filter((guide) => {
6048
6146
  const key = `${guide.type}-${guide.position.toFixed(1)}`;
@@ -6051,6 +6149,349 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
6051
6149
  return true;
6052
6150
  });
6053
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
+ }
6054
6495
  const clamp01$1 = (v) => Math.max(0, Math.min(1, Number.isFinite(v) ? v : 0));
6055
6496
  function arcPath(w, sag, up) {
6056
6497
  if (sag <= 0.5) return `M 0 0 L ${w} 0`;
@@ -6221,12 +6662,87 @@ if (Array.isArray(stateProps)) {
6221
6662
  if (!stateProps.includes("minBoxHeight")) stateProps.push("minBoxHeight");
6222
6663
  if (!stateProps.includes("verticalAlign")) stateProps.push("verticalAlign");
6223
6664
  if (!stateProps.includes("textPath")) stateProps.push("textPath");
6665
+ if (!stateProps.includes("smartWrap")) stateProps.push("smartWrap");
6224
6666
  }
6225
6667
  const cacheProps = fabric.Textbox.prototype.cacheProperties;
6226
6668
  if (Array.isArray(cacheProps)) {
6227
6669
  if (!cacheProps.includes("minBoxHeight")) cacheProps.push("minBoxHeight");
6228
6670
  if (!cacheProps.includes("verticalAlign")) cacheProps.push("verticalAlign");
6229
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
+ };
6230
6746
  }
6231
6747
  const hasActiveTextPath = (obj) => {
6232
6748
  const tp = obj.textPath;
@@ -6383,14 +6899,14 @@ function scaleLocalToScreen(target, p) {
6383
6899
  return new fabric.Point(p.x * sx * zx, p.y * sy * zy);
6384
6900
  }
6385
6901
  function applyTextPathControls(textbox) {
6386
- 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;
6387
6903
  const obj = textbox;
6388
6904
  if (!hasActiveTextPath(obj)) {
6389
6905
  obj.__pdTextPathHovered = false;
6390
6906
  if (obj.__pdTextPathControls) {
6391
6907
  try {
6392
6908
  const cu2 = fabric.controlsUtils;
6393
- 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));
6394
6910
  if (defaults) obj.controls = defaults;
6395
6911
  } catch {
6396
6912
  }
@@ -6654,7 +7170,7 @@ function applyTextPathControls(textbox) {
6654
7170
  actionName: "tpPivot",
6655
7171
  positionHandler: (_d2, finalMatrix, fabricObject) => scaleLocalToScreen(fabricObject, getPivotLocalCentered(fabricObject)).transform(finalMatrix),
6656
7172
  actionHandler: (_e2, transform, x, y) => {
6657
- var _a3, _b2;
7173
+ var _a3, _b3;
6658
7174
  const target = transform.target;
6659
7175
  const state = transform.__pdCirclePivotDrag || (transform.__pdCirclePivotDrag = {
6660
7176
  startMatrix: target.calcTransformMatrix(),
@@ -6671,7 +7187,7 @@ function applyTextPathControls(textbox) {
6671
7187
  y: (state.startOffset.y || 0) + dy
6672
7188
  };
6673
7189
  target.setCoords();
6674
- (_b2 = target.canvas) == null ? void 0 : _b2.requestRenderAll();
7190
+ (_b3 = target.canvas) == null ? void 0 : _b3.requestRenderAll();
6675
7191
  return true;
6676
7192
  },
6677
7193
  render: renderPivot
@@ -6722,13 +7238,13 @@ function applyTextPathControls(textbox) {
6722
7238
  }
6723
7239
  if (!obj.__pdCirclePivotDblWired) {
6724
7240
  obj.on("mousedblclick", () => {
6725
- var _a3, _b2;
7241
+ var _a3, _b3;
6726
7242
  if (((_a3 = obj.textPath) == null ? void 0 : _a3.preset) !== "circle") return;
6727
7243
  if (obj.__corner !== "tpPivot") return;
6728
7244
  if (!obj.textPath.pivot) return;
6729
7245
  obj.textPath.pivot = { x: 0, y: 0 };
6730
7246
  obj.setCoords();
6731
- (_b2 = obj.canvas) == null ? void 0 : _b2.requestRenderAll();
7247
+ (_b3 = obj.canvas) == null ? void 0 : _b3.requestRenderAll();
6732
7248
  });
6733
7249
  obj.__pdCirclePivotDblWired = true;
6734
7250
  }
@@ -7096,10 +7612,10 @@ function textPathBoundsContainScenePoint(obj, point) {
7096
7612
  }
7097
7613
  }
7098
7614
  function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7099
- var _a2, _b, _c;
7615
+ var _a2, _b2, _c;
7100
7616
  const host = hostCanvas || obj.canvas || ((_a2 = obj.group) == null ? void 0 : _a2.canvas);
7101
7617
  if (!host) return;
7102
- 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;
7103
7619
  const matrix = fabric.util.multiplyTransformMatrices(host.viewportTransform || [1, 0, 0, 1, 0, 0], obj.calcTransformMatrix());
7104
7620
  const corners = [
7105
7621
  new fabric.Point(bounds.minX, bounds.minY),
@@ -7121,8 +7637,8 @@ function drawTextPathBounds(ctx, obj, bounds, hostCanvas) {
7121
7637
  }
7122
7638
  if (typeof TextboxProto._renderControls === "function" && !TextboxProto.__pixldocsOrigRenderControls) {
7123
7639
  let drawWarpGuides = function(ctx) {
7124
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7125
- 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);
7126
7642
  if (!hostCanvas) return;
7127
7643
  const hoverBounds = getTextPathHitBounds(this);
7128
7644
  const active = (_d = hostCanvas.getActiveObject) == null ? void 0 : _d.call(hostCanvas);
@@ -7545,12 +8061,12 @@ function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
7545
8061
  ctx.closePath();
7546
8062
  }
7547
8063
  function measureLineGlyphWidth(obj, lineIndex) {
7548
- var _a2, _b, _c, _d, _e, _f;
8064
+ var _a2, _b2, _c, _d, _e, _f;
7549
8065
  try {
7550
8066
  const rawLine = (_a2 = obj == null ? void 0 : obj._textLines) == null ? void 0 : _a2[lineIndex];
7551
8067
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
7552
8068
  if (!lineText) return 0;
7553
- 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);
7554
8070
  if (!fontSize) return 0;
7555
8071
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
7556
8072
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
@@ -7819,7 +8335,7 @@ function applyTextBackground(obj, cfg) {
7819
8335
  const originalToSVG = (_a2 = obj.toSVG) == null ? void 0 : _a2.bind(obj);
7820
8336
  if (typeof originalToSVG === "function") {
7821
8337
  obj.toSVG = function(reviver) {
7822
- var _a3, _b;
8338
+ var _a3, _b2;
7823
8339
  let svg = originalToSVG(reviver);
7824
8340
  const bg = this[PD_BG_KEY];
7825
8341
  const shadow = this.shadow;
@@ -7868,7 +8384,7 @@ function applyTextBackground(obj, cfg) {
7868
8384
  const bgOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
7869
8385
  let bgGradDefs = "";
7870
8386
  let bgFillAttr = escapeXmlAttr(bgFill);
7871
- 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) {
7872
8388
  const bounds = ribbonD ? computeRibbonBoundsFor(this, pT, pR, pB, pL) : unionBounds(rects);
7873
8389
  const gid = `__pdBgGrad_${Math.random().toString(36).slice(2, 9)}`;
7874
8390
  const def = buildSvgGradientDef(bg.gradient, gid, bounds.x, bounds.y, bounds.w, bounds.h);
@@ -8897,9 +9413,9 @@ function createShape(element) {
8897
9413
  });
8898
9414
  }
8899
9415
  case "circle": {
8900
- const radius = Math.min(w, h) / 2;
8901
- return new fabric.Circle({
8902
- radius,
9416
+ return new fabric.Ellipse({
9417
+ rx: w / 2,
9418
+ ry: h / 2,
8903
9419
  fill,
8904
9420
  stroke,
8905
9421
  strokeWidth,
@@ -8908,8 +9424,7 @@ function createShape(element) {
8908
9424
  objectCaching: true,
8909
9425
  strokeUniform: true,
8910
9426
  strokeLineJoin: "round",
8911
- strokeLineCap: "round",
8912
- lockUniScaling: true
9427
+ strokeLineCap: "round"
8913
9428
  });
8914
9429
  }
8915
9430
  case "triangle": {
@@ -8944,7 +9459,7 @@ function createShape(element) {
8944
9459
  }
8945
9460
  }
8946
9461
  function createText(element) {
8947
- var _a2, _b, _c, _d, _e;
9462
+ var _a2, _b2, _c, _d, _e;
8948
9463
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
8949
9464
  let text = element.text || "Text";
8950
9465
  let fontSize = element.fontSize || 16;
@@ -9145,7 +9660,7 @@ function createText(element) {
9145
9660
  textbox.setCoords();
9146
9661
  const widthAfterSet = textbox.width ?? 0;
9147
9662
  try {
9148
- (_b = textbox.setControlsVisibility) == null ? void 0 : _b.call(textbox, {
9663
+ (_b2 = textbox.setControlsVisibility) == null ? void 0 : _b2.call(textbox, {
9149
9664
  tl: true,
9150
9665
  tr: true,
9151
9666
  bl: true,
@@ -9944,34 +10459,595 @@ function bakeEdgeFade(source, fade) {
9944
10459
  return canvas;
9945
10460
  }
9946
10461
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10462
+ const SELECTION_BORDER_SCALE = 2;
10463
+ let ensureCanvaControlRenders = () => {
10464
+ };
9947
10465
  try {
9948
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
+ }
9949
10476
  if (InteractiveBase == null ? void 0 : InteractiveBase.ownDefaults) {
9950
10477
  Object.assign(InteractiveBase.ownDefaults, {
9951
10478
  borderColor: SELECTION_PRIMARY,
9952
- borderScaleFactor: 1.25,
10479
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9953
10480
  cornerColor: SELECTION_PRIMARY,
9954
10481
  cornerStrokeColor: "#ffffff",
9955
- cornerStyle: "rect",
10482
+ cornerStyle: "circle",
9956
10483
  transparentCorners: false,
9957
- cornerSize: 8,
10484
+ cornerSize: 10,
9958
10485
  borderOpacityWhenMoving: 0.9
9959
10486
  });
9960
10487
  } else if (InteractiveBase == null ? void 0 : InteractiveBase.prototype) {
9961
10488
  Object.assign(InteractiveBase.prototype, {
9962
10489
  borderColor: SELECTION_PRIMARY,
9963
- borderScaleFactor: 1.25,
10490
+ borderScaleFactor: SELECTION_BORDER_SCALE,
9964
10491
  cornerColor: SELECTION_PRIMARY,
9965
10492
  cornerStrokeColor: "#ffffff",
9966
- cornerStyle: "rect",
10493
+ cornerStyle: "circle",
9967
10494
  transparentCorners: false,
9968
- cornerSize: 8,
10495
+ cornerSize: 10,
9969
10496
  borderOpacityWhenMoving: 0.9
9970
10497
  });
9971
10498
  }
9972
10499
  } catch (e) {
9973
10500
  console.warn("[PageCanvas] Failed to apply global selection defaults:", e);
9974
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
+ };
9975
11051
  function applyWarpAwareSelectionBorders(selection) {
9976
11052
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
9977
11053
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -10039,6 +11115,8 @@ const PageCanvas = forwardRef(
10039
11115
  const hasRunPostReadyReflowForPageRef = useRef(null);
10040
11116
  const hasNotifiedReadyForPageRef = useRef(null);
10041
11117
  const hasClearedCachesBeforeFirstSyncRef = useRef(false);
11118
+ const projectSettingsRef = useRef(projectSettings);
11119
+ projectSettingsRef.current = projectSettings;
10042
11120
  const [guides, setGuides] = useState([]);
10043
11121
  const [gridResizeLabel, setGridResizeLabel] = useState(null);
10044
11122
  const [hoverBounds, setHoverBounds] = useState(null);
@@ -10108,7 +11186,8 @@ const PageCanvas = forwardRef(
10108
11186
  useRef(null);
10109
11187
  useRef(null);
10110
11188
  useRef(/* @__PURE__ */ new Map());
10111
- useRef(null);
11189
+ const groupResizeActiveSnapRef = useRef(null);
11190
+ const objectResizeActiveSnapRef = useRef(null);
10112
11191
  useRef(null);
10113
11192
  useRef(null);
10114
11193
  useRef(null);
@@ -10250,33 +11329,358 @@ const PageCanvas = forwardRef(
10250
11329
  (movingObj) => {
10251
11330
  const fabricCanvas = fabricRef.current;
10252
11331
  if (!fabricCanvas) return { guides: [], snapDx: 0, snapDy: 0 };
11332
+ const ps = projectSettingsRef.current;
10253
11333
  return calculateSnapGuides(
10254
11334
  movingObj,
10255
11335
  fabricCanvas,
10256
11336
  canvasWidth,
10257
11337
  canvasHeight,
10258
- projectSettings.snapToGuides,
10259
- projectSettings.snapThreshold
11338
+ ps.snapToGuides,
11339
+ ps.snapThreshold
10260
11340
  );
10261
11341
  },
10262
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11342
+ [canvasWidth, canvasHeight]
10263
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]);
10264
11412
  const calculateScaleSnapGuidesCallback = useCallback(
10265
11413
  (scalingObj, corner) => {
10266
11414
  const fabricCanvas = fabricRef.current;
10267
11415
  if (!fabricCanvas) return [];
11416
+ const ps = projectSettingsRef.current;
11417
+ const excludeIds = getResizeExcludeIdsCallback(scalingObj);
10268
11418
  return calculateScaleSnapGuides(
10269
11419
  scalingObj,
10270
11420
  corner,
10271
- 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,
10272
11452
  canvasWidth,
10273
11453
  canvasHeight,
10274
- projectSettings.snapToGuides,
10275
- 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
+ }
10276
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();
10277
11514
  },
10278
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
11515
+ [canvasWidth, canvasHeight, getLogicalGroupSnapBoundsCallback, getResizeExcludeIdsCallback]
10279
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]);
10280
11684
  const isTransforming = useCallback((canvas2) => {
10281
11685
  if (!canvas2) return false;
10282
11686
  return !!canvas2._currentTransform || !!canvas2.__isUserTransforming;
@@ -10402,6 +11806,8 @@ const PageCanvas = forwardRef(
10402
11806
  // Transparent so underlay (page bg + group bgs) shows through
10403
11807
  backgroundColor: "transparent"
10404
11808
  });
11809
+ fabricCanvas.hoverCursor = "default";
11810
+ fabricCanvas.moveCursor = "move";
10405
11811
  if (!allowSelection) {
10406
11812
  fabricCanvas.selection = false;
10407
11813
  fabricCanvas.on("selection:created", () => {
@@ -10423,6 +11829,61 @@ const PageCanvas = forwardRef(
10423
11829
  fabricRef.current = fabricCanvas;
10424
11830
  const storeRegistryKey = registerFabricCanvas(pageId, fabricCanvas);
10425
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
+ }
10426
11887
  const initFonts = async () => {
10427
11888
  try {
10428
11889
  await preloadAllFonts();
@@ -10512,6 +11973,17 @@ const PageCanvas = forwardRef(
10512
11973
  });
10513
11974
  fabricCanvas.on("mouse:up", () => {
10514
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
+ }
10515
11987
  });
10516
11988
  fabricCanvas.on("object:scaling", () => {
10517
11989
  fabricCanvas.__isUserTransforming = true;
@@ -10558,7 +12030,7 @@ const PageCanvas = forwardRef(
10558
12030
  didTransformRef.current = true;
10559
12031
  });
10560
12032
  const syncSelectionToStore = () => {
10561
- var _a2, _b, _c, _d;
12033
+ var _a2, _b2, _c, _d;
10562
12034
  if (!isActiveRef.current || isRebuildingRef.current || isSyncingSelectionToFabricRef.current || !allowSelection) return;
10563
12035
  const walkToTopmostGroup = (childId, children, activeEditingGroupId) => {
10564
12036
  let topmost = null;
@@ -10597,7 +12069,7 @@ const PageCanvas = forwardRef(
10597
12069
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
10598
12070
  const clickedId = ids[0];
10599
12071
  const state = useEditorStore.getState();
10600
- 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);
10601
12073
  const children = (currentPage2 == null ? void 0 : currentPage2.children) ?? [];
10602
12074
  const parent = walkToTopmostGroup(clickedId, children, activeEditingGroupId);
10603
12075
  const targetIsInCrop = !!(active instanceof fabric.Group && isCropGroupInCropMode(active) || (active == null ? void 0 : active.group) instanceof fabric.Group && isCropGroupInCropMode(active.group));
@@ -10744,8 +12216,10 @@ const PageCanvas = forwardRef(
10744
12216
  const activeObj = fabricCanvas.getActiveObject();
10745
12217
  if (activeObj instanceof fabric.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10746
12218
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12219
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10747
12220
  if (activeObj && !(activeObj instanceof fabric.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10748
- installCanvaMaskControls(activeObj);
12221
+ installImageResizeControlsWithSnap(activeObj);
12222
+ ensureCanvaControlRenders(activeObj);
10749
12223
  }
10750
12224
  });
10751
12225
  fabricCanvas.on("selection:updated", () => {
@@ -10757,12 +12231,14 @@ const PageCanvas = forwardRef(
10757
12231
  const activeObj = fabricCanvas.getActiveObject();
10758
12232
  if (activeObj instanceof fabric.ActiveSelection) applyWarpAwareSelectionBorders(activeObj);
10759
12233
  if (activeObj) applyControlSizeForZoom(fabricCanvas, activeObj);
12234
+ if (activeObj) ensureCanvaControlRenders(activeObj);
10760
12235
  if (activeObj && !(activeObj instanceof fabric.ActiveSelection) && (((_a2 = activeObj._ct) == null ? void 0 : _a2.isCropGroup) || activeObj.__cropGroup)) {
10761
- installCanvaMaskControls(activeObj);
12236
+ installImageResizeControlsWithSnap(activeObj);
12237
+ ensureCanvaControlRenders(activeObj);
10762
12238
  }
10763
12239
  });
10764
12240
  fabricCanvas.on("mouse:dblclick", (opt) => {
10765
- var _a2, _b;
12241
+ var _a2, _b2;
10766
12242
  const target = opt == null ? void 0 : opt.target;
10767
12243
  if (!target) return;
10768
12244
  if (target.isEditing) return;
@@ -10786,7 +12262,7 @@ const PageCanvas = forwardRef(
10786
12262
  const childId = getObjectId(hitChild);
10787
12263
  if (!childId) return;
10788
12264
  const stateNow = useEditorStore.getState();
10789
- 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);
10790
12266
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
10791
12267
  const chain = [];
10792
12268
  {
@@ -10913,7 +12389,7 @@ const PageCanvas = forwardRef(
10913
12389
  transformingIdsRef.current.clear();
10914
12390
  };
10915
12391
  const prepareGroupSelectionTransformStart = (target) => {
10916
- var _a2, _b;
12392
+ var _a2, _b2;
10917
12393
  const active = target instanceof fabric.ActiveSelection ? target : fabricCanvas.getActiveObject();
10918
12394
  if (!(active instanceof fabric.ActiveSelection)) return;
10919
12395
  if (!activeSelectionMoveStartRef.current || activeSelectionMoveStartRef.current.selection !== active) {
@@ -10927,7 +12403,7 @@ const PageCanvas = forwardRef(
10927
12403
  const groupId = active.__pixldocsGroupSelection;
10928
12404
  if (!groupId) return;
10929
12405
  if (((_a2 = groupSelectionTransformStartRef.current) == null ? void 0 : _a2.groupId) === groupId && groupSelectionTransformStartRef.current.selection === active) return;
10930
- 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) ?? [];
10931
12407
  const groupNode = findNodeById(pageChildren2, groupId);
10932
12408
  if (!groupNode) return;
10933
12409
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
@@ -11011,7 +12487,7 @@ const PageCanvas = forwardRef(
11011
12487
  };
11012
12488
  let pendingShiftMultiSelect = null;
11013
12489
  const applyShiftMultiSelect = (target, event, baselineActive = fabricCanvas.getActiveObject(), baselineObjects = fabricCanvas.getActiveObjects()) => {
11014
- var _a2, _b, _c;
12490
+ var _a2, _b2, _c;
11015
12491
  if (!target || !target.selectable) return false;
11016
12492
  const active = baselineActive;
11017
12493
  if (!active || ((_a2 = active.getActiveControl) == null ? void 0 : _a2.call(active))) return false;
@@ -11045,7 +12521,7 @@ const PageCanvas = forwardRef(
11045
12521
  isSyncingSelectionToFabricRef.current = false;
11046
12522
  });
11047
12523
  }
11048
- (_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);
11049
12525
  (_c = event == null ? void 0 : event.stopPropagation) == null ? void 0 : _c.call(event);
11050
12526
  return true;
11051
12527
  };
@@ -11096,9 +12572,9 @@ const PageCanvas = forwardRef(
11096
12572
  return !!(((_a2 = o == null ? void 0 : o._ct) == null ? void 0 : _a2.isCropGroup) || (o == null ? void 0 : o.__cropGroup));
11097
12573
  };
11098
12574
  const promoteToCropGroup = (opt) => {
11099
- var _a2, _b, _c, _d, _e, _f;
12575
+ var _a2, _b2, _c, _d, _e, _f;
11100
12576
  const t = opt.target;
11101
- 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") {
11102
12578
  if (t && isCropGroup2(t)) {
11103
12579
  fabricCanvas._hoveredTarget = t;
11104
12580
  } else if ((t == null ? void 0 : t.group) && isCropGroup2(t.group)) {
@@ -11114,6 +12590,8 @@ const PageCanvas = forwardRef(
11114
12590
  const objects = fabricCanvas.getObjects();
11115
12591
  for (const obj of objects) {
11116
12592
  if (isCropGroup2(obj) && obj.containsPoint(pointer)) {
12593
+ installImageResizeControlsWithSnap(obj);
12594
+ ensureCanvaControlRenders(obj);
11117
12595
  fabricCanvas.setActiveObject(obj);
11118
12596
  opt.target = obj;
11119
12597
  fabricCanvas._hoveredTarget = obj;
@@ -11124,12 +12602,16 @@ const PageCanvas = forwardRef(
11124
12602
  }
11125
12603
  const g = t.group;
11126
12604
  if (g && isCropGroup2(g)) {
12605
+ installImageResizeControlsWithSnap(g);
12606
+ ensureCanvaControlRenders(g);
11127
12607
  fabricCanvas.setActiveObject(g);
11128
12608
  opt.target = g;
11129
12609
  fabricCanvas._hoveredTarget = g;
11130
12610
  return;
11131
12611
  }
11132
12612
  if (isCropGroup2(t)) {
12613
+ installImageResizeControlsWithSnap(t);
12614
+ ensureCanvaControlRenders(t);
11133
12615
  fabricCanvas.setActiveObject(t);
11134
12616
  t.set({
11135
12617
  selectable: true,
@@ -11182,7 +12664,7 @@ const PageCanvas = forwardRef(
11182
12664
  });
11183
12665
  }
11184
12666
  fabricCanvas.on("mouse:down", (opt) => {
11185
- var _a2, _b;
12667
+ var _a2, _b2;
11186
12668
  if (pendingShiftMultiSelect) {
11187
12669
  const pending = pendingShiftMultiSelect;
11188
12670
  pendingShiftMultiSelect = null;
@@ -11190,17 +12672,19 @@ const PageCanvas = forwardRef(
11190
12672
  return;
11191
12673
  }
11192
12674
  const target = opt.target;
11193
- 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;
11194
12676
  if (cropGroup) {
11195
12677
  lockEdits();
11196
12678
  didTransformRef.current = false;
12679
+ installImageResizeControlsWithSnap(cropGroup);
12680
+ ensureCanvaControlRenders(cropGroup);
11197
12681
  fabricCanvas.setActiveObject(cropGroup);
11198
12682
  cropGroup.setCoords();
11199
12683
  fabricCanvas.requestRenderAll();
11200
12684
  }
11201
12685
  });
11202
12686
  const groupFabricUnionBBox = (g) => {
11203
- var _a2, _b;
12687
+ var _a2, _b2;
11204
12688
  const memberIds = new Set(getAllElementIds(g.children ?? []));
11205
12689
  if (memberIds.size === 0) return null;
11206
12690
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -11208,7 +12692,7 @@ const PageCanvas = forwardRef(
11208
12692
  for (const o of fabricCanvas.getObjects()) {
11209
12693
  const oid = getObjectId(o);
11210
12694
  if (!oid || !memberIds.has(oid)) continue;
11211
- 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));
11212
12696
  if (!br) continue;
11213
12697
  minX = Math.min(minX, br.left);
11214
12698
  minY = Math.min(minY, br.top);
@@ -11234,13 +12718,13 @@ const PageCanvas = forwardRef(
11234
12718
  return pick;
11235
12719
  };
11236
12720
  fabricCanvas.on("mouse:down:before", (opt) => {
11237
- 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;
11238
12722
  if (editLockRef.current) {
11239
12723
  const active = fabricCanvas.getActiveObject();
11240
12724
  if (active && (((_a2 = active._ct) == null ? void 0 : _a2.isCropGroup) || active.__cropGroup)) {
11241
12725
  opt.target = active;
11242
12726
  if (opt.e) {
11243
- (_c = (_b = opt.e).preventDefault) == null ? void 0 : _c.call(_b);
12727
+ (_c = (_b2 = opt.e).preventDefault) == null ? void 0 : _c.call(_b2);
11244
12728
  (_e = (_d = opt.e).stopPropagation) == null ? void 0 : _e.call(_d);
11245
12729
  }
11246
12730
  }
@@ -11255,6 +12739,10 @@ const PageCanvas = forwardRef(
11255
12739
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11256
12740
  const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11257
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
+ }
11258
12746
  if (isMultiSelectModifier(opt.e)) {
11259
12747
  const manualTarget = target && !(target instanceof fabric.ActiveSelection) && targetId && targetId !== "__background__" ? target : pickSelectableObjectAtPointer(opt.e);
11260
12748
  if (manualTarget) {
@@ -11281,11 +12769,39 @@ const PageCanvas = forwardRef(
11281
12769
  return topmost;
11282
12770
  };
11283
12771
  if (target && targetId && targetId !== "__background__") {
11284
- const parent = findTopmostPromotableGroup(targetId);
11285
- 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));
11286
12797
  const activeNow = fabricCanvas.getActiveObject();
11287
12798
  const alreadyThisGroup = activeNow instanceof fabric.ActiveSelection && activeNow.__pixldocsGroupSelection === (parent == null ? void 0 : parent.id);
11288
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
+ }
11289
12805
  if (parent && !parent.backgroundColor && !targetIsInCrop && activeEditingGroupId !== parent.id && !alreadyThisGroup && !isMultiSelectKey) {
11290
12806
  const memberIds = new Set(getAllElementIds(parent.children ?? []));
11291
12807
  const memberObjs = fabricCanvas.getObjects().filter((o) => {
@@ -11309,10 +12825,33 @@ const PageCanvas = forwardRef(
11309
12825
  opt.target = only;
11310
12826
  pendingGroupPromotionRef.current = { groupId: parent.id, selection: only };
11311
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;
11312
12850
  }
11313
12851
  } else if (!target || targetId === "__background__") {
11314
- 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));
11315
12853
  if (isMultiSelectKey) return;
12854
+ if ((_n = opt.e) == null ? void 0 : _n.altKey) return;
11316
12855
  try {
11317
12856
  const pointer = fabricCanvas.getPointer(opt.e);
11318
12857
  const px = pointer.x;
@@ -11385,11 +12924,28 @@ const PageCanvas = forwardRef(
11385
12924
  const tid = t ? getObjectId(t) : null;
11386
12925
  try {
11387
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
+ }
11388
12945
  const isHoveringSelected = !!(tid && activeIds.has(tid));
11389
12946
  if (t && tid && tid !== "__background__" && !isHoveringSelected) {
11390
- const outer = t.group && !t.group.__pixldocsGroupSelection ? t.group : t;
11391
- outer.setCoords();
11392
- const br = outer.getBoundingRect();
12947
+ t.setCoords();
12948
+ const br = t.getBoundingRect();
11393
12949
  setHoverBounds({
11394
12950
  left: br.left,
11395
12951
  top: br.top,
@@ -11410,7 +12966,7 @@ const PageCanvas = forwardRef(
11410
12966
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
11411
12967
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11412
12968
  const pick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
11413
- fabricCanvas.defaultCursor = pick ? "move" : "default";
12969
+ fabricCanvas.defaultCursor = "default";
11414
12970
  } catch {
11415
12971
  fabricCanvas.defaultCursor = "default";
11416
12972
  }
@@ -11418,6 +12974,11 @@ const PageCanvas = forwardRef(
11418
12974
  fabricCanvas.on("mouse:out", () => {
11419
12975
  setHoverBounds(null);
11420
12976
  });
12977
+ fabricCanvas.on("mouse:down", () => {
12978
+ setHoverBounds(null);
12979
+ });
12980
+ fabricCanvas.on("selection:created", () => setHoverBounds(null));
12981
+ fabricCanvas.on("selection:updated", () => setHoverBounds(null));
11421
12982
  fabricCanvas.on("mouse:down", (ev) => {
11422
12983
  if (fabricCanvas._currentTransform) {
11423
12984
  lockEdits();
@@ -11433,6 +12994,8 @@ const PageCanvas = forwardRef(
11433
12994
  setGuides([]);
11434
12995
  setRotationLabel(null);
11435
12996
  setSizeLabel(null);
12997
+ objectResizeActiveSnapRef.current = null;
12998
+ groupResizeActiveSnapRef.current = null;
11436
12999
  dragStarted = false;
11437
13000
  const pendingPromotion = pendingGroupPromotionRef.current;
11438
13001
  pendingGroupPromotionRef.current = null;
@@ -11587,7 +13150,9 @@ const PageCanvas = forwardRef(
11587
13150
  const intrH = obj.height ?? 1;
11588
13151
  let newW = Math.max(1, intrW * Math.abs(sx));
11589
13152
  let newH = Math.max(1, intrH * Math.abs(sy));
11590
- 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) {
11591
13156
  const diameter = Math.max(1, Math.min(newW, newH));
11592
13157
  newW = diameter;
11593
13158
  newH = diameter;
@@ -11704,8 +13269,128 @@ const PageCanvas = forwardRef(
11704
13269
  }
11705
13270
  const transform = e.transform;
11706
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);
11707
13310
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11708
- 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);
11709
13394
  });
11710
13395
  fabricCanvas.on("object:resizing", (e) => {
11711
13396
  if (!isActiveRef.current) return;
@@ -11731,13 +13416,70 @@ const PageCanvas = forwardRef(
11731
13416
  }
11732
13417
  const transform = e.transform;
11733
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);
11734
13425
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11735
- 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);
11736
13472
  });
11737
13473
  fabricCanvas.on("object:rotating", (e) => {
11738
13474
  markSimpleTransform(e);
11739
13475
  didTransformRef.current = true;
11740
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
+ }
11741
13483
  const rotateTargetId = tr ? getObjectId(tr) : null;
11742
13484
  if (rotateTargetId && rotateTargetId !== "__background__") {
11743
13485
  preserveSelectionAfterTransformIdRef.current = rotateTargetId;
@@ -11764,6 +13506,7 @@ const PageCanvas = forwardRef(
11764
13506
  prepareGroupSelectionTransformStart(e.target);
11765
13507
  markTransforming(e.target);
11766
13508
  didTransformRef.current = true;
13509
+ if (e.target) e.target.__pixldocsDragMoved = true;
11767
13510
  const moveTargetId = e.target ? getObjectId(e.target) : null;
11768
13511
  if (moveTargetId && moveTargetId !== "__background__") {
11769
13512
  preserveSelectionAfterTransformIdRef.current = moveTargetId;
@@ -11780,20 +13523,57 @@ const PageCanvas = forwardRef(
11780
13523
  if (!obj) return;
11781
13524
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11782
13525
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11783
- setGuides(newGuides);
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);
11784
13552
  setHoverBounds(null);
11785
- if (snapDx !== 0 || snapDy !== 0) {
11786
- snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
13553
+ if (finalDx !== 0 || finalDy !== 0) {
13554
+ snapTarget.set({ left: (snapTarget.left ?? 0) + finalDx, top: (snapTarget.top ?? 0) + finalDy });
11787
13555
  }
11788
13556
  });
11789
13557
  let cropGroupSaveTimer = null;
11790
13558
  fabricCanvas.on("object:modified", (e) => {
11791
- var _a2, _b, _c, _d, _e, _f, _g;
13559
+ var _a2, _b2, _c, _d, _e, _f, _g;
11792
13560
  try {
11793
13561
  dragStarted = false;
11794
13562
  setGuides([]);
11795
13563
  setGroupOverlayLiveBoundsRef.current(null);
13564
+ objectResizeActiveSnapRef.current = null;
13565
+ groupResizeActiveSnapRef.current = null;
11796
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
+ }
11797
13577
  lockEdits();
11798
13578
  const modifiedTarget = e.target;
11799
13579
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -11902,7 +13682,7 @@ const PageCanvas = forwardRef(
11902
13682
  useEditorStore.getState().reflowStackGroupInPage(pageId, groupId);
11903
13683
  }
11904
13684
  const stateAfter = useEditorStore.getState();
11905
- 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) ?? [];
11906
13686
  const groupNodeAfter = findNodeById(pageAfter, groupId);
11907
13687
  if (groupNodeAfter) {
11908
13688
  const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);
@@ -11929,11 +13709,11 @@ const PageCanvas = forwardRef(
11929
13709
  clearTimeout(cropGroupSaveTimer);
11930
13710
  }
11931
13711
  cropGroupSaveTimer = setTimeout(() => {
11932
- var _a3, _b2, _c2;
13712
+ var _a3, _b3, _c2;
11933
13713
  const { updateElement: updateElement2 } = useEditorStore.getState();
11934
13714
  const img = ct._img;
11935
13715
  const zoom3 = ((_a3 = img == null ? void 0 : img._ct) == null ? void 0 : _a3.zoom) ?? 1;
11936
- 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;
11937
13717
  const panY = ((_c2 = img == null ? void 0 : img._ct) == null ? void 0 : _c2.panY) ?? 0.5;
11938
13718
  const stateCrop = useEditorStore.getState();
11939
13719
  const pageCrop = stateCrop.canvas.pages.find((p) => p.id === pageId);
@@ -11952,6 +13732,8 @@ const PageCanvas = forwardRef(
11952
13732
  cropZoom: zoom3
11953
13733
  }, { recordHistory: false });
11954
13734
  active.__isInternalCropUpdate = false;
13735
+ installImageResizeControlsWithSnap(active);
13736
+ ensureCanvaControlRenders(active);
11955
13737
  fabricCanvas.setActiveObject(active);
11956
13738
  setTimeout(() => justModifiedIdsRef.current.delete(objId), 150);
11957
13739
  }, 0);
@@ -12205,6 +13987,10 @@ const PageCanvas = forwardRef(
12205
13987
  for (const obj of activeObjects) {
12206
13988
  const objId = getObjectId(obj);
12207
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
+ }
12208
13994
  let intrinsicWidth;
12209
13995
  let intrinsicHeight;
12210
13996
  if (obj instanceof fabric.Circle) {
@@ -12271,7 +14057,6 @@ const PageCanvas = forwardRef(
12271
14057
  absoluteLeft = (absoluteLeft ?? 0) - w / 2;
12272
14058
  absoluteTop = (absoluteTop ?? 0) - h / 2;
12273
14059
  }
12274
- const sourceElement = elementsRef.current.find((el) => el.id === objId);
12275
14060
  const preserveCornerGeometry = (sourceElement == null ? void 0 : sourceElement.type) === "shape" && (sourceElement.shapeType === "circle" || sourceElement.shapeType === "rounded-rect" || sourceElement.shapeType === "triangle");
12276
14061
  let finalWidth = intrinsicWidth;
12277
14062
  let finalHeight = intrinsicHeight;
@@ -12357,17 +14142,35 @@ const PageCanvas = forwardRef(
12357
14142
  finalHeight = 0;
12358
14143
  finalScaleX = 1;
12359
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
+ });
12360
14169
  } else if (preserveCornerGeometry) {
12361
14170
  const scaledW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));
12362
14171
  const scaledH = Math.max(1, intrinsicHeight * Math.abs(decomposed.scaleY || 1));
12363
- if ((sourceElement == null ? void 0 : sourceElement.shapeType) === "circle") {
12364
- const diameter = Math.max(1, Math.min(scaledW, scaledH));
12365
- finalWidth = diameter;
12366
- finalHeight = diameter;
12367
- } else {
12368
- finalWidth = scaledW;
12369
- finalHeight = scaledH;
12370
- }
14172
+ finalWidth = scaledW;
14173
+ finalHeight = scaledH;
12371
14174
  finalScaleX = 1;
12372
14175
  finalScaleY = 1;
12373
14176
  obj.set({ scaleX: 1, scaleY: 1 });
@@ -12406,6 +14209,11 @@ const PageCanvas = forwardRef(
12406
14209
  transformMatrix: finalAbsoluteMatrix
12407
14210
  };
12408
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
+ }
12409
14217
  const baked = obj.minBoxHeight;
12410
14218
  if (typeof baked === "number" && baked > 0) {
12411
14219
  elementUpdate.minBoxHeight = baked;
@@ -12514,7 +14322,7 @@ const PageCanvas = forwardRef(
12514
14322
  }
12515
14323
  });
12516
14324
  fabricCanvas.on("mouse:dblclick", (e) => {
12517
- var _a2, _b;
14325
+ var _a2, _b2;
12518
14326
  if (!isActiveRef.current || !allowEditing) return;
12519
14327
  let target = e.target;
12520
14328
  if (!target) {
@@ -12524,7 +14332,7 @@ const PageCanvas = forwardRef(
12524
14332
  if (target && target instanceof fabric.Group && target.__cropGroup) {
12525
14333
  const ct = target.__cropData;
12526
14334
  const innerImg = ct == null ? void 0 : ct._img;
12527
- 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) || "";
12528
14336
  const isPlaceholder = !innerSrc || innerSrc === EMPTY_IMAGE_PLACEHOLDER_DATA_URL;
12529
14337
  if (innerImg && !isPlaceholder && !isCropGroupInCropMode(target)) {
12530
14338
  enterCropMode(target);
@@ -12703,13 +14511,13 @@ const PageCanvas = forwardRef(
12703
14511
  visibilityUpdateInProgressRef.current = false;
12704
14512
  }
12705
14513
  doSyncRef.current = () => {
12706
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
14514
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
12707
14515
  const shouldSkipUpdates2 = syncLockedRef.current || editLockRef.current;
12708
14516
  const state = useEditorStore.getState();
12709
14517
  const elementsToSync = elements;
12710
14518
  elementsRef.current = elementsToSync;
12711
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) ?? [];
12712
- 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) ?? []);
12713
14521
  isRebuildingRef.current = true;
12714
14522
  const allElementIds = new Set(elementsToSync.map((el) => el.id));
12715
14523
  const sectionGroups = pageTree.filter(
@@ -12736,7 +14544,8 @@ const PageCanvas = forwardRef(
12736
14544
  const activeObj = fc.getActiveObject();
12737
14545
  const activeObjId = activeObj ? getObjectId(activeObj) : null;
12738
14546
  const isTextBeingEdited = activeObjId && editingTextIdRef.current === activeObjId;
12739
- 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) {
12740
14549
  fc.discardActiveObject();
12741
14550
  }
12742
14551
  }
@@ -13056,7 +14865,7 @@ const PageCanvas = forwardRef(
13056
14865
  updateCoverLayout(existingObj);
13057
14866
  applyEdgeFadeFrameClipPath(existingObj, element, ct.frameW, ct.frameH, ct.shape || "rect", ct.rx || 0);
13058
14867
  if (allowEditing) {
13059
- installCanvaMaskControls(existingObj);
14868
+ installImageResizeControlsWithSnap(existingObj);
13060
14869
  } else {
13061
14870
  existingObj.set({
13062
14871
  hasControls: false,
@@ -13138,7 +14947,7 @@ const PageCanvas = forwardRef(
13138
14947
  hoverCursor: isDynamicField && isPreviewMode ? "pointer" : void 0
13139
14948
  });
13140
14949
  if (allowEditing) {
13141
- installCanvaMaskControls(existingObj);
14950
+ installImageResizeControlsWithSnap(existingObj);
13142
14951
  }
13143
14952
  existingObj.setCoords();
13144
14953
  fc.requestRenderAll();
@@ -13864,7 +15673,7 @@ const PageCanvas = forwardRef(
13864
15673
  return unsub;
13865
15674
  }, []);
13866
15675
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
13867
- var _a2, _b, _c;
15676
+ var _a2, _b2, _c;
13868
15677
  const fc = fabricRef.current;
13869
15678
  if (fc && isTransforming(fc)) {
13870
15679
  return;
@@ -13932,11 +15741,12 @@ const PageCanvas = forwardRef(
13932
15741
  // Disable rotation for crop groups (simplifies resize math)
13933
15742
  hasRotatingPoint: false,
13934
15743
  // Hide rotation handle
13935
- // Scale with zoom so handles stay same visual size (controls drawn in canvas pixel space)
13936
- cornerSize: Math.max(6, Math.round(8 * (fc.getZoom() || 1))),
13937
- 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,
13938
15748
  transparentCorners: false,
13939
- cornerStyle: "rect",
15749
+ cornerStyle: "circle",
13940
15750
  cornerColor: SELECTION_PRIMARY,
13941
15751
  cornerStrokeColor: "#ffffff",
13942
15752
  borderColor: SELECTION_PRIMARY,
@@ -14046,7 +15856,8 @@ const PageCanvas = forwardRef(
14046
15856
  obj.clipPath.dirty = true;
14047
15857
  obj.clipPath.setCoords();
14048
15858
  }
14049
- installCanvaMaskControls(obj);
15859
+ installImageResizeControlsWithSnap(obj);
15860
+ ensureCanvaControlRenders(obj);
14050
15861
  obj.set({
14051
15862
  selectable: true,
14052
15863
  evented: true,
@@ -14141,7 +15952,7 @@ const PageCanvas = forwardRef(
14141
15952
  obj.setCoords();
14142
15953
  }
14143
15954
  if (!isLine) {
14144
- 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";
14145
15956
  const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
14146
15957
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
14147
15958
  if (!skipPositionUpdate && (obj instanceof fabric.FabricImage && obj.originX === "center" || obj instanceof fabric.Group && obj.__cropGroup)) {
@@ -14218,7 +16029,17 @@ const PageCanvas = forwardRef(
14218
16029
  objectCaching: true
14219
16030
  });
14220
16031
  } else if (obj instanceof fabric.Ellipse) {
14221
- 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
+ });
14222
16043
  } else if (obj instanceof fabric.Textbox) {
14223
16044
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
14224
16045
  let text = element.text || "Text";
@@ -14412,6 +16233,16 @@ const PageCanvas = forwardRef(
14412
16233
  strokeUniform: true,
14413
16234
  objectCaching: true
14414
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
+ });
14415
16246
  } else if (obj instanceof fabric.Rect && element.shapeType === "rounded-rect") {
14416
16247
  const toRadius = (value, fallback) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
14417
16248
  const baseRx = Math.max(0, Number(element.rx ?? 0));
@@ -14671,7 +16502,7 @@ const PageCanvas = forwardRef(
14671
16502
  return Math.max(min, Math.min(max, v));
14672
16503
  };
14673
16504
  const applyEdgeFadeFrameClipPath = (group, element, frameW, frameH, shape, rxRatio) => {
14674
- var _a2, _b, _c;
16505
+ var _a2, _b2, _c;
14675
16506
  const fadeElement = element;
14676
16507
  const fadeGroup = group;
14677
16508
  const inputKey = edgeFadeKey(fadeElement);
@@ -14792,7 +16623,7 @@ const PageCanvas = forwardRef(
14792
16623
  delete fadeGroup.__edgeFadeRenderConfig;
14793
16624
  delete fadeGroup.__edgeFadeKey;
14794
16625
  delete fadeGroup.__edgeFadeInputKey;
14795
- if ((_b = group.clipPath) == null ? void 0 : _b.__edgeFadeMask) {
16626
+ if ((_b2 = group.clipPath) == null ? void 0 : _b2.__edgeFadeMask) {
14796
16627
  group.clipPath = void 0;
14797
16628
  updateCoverLayout(group);
14798
16629
  }
@@ -14809,7 +16640,7 @@ const PageCanvas = forwardRef(
14809
16640
  (_c = group.canvas) == null ? void 0 : _c.requestRenderAll();
14810
16641
  };
14811
16642
  const loadImageAsync2 = async (element, placeholder, fc) => {
14812
- 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;
14813
16644
  const imageUrl = element.src || element.imageUrl;
14814
16645
  if (!imageUrl) return;
14815
16646
  const elementId = element.id;
@@ -14840,7 +16671,7 @@ const PageCanvas = forwardRef(
14840
16671
  await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);
14841
16672
  if (!isLatestRequest()) return;
14842
16673
  const imageFitForFade = element.imageFit || ((_a2 = element.style) == null ? void 0 : _a2.imageFit) || "cover";
14843
- 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");
14844
16675
  const willUseCropGroupForFade = imageFitForFade !== "fill" || clipShapeForFade && clipShapeForFade !== "none";
14845
16676
  try {
14846
16677
  if (hasEdgeFade(element) && !willUseCropGroupForFade) {
@@ -15058,7 +16889,7 @@ const PageCanvas = forwardRef(
15058
16889
  evented: canBeEvented && !isHidden
15059
16890
  });
15060
16891
  } else {
15061
- installCanvaMaskControls(cropGroup);
16892
+ installImageResizeControlsWithSnap(cropGroup);
15062
16893
  }
15063
16894
  const cropImg = (_o = cropGroup.__cropData) == null ? void 0 : _o._img;
15064
16895
  if (cropImg) {
@@ -15269,38 +17100,7 @@ const PageCanvas = forwardRef(
15269
17100
  ),
15270
17101
  sectionsOverlay,
15271
17102
  groupBoundsOverlay,
15272
- canvas.projectSettings.showGrid && (canvas.projectSettings.gridSize ?? 0) > 0 && /* @__PURE__ */ jsxs(
15273
- "svg",
15274
- {
15275
- className: "absolute inset-0 pointer-events-none",
15276
- style: { width: scaledWidth, height: scaledHeight },
15277
- viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15278
- children: [
15279
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
15280
- "pattern",
15281
- {
15282
- id: `pixldocs-grid-${pageId}`,
15283
- width: canvas.projectSettings.gridSize,
15284
- height: canvas.projectSettings.gridSize,
15285
- patternUnits: "userSpaceOnUse",
15286
- children: /* @__PURE__ */ jsx(
15287
- "path",
15288
- {
15289
- d: `M ${canvas.projectSettings.gridSize} 0 L 0 0 0 ${canvas.projectSettings.gridSize}`,
15290
- fill: "none",
15291
- stroke: "currentColor",
15292
- strokeWidth: 0.5,
15293
- opacity: 0.18,
15294
- className: "text-foreground"
15295
- }
15296
- )
15297
- }
15298
- ) }),
15299
- /* @__PURE__ */ jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
15300
- ]
15301
- }
15302
- ),
15303
- hoverBounds && !guides.length && /* @__PURE__ */ jsx(
17103
+ hoverBounds && !guides.length && !(selectedIdsRef.current && selectedIdsRef.current.length) && /* @__PURE__ */ jsx(
15304
17104
  "svg",
15305
17105
  {
15306
17106
  className: "absolute inset-0 pointer-events-none",
@@ -15314,108 +17114,177 @@ const PageCanvas = forwardRef(
15314
17114
  width: hoverBounds.width,
15315
17115
  height: hoverBounds.height,
15316
17116
  fill: "none",
15317
- stroke: "hsl(256 80% 58%)",
15318
- strokeWidth: 1,
17117
+ stroke: SELECTION_PRIMARY,
17118
+ strokeWidth: 2,
17119
+ vectorEffect: "non-scaling-stroke",
15319
17120
  strokeDasharray: "0",
15320
- opacity: 0.65
17121
+ opacity: 1
15321
17122
  }
15322
17123
  )
15323
17124
  }
15324
17125
  ),
15325
- guides.length > 0 && /* @__PURE__ */ jsx(
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(
15326
17163
  "svg",
15327
17164
  {
15328
17165
  className: "absolute inset-0 pointer-events-none",
15329
17166
  style: { width: scaledWidth, height: scaledHeight },
15330
17167
  viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15331
- children: (() => {
15332
- const seen = /* @__PURE__ */ new Set();
15333
- return guides.filter((guide) => {
15334
- if (guide.kind === "gap") return true;
15335
- const key = `${guide.type}-${guide.position.toFixed(1)}`;
15336
- if (seen.has(key)) return false;
15337
- seen.add(key);
15338
- return true;
15339
- }).map((guide, i) => {
15340
- if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
15341
- const gapColor = "#ec4899";
15342
- const label = `${guide.gap}`;
15343
- const labelW2 = Math.max(22, label.length * 7 + 8);
15344
- if (guide.type === "horizontal") {
15345
- const y = guide.bracketAt ?? guide.position;
15346
- const x1 = guide.start;
15347
- const x2 = guide.end;
15348
- const mid = (x1 + x2) / 2;
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
+ }
17180
+ }
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;
15349
17255
  return /* @__PURE__ */ jsxs("g", { children: [
15350
- /* @__PURE__ */ jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1 }),
15351
- /* @__PURE__ */ jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15352
- /* @__PURE__ */ jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15353
- /* @__PURE__ */ jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
15354
- /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15355
- /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
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 })
15356
17265
  ] })
15357
17266
  ] }, i);
15358
17267
  } else {
15359
- const x = guide.bracketAt ?? guide.position;
15360
- const y1 = guide.start;
15361
- const y2 = guide.end;
15362
- const mid = (y1 + y2) / 2;
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;
15363
17272
  return /* @__PURE__ */ jsxs("g", { children: [
15364
- /* @__PURE__ */ jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1 }),
15365
- /* @__PURE__ */ jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1 }),
15366
- /* @__PURE__ */ jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1 }),
15367
- /* @__PURE__ */ jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
15368
- /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15369
- /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
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 })
15370
17282
  ] })
15371
17283
  ] }, i);
15372
17284
  }
15373
- }
15374
- const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
15375
- const isActive2 = guide.active === true;
15376
- const strokeColor = isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#3b82f6";
15377
- const strokeWidth = isActive2 ? 2.5 : 1;
15378
- const strokeDasharray = isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
15379
- const showDistance = typeof guide.distance === "number";
15380
- const labelText = showDistance ? String(Math.round(guide.distance)) : "";
15381
- const labelW = Math.max(24, labelText.length * 7);
15382
- if (guide.type === "vertical") {
15383
- const padding = isElementRelative || isActive2 ? 20 : 0;
15384
- const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15385
- const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
15386
- const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
15387
- return /* @__PURE__ */ jsxs("g", { children: [
15388
- /* @__PURE__ */ jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray }),
15389
- isActive2 && /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
15390
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
15391
- /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
15392
- /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
15393
- ] }),
15394
- showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
15395
- /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15396
- /* @__PURE__ */ jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15397
- ] })
15398
- ] }, i);
15399
- } else {
15400
- const padding = isElementRelative || isActive2 ? 20 : 0;
15401
- const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
15402
- const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
15403
- const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
15404
- return /* @__PURE__ */ jsxs("g", { children: [
15405
- /* @__PURE__ */ jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray }),
15406
- isActive2 && /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
15407
- isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
15408
- /* @__PURE__ */ jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
15409
- /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
15410
- ] }),
15411
- showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
15412
- /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
15413
- /* @__PURE__ */ jsx("text", { x: labelW / 2 - 2, y: 4, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 500, children: labelText })
15414
- ] })
15415
- ] }, i);
15416
- }
15417
- });
15418
- })()
17285
+ });
17286
+ })()
17287
+ ]
15419
17288
  }
15420
17289
  ),
15421
17290
  gridResizeLabel && /* @__PURE__ */ jsx(
@@ -15551,13 +17420,13 @@ function PreviewCanvas({
15551
17420
  onDynamicFieldClick,
15552
17421
  onReady
15553
17422
  }) {
15554
- var _a2, _b, _c, _d, _e;
17423
+ var _a2, _b2, _c, _d, _e;
15555
17424
  const canvasRef = useRef(null);
15556
17425
  const containerRef = useRef(null);
15557
17426
  const [containerWidth, setContainerWidth] = useState(0);
15558
17427
  const [hoveredFieldId, setHoveredFieldId] = useState(null);
15559
17428
  const page = (_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2[pageIndex];
15560
- 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;
15561
17430
  const canvasHeight = ((_c = config == null ? void 0 : config.canvas) == null ? void 0 : _c.height) || 792;
15562
17431
  const elementToFieldMap = useMemo(
15563
17432
  () => buildElementToFieldMap(config == null ? void 0 : config.dynamicFields),
@@ -15607,14 +17476,14 @@ function PreviewCanvas({
15607
17476
  }
15608
17477
  }, [elements]);
15609
17478
  const pageSettings = useMemo(() => {
15610
- var _a3, _b2;
17479
+ var _a3, _b3;
15611
17480
  return {
15612
17481
  backgroundColor: ((_a3 = page == null ? void 0 : page.settings) == null ? void 0 : _a3.backgroundColor) || "#ffffff",
15613
- 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
15614
17483
  };
15615
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]);
15616
17485
  const projectSettings = useMemo(() => {
15617
- var _a3, _b2, _c2;
17486
+ var _a3, _b3, _c2;
15618
17487
  const vars = ((_a3 = config.themeConfig) == null ? void 0 : _a3.variables) || {};
15619
17488
  return {
15620
17489
  showGrid: false,
@@ -15622,7 +17491,7 @@ function PreviewCanvas({
15622
17491
  gridSize: 10,
15623
17492
  snapToGuides: false,
15624
17493
  snapThreshold: 5,
15625
- primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
17494
+ primaryColor: (_b3 = vars.primary) == null ? void 0 : _b3.value,
15626
17495
  secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
15627
17496
  };
15628
17497
  }, [config.themeConfig]);
@@ -15774,7 +17643,7 @@ const PreviewCanvas$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
15774
17643
  PreviewCanvas
15775
17644
  }, Symbol.toStringTag, { value: "Module" }));
15776
17645
  function applyThemeToConfig(config, themeOverrides) {
15777
- var _a2, _b, _c;
17646
+ var _a2, _b2, _c;
15778
17647
  if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;
15779
17648
  const cloned = JSON.parse(JSON.stringify(config));
15780
17649
  if ((_a2 = cloned.themeConfig) == null ? void 0 : _a2.variables) {
@@ -15785,7 +17654,7 @@ function applyThemeToConfig(config, themeOverrides) {
15785
17654
  }
15786
17655
  }
15787
17656
  const varMap = /* @__PURE__ */ new Map();
15788
- if ((_b = cloned.themeConfig) == null ? void 0 : _b.variables) {
17657
+ if ((_b2 = cloned.themeConfig) == null ? void 0 : _b2.variables) {
15789
17658
  for (const [key, def] of Object.entries(cloned.themeConfig.variables)) {
15790
17659
  varMap.set(key, themeOverrides[key] ?? def.value);
15791
17660
  }
@@ -15822,7 +17691,7 @@ function mapFormDefFieldType(t) {
15822
17691
  function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15823
17692
  const sections = [];
15824
17693
  function convert(defs, parentId) {
15825
- var _a2, _b;
17694
+ var _a2, _b2;
15826
17695
  for (const def of defs) {
15827
17696
  const isRepeatable = def.repeatable === true || def.type === "repeatable";
15828
17697
  const defFields = def.fields ?? def.entryFields ?? [];
@@ -15874,7 +17743,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15874
17743
  placeholder: f.placeholder
15875
17744
  }))
15876
17745
  });
15877
- if ((_b = def.children) == null ? void 0 : _b.length) {
17746
+ if ((_b2 = def.children) == null ? void 0 : _b2.length) {
15878
17747
  convert(def.children, def.id);
15879
17748
  }
15880
17749
  }
@@ -16503,7 +18372,7 @@ function findAllRepeatableElementsBySourceId(pages, baseNodeId, oneBasedIndex, s
16503
18372
  return out;
16504
18373
  }
16505
18374
  function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi, childBaseNodeId, childCi, sourceElementId) {
16506
- var _a2, _b;
18375
+ var _a2, _b2;
16507
18376
  const parentGroups = collectGroupsByBaseId(pages, parentBaseNodeId);
16508
18377
  const parentGroup = parentGroups[parentPi - 1];
16509
18378
  if (!parentGroup || !isGroup(parentGroup) || !((_a2 = parentGroup.children) == null ? void 0 : _a2.length)) return void 0;
@@ -16518,7 +18387,7 @@ function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi
16518
18387
  }
16519
18388
  collectChild(parentGroup.children);
16520
18389
  const childGroup = childGroups[childCi - 1];
16521
- 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;
16522
18391
  return findElementBySourceIdInSubtree(childGroup.children, sourceElementId) ?? (childGroup.__sourceId === sourceElementId ? childGroup.id : void 0);
16523
18392
  }
16524
18393
  function repeatableLabelKey(label) {
@@ -16568,7 +18437,7 @@ function getRepeatableFromConfig(pages) {
16568
18437
  var _a2;
16569
18438
  const result = [];
16570
18439
  function walk(children) {
16571
- var _a3, _b;
18440
+ var _a3, _b2;
16572
18441
  if (!children) return;
16573
18442
  for (const node of children) {
16574
18443
  if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && ((_a3 = node.children) == null ? void 0 : _a3.length)) {
@@ -16577,7 +18446,7 @@ function getRepeatableFromConfig(pages) {
16577
18446
  if (rep == null ? void 0 : rep.label) result.push({ nodeId: child.id, label: rep.label });
16578
18447
  }
16579
18448
  }
16580
- 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);
16581
18450
  }
16582
18451
  }
16583
18452
  for (const page of pages) if ((_a2 = page.children) == null ? void 0 : _a2.length) walk(page.children);
@@ -16593,7 +18462,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16593
18462
  return !!((_a3 = n.repeatableSection) == null ? void 0 : _a3.label);
16594
18463
  };
16595
18464
  function walk(children, parentRepeatableBaseId, parentInfo, parentRepeatableEntryIndex) {
16596
- var _a3, _b;
18465
+ var _a3, _b2;
16597
18466
  if (!Array.isArray(children)) return;
16598
18467
  for (let i = 0; i < children.length; i++) {
16599
18468
  const node = children[i];
@@ -16636,7 +18505,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16636
18505
  if (isGroup(child) && Array.isArray(child.children)) walk(child.children, effectiveChildBase, void 0, childEntryIndex);
16637
18506
  j += count;
16638
18507
  }
16639
- } 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)) {
16640
18509
  const nodeBase = baseId(node.id);
16641
18510
  const selfMatch = schemaBaseIds.has(node.id) || schemaBaseIds.has(nodeBase) || node.__baseNodeId != null && schemaBaseIds.has(node.__baseNodeId);
16642
18511
  const isSelfRepeatable = selfMatch && hasRepeatableSection(node);
@@ -16725,7 +18594,7 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
16725
18594
  return Math.max(1, maxIndex);
16726
18595
  }
16727
18596
  function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
16728
- var _a2, _b, _c;
18597
+ var _a2, _b2, _c;
16729
18598
  const cloned = JSON.parse(JSON.stringify(config));
16730
18599
  if (!cloned.pages) return cloned;
16731
18600
  const dynamicFields = cloned.dynamicFields;
@@ -16906,7 +18775,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
16906
18775
  const { node, baseNodeId, startIndex, count } = block;
16907
18776
  const occIdx = block.__occurrenceIndex ?? 1;
16908
18777
  const N = computeN(baseNodeId, node.id);
16909
- 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);
16910
18779
  let entryIndices;
16911
18780
  if (entryFilter && entryFilter.mode === "range" && entryFilter.range) {
16912
18781
  const parsed = parseEntryRange(entryFilter.range, N, entryMetaFromList(baseNodeId));
@@ -17363,7 +19232,7 @@ function findAllFlowStacks(pageChildren) {
17363
19232
  return result;
17364
19233
  }
17365
19234
  function findNestedFlowStack(entry) {
17366
- var _a2, _b;
19235
+ var _a2, _b2;
17367
19236
  const queue = [...entry.children ?? []];
17368
19237
  while (queue.length > 0) {
17369
19238
  const node = queue.shift();
@@ -17371,7 +19240,7 @@ function findNestedFlowStack(entry) {
17371
19240
  const g = node;
17372
19241
  const hasRepeatableChildren = ((_a2 = g.children) == null ? void 0 : _a2.length) && (g.children.some(hasBaseNodeId) || g.children.some((c) => isGroup(c)));
17373
19242
  if (isVerticalStackLayoutMode(g.layoutMode) && hasRepeatableChildren) return g;
17374
- 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);
17375
19244
  }
17376
19245
  return null;
17377
19246
  }
@@ -17502,9 +19371,9 @@ function splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overf
17502
19371
  return { stayChildren: newStay, overflowChildren: newOverflow };
17503
19372
  }
17504
19373
  function paginateSinglePage(sourcePage, pageOffsetIndex) {
17505
- var _a2, _b, _c, _d, _e, _f;
19374
+ var _a2, _b2, _c, _d, _e, _f;
17506
19375
  const contentTop = (_a2 = sourcePage.settings) == null ? void 0 : _a2.contentTop;
17507
- const contentBottom = (_b = sourcePage.settings) == null ? void 0 : _b.contentBottom;
19376
+ const contentBottom = (_b2 = sourcePage.settings) == null ? void 0 : _b2.contentBottom;
17508
19377
  if (contentTop == null || contentBottom == null || contentBottom <= contentTop) {
17509
19378
  return [sourcePage];
17510
19379
  }
@@ -18694,7 +20563,7 @@ function splitIntoRuns(text, mainSupportsChar) {
18694
20563
  return runs;
18695
20564
  }
18696
20565
  function rewriteSvgFontsForJsPDF(svgStr) {
18697
- var _a2, _b;
20566
+ var _a2, _b2;
18698
20567
  const parser = new DOMParser();
18699
20568
  const doc = parser.parseFromString(svgStr, "image/svg+xml");
18700
20569
  const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
@@ -18799,7 +20668,7 @@ function rewriteSvgFontsForJsPDF(svgStr) {
18799
20668
  for (const node of childNodes) {
18800
20669
  if (node.nodeType !== 3 || !node.textContent) continue;
18801
20670
  const runs = splitIntoRuns(node.textContent, mainSupportsChar);
18802
- 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;
18803
20672
  const fragment = doc.createDocumentFragment();
18804
20673
  for (const run of runs) {
18805
20674
  const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
@@ -19109,9 +20978,9 @@ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
19109
20978
  styleEl.appendChild(document.createTextNode(cssText));
19110
20979
  }
19111
20980
  function resolveHarnessFontProxyUrl() {
19112
- var _a2, _b;
20981
+ var _a2, _b2;
19113
20982
  try {
19114
- 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)) || "";
19115
20984
  const base = String(runtimeBase || "").replace(/\/$/, "");
19116
20985
  return base ? `${base}/functions/v1/font-proxy` : "";
19117
20986
  } catch {
@@ -19773,7 +21642,7 @@ async function resolveTemplateData(options) {
19773
21642
  };
19774
21643
  }
19775
21644
  async function resolveFromForm(options) {
19776
- var _a2, _b, _c;
21645
+ var _a2, _b2, _c;
19777
21646
  const { templateId, formSchemaId, sectionState, flatFormData: directFlatFormData, themeId, supabaseUrl, supabaseAnonKey, prefetched } = options;
19778
21647
  const hasSectionStateInput = !!sectionState && Object.keys(sectionState).length > 0;
19779
21648
  if (!formSchemaId && !hasSectionStateInput) {
@@ -19823,7 +21692,7 @@ async function resolveFromForm(options) {
19823
21692
  inferredSections = inferFormSchemaFromTemplate(
19824
21693
  templateConfig.dynamicFields,
19825
21694
  groups,
19826
- ((_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
19827
21696
  );
19828
21697
  } else {
19829
21698
  inferredSections = [];
@@ -20010,10 +21879,10 @@ function themeBaseId(id) {
20010
21879
  return out;
20011
21880
  }
20012
21881
  function applyThemeVariantToConfig(config, themeConfig, themeId) {
20013
- var _a2, _b, _c, _d;
21882
+ var _a2, _b2, _c, _d;
20014
21883
  if (!themeConfig) return config;
20015
21884
  const variant = themeId && themeId !== "default" ? (_a2 = themeConfig.variants) == null ? void 0 : _a2.find((v) => v.id === themeId) : null;
20016
- const shouldApplyDefaults = !variant && ((_b = themeConfig.properties) == null ? void 0 : _b.length);
21885
+ const shouldApplyDefaults = !variant && ((_b2 = themeConfig.properties) == null ? void 0 : _b2.length);
20017
21886
  if (!variant && !shouldApplyDefaults) return config;
20018
21887
  if (!((_c = themeConfig.properties) == null ? void 0 : _c.length)) return config;
20019
21888
  const result = JSON.parse(JSON.stringify(config));
@@ -20034,8 +21903,8 @@ function applyThemeVariantToConfig(config, themeConfig, themeId) {
20034
21903
  if (stopMatch) {
20035
21904
  const stopIndex = parseInt(stopMatch[1], 10);
20036
21905
  result.pages.forEach((p) => {
20037
- var _a3, _b2;
20038
- 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]) {
20039
21908
  p.settings.backgroundGradient = {
20040
21909
  ...p.settings.backgroundGradient,
20041
21910
  stops: p.settings.backgroundGradient.stops.map(
@@ -20112,7 +21981,7 @@ function normalizeLayoutModes(config) {
20112
21981
  }
20113
21982
  }
20114
21983
  function paintRepeatableSections(config, repeatableSections, formSchema) {
20115
- var _a2, _b;
21984
+ var _a2, _b2;
20116
21985
  const pages = config.pages ?? [];
20117
21986
  const entryFilters = (formSchema == null ? void 0 : formSchema.entryFilters) ?? [];
20118
21987
  const entryFilterBases = new Set(entryFilters.map((f) => baseId(f.nodeId)));
@@ -20140,7 +22009,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
20140
22009
  return painted;
20141
22010
  }
20142
22011
  for (const section of repeatableSections) {
20143
- 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;
20144
22013
  const shouldUseInlineFilter = !!inlineRange && !entryFilterBases.has(baseId(section.nodeId));
20145
22014
  const payload = { label: section.label };
20146
22015
  if (section.minEntries !== void 0) payload.minEntries = section.minEntries;
@@ -20176,7 +22045,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
20176
22045
  }
20177
22046
  }
20178
22047
  async function getTemplateForm(options) {
20179
- var _a2, _b;
22048
+ var _a2, _b2;
20180
22049
  const { templateId, supabaseUrl, supabaseAnonKey } = options;
20181
22050
  if (!supabaseUrl || !supabaseAnonKey) {
20182
22051
  throw new Error("[getTemplateForm] supabaseUrl and supabaseAnonKey are required");
@@ -20220,7 +22089,7 @@ async function getTemplateForm(options) {
20220
22089
  sections = inferFormSchemaFromTemplate(
20221
22090
  templateConfig.dynamicFields,
20222
22091
  templateConfig.fieldGroups || [],
20223
- ((_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
20224
22093
  );
20225
22094
  } else {
20226
22095
  sections = [];
@@ -20604,7 +22473,7 @@ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExact
20604
22473
  return out;
20605
22474
  }
20606
22475
  function PixldocsPreview(props) {
20607
- var _a2, _b;
22476
+ var _a2, _b2;
20608
22477
  const {
20609
22478
  pageIndex = 0,
20610
22479
  zoom = 1,
@@ -20672,10 +22541,10 @@ function PixldocsPreview(props) {
20672
22541
  supabaseUrl: p.supabaseUrl,
20673
22542
  supabaseAnonKey: p.supabaseAnonKey
20674
22543
  }).then((resolved) => {
20675
- var _a3, _b2;
22544
+ var _a3, _b3;
20676
22545
  if (!cancelled) {
20677
22546
  console.log(PREVIEW_DEBUG_PREFIX, "resolve-done", {
20678
- 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,
20679
22548
  underlinedNodes: countUnderlinedNodes(resolved.config)
20680
22549
  });
20681
22550
  setResolvedConfig(resolved.config);
@@ -20785,7 +22654,7 @@ function PixldocsPreview(props) {
20785
22654
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
20786
22655
  const bgTint = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.background) ?? "rgba(255,255,255,0.12)";
20787
22656
  const canvasW = getNum((_a2 = config == null ? void 0 : config.canvas) == null ? void 0 : _a2.width, 0);
20788
- 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);
20789
22658
  const hasOverlays = frostedBounds.length > 0 && canvasW > 0 && canvasH > 0;
20790
22659
  if (isLoading) {
20791
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..." }) });
@@ -20926,7 +22795,7 @@ function ensureFabricGradientDef(doc, obj, width, height) {
20926
22795
  return `url(#${id})`;
20927
22796
  }
20928
22797
  function warpTextboxSvgAlongPath(svg, obj) {
20929
- var _a2, _b, _c, _d, _e;
22798
+ var _a2, _b2, _c, _d, _e;
20930
22799
  const tp = obj == null ? void 0 : obj.textPath;
20931
22800
  if (!tp || !tp.preset || tp.preset === "none") return svg;
20932
22801
  if (tp.preset === "rise" || tp.preset === "angle") {
@@ -20990,7 +22859,7 @@ function warpTextboxSvgAlongPath(svg, obj) {
20990
22859
  }
20991
22860
  for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
20992
22861
  try {
20993
- (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
22862
+ (_b2 = clone.parentNode) == null ? void 0 : _b2.removeChild(clone);
20994
22863
  } catch {
20995
22864
  }
20996
22865
  }
@@ -21010,9 +22879,9 @@ function warpTextboxSvgAlongPath(svg, obj) {
21010
22879
  const fw = obj.fontWeight ?? 400;
21011
22880
  const fst = obj.fontStyle || "normal";
21012
22881
  const readFill = (el) => {
21013
- var _a3, _b2;
22882
+ var _a3, _b3;
21014
22883
  if (!el) return "";
21015
- 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()) || "";
21016
22885
  };
21017
22886
  const w = Number(obj.width) || 0;
21018
22887
  const h = Number(obj.height) || 0;
@@ -21365,9 +23234,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21365
23234
  }
21366
23235
  return svgString;
21367
23236
  }
21368
- const resolvedPackageVersion = "0.5.241";
23237
+ const resolvedPackageVersion = "0.5.242";
21369
23238
  const PACKAGE_VERSION = resolvedPackageVersion;
21370
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.241";
23239
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.242";
21371
23240
  const roundParityValue = (value) => {
21372
23241
  if (typeof value !== "number") return value;
21373
23242
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21544,11 +23413,11 @@ function installUnderlineFix(fab) {
21544
23413
  if (!TextProto || typeof TextProto._renderTextDecoration !== "function") return;
21545
23414
  const original = TextProto._renderTextDecoration;
21546
23415
  const measureLineTextWidth = (obj, ctx, lineIndex) => {
21547
- var _a3, _b, _c, _d, _e, _f;
23416
+ var _a3, _b2, _c, _d, _e, _f;
21548
23417
  const rawLine = (_a3 = obj._textLines) == null ? void 0 : _a3[lineIndex];
21549
23418
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
21550
23419
  if (!lineText) return 0;
21551
- 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);
21552
23421
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
21553
23422
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
21554
23423
  const fontFamily = String(((_e = obj.getValueOfPropertyAt) == null ? void 0 : _e.call(obj, lineIndex, 0, "fontFamily")) ?? obj.fontFamily ?? "sans-serif");
@@ -21688,11 +23557,78 @@ function installTextboxBoxExtensions(fab) {
21688
23557
  if (Array.isArray(stateProps2)) {
21689
23558
  if (!stateProps2.includes("minBoxHeight")) stateProps2.push("minBoxHeight");
21690
23559
  if (!stateProps2.includes("verticalAlign")) stateProps2.push("verticalAlign");
23560
+ if (!stateProps2.includes("smartWrap")) stateProps2.push("smartWrap");
21691
23561
  }
21692
23562
  const cacheProps2 = TextboxProto2.cacheProperties;
21693
23563
  if (Array.isArray(cacheProps2)) {
21694
23564
  if (!cacheProps2.includes("minBoxHeight")) cacheProps2.push("minBoxHeight");
21695
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
+ };
21696
23632
  }
21697
23633
  TextboxProto2.__pixldocsTextboxExtended = true;
21698
23634
  __textboxBoxExtensionsInstalled = true;
@@ -22114,7 +24050,7 @@ class PixldocsRenderer {
22114
24050
  await this.waitForCanvasScene(container, cloned, i);
22115
24051
  }
22116
24052
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
22117
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-BBY4avH9.js");
24053
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CsrQStE8.js");
22118
24054
  const prepared = preparePagesForExport(
22119
24055
  cloned.pages,
22120
24056
  canvasWidth,
@@ -22331,9 +24267,9 @@ class PixldocsRenderer {
22331
24267
  }
22332
24268
  waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
22333
24269
  return new Promise((resolve) => {
22334
- var _a2, _b;
24270
+ var _a2, _b2;
22335
24271
  const start = Date.now();
22336
- 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;
22337
24273
  const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
22338
24274
  const check = () => {
22339
24275
  const fabricCanvas = this.getFabricCanvasFromContainer(container);
@@ -22408,9 +24344,9 @@ class PixldocsRenderer {
22408
24344
  return normalized;
22409
24345
  }
22410
24346
  paintPageBackground(ctx, page, width, height) {
22411
- var _a2, _b;
24347
+ var _a2, _b2;
22412
24348
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22413
- 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;
22414
24350
  ctx.clearRect(0, 0, width, height);
22415
24351
  ctx.fillStyle = backgroundColor;
22416
24352
  ctx.fillRect(0, 0, width, height);
@@ -22658,7 +24594,7 @@ class PixldocsRenderer {
22658
24594
  };
22659
24595
  const onReady = () => {
22660
24596
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
22661
- var _a2, _b;
24597
+ var _a2, _b2;
22662
24598
  try {
22663
24599
  const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
22664
24600
  await this.waitForCanvasImages(container, expectedImageIds);
@@ -22677,7 +24613,7 @@ class PixldocsRenderer {
22677
24613
  );
22678
24614
  const page = renderConfig.pages[pageIndex];
22679
24615
  const backgroundColor = ((_a2 = page == null ? void 0 : page.settings) == null ? void 0 : _a2.backgroundColor) || "#ffffff";
22680
- 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;
22681
24617
  cleanup();
22682
24618
  resolve({
22683
24619
  svg: svgString,
@@ -22798,7 +24734,7 @@ class PixldocsRenderer {
22798
24734
  logJsonLine("[canvas-renderer][fabric-text-parity]", { stage, textboxes: sample.length, sample });
22799
24735
  }
22800
24736
  async waitForStableTextMetrics(container, config, options = {}) {
22801
- var _a2, _b, _c;
24737
+ var _a2, _b2, _c;
22802
24738
  if (typeof document !== "undefined") {
22803
24739
  void ensureFontsForResolvedConfig(config);
22804
24740
  await this.waitForRelevantFonts(config);
@@ -22840,7 +24776,7 @@ class PixldocsRenderer {
22840
24776
  }
22841
24777
  fabricInstance.getObjects().forEach(primeCharBounds);
22842
24778
  (_a2 = fabricInstance.calcOffset) == null ? void 0 : _a2.call(fabricInstance);
22843
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
24779
+ (_b2 = fabricInstance.renderAll) == null ? void 0 : _b2.call(fabricInstance);
22844
24780
  await waitForPaint();
22845
24781
  (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
22846
24782
  await waitForPaint();
@@ -22881,7 +24817,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22881
24817
  };
22882
24818
  logParityJson(tag, stage, { kind: "summary", ...summary });
22883
24819
  const sample = texts.slice(0, maxItems).map((t, idx) => {
22884
- var _a2, _b;
24820
+ var _a2, _b2;
22885
24821
  const tspans = Array.from(t.querySelectorAll("tspan"));
22886
24822
  const tspanInfo = tspans.slice(0, 8).map((s) => ({
22887
24823
  x: s.getAttribute("x"),
@@ -22898,7 +24834,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22898
24834
  while (cursor && !containerWidth) {
22899
24835
  containerWidth = (_a2 = cursor.getAttribute) == null ? void 0 : _a2.call(cursor, "width");
22900
24836
  cursor = cursor.parentElement;
22901
- 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;
22902
24838
  }
22903
24839
  return {
22904
24840
  idx,
@@ -23286,10 +25222,10 @@ function normalizeSvgExplicitColors(svg) {
23286
25222
  return normalizeGradientPaintRef(v);
23287
25223
  };
23288
25224
  const hasExplicitNonePaint = (el, attr) => {
23289
- var _a2, _b;
25225
+ var _a2, _b2;
23290
25226
  const raw = (el.getAttribute(attr) ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue(attr)) ?? "").trim().toLowerCase();
23291
25227
  if (raw === "none" || raw === "transparent") return true;
23292
- 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();
23293
25229
  return rawOpacity === "0" || rawOpacity === "0%" || rawOpacity === "0.0";
23294
25230
  };
23295
25231
  function walk(el, parentFill, parentStroke, parentColor) {
@@ -23356,14 +25292,14 @@ function normalizeSvgExplicitColors(svg) {
23356
25292
  function bakeGroupOpacityIntoChildren(svg) {
23357
25293
  const DRAWABLE = /* @__PURE__ */ new Set(["path", "rect", "circle", "ellipse", "polygon", "polyline", "line", "text", "tspan"]);
23358
25294
  function walkAndBake(el, inheritedOpacity) {
23359
- var _a2, _b, _c, _d;
25295
+ var _a2, _b2, _c, _d;
23360
25296
  if (isInSvgDefinitionSubtree(el)) {
23361
25297
  for (let i = 0; i < el.children.length; i++) walkAndBake(el.children[i], 1);
23362
25298
  return;
23363
25299
  }
23364
25300
  const tag = ((_a2 = el.tagName) == null ? void 0 : _a2.toLowerCase()) ?? "";
23365
25301
  const opacityAttr = parseSvgOpacity(el.getAttribute("opacity"));
23366
- 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);
23367
25303
  const ownOpacity = opacityAttr ?? styleOpacity ?? 1;
23368
25304
  const combinedOpacity = inheritedOpacity * ownOpacity;
23369
25305
  if (ownOpacity < 0.999 && tag !== "image") {
@@ -23886,10 +25822,10 @@ function getFirstExplicitColorFromSvg(svg) {
23886
25822
  return getGradientStopColorAsHex(svg, gradientId);
23887
25823
  };
23888
25824
  function walk(el) {
23889
- var _a2, _b;
25825
+ var _a2, _b2;
23890
25826
  if (fill && stroke) return;
23891
25827
  const f = el.getAttribute("fill") ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue("fill"));
23892
- 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"));
23893
25829
  if (!fill) {
23894
25830
  if (isRealColor(f)) fill = f;
23895
25831
  else if (f) {
@@ -23917,8 +25853,8 @@ function isNearWhite(hex) {
23917
25853
  return r >= 250 && g >= 250 && b >= 250;
23918
25854
  }
23919
25855
  function getStopColorRaw(stop) {
23920
- var _a2, _b;
23921
- 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");
23922
25858
  }
23923
25859
  function getGradientStopColorAsHex(svgRoot, gradientId, visited = /* @__PURE__ */ new Set()) {
23924
25860
  var _a2;
@@ -23982,12 +25918,12 @@ async function convertTextDecorationsToLines(svg) {
23982
25918
  else el.removeAttribute("style");
23983
25919
  };
23984
25920
  const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
23985
- var _a2, _b;
25921
+ var _a2, _b2;
23986
25922
  let current = el;
23987
25923
  while (current) {
23988
25924
  const attrValue = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
23989
25925
  if (attrValue) return attrValue;
23990
- const styleValue = (_b = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b.trim();
25926
+ const styleValue = (_b2 = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b2.trim();
23991
25927
  if (styleValue) return styleValue;
23992
25928
  current = current.parentElement;
23993
25929
  }
@@ -24165,7 +26101,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
24165
26101
  }
24166
26102
  }
24167
26103
  async function rasterizeShadowMarkers(svg) {
24168
- var _a2, _b, _c, _d, _e, _f;
26104
+ var _a2, _b2, _c, _d, _e, _f;
24169
26105
  if (typeof window === "undefined" || typeof document === "undefined") return;
24170
26106
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
24171
26107
  if (markers.length === 0) return;
@@ -24189,7 +26125,7 @@ async function rasterizeShadowMarkers(svg) {
24189
26125
  const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
24190
26126
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
24191
26127
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
24192
- (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
26128
+ (_b2 = marker.parentNode) == null ? void 0 : _b2.removeChild(marker);
24193
26129
  continue;
24194
26130
  }
24195
26131
  const innerXml = restoreSourceFontsForShadowRaster(
@@ -24434,7 +26370,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24434
26370
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24435
26371
  sanitizeSvgTreeForPdf(svgToDraw);
24436
26372
  try {
24437
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-BBY4avH9.js");
26373
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CsrQStE8.js");
24438
26374
  try {
24439
26375
  await logTextMeasurementDiagnostic(svgToDraw);
24440
26376
  } catch {
@@ -24450,7 +26386,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24450
26386
  }
24451
26387
  }
24452
26388
  function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundColor, backgroundGradient) {
24453
- var _a2, _b;
26389
+ var _a2, _b2;
24454
26390
  if (backgroundGradient && ((_a2 = backgroundGradient.stops) == null ? void 0 : _a2.length) >= 2) {
24455
26391
  const grad = backgroundGradient;
24456
26392
  const colorStops = grad.stops.map((s) => {
@@ -24505,7 +26441,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24505
26441
  doc.fill({ key: patternKey, matrix: doc.Matrix(1, 0, 0, 1, 0, 0) });
24506
26442
  });
24507
26443
  } catch {
24508
- 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];
24509
26445
  pdf.setFillColor(fallback[0], fallback[1], fallback[2]);
24510
26446
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
24511
26447
  }
@@ -24518,7 +26454,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24518
26454
  }
24519
26455
  }
24520
26456
  async function assemblePdfFromSvgs(svgResults, options = {}) {
24521
- var _a2, _b;
26457
+ var _a2, _b2;
24522
26458
  if (svgResults.length === 0) throw new Error("No pages to export");
24523
26459
  const { title, stripPageBackground } = options;
24524
26460
  const firstPage = svgResults[0];
@@ -24551,7 +26487,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
24551
26487
  const pageOrientation = page.width > page.height ? "landscape" : "portrait";
24552
26488
  pdf.addPage([page.width, page.height], pageOrientation);
24553
26489
  }
24554
- 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);
24555
26491
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
24556
26492
  const shouldStripBg = stripPageBackground ?? hasGradient;
24557
26493
  const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
@@ -24834,4 +26770,4 @@ export {
24834
26770
  buildTeaserBlurFlatKeys as y,
24835
26771
  collectFontDescriptorsFromConfig as z
24836
26772
  };
24837
- //# sourceMappingURL=index-DtyVze4W.js.map
26773
+ //# sourceMappingURL=index-BQ5V2uT8.js.map