@pixldocs/canvas-renderer 0.5.241 → 0.5.243

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
11421
  fabricCanvas,
10272
11422
  canvasWidth,
10273
11423
  canvasHeight,
10274
- projectSettings.snapToGuides,
10275
- projectSettings.snapThreshold
11424
+ ps.snapToGuides,
11425
+ ps.snapThreshold || 4,
11426
+ getLogicalGroupSnapBoundsCallback(excludeIds)
10276
11427
  );
10277
11428
  },
10278
- [canvasWidth, canvasHeight, projectSettings.snapToGuides, projectSettings.snapThreshold]
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,
11452
+ canvasWidth,
11453
+ canvasHeight,
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
+ }
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();
11514
+ },
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
  }
@@ -11250,11 +12734,34 @@ const PageCanvas = forwardRef(
11250
12734
  syncLockedRef.current = true;
11251
12735
  lockEdits();
11252
12736
  }
11253
- const target = opt.target;
11254
- const targetId = target ? getObjectId(target) : null;
12737
+ let target = opt.target;
12738
+ let targetId = target ? getObjectId(target) : null;
12739
+ if (target instanceof fabric.ActiveSelection && target.__pixldocsGroupSelection && target === fabricCanvas.getActiveObject()) {
12740
+ try {
12741
+ const pointer = fabricCanvas.getViewportPoint(opt.e);
12742
+ const members = target.getObjects();
12743
+ for (let i = members.length - 1; i >= 0; i--) {
12744
+ const m = members[i];
12745
+ if (m.visible === false) continue;
12746
+ if (typeof m.containsPoint === "function" && m.containsPoint(pointer)) {
12747
+ const mid = getObjectId(m);
12748
+ if (mid) {
12749
+ target = m;
12750
+ targetId = mid;
12751
+ }
12752
+ break;
12753
+ }
12754
+ }
12755
+ } catch {
12756
+ }
12757
+ }
11255
12758
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11256
12759
  const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11257
12760
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12761
+ if (target instanceof fabric.Textbox && target.__corner && (target.__corner === "ml" || target.__corner === "mr")) {
12762
+ const sourceEl = targetId ? elementsRef.current.find((el) => el.id === targetId) : void 0;
12763
+ bakeTextboxScaleIntoTypography(target, sourceEl);
12764
+ }
11258
12765
  if (isMultiSelectModifier(opt.e)) {
11259
12766
  const manualTarget = target && !(target instanceof fabric.ActiveSelection) && targetId && targetId !== "__background__" ? target : pickSelectableObjectAtPointer(opt.e);
11260
12767
  if (manualTarget) {
@@ -11281,11 +12788,39 @@ const PageCanvas = forwardRef(
11281
12788
  return topmost;
11282
12789
  };
11283
12790
  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));
12791
+ let effectiveTarget = target;
12792
+ let effectiveTargetId = targetId;
12793
+ const activeNowEarly = fabricCanvas.getActiveObject();
12794
+ const asGroupId = target instanceof fabric.ActiveSelection ? target.__pixldocsGroupSelection : void 0;
12795
+ if (target instanceof fabric.ActiveSelection && asGroupId && target === activeNowEarly) {
12796
+ try {
12797
+ const pointer = fabricCanvas.getViewportPoint(opt.e);
12798
+ const members = target.getObjects();
12799
+ for (let i = members.length - 1; i >= 0; i--) {
12800
+ const m = members[i];
12801
+ if (m.visible === false) continue;
12802
+ if (typeof m.containsPoint === "function" && m.containsPoint(pointer)) {
12803
+ const mid = getObjectId(m);
12804
+ if (mid) {
12805
+ effectiveTarget = m;
12806
+ effectiveTargetId = mid;
12807
+ }
12808
+ break;
12809
+ }
12810
+ }
12811
+ } catch {
12812
+ }
12813
+ }
12814
+ const parent = findTopmostPromotableGroup(effectiveTargetId);
12815
+ const targetIsInCrop = !!(effectiveTarget instanceof fabric.Group && isCropGroupInCropMode(effectiveTarget) || (effectiveTarget == null ? void 0 : effectiveTarget.group) instanceof fabric.Group && isCropGroupInCropMode(effectiveTarget.group));
11286
12816
  const activeNow = fabricCanvas.getActiveObject();
11287
12817
  const alreadyThisGroup = activeNow instanceof fabric.ActiveSelection && activeNow.__pixldocsGroupSelection === (parent == null ? void 0 : parent.id);
11288
12818
  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));
12819
+ const isDeepSelectKey = !!((_i = opt.e) == null ? void 0 : _i.altKey);
12820
+ if (isDeepSelectKey) {
12821
+ pendingGroupPromotionRef.current = null;
12822
+ return;
12823
+ }
11289
12824
  if (parent && !parent.backgroundColor && !targetIsInCrop && activeEditingGroupId !== parent.id && !alreadyThisGroup && !isMultiSelectKey) {
11290
12825
  const memberIds = new Set(getAllElementIds(parent.children ?? []));
11291
12826
  const memberObjs = fabricCanvas.getObjects().filter((o) => {
@@ -11309,10 +12844,33 @@ const PageCanvas = forwardRef(
11309
12844
  opt.target = only;
11310
12845
  pendingGroupPromotionRef.current = { groupId: parent.id, selection: only };
11311
12846
  }
12847
+ } else if (parent && !parent.backgroundColor && !targetIsInCrop && alreadyThisGroup && !isMultiSelectKey && effectiveTarget !== activeNow) {
12848
+ try {
12849
+ skipSelectionClearOnDiscardRef.current = true;
12850
+ preserveEditingScopeOnSelectionClearRef.current = true;
12851
+ restoreSuppressedGroupBorders();
12852
+ fabricCanvas.discardActiveObject();
12853
+ } finally {
12854
+ skipSelectionClearOnDiscardRef.current = false;
12855
+ preserveEditingScopeOnSelectionClearRef.current = false;
12856
+ }
12857
+ fabricCanvas.__activeEditingGroupId = parent.id;
12858
+ delete effectiveTarget.__pixldocsGroupSelection;
12859
+ delete effectiveTarget.__pixldocsLogicalGroupIds;
12860
+ try {
12861
+ (_j = effectiveTarget.set) == null ? void 0 : _j.call(effectiveTarget, { selectable: true, evented: true, hasBorders: true, hasControls: true });
12862
+ } catch {
12863
+ }
12864
+ fabricCanvas.setActiveObject(effectiveTarget);
12865
+ effectiveTarget.setCoords();
12866
+ fabricCanvas._target = effectiveTarget;
12867
+ opt.target = effectiveTarget;
12868
+ pendingGroupPromotionRef.current = null;
11312
12869
  }
11313
12870
  } 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));
12871
+ 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
12872
  if (isMultiSelectKey) return;
12873
+ if ((_n = opt.e) == null ? void 0 : _n.altKey) return;
11316
12874
  try {
11317
12875
  const pointer = fabricCanvas.getPointer(opt.e);
11318
12876
  const px = pointer.x;
@@ -11385,11 +12943,28 @@ const PageCanvas = forwardRef(
11385
12943
  const tid = t ? getObjectId(t) : null;
11386
12944
  try {
11387
12945
  const activeIds = new Set(selectedIdsRef.current);
12946
+ const pointer = fabricCanvas.getPointer(opt.e);
12947
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12948
+ const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
12949
+ const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
12950
+ const groupPick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
12951
+ if (groupPick && !activeIds.has(groupPick.group.id)) {
12952
+ const b = groupFabricUnionBBox(groupPick.group);
12953
+ if (b) {
12954
+ setHoverBounds({
12955
+ left: b.left,
12956
+ top: b.top,
12957
+ width: b.right - b.left,
12958
+ height: b.bottom - b.top,
12959
+ angle: 0
12960
+ });
12961
+ return;
12962
+ }
12963
+ }
11388
12964
  const isHoveringSelected = !!(tid && activeIds.has(tid));
11389
12965
  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();
12966
+ t.setCoords();
12967
+ const br = t.getBoundingRect();
11393
12968
  setHoverBounds({
11394
12969
  left: br.left,
11395
12970
  top: br.top,
@@ -11410,7 +12985,7 @@ const PageCanvas = forwardRef(
11410
12985
  const childrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
11411
12986
  const activeEditingGroupId = fabricCanvas.__activeEditingGroupId ?? null;
11412
12987
  const pick = pickGroupAtPointer(pointer.x, pointer.y, childrenNow, activeEditingGroupId);
11413
- fabricCanvas.defaultCursor = pick ? "move" : "default";
12988
+ fabricCanvas.defaultCursor = "default";
11414
12989
  } catch {
11415
12990
  fabricCanvas.defaultCursor = "default";
11416
12991
  }
@@ -11418,6 +12993,11 @@ const PageCanvas = forwardRef(
11418
12993
  fabricCanvas.on("mouse:out", () => {
11419
12994
  setHoverBounds(null);
11420
12995
  });
12996
+ fabricCanvas.on("mouse:down", () => {
12997
+ setHoverBounds(null);
12998
+ });
12999
+ fabricCanvas.on("selection:created", () => setHoverBounds(null));
13000
+ fabricCanvas.on("selection:updated", () => setHoverBounds(null));
11421
13001
  fabricCanvas.on("mouse:down", (ev) => {
11422
13002
  if (fabricCanvas._currentTransform) {
11423
13003
  lockEdits();
@@ -11433,6 +13013,8 @@ const PageCanvas = forwardRef(
11433
13013
  setGuides([]);
11434
13014
  setRotationLabel(null);
11435
13015
  setSizeLabel(null);
13016
+ objectResizeActiveSnapRef.current = null;
13017
+ groupResizeActiveSnapRef.current = null;
11436
13018
  dragStarted = false;
11437
13019
  const pendingPromotion = pendingGroupPromotionRef.current;
11438
13020
  pendingGroupPromotionRef.current = null;
@@ -11587,7 +13169,9 @@ const PageCanvas = forwardRef(
11587
13169
  const intrH = obj.height ?? 1;
11588
13170
  let newW = Math.max(1, intrW * Math.abs(sx));
11589
13171
  let newH = Math.max(1, intrH * Math.abs(sy));
11590
- if (obj instanceof fabric.Circle) {
13172
+ if (obj instanceof fabric.Ellipse) {
13173
+ obj.set({ rx: newW / 2, ry: newH / 2 });
13174
+ } else if (obj instanceof fabric.Circle) {
11591
13175
  const diameter = Math.max(1, Math.min(newW, newH));
11592
13176
  newW = diameter;
11593
13177
  newH = diameter;
@@ -11704,8 +13288,128 @@ const PageCanvas = forwardRef(
11704
13288
  }
11705
13289
  const transform = e.transform;
11706
13290
  const corner = (transform == null ? void 0 : transform.corner) || "";
13291
+ if (obj instanceof fabric.ActiveSelection && (corner === "ml" || corner === "mr" || corner === "mt" || corner === "mb")) {
13292
+ const isXSide = corner === "ml" || corner === "mr";
13293
+ const sAxis = isXSide ? Math.abs(obj.scaleX ?? 1) : Math.abs(obj.scaleY ?? 1);
13294
+ if (sAxis > 1e-3) {
13295
+ for (const child of obj.getObjects()) {
13296
+ if (!(child instanceof fabric.Textbox)) continue;
13297
+ if (isXSide) {
13298
+ if (child.__asLiveOrigW == null) {
13299
+ child.__asLiveOrigW = (child.width ?? 0) * (child.scaleX ?? 1);
13300
+ }
13301
+ const origW = child.__asLiveOrigW;
13302
+ const newW = Math.max(20, origW * sAxis);
13303
+ if (Math.abs((child.width ?? 0) - newW) > 0.5) {
13304
+ child.set({ width: newW, scaleX: 1 / sAxis });
13305
+ try {
13306
+ child.initDimensions();
13307
+ } catch {
13308
+ }
13309
+ child.dirty = true;
13310
+ }
13311
+ } else {
13312
+ if (child.__asLiveOrigH == null) {
13313
+ child.__asLiveOrigH = (child.height ?? 0) * (child.scaleY ?? 1);
13314
+ }
13315
+ const origH = child.__asLiveOrigH;
13316
+ const newH = Math.max(20, origH * sAxis);
13317
+ child.minBoxHeight = newH;
13318
+ child.set({ scaleY: 1 / sAxis });
13319
+ try {
13320
+ child.initDimensions();
13321
+ } catch {
13322
+ }
13323
+ child.dirty = true;
13324
+ }
13325
+ }
13326
+ }
13327
+ }
13328
+ snapDuringScaleCallback(obj, corner);
11707
13329
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11708
- setGuides(scaleGuides);
13330
+ const gridGuidesForScale = [];
13331
+ try {
13332
+ const psGrid = projectSettingsRef.current;
13333
+ const canApplyGridSnap = psGrid.snapToGrid && corner && !obj.__cropGroup && !obj.__resizeSnapHandler;
13334
+ if (canApplyGridSnap) {
13335
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13336
+ const gridY = psGrid.gridSizeY ?? psGrid.gridSize ?? 0;
13337
+ if (gridX > 0 && gridY > 0) {
13338
+ obj.setCoords();
13339
+ const br = obj.getBoundingRect();
13340
+ if (br.width > 1 && br.height > 1) {
13341
+ const hasL = corner.includes("l");
13342
+ const hasR = corner.includes("r");
13343
+ const hasT = corner.includes("t");
13344
+ const hasB = corner.includes("b");
13345
+ const anchorRight = br.left + br.width;
13346
+ const anchorBottom = br.top + br.height;
13347
+ let newLeft = br.left;
13348
+ let newTop = br.top;
13349
+ let newWidth = br.width;
13350
+ let newHeight = br.height;
13351
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13352
+ const yAlreadySnapped = scaleGuides.some((g) => g.type === "horizontal");
13353
+ if (!xAlreadySnapped) {
13354
+ if (hasL) {
13355
+ const nL = Math.round(br.left / gridX) * gridX;
13356
+ newWidth = Math.max(20, anchorRight - nL);
13357
+ newLeft = anchorRight - newWidth;
13358
+ } else if (hasR) {
13359
+ const nR = Math.round(anchorRight / gridX) * gridX;
13360
+ newWidth = Math.max(20, nR - br.left);
13361
+ }
13362
+ }
13363
+ if (!yAlreadySnapped) {
13364
+ if (hasT) {
13365
+ const nT = Math.round(br.top / gridY) * gridY;
13366
+ newHeight = Math.max(20, anchorBottom - nT);
13367
+ newTop = anchorBottom - newHeight;
13368
+ } else if (hasB) {
13369
+ const nB = Math.round(anchorBottom / gridY) * gridY;
13370
+ newHeight = Math.max(20, nB - br.top);
13371
+ }
13372
+ }
13373
+ const widthChanged = Math.abs(newWidth - br.width) > 0.5;
13374
+ const heightChanged = Math.abs(newHeight - br.height) > 0.5;
13375
+ if (widthChanged || heightChanged) {
13376
+ const isTextWidth = obj instanceof fabric.Textbox && widthChanged && !heightChanged;
13377
+ if (isTextWidth) {
13378
+ obj.set({ width: Math.max(20, newWidth) });
13379
+ try {
13380
+ obj.initDimensions();
13381
+ } catch {
13382
+ }
13383
+ } else {
13384
+ const ratioX = widthChanged ? newWidth / br.width : 1;
13385
+ const ratioY = heightChanged ? newHeight / br.height : 1;
13386
+ const curSx = obj.scaleX ?? 1;
13387
+ const curSy = obj.scaleY ?? 1;
13388
+ obj.set({ scaleX: curSx * ratioX, scaleY: curSy * ratioY });
13389
+ }
13390
+ obj.setCoords();
13391
+ const after = obj.getBoundingRect();
13392
+ const dx = newLeft - after.left;
13393
+ const dy = newTop - after.top;
13394
+ if (Math.abs(dx) > 0.01 || Math.abs(dy) > 0.01) {
13395
+ obj.set({ left: (obj.left ?? 0) + dx, top: (obj.top ?? 0) + dy });
13396
+ obj.setCoords();
13397
+ }
13398
+ if (!xAlreadySnapped) {
13399
+ if (hasL) gridGuidesForScale.push({ type: "vertical", position: newLeft, kind: "grid" });
13400
+ else if (hasR) gridGuidesForScale.push({ type: "vertical", position: newLeft + newWidth, kind: "grid" });
13401
+ }
13402
+ if (!yAlreadySnapped) {
13403
+ if (hasT) gridGuidesForScale.push({ type: "horizontal", position: newTop, kind: "grid" });
13404
+ else if (hasB) gridGuidesForScale.push({ type: "horizontal", position: newTop + newHeight, kind: "grid" });
13405
+ }
13406
+ }
13407
+ }
13408
+ }
13409
+ }
13410
+ } catch {
13411
+ }
13412
+ setGuides(gridGuidesForScale.length ? [...scaleGuides, ...gridGuidesForScale] : scaleGuides);
11709
13413
  });
11710
13414
  fabricCanvas.on("object:resizing", (e) => {
11711
13415
  if (!isActiveRef.current) return;
@@ -11731,13 +13435,70 @@ const PageCanvas = forwardRef(
11731
13435
  }
11732
13436
  const transform = e.transform;
11733
13437
  const corner = (transform == null ? void 0 : transform.corner) || "";
13438
+ if (obj instanceof fabric.Textbox && (corner === "ml" || corner === "mr")) {
13439
+ const objId = getObjectId(obj);
13440
+ const sourceEl = objId ? elementsRef.current.find((el) => el.id === objId) : void 0;
13441
+ bakeTextboxScaleIntoTypography(obj, sourceEl);
13442
+ }
13443
+ snapDuringScaleCallback(obj, corner);
11734
13444
  const scaleGuides = calculateScaleSnapGuidesCallback(obj, corner);
11735
- setGuides(scaleGuides);
13445
+ const gridGuidesForTextResize = [];
13446
+ try {
13447
+ const psGrid = projectSettingsRef.current;
13448
+ if (psGrid.snapToGrid && corner && obj instanceof fabric.Textbox && (corner === "ml" || corner === "mr")) {
13449
+ const gridX = psGrid.gridSizeX ?? psGrid.gridSize ?? 0;
13450
+ if (gridX > 0) {
13451
+ obj.setCoords();
13452
+ const br = obj.getBoundingRect();
13453
+ const xAlreadySnapped = scaleGuides.some((g) => g.type === "vertical");
13454
+ if (!xAlreadySnapped && br.width > 1) {
13455
+ const anchorRight = br.left + br.width;
13456
+ let newLeft = br.left;
13457
+ let newWidth = br.width;
13458
+ if (corner === "ml") {
13459
+ const nL = Math.round(br.left / gridX) * gridX;
13460
+ newWidth = Math.max(20, anchorRight - nL);
13461
+ newLeft = anchorRight - newWidth;
13462
+ } else {
13463
+ const nR = Math.round(anchorRight / gridX) * gridX;
13464
+ newWidth = Math.max(20, nR - br.left);
13465
+ }
13466
+ if (Math.abs(newWidth - br.width) > 0.5) {
13467
+ obj.set({ width: Math.max(20, newWidth) });
13468
+ try {
13469
+ obj.initDimensions();
13470
+ } catch {
13471
+ }
13472
+ obj.setCoords();
13473
+ const after = obj.getBoundingRect();
13474
+ const dx = newLeft - after.left;
13475
+ if (Math.abs(dx) > 0.01) {
13476
+ obj.set({ left: (obj.left ?? 0) + dx });
13477
+ obj.setCoords();
13478
+ }
13479
+ gridGuidesForTextResize.push({
13480
+ type: "vertical",
13481
+ position: corner === "ml" ? newLeft : newLeft + newWidth,
13482
+ kind: "grid"
13483
+ });
13484
+ }
13485
+ }
13486
+ }
13487
+ }
13488
+ } catch {
13489
+ }
13490
+ setGuides(gridGuidesForTextResize.length ? [...scaleGuides, ...gridGuidesForTextResize] : scaleGuides);
11736
13491
  });
11737
13492
  fabricCanvas.on("object:rotating", (e) => {
11738
13493
  markSimpleTransform(e);
11739
13494
  didTransformRef.current = true;
11740
13495
  const tr = e.target;
13496
+ try {
13497
+ const getCursor = fabricCanvas.__pixldocsGetRotateCursor;
13498
+ const upper = fabricCanvas.upperCanvasEl;
13499
+ if (typeof getCursor === "function" && upper && tr) upper.style.cursor = getCursor(tr);
13500
+ } catch {
13501
+ }
11741
13502
  const rotateTargetId = tr ? getObjectId(tr) : null;
11742
13503
  if (rotateTargetId && rotateTargetId !== "__background__") {
11743
13504
  preserveSelectionAfterTransformIdRef.current = rotateTargetId;
@@ -11764,6 +13525,7 @@ const PageCanvas = forwardRef(
11764
13525
  prepareGroupSelectionTransformStart(e.target);
11765
13526
  markTransforming(e.target);
11766
13527
  didTransformRef.current = true;
13528
+ if (e.target) e.target.__pixldocsDragMoved = true;
11767
13529
  const moveTargetId = e.target ? getObjectId(e.target) : null;
11768
13530
  if (moveTargetId && moveTargetId !== "__background__") {
11769
13531
  preserveSelectionAfterTransformIdRef.current = moveTargetId;
@@ -11780,20 +13542,57 @@ const PageCanvas = forwardRef(
11780
13542
  if (!obj) return;
11781
13543
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11782
13544
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11783
- setGuides(newGuides);
13545
+ let finalDx = snapDx;
13546
+ let finalDy = snapDy;
13547
+ let mergedGuides = newGuides;
13548
+ const psLive = projectSettingsRef.current;
13549
+ const gridX = psLive.gridSizeX ?? psLive.gridSize ?? 0;
13550
+ const gridY = psLive.gridSizeY ?? psLive.gridSize ?? 0;
13551
+ if (psLive.snapToGrid && gridX > 0 && gridY > 0) {
13552
+ try {
13553
+ snapTarget.setCoords();
13554
+ } catch {
13555
+ }
13556
+ const br = snapTarget.getBoundingRect();
13557
+ const gridGuides = [];
13558
+ if (finalDx === 0) {
13559
+ const newLeft = Math.round(br.left / gridX) * gridX;
13560
+ finalDx = newLeft - br.left;
13561
+ gridGuides.push({ type: "vertical", position: newLeft, kind: "grid" });
13562
+ }
13563
+ if (finalDy === 0) {
13564
+ const newTop = Math.round(br.top / gridY) * gridY;
13565
+ finalDy = newTop - br.top;
13566
+ gridGuides.push({ type: "horizontal", position: newTop, kind: "grid" });
13567
+ }
13568
+ if (gridGuides.length) mergedGuides = [...newGuides, ...gridGuides];
13569
+ }
13570
+ setGuides(mergedGuides);
11784
13571
  setHoverBounds(null);
11785
- if (snapDx !== 0 || snapDy !== 0) {
11786
- snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
13572
+ if (finalDx !== 0 || finalDy !== 0) {
13573
+ snapTarget.set({ left: (snapTarget.left ?? 0) + finalDx, top: (snapTarget.top ?? 0) + finalDy });
11787
13574
  }
11788
13575
  });
11789
13576
  let cropGroupSaveTimer = null;
11790
13577
  fabricCanvas.on("object:modified", (e) => {
11791
- var _a2, _b, _c, _d, _e, _f, _g;
13578
+ var _a2, _b2, _c, _d, _e, _f, _g;
11792
13579
  try {
11793
13580
  dragStarted = false;
11794
13581
  setGuides([]);
11795
13582
  setGroupOverlayLiveBoundsRef.current(null);
13583
+ objectResizeActiveSnapRef.current = null;
13584
+ groupResizeActiveSnapRef.current = null;
11796
13585
  onDragEnd == null ? void 0 : onDragEnd();
13586
+ try {
13587
+ const t = e.target;
13588
+ if (t instanceof fabric.ActiveSelection) {
13589
+ for (const child of t.getObjects()) {
13590
+ delete child.__asLiveOrigW;
13591
+ delete child.__asLiveOrigH;
13592
+ }
13593
+ }
13594
+ } catch {
13595
+ }
11797
13596
  lockEdits();
11798
13597
  const modifiedTarget = e.target;
11799
13598
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -11902,7 +13701,7 @@ const PageCanvas = forwardRef(
11902
13701
  useEditorStore.getState().reflowStackGroupInPage(pageId, groupId);
11903
13702
  }
11904
13703
  const stateAfter = useEditorStore.getState();
11905
- const pageAfter = ((_b = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b.children) ?? [];
13704
+ const pageAfter = ((_b2 = stateAfter.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _b2.children) ?? [];
11906
13705
  const groupNodeAfter = findNodeById(pageAfter, groupId);
11907
13706
  if (groupNodeAfter) {
11908
13707
  const abs = getAbsoluteBounds(groupNodeAfter, pageAfter);
@@ -11929,11 +13728,11 @@ const PageCanvas = forwardRef(
11929
13728
  clearTimeout(cropGroupSaveTimer);
11930
13729
  }
11931
13730
  cropGroupSaveTimer = setTimeout(() => {
11932
- var _a3, _b2, _c2;
13731
+ var _a3, _b3, _c2;
11933
13732
  const { updateElement: updateElement2 } = useEditorStore.getState();
11934
13733
  const img = ct._img;
11935
13734
  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;
13735
+ const panX = ((_b3 = img == null ? void 0 : img._ct) == null ? void 0 : _b3.panX) ?? 0.5;
11937
13736
  const panY = ((_c2 = img == null ? void 0 : img._ct) == null ? void 0 : _c2.panY) ?? 0.5;
11938
13737
  const stateCrop = useEditorStore.getState();
11939
13738
  const pageCrop = stateCrop.canvas.pages.find((p) => p.id === pageId);
@@ -11952,6 +13751,8 @@ const PageCanvas = forwardRef(
11952
13751
  cropZoom: zoom3
11953
13752
  }, { recordHistory: false });
11954
13753
  active.__isInternalCropUpdate = false;
13754
+ installImageResizeControlsWithSnap(active);
13755
+ ensureCanvaControlRenders(active);
11955
13756
  fabricCanvas.setActiveObject(active);
11956
13757
  setTimeout(() => justModifiedIdsRef.current.delete(objId), 150);
11957
13758
  }, 0);
@@ -12205,6 +14006,10 @@ const PageCanvas = forwardRef(
12205
14006
  for (const obj of activeObjects) {
12206
14007
  const objId = getObjectId(obj);
12207
14008
  if (!objId || objId === "__background__") continue;
14009
+ const sourceElement = elementsRef.current.find((el) => el.id === objId);
14010
+ if (obj instanceof fabric.Textbox && !isActiveSelection) {
14011
+ bakeTextboxScaleIntoTypography(obj, sourceElement);
14012
+ }
12208
14013
  let intrinsicWidth;
12209
14014
  let intrinsicHeight;
12210
14015
  if (obj instanceof fabric.Circle) {
@@ -12271,7 +14076,6 @@ const PageCanvas = forwardRef(
12271
14076
  absoluteLeft = (absoluteLeft ?? 0) - w / 2;
12272
14077
  absoluteTop = (absoluteTop ?? 0) - h / 2;
12273
14078
  }
12274
- const sourceElement = elementsRef.current.find((el) => el.id === objId);
12275
14079
  const preserveCornerGeometry = (sourceElement == null ? void 0 : sourceElement.type) === "shape" && (sourceElement.shapeType === "circle" || sourceElement.shapeType === "rounded-rect" || sourceElement.shapeType === "triangle");
12276
14080
  let finalWidth = intrinsicWidth;
12277
14081
  let finalHeight = intrinsicHeight;
@@ -12357,17 +14161,35 @@ const PageCanvas = forwardRef(
12357
14161
  finalHeight = 0;
12358
14162
  finalScaleX = 1;
12359
14163
  finalScaleY = 1;
14164
+ } else if (obj instanceof fabric.Textbox && isActiveSelection && (Math.abs((decomposed.scaleX ?? 1) - 1) > 1e-3 || Math.abs((decomposed.scaleY ?? 1) - 1) > 1e-3)) {
14165
+ const sx = Math.abs(decomposed.scaleX || 1);
14166
+ const sy = Math.abs(decomposed.scaleY || 1);
14167
+ const bakedWidth = Math.max(20, intrinsicWidth * sx);
14168
+ const bakedHeight = Math.max(1, intrinsicHeight * sy);
14169
+ finalWidth = bakedWidth;
14170
+ finalHeight = bakedHeight;
14171
+ finalScaleX = 1;
14172
+ finalScaleY = 1;
14173
+ try {
14174
+ obj.set({ width: bakedWidth, scaleX: 1, scaleY: 1 });
14175
+ obj.initDimensions();
14176
+ obj.setCoords();
14177
+ } catch {
14178
+ }
14179
+ finalAbsoluteMatrix = fabric.util.composeMatrix({
14180
+ translateX: decomposed.translateX,
14181
+ translateY: decomposed.translateY,
14182
+ angle: decomposed.angle ?? 0,
14183
+ scaleX: 1,
14184
+ scaleY: 1,
14185
+ skewX: 0,
14186
+ skewY: 0
14187
+ });
12360
14188
  } else if (preserveCornerGeometry) {
12361
14189
  const scaledW = Math.max(1, intrinsicWidth * Math.abs(decomposed.scaleX || 1));
12362
14190
  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
- }
14191
+ finalWidth = scaledW;
14192
+ finalHeight = scaledH;
12371
14193
  finalScaleX = 1;
12372
14194
  finalScaleY = 1;
12373
14195
  obj.set({ scaleX: 1, scaleY: 1 });
@@ -12406,6 +14228,11 @@ const PageCanvas = forwardRef(
12406
14228
  transformMatrix: finalAbsoluteMatrix
12407
14229
  };
12408
14230
  if (obj instanceof fabric.Textbox) {
14231
+ const bakedTextScaleUpdates = obj.__pixldocsBakedTextScaleUpdates;
14232
+ if (bakedTextScaleUpdates && typeof bakedTextScaleUpdates === "object") {
14233
+ Object.assign(elementUpdate, bakedTextScaleUpdates);
14234
+ delete obj.__pixldocsBakedTextScaleUpdates;
14235
+ }
12409
14236
  const baked = obj.minBoxHeight;
12410
14237
  if (typeof baked === "number" && baked > 0) {
12411
14238
  elementUpdate.minBoxHeight = baked;
@@ -12514,7 +14341,7 @@ const PageCanvas = forwardRef(
12514
14341
  }
12515
14342
  });
12516
14343
  fabricCanvas.on("mouse:dblclick", (e) => {
12517
- var _a2, _b;
14344
+ var _a2, _b2;
12518
14345
  if (!isActiveRef.current || !allowEditing) return;
12519
14346
  let target = e.target;
12520
14347
  if (!target) {
@@ -12524,7 +14351,7 @@ const PageCanvas = forwardRef(
12524
14351
  if (target && target instanceof fabric.Group && target.__cropGroup) {
12525
14352
  const ct = target.__cropData;
12526
14353
  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) || "";
14354
+ 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
14355
  const isPlaceholder = !innerSrc || innerSrc === EMPTY_IMAGE_PLACEHOLDER_DATA_URL;
12529
14356
  if (innerImg && !isPlaceholder && !isCropGroupInCropMode(target)) {
12530
14357
  enterCropMode(target);
@@ -12703,13 +14530,13 @@ const PageCanvas = forwardRef(
12703
14530
  visibilityUpdateInProgressRef.current = false;
12704
14531
  }
12705
14532
  doSyncRef.current = () => {
12706
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
14533
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
12707
14534
  const shouldSkipUpdates2 = syncLockedRef.current || editLockRef.current;
12708
14535
  const state = useEditorStore.getState();
12709
14536
  const elementsToSync = elements;
12710
14537
  elementsRef.current = elementsToSync;
12711
14538
  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) ?? []);
14539
+ const selectedIdsFromStore = new Set(((_b2 = state.canvas) == null ? void 0 : _b2.selectedIds) ?? []);
12713
14540
  isRebuildingRef.current = true;
12714
14541
  const allElementIds = new Set(elementsToSync.map((el) => el.id));
12715
14542
  const sectionGroups = pageTree.filter(
@@ -12736,7 +14563,8 @@ const PageCanvas = forwardRef(
12736
14563
  const activeObj = fc.getActiveObject();
12737
14564
  const activeObjId = activeObj ? getObjectId(activeObj) : null;
12738
14565
  const isTextBeingEdited = activeObjId && editingTextIdRef.current === activeObjId;
12739
- if (activeObj && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
14566
+ const isMultiSelect = activeObj instanceof fabric.ActiveSelection;
14567
+ if (activeObj && isMultiSelect && !(((_c = activeObj._ct) == null ? void 0 : _c.isCropGroup) || activeObj.__cropGroup) && !isTextBeingEdited) {
12740
14568
  fc.discardActiveObject();
12741
14569
  }
12742
14570
  }
@@ -13056,7 +14884,7 @@ const PageCanvas = forwardRef(
13056
14884
  updateCoverLayout(existingObj);
13057
14885
  applyEdgeFadeFrameClipPath(existingObj, element, ct.frameW, ct.frameH, ct.shape || "rect", ct.rx || 0);
13058
14886
  if (allowEditing) {
13059
- installCanvaMaskControls(existingObj);
14887
+ installImageResizeControlsWithSnap(existingObj);
13060
14888
  } else {
13061
14889
  existingObj.set({
13062
14890
  hasControls: false,
@@ -13138,7 +14966,7 @@ const PageCanvas = forwardRef(
13138
14966
  hoverCursor: isDynamicField && isPreviewMode ? "pointer" : void 0
13139
14967
  });
13140
14968
  if (allowEditing) {
13141
- installCanvaMaskControls(existingObj);
14969
+ installImageResizeControlsWithSnap(existingObj);
13142
14970
  }
13143
14971
  existingObj.setCoords();
13144
14972
  fc.requestRenderAll();
@@ -13864,7 +15692,7 @@ const PageCanvas = forwardRef(
13864
15692
  return unsub;
13865
15693
  }, []);
13866
15694
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
13867
- var _a2, _b, _c;
15695
+ var _a2, _b2, _c;
13868
15696
  const fc = fabricRef.current;
13869
15697
  if (fc && isTransforming(fc)) {
13870
15698
  return;
@@ -13932,11 +15760,12 @@ const PageCanvas = forwardRef(
13932
15760
  // Disable rotation for crop groups (simplifies resize math)
13933
15761
  hasRotatingPoint: false,
13934
15762
  // 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)),
15763
+ // Handles are drawn in screen-space keep them constant on-screen.
15764
+ // Match the global Canva-style defaults (circular dots, pill sides).
15765
+ cornerSize: 10,
15766
+ borderScaleFactor: SELECTION_BORDER_SCALE,
13938
15767
  transparentCorners: false,
13939
- cornerStyle: "rect",
15768
+ cornerStyle: "circle",
13940
15769
  cornerColor: SELECTION_PRIMARY,
13941
15770
  cornerStrokeColor: "#ffffff",
13942
15771
  borderColor: SELECTION_PRIMARY,
@@ -14046,7 +15875,8 @@ const PageCanvas = forwardRef(
14046
15875
  obj.clipPath.dirty = true;
14047
15876
  obj.clipPath.setCoords();
14048
15877
  }
14049
- installCanvaMaskControls(obj);
15878
+ installImageResizeControlsWithSnap(obj);
15879
+ ensureCanvaControlRenders(obj);
14050
15880
  obj.set({
14051
15881
  selectable: true,
14052
15882
  evented: true,
@@ -14141,7 +15971,7 @@ const PageCanvas = forwardRef(
14141
15971
  obj.setCoords();
14142
15972
  }
14143
15973
  if (!isLine) {
14144
- const angleTextPathActive = isTextbox && ((_b = element.textPath) == null ? void 0 : _b.preset) === "rise";
15974
+ const angleTextPathActive = isTextbox && ((_b2 = element.textPath) == null ? void 0 : _b2.preset) === "rise";
14145
15975
  const appliedSkewY = angleTextPathActive ? 0 : element.skewY ?? 0;
14146
15976
  let posIfNotSkipped = skipPositionUpdate ? {} : { left: fabricPos.left, top: fabricPos.top };
14147
15977
  if (!skipPositionUpdate && (obj instanceof fabric.FabricImage && obj.originX === "center" || obj instanceof fabric.Group && obj.__cropGroup)) {
@@ -14218,7 +16048,17 @@ const PageCanvas = forwardRef(
14218
16048
  objectCaching: true
14219
16049
  });
14220
16050
  } else if (obj instanceof fabric.Ellipse) {
14221
- obj.set({ rx: rW / 2, ry: rH / 2 });
16051
+ obj.set({
16052
+ rx: cornerSafeW / 2,
16053
+ ry: cornerSafeH / 2,
16054
+ fill: element.fill || "transparent",
16055
+ stroke: element.stroke || "transparent",
16056
+ strokeWidth: element.strokeWidth || 0,
16057
+ strokeUniform: true,
16058
+ strokeLineJoin: "round",
16059
+ strokeLineCap: "round",
16060
+ objectCaching: true
16061
+ });
14222
16062
  } else if (obj instanceof fabric.Textbox) {
14223
16063
  const overflowPolicy = element.overflowPolicy || "grow-and-push";
14224
16064
  let text = element.text || "Text";
@@ -14412,6 +16252,16 @@ const PageCanvas = forwardRef(
14412
16252
  strokeUniform: true,
14413
16253
  objectCaching: true
14414
16254
  });
16255
+ } else if (obj instanceof fabric.Ellipse) {
16256
+ obj.set({
16257
+ rx: cornerSafeW / 2,
16258
+ ry: cornerSafeH / 2,
16259
+ fill: element.fill || "transparent",
16260
+ stroke: element.stroke || "transparent",
16261
+ strokeWidth: element.strokeWidth || 0,
16262
+ strokeUniform: true,
16263
+ objectCaching: true
16264
+ });
14415
16265
  } else if (obj instanceof fabric.Rect && element.shapeType === "rounded-rect") {
14416
16266
  const toRadius = (value, fallback) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
14417
16267
  const baseRx = Math.max(0, Number(element.rx ?? 0));
@@ -14671,7 +16521,7 @@ const PageCanvas = forwardRef(
14671
16521
  return Math.max(min, Math.min(max, v));
14672
16522
  };
14673
16523
  const applyEdgeFadeFrameClipPath = (group, element, frameW, frameH, shape, rxRatio) => {
14674
- var _a2, _b, _c;
16524
+ var _a2, _b2, _c;
14675
16525
  const fadeElement = element;
14676
16526
  const fadeGroup = group;
14677
16527
  const inputKey = edgeFadeKey(fadeElement);
@@ -14792,7 +16642,7 @@ const PageCanvas = forwardRef(
14792
16642
  delete fadeGroup.__edgeFadeRenderConfig;
14793
16643
  delete fadeGroup.__edgeFadeKey;
14794
16644
  delete fadeGroup.__edgeFadeInputKey;
14795
- if ((_b = group.clipPath) == null ? void 0 : _b.__edgeFadeMask) {
16645
+ if ((_b2 = group.clipPath) == null ? void 0 : _b2.__edgeFadeMask) {
14796
16646
  group.clipPath = void 0;
14797
16647
  updateCoverLayout(group);
14798
16648
  }
@@ -14809,7 +16659,7 @@ const PageCanvas = forwardRef(
14809
16659
  (_c = group.canvas) == null ? void 0 : _c.requestRenderAll();
14810
16660
  };
14811
16661
  const loadImageAsync2 = async (element, placeholder, fc) => {
14812
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
16662
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
14813
16663
  const imageUrl = element.src || element.imageUrl;
14814
16664
  if (!imageUrl) return;
14815
16665
  const elementId = element.id;
@@ -14840,7 +16690,7 @@ const PageCanvas = forwardRef(
14840
16690
  await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);
14841
16691
  if (!isLatestRequest()) return;
14842
16692
  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");
16693
+ const clipShapeForFade = element.clipShape ?? ((_b2 = element.style) == null ? void 0 : _b2.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
14844
16694
  const willUseCropGroupForFade = imageFitForFade !== "fill" || clipShapeForFade && clipShapeForFade !== "none";
14845
16695
  try {
14846
16696
  if (hasEdgeFade(element) && !willUseCropGroupForFade) {
@@ -15058,7 +16908,7 @@ const PageCanvas = forwardRef(
15058
16908
  evented: canBeEvented && !isHidden
15059
16909
  });
15060
16910
  } else {
15061
- installCanvaMaskControls(cropGroup);
16911
+ installImageResizeControlsWithSnap(cropGroup);
15062
16912
  }
15063
16913
  const cropImg = (_o = cropGroup.__cropData) == null ? void 0 : _o._img;
15064
16914
  if (cropImg) {
@@ -15269,38 +17119,7 @@ const PageCanvas = forwardRef(
15269
17119
  ),
15270
17120
  sectionsOverlay,
15271
17121
  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(
17122
+ hoverBounds && !guides.length && !(selectedIdsRef.current && selectedIdsRef.current.length) && /* @__PURE__ */ jsx(
15304
17123
  "svg",
15305
17124
  {
15306
17125
  className: "absolute inset-0 pointer-events-none",
@@ -15314,108 +17133,177 @@ const PageCanvas = forwardRef(
15314
17133
  width: hoverBounds.width,
15315
17134
  height: hoverBounds.height,
15316
17135
  fill: "none",
15317
- stroke: "hsl(256 80% 58%)",
15318
- strokeWidth: 1,
17136
+ stroke: SELECTION_PRIMARY,
17137
+ strokeWidth: 2,
17138
+ vectorEffect: "non-scaling-stroke",
15319
17139
  strokeDasharray: "0",
15320
- opacity: 0.65
17140
+ opacity: 1
15321
17141
  }
15322
17142
  )
15323
17143
  }
15324
17144
  ),
15325
- guides.length > 0 && /* @__PURE__ */ jsx(
17145
+ canvas.projectSettings.showGrid && (() => {
17146
+ const ps = canvas.projectSettings;
17147
+ const gx = Math.max(1, ps.gridSizeX ?? ps.gridSize ?? 0);
17148
+ const gy = Math.max(1, ps.gridSizeY ?? ps.gridSize ?? 0);
17149
+ if (gx <= 0 || gy <= 0) return null;
17150
+ return /* @__PURE__ */ jsxs(
17151
+ "svg",
17152
+ {
17153
+ className: "absolute inset-0 pointer-events-none",
17154
+ style: { width: scaledWidth, height: scaledHeight },
17155
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
17156
+ children: [
17157
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
17158
+ "pattern",
17159
+ {
17160
+ id: `pixldocs-grid-${pageId}`,
17161
+ width: gx,
17162
+ height: gy,
17163
+ patternUnits: "userSpaceOnUse",
17164
+ children: /* @__PURE__ */ jsx(
17165
+ "path",
17166
+ {
17167
+ d: `M ${gx} 0 L 0 0 0 ${gy}`,
17168
+ fill: "none",
17169
+ stroke: ps.gridColor || "#0f172a",
17170
+ strokeWidth: 0.5,
17171
+ opacity: 0.22
17172
+ }
17173
+ )
17174
+ }
17175
+ ) }),
17176
+ /* @__PURE__ */ jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
17177
+ ]
17178
+ }
17179
+ );
17180
+ })(),
17181
+ guides.length > 0 && /* @__PURE__ */ jsxs(
15326
17182
  "svg",
15327
17183
  {
15328
17184
  className: "absolute inset-0 pointer-events-none",
15329
17185
  style: { width: scaledWidth, height: scaledHeight },
15330
17186
  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;
17187
+ children: [
17188
+ (() => {
17189
+ const seenBoxes = /* @__PURE__ */ new Set();
17190
+ const boxes = [];
17191
+ for (const g of guides) {
17192
+ const list = g.targetBoundsList ?? (g.targetBounds ? [g.targetBounds] : []);
17193
+ for (const b of list) {
17194
+ const key = `${b.left.toFixed(1)}-${b.top.toFixed(1)}-${b.width.toFixed(1)}-${b.height.toFixed(1)}`;
17195
+ if (seenBoxes.has(key)) continue;
17196
+ seenBoxes.add(key);
17197
+ boxes.push(b);
17198
+ }
17199
+ }
17200
+ return boxes.map((b, i) => /* @__PURE__ */ jsx(
17201
+ "rect",
17202
+ {
17203
+ x: b.left,
17204
+ y: b.top,
17205
+ width: b.width,
17206
+ height: b.height,
17207
+ fill: "none",
17208
+ stroke: "#f43f5e",
17209
+ strokeWidth: 1.5,
17210
+ strokeDasharray: "4,3",
17211
+ opacity: 0.9,
17212
+ vectorEffect: "non-scaling-stroke"
17213
+ },
17214
+ `tb-${i}`
17215
+ ));
17216
+ })(),
17217
+ (() => {
17218
+ const seen = /* @__PURE__ */ new Set();
17219
+ return guides.filter((guide) => {
17220
+ if (guide.kind === "gap") return true;
17221
+ const key = `${guide.type}-${guide.position.toFixed(1)}`;
17222
+ if (seen.has(key)) return false;
17223
+ seen.add(key);
17224
+ return true;
17225
+ }).map((guide, i) => {
17226
+ if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
17227
+ const gapColor = "#ec4899";
17228
+ const label = `${guide.gap}`;
17229
+ const labelW2 = Math.max(22, label.length * 7 + 8);
17230
+ if (guide.type === "horizontal") {
17231
+ const y = guide.bracketAt ?? guide.position;
17232
+ const x1 = guide.start;
17233
+ const x2 = guide.end;
17234
+ const mid = (x1 + x2) / 2;
17235
+ return /* @__PURE__ */ jsxs("g", { children: [
17236
+ /* @__PURE__ */ jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17237
+ /* @__PURE__ */ jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17238
+ /* @__PURE__ */ jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17239
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
17240
+ /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17241
+ /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17242
+ ] })
17243
+ ] }, i);
17244
+ } else {
17245
+ const x = guide.bracketAt ?? guide.position;
17246
+ const y1 = guide.start;
17247
+ const y2 = guide.end;
17248
+ const mid = (y1 + y2) / 2;
17249
+ return /* @__PURE__ */ jsxs("g", { children: [
17250
+ /* @__PURE__ */ jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17251
+ /* @__PURE__ */ jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17252
+ /* @__PURE__ */ jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1.75, vectorEffect: "non-scaling-stroke" }),
17253
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
17254
+ /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
17255
+ /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
17256
+ ] })
17257
+ ] }, i);
17258
+ }
17259
+ }
17260
+ const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
17261
+ const isActive2 = guide.active === true;
17262
+ const isGrid = guide.kind === "grid";
17263
+ const strokeColor = isGrid ? "#f59e0b" : isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#ec4899";
17264
+ const strokeWidth = isGrid ? 1.75 : 1.75;
17265
+ const strokeDasharray = isGrid ? "2,3" : isActive2 ? "none" : isElementRelative ? "3,3" : "4,4";
17266
+ const showDistance = typeof guide.distance === "number";
17267
+ const labelText = showDistance ? String(Math.round(guide.distance)) : "";
17268
+ const labelW = Math.max(24, labelText.length * 7);
17269
+ if (guide.type === "vertical") {
17270
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17271
+ const y1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17272
+ const y2 = guide.start != null && guide.end != null ? Math.min(canvasHeight, guide.end + padding) : canvasHeight;
17273
+ const labelY = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasHeight / 2;
15349
17274
  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 })
17275
+ /* @__PURE__ */ jsx("line", { x1: guide.position, y1, x2: guide.position, y2, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17276
+ isActive2 && /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 4, fill: "#22c55e", opacity: 0.9 }),
17277
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
17278
+ /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y1, r: 2, fill: "#f43f5e" }),
17279
+ /* @__PURE__ */ jsx("circle", { cx: guide.position, cy: y2, r: 2, fill: "#f43f5e" })
17280
+ ] }),
17281
+ showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${guide.position + 6}, ${labelY})`, children: [
17282
+ /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17283
+ /* @__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
17284
  ] })
15357
17285
  ] }, i);
15358
17286
  } else {
15359
- const x = guide.bracketAt ?? guide.position;
15360
- const y1 = guide.start;
15361
- const y2 = guide.end;
15362
- const mid = (y1 + y2) / 2;
17287
+ const padding = isElementRelative || isActive2 ? 20 : 0;
17288
+ const x1 = guide.start != null && guide.end != null ? Math.max(0, guide.start - padding) : 0;
17289
+ const x2 = guide.start != null && guide.end != null ? Math.min(canvasWidth, guide.end + padding) : canvasWidth;
17290
+ const labelX = guide.start != null && guide.end != null ? (guide.start + guide.end) / 2 : canvasWidth / 2;
15363
17291
  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 })
17292
+ /* @__PURE__ */ jsx("line", { x1, y1: guide.position, x2, y2: guide.position, stroke: strokeColor, strokeWidth, strokeDasharray, vectorEffect: "non-scaling-stroke" }),
17293
+ isActive2 && /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 4, fill: "#22c55e", opacity: 0.9 }),
17294
+ isElementRelative && !isActive2 && /* @__PURE__ */ jsxs(Fragment, { children: [
17295
+ /* @__PURE__ */ jsx("circle", { cx: x1, cy: guide.position, r: 2, fill: "#f43f5e" }),
17296
+ /* @__PURE__ */ jsx("circle", { cx: x2, cy: guide.position, r: 2, fill: "#f43f5e" })
17297
+ ] }),
17298
+ showDistance && /* @__PURE__ */ jsxs("g", { transform: `translate(${labelX}, ${guide.position - 10})`, children: [
17299
+ /* @__PURE__ */ jsx("rect", { x: -2, y: -9, width: labelW, height: 16, rx: 4, fill: "rgba(0,0,0,0.75)" }),
17300
+ /* @__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
17301
  ] })
15371
17302
  ] }, i);
15372
17303
  }
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
- })()
17304
+ });
17305
+ })()
17306
+ ]
15419
17307
  }
15420
17308
  ),
15421
17309
  gridResizeLabel && /* @__PURE__ */ jsx(
@@ -15551,13 +17439,13 @@ function PreviewCanvas({
15551
17439
  onDynamicFieldClick,
15552
17440
  onReady
15553
17441
  }) {
15554
- var _a2, _b, _c, _d, _e;
17442
+ var _a2, _b2, _c, _d, _e;
15555
17443
  const canvasRef = useRef(null);
15556
17444
  const containerRef = useRef(null);
15557
17445
  const [containerWidth, setContainerWidth] = useState(0);
15558
17446
  const [hoveredFieldId, setHoveredFieldId] = useState(null);
15559
17447
  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;
17448
+ const canvasWidth = ((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.width) || 612;
15561
17449
  const canvasHeight = ((_c = config == null ? void 0 : config.canvas) == null ? void 0 : _c.height) || 792;
15562
17450
  const elementToFieldMap = useMemo(
15563
17451
  () => buildElementToFieldMap(config == null ? void 0 : config.dynamicFields),
@@ -15607,14 +17495,14 @@ function PreviewCanvas({
15607
17495
  }
15608
17496
  }, [elements]);
15609
17497
  const pageSettings = useMemo(() => {
15610
- var _a3, _b2;
17498
+ var _a3, _b3;
15611
17499
  return {
15612
17500
  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
17501
+ backgroundGradient: (_b3 = page == null ? void 0 : page.settings) == null ? void 0 : _b3.backgroundGradient
15614
17502
  };
15615
17503
  }, [(_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
17504
  const projectSettings = useMemo(() => {
15617
- var _a3, _b2, _c2;
17505
+ var _a3, _b3, _c2;
15618
17506
  const vars = ((_a3 = config.themeConfig) == null ? void 0 : _a3.variables) || {};
15619
17507
  return {
15620
17508
  showGrid: false,
@@ -15622,7 +17510,7 @@ function PreviewCanvas({
15622
17510
  gridSize: 10,
15623
17511
  snapToGuides: false,
15624
17512
  snapThreshold: 5,
15625
- primaryColor: (_b2 = vars.primary) == null ? void 0 : _b2.value,
17513
+ primaryColor: (_b3 = vars.primary) == null ? void 0 : _b3.value,
15626
17514
  secondaryColor: (_c2 = vars.secondary) == null ? void 0 : _c2.value
15627
17515
  };
15628
17516
  }, [config.themeConfig]);
@@ -15774,7 +17662,7 @@ const PreviewCanvas$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.def
15774
17662
  PreviewCanvas
15775
17663
  }, Symbol.toStringTag, { value: "Module" }));
15776
17664
  function applyThemeToConfig(config, themeOverrides) {
15777
- var _a2, _b, _c;
17665
+ var _a2, _b2, _c;
15778
17666
  if (!themeOverrides || Object.keys(themeOverrides).length === 0) return config;
15779
17667
  const cloned = JSON.parse(JSON.stringify(config));
15780
17668
  if ((_a2 = cloned.themeConfig) == null ? void 0 : _a2.variables) {
@@ -15785,7 +17673,7 @@ function applyThemeToConfig(config, themeOverrides) {
15785
17673
  }
15786
17674
  }
15787
17675
  const varMap = /* @__PURE__ */ new Map();
15788
- if ((_b = cloned.themeConfig) == null ? void 0 : _b.variables) {
17676
+ if ((_b2 = cloned.themeConfig) == null ? void 0 : _b2.variables) {
15789
17677
  for (const [key, def] of Object.entries(cloned.themeConfig.variables)) {
15790
17678
  varMap.set(key, themeOverrides[key] ?? def.value);
15791
17679
  }
@@ -15822,7 +17710,7 @@ function mapFormDefFieldType(t) {
15822
17710
  function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15823
17711
  const sections = [];
15824
17712
  function convert(defs, parentId) {
15825
- var _a2, _b;
17713
+ var _a2, _b2;
15826
17714
  for (const def of defs) {
15827
17715
  const isRepeatable = def.repeatable === true || def.type === "repeatable";
15828
17716
  const defFields = def.fields ?? def.entryFields ?? [];
@@ -15874,7 +17762,7 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
15874
17762
  placeholder: f.placeholder
15875
17763
  }))
15876
17764
  });
15877
- if ((_b = def.children) == null ? void 0 : _b.length) {
17765
+ if ((_b2 = def.children) == null ? void 0 : _b2.length) {
15878
17766
  convert(def.children, def.id);
15879
17767
  }
15880
17768
  }
@@ -16503,7 +18391,7 @@ function findAllRepeatableElementsBySourceId(pages, baseNodeId, oneBasedIndex, s
16503
18391
  return out;
16504
18392
  }
16505
18393
  function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi, childBaseNodeId, childCi, sourceElementId) {
16506
- var _a2, _b;
18394
+ var _a2, _b2;
16507
18395
  const parentGroups = collectGroupsByBaseId(pages, parentBaseNodeId);
16508
18396
  const parentGroup = parentGroups[parentPi - 1];
16509
18397
  if (!parentGroup || !isGroup(parentGroup) || !((_a2 = parentGroup.children) == null ? void 0 : _a2.length)) return void 0;
@@ -16518,7 +18406,7 @@ function findNestedRepeatableElementBySourceId(pages, parentBaseNodeId, parentPi
16518
18406
  }
16519
18407
  collectChild(parentGroup.children);
16520
18408
  const childGroup = childGroups[childCi - 1];
16521
- if (!childGroup || !isGroup(childGroup) || !((_b = childGroup.children) == null ? void 0 : _b.length)) return void 0;
18409
+ if (!childGroup || !isGroup(childGroup) || !((_b2 = childGroup.children) == null ? void 0 : _b2.length)) return void 0;
16522
18410
  return findElementBySourceIdInSubtree(childGroup.children, sourceElementId) ?? (childGroup.__sourceId === sourceElementId ? childGroup.id : void 0);
16523
18411
  }
16524
18412
  function repeatableLabelKey(label) {
@@ -16568,7 +18456,7 @@ function getRepeatableFromConfig(pages) {
16568
18456
  var _a2;
16569
18457
  const result = [];
16570
18458
  function walk(children) {
16571
- var _a3, _b;
18459
+ var _a3, _b2;
16572
18460
  if (!children) return;
16573
18461
  for (const node of children) {
16574
18462
  if (isGroup(node) && isVerticalStackLayoutMode(node.layoutMode) && ((_a3 = node.children) == null ? void 0 : _a3.length)) {
@@ -16577,7 +18465,7 @@ function getRepeatableFromConfig(pages) {
16577
18465
  if (rep == null ? void 0 : rep.label) result.push({ nodeId: child.id, label: rep.label });
16578
18466
  }
16579
18467
  }
16580
- if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) walk(node.children);
18468
+ if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) walk(node.children);
16581
18469
  }
16582
18470
  }
16583
18471
  for (const page of pages) if ((_a2 = page.children) == null ? void 0 : _a2.length) walk(page.children);
@@ -16593,7 +18481,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16593
18481
  return !!((_a3 = n.repeatableSection) == null ? void 0 : _a3.label);
16594
18482
  };
16595
18483
  function walk(children, parentRepeatableBaseId, parentInfo, parentRepeatableEntryIndex) {
16596
- var _a3, _b;
18484
+ var _a3, _b2;
16597
18485
  if (!Array.isArray(children)) return;
16598
18486
  for (let i = 0; i < children.length; i++) {
16599
18487
  const node = children[i];
@@ -16636,7 +18524,7 @@ function findRepeatableByNodeIds(pages, nodeIds) {
16636
18524
  if (isGroup(child) && Array.isArray(child.children)) walk(child.children, effectiveChildBase, void 0, childEntryIndex);
16637
18525
  j += count;
16638
18526
  }
16639
- } else if (isGroup(node) && ((_b = node.children) == null ? void 0 : _b.length)) {
18527
+ } else if (isGroup(node) && ((_b2 = node.children) == null ? void 0 : _b2.length)) {
16640
18528
  const nodeBase = baseId(node.id);
16641
18529
  const selfMatch = schemaBaseIds.has(node.id) || schemaBaseIds.has(nodeBase) || node.__baseNodeId != null && schemaBaseIds.has(node.__baseNodeId);
16642
18530
  const isSelfRepeatable = selfMatch && hasRepeatableSection(node);
@@ -16725,7 +18613,7 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
16725
18613
  return Math.max(1, maxIndex);
16726
18614
  }
16727
18615
  function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
16728
- var _a2, _b, _c;
18616
+ var _a2, _b2, _c;
16729
18617
  const cloned = JSON.parse(JSON.stringify(config));
16730
18618
  if (!cloned.pages) return cloned;
16731
18619
  const dynamicFields = cloned.dynamicFields;
@@ -16906,7 +18794,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
16906
18794
  const { node, baseNodeId, startIndex, count } = block;
16907
18795
  const occIdx = block.__occurrenceIndex ?? 1;
16908
18796
  const N = computeN(baseNodeId, node.id);
16909
- const entryFilter = ((_b = node.repeatableSection) == null ? void 0 : _b.entryFilter) ?? entryFilterFromList(baseNodeId);
18797
+ const entryFilter = ((_b2 = node.repeatableSection) == null ? void 0 : _b2.entryFilter) ?? entryFilterFromList(baseNodeId);
16910
18798
  let entryIndices;
16911
18799
  if (entryFilter && entryFilter.mode === "range" && entryFilter.range) {
16912
18800
  const parsed = parseEntryRange(entryFilter.range, N, entryMetaFromList(baseNodeId));
@@ -17363,7 +19251,7 @@ function findAllFlowStacks(pageChildren) {
17363
19251
  return result;
17364
19252
  }
17365
19253
  function findNestedFlowStack(entry) {
17366
- var _a2, _b;
19254
+ var _a2, _b2;
17367
19255
  const queue = [...entry.children ?? []];
17368
19256
  while (queue.length > 0) {
17369
19257
  const node = queue.shift();
@@ -17371,7 +19259,7 @@ function findNestedFlowStack(entry) {
17371
19259
  const g = node;
17372
19260
  const hasRepeatableChildren = ((_a2 = g.children) == null ? void 0 : _a2.length) && (g.children.some(hasBaseNodeId) || g.children.some((c) => isGroup(c)));
17373
19261
  if (isVerticalStackLayoutMode(g.layoutMode) && hasRepeatableChildren) return g;
17374
- if ((_b = g.children) == null ? void 0 : _b.length) queue.push(...g.children);
19262
+ if ((_b2 = g.children) == null ? void 0 : _b2.length) queue.push(...g.children);
17375
19263
  }
17376
19264
  return null;
17377
19265
  }
@@ -17502,9 +19390,9 @@ function splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overf
17502
19390
  return { stayChildren: newStay, overflowChildren: newOverflow };
17503
19391
  }
17504
19392
  function paginateSinglePage(sourcePage, pageOffsetIndex) {
17505
- var _a2, _b, _c, _d, _e, _f;
19393
+ var _a2, _b2, _c, _d, _e, _f;
17506
19394
  const contentTop = (_a2 = sourcePage.settings) == null ? void 0 : _a2.contentTop;
17507
- const contentBottom = (_b = sourcePage.settings) == null ? void 0 : _b.contentBottom;
19395
+ const contentBottom = (_b2 = sourcePage.settings) == null ? void 0 : _b2.contentBottom;
17508
19396
  if (contentTop == null || contentBottom == null || contentBottom <= contentTop) {
17509
19397
  return [sourcePage];
17510
19398
  }
@@ -18694,7 +20582,7 @@ function splitIntoRuns(text, mainSupportsChar) {
18694
20582
  return runs;
18695
20583
  }
18696
20584
  function rewriteSvgFontsForJsPDF(svgStr) {
18697
- var _a2, _b;
20585
+ var _a2, _b2;
18698
20586
  const parser = new DOMParser();
18699
20587
  const doc = parser.parseFromString(svgStr, "image/svg+xml");
18700
20588
  const allTextEls = Array.from(doc.querySelectorAll("text, tspan, textPath"));
@@ -18799,7 +20687,7 @@ function rewriteSvgFontsForJsPDF(svgStr) {
18799
20687
  for (const node of childNodes) {
18800
20688
  if (node.nodeType !== 3 || !node.textContent) continue;
18801
20689
  const runs = splitIntoRuns(node.textContent, mainSupportsChar);
18802
- if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) === "main") continue;
20690
+ if (runs.length <= 1 && ((_b2 = runs[0]) == null ? void 0 : _b2.runType) === "main") continue;
18803
20691
  const fragment = doc.createDocumentFragment();
18804
20692
  for (const run of runs) {
18805
20693
  const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
@@ -19109,9 +20997,9 @@ function appendDataUriFontFaceRule(family, weight, style, dataUri) {
19109
20997
  styleEl.appendChild(document.createTextNode(cssText));
19110
20998
  }
19111
20999
  function resolveHarnessFontProxyUrl() {
19112
- var _a2, _b;
21000
+ var _a2, _b2;
19113
21001
  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)) || "";
21002
+ 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
21003
  const base = String(runtimeBase || "").replace(/\/$/, "");
19116
21004
  return base ? `${base}/functions/v1/font-proxy` : "";
19117
21005
  } catch {
@@ -19773,7 +21661,7 @@ async function resolveTemplateData(options) {
19773
21661
  };
19774
21662
  }
19775
21663
  async function resolveFromForm(options) {
19776
- var _a2, _b, _c;
21664
+ var _a2, _b2, _c;
19777
21665
  const { templateId, formSchemaId, sectionState, flatFormData: directFlatFormData, themeId, supabaseUrl, supabaseAnonKey, prefetched } = options;
19778
21666
  const hasSectionStateInput = !!sectionState && Object.keys(sectionState).length > 0;
19779
21667
  if (!formSchemaId && !hasSectionStateInput) {
@@ -19823,7 +21711,7 @@ async function resolveFromForm(options) {
19823
21711
  inferredSections = inferFormSchemaFromTemplate(
19824
21712
  templateConfig.dynamicFields,
19825
21713
  groups,
19826
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
21714
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
19827
21715
  );
19828
21716
  } else {
19829
21717
  inferredSections = [];
@@ -20010,10 +21898,10 @@ function themeBaseId(id) {
20010
21898
  return out;
20011
21899
  }
20012
21900
  function applyThemeVariantToConfig(config, themeConfig, themeId) {
20013
- var _a2, _b, _c, _d;
21901
+ var _a2, _b2, _c, _d;
20014
21902
  if (!themeConfig) return config;
20015
21903
  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);
21904
+ const shouldApplyDefaults = !variant && ((_b2 = themeConfig.properties) == null ? void 0 : _b2.length);
20017
21905
  if (!variant && !shouldApplyDefaults) return config;
20018
21906
  if (!((_c = themeConfig.properties) == null ? void 0 : _c.length)) return config;
20019
21907
  const result = JSON.parse(JSON.stringify(config));
@@ -20034,8 +21922,8 @@ function applyThemeVariantToConfig(config, themeConfig, themeId) {
20034
21922
  if (stopMatch) {
20035
21923
  const stopIndex = parseInt(stopMatch[1], 10);
20036
21924
  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]) {
21925
+ var _a3, _b3;
21926
+ if ((_b3 = (_a3 = p.settings.backgroundGradient) == null ? void 0 : _a3.stops) == null ? void 0 : _b3[stopIndex]) {
20039
21927
  p.settings.backgroundGradient = {
20040
21928
  ...p.settings.backgroundGradient,
20041
21929
  stops: p.settings.backgroundGradient.stops.map(
@@ -20112,7 +22000,7 @@ function normalizeLayoutModes(config) {
20112
22000
  }
20113
22001
  }
20114
22002
  function paintRepeatableSections(config, repeatableSections, formSchema) {
20115
- var _a2, _b;
22003
+ var _a2, _b2;
20116
22004
  const pages = config.pages ?? [];
20117
22005
  const entryFilters = (formSchema == null ? void 0 : formSchema.entryFilters) ?? [];
20118
22006
  const entryFilterBases = new Set(entryFilters.map((f) => baseId(f.nodeId)));
@@ -20140,7 +22028,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
20140
22028
  return painted;
20141
22029
  }
20142
22030
  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;
22031
+ const inlineRange = ((_a2 = section.entryFilter) == null ? void 0 : _a2.mode) === "range" ? (_b2 = section.entryFilter.range) == null ? void 0 : _b2.trim() : void 0;
20144
22032
  const shouldUseInlineFilter = !!inlineRange && !entryFilterBases.has(baseId(section.nodeId));
20145
22033
  const payload = { label: section.label };
20146
22034
  if (section.minEntries !== void 0) payload.minEntries = section.minEntries;
@@ -20176,7 +22064,7 @@ function paintRepeatableSections(config, repeatableSections, formSchema) {
20176
22064
  }
20177
22065
  }
20178
22066
  async function getTemplateForm(options) {
20179
- var _a2, _b;
22067
+ var _a2, _b2;
20180
22068
  const { templateId, supabaseUrl, supabaseAnonKey } = options;
20181
22069
  if (!supabaseUrl || !supabaseAnonKey) {
20182
22070
  throw new Error("[getTemplateForm] supabaseUrl and supabaseAnonKey are required");
@@ -20220,7 +22108,7 @@ async function getTemplateForm(options) {
20220
22108
  sections = inferFormSchemaFromTemplate(
20221
22109
  templateConfig.dynamicFields,
20222
22110
  templateConfig.fieldGroups || [],
20223
- ((_b = templateConfig.pages) == null ? void 0 : _b.length) ? { pages: templateConfig.pages } : void 0
22111
+ ((_b2 = templateConfig.pages) == null ? void 0 : _b2.length) ? { pages: templateConfig.pages } : void 0
20224
22112
  );
20225
22113
  } else {
20226
22114
  sections = [];
@@ -20604,7 +22492,7 @@ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExact
20604
22492
  return out;
20605
22493
  }
20606
22494
  function PixldocsPreview(props) {
20607
- var _a2, _b;
22495
+ var _a2, _b2;
20608
22496
  const {
20609
22497
  pageIndex = 0,
20610
22498
  zoom = 1,
@@ -20672,10 +22560,10 @@ function PixldocsPreview(props) {
20672
22560
  supabaseUrl: p.supabaseUrl,
20673
22561
  supabaseAnonKey: p.supabaseAnonKey
20674
22562
  }).then((resolved) => {
20675
- var _a3, _b2;
22563
+ var _a3, _b3;
20676
22564
  if (!cancelled) {
20677
22565
  console.log(PREVIEW_DEBUG_PREFIX, "resolve-done", {
20678
- pages: ((_b2 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b2.length) ?? 0,
22566
+ pages: ((_b3 = (_a3 = resolved.config) == null ? void 0 : _a3.pages) == null ? void 0 : _b3.length) ?? 0,
20679
22567
  underlinedNodes: countUnderlinedNodes(resolved.config)
20680
22568
  });
20681
22569
  setResolvedConfig(resolved.config);
@@ -20785,7 +22673,7 @@ function PixldocsPreview(props) {
20785
22673
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
20786
22674
  const bgTint = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.background) ?? "rgba(255,255,255,0.12)";
20787
22675
  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);
22676
+ const canvasH = getNum((_b2 = config == null ? void 0 : config.canvas) == null ? void 0 : _b2.height, 0);
20789
22677
  const hasOverlays = frostedBounds.length > 0 && canvasW > 0 && canvasH > 0;
20790
22678
  if (isLoading) {
20791
22679
  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 +22814,7 @@ function ensureFabricGradientDef(doc, obj, width, height) {
20926
22814
  return `url(#${id})`;
20927
22815
  }
20928
22816
  function warpTextboxSvgAlongPath(svg, obj) {
20929
- var _a2, _b, _c, _d, _e;
22817
+ var _a2, _b2, _c, _d, _e;
20930
22818
  const tp = obj == null ? void 0 : obj.textPath;
20931
22819
  if (!tp || !tp.preset || tp.preset === "none") return svg;
20932
22820
  if (tp.preset === "rise" || tp.preset === "angle") {
@@ -20990,7 +22878,7 @@ function warpTextboxSvgAlongPath(svg, obj) {
20990
22878
  }
20991
22879
  for (const clone of Array.from(doc.querySelectorAll("g.__pdTextShadowClone"))) {
20992
22880
  try {
20993
- (_b = clone.parentNode) == null ? void 0 : _b.removeChild(clone);
22881
+ (_b2 = clone.parentNode) == null ? void 0 : _b2.removeChild(clone);
20994
22882
  } catch {
20995
22883
  }
20996
22884
  }
@@ -21010,9 +22898,9 @@ function warpTextboxSvgAlongPath(svg, obj) {
21010
22898
  const fw = obj.fontWeight ?? 400;
21011
22899
  const fst = obj.fontStyle || "normal";
21012
22900
  const readFill = (el) => {
21013
- var _a3, _b2;
22901
+ var _a3, _b3;
21014
22902
  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()) || "";
22903
+ 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
22904
  };
21017
22905
  const w = Number(obj.width) || 0;
21018
22906
  const h = Number(obj.height) || 0;
@@ -21365,9 +23253,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21365
23253
  }
21366
23254
  return svgString;
21367
23255
  }
21368
- const resolvedPackageVersion = "0.5.241";
23256
+ const resolvedPackageVersion = "0.5.243";
21369
23257
  const PACKAGE_VERSION = resolvedPackageVersion;
21370
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.241";
23258
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.243";
21371
23259
  const roundParityValue = (value) => {
21372
23260
  if (typeof value !== "number") return value;
21373
23261
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21544,11 +23432,11 @@ function installUnderlineFix(fab) {
21544
23432
  if (!TextProto || typeof TextProto._renderTextDecoration !== "function") return;
21545
23433
  const original = TextProto._renderTextDecoration;
21546
23434
  const measureLineTextWidth = (obj, ctx, lineIndex) => {
21547
- var _a3, _b, _c, _d, _e, _f;
23435
+ var _a3, _b2, _c, _d, _e, _f;
21548
23436
  const rawLine = (_a3 = obj._textLines) == null ? void 0 : _a3[lineIndex];
21549
23437
  const lineText = Array.isArray(rawLine) ? rawLine.join("") : String(rawLine ?? "");
21550
23438
  if (!lineText) return 0;
21551
- const fontSize = Number(((_b = obj.getValueOfPropertyAt) == null ? void 0 : _b.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
23439
+ const fontSize = Number(((_b2 = obj.getValueOfPropertyAt) == null ? void 0 : _b2.call(obj, lineIndex, 0, "fontSize")) ?? obj.fontSize ?? 0);
21552
23440
  const fontStyle = String(((_c = obj.getValueOfPropertyAt) == null ? void 0 : _c.call(obj, lineIndex, 0, "fontStyle")) ?? obj.fontStyle ?? "normal");
21553
23441
  const fontWeight = String(((_d = obj.getValueOfPropertyAt) == null ? void 0 : _d.call(obj, lineIndex, 0, "fontWeight")) ?? obj.fontWeight ?? "400");
21554
23442
  const fontFamily = String(((_e = obj.getValueOfPropertyAt) == null ? void 0 : _e.call(obj, lineIndex, 0, "fontFamily")) ?? obj.fontFamily ?? "sans-serif");
@@ -21688,11 +23576,78 @@ function installTextboxBoxExtensions(fab) {
21688
23576
  if (Array.isArray(stateProps2)) {
21689
23577
  if (!stateProps2.includes("minBoxHeight")) stateProps2.push("minBoxHeight");
21690
23578
  if (!stateProps2.includes("verticalAlign")) stateProps2.push("verticalAlign");
23579
+ if (!stateProps2.includes("smartWrap")) stateProps2.push("smartWrap");
21691
23580
  }
21692
23581
  const cacheProps2 = TextboxProto2.cacheProperties;
21693
23582
  if (Array.isArray(cacheProps2)) {
21694
23583
  if (!cacheProps2.includes("minBoxHeight")) cacheProps2.push("minBoxHeight");
21695
23584
  if (!cacheProps2.includes("verticalAlign")) cacheProps2.push("verticalAlign");
23585
+ if (!cacheProps2.includes("smartWrap")) cacheProps2.push("smartWrap");
23586
+ }
23587
+ TextboxProto2.smartWrap = true;
23588
+ if (typeof TextboxProto2._wrapLine === "function" && !TextboxProto2.__pixldocsOrigWrapLine) {
23589
+ const origWrapLine = TextboxProto2._wrapLine;
23590
+ TextboxProto2.__pixldocsOrigWrapLine = origWrapLine;
23591
+ TextboxProto2._wrapLine = function(lineIndex, desiredWidth, ref, reservedSpace = 0) {
23592
+ var _a3;
23593
+ if (!this.smartWrap || this.splitByGrapheme) {
23594
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23595
+ }
23596
+ try {
23597
+ const additionalSpace = this._getWidthOfCharSpacing();
23598
+ const data = ((_a3 = ref == null ? void 0 : ref.wordsData) == null ? void 0 : _a3[lineIndex]) || [];
23599
+ const effective = Math.max(1, desiredWidth - reservedSpace);
23600
+ const infix = " ";
23601
+ const infixWidth = this._measureWord([infix], lineIndex, 0);
23602
+ const graphemeLines = [];
23603
+ let line = [];
23604
+ let lineWidth = 0;
23605
+ let lineJustStarted = true;
23606
+ let largestWordWidth = 0;
23607
+ let offset = 0;
23608
+ const pushLine = () => {
23609
+ graphemeLines.push(line);
23610
+ line = [];
23611
+ lineWidth = 0;
23612
+ lineJustStarted = true;
23613
+ };
23614
+ for (let i = 0; i < data.length; i++) {
23615
+ const { word, width: wordWidth } = data[i];
23616
+ if (wordWidth <= effective) {
23617
+ if (wordWidth > largestWordWidth) largestWordWidth = wordWidth;
23618
+ const projected = lineJustStarted ? wordWidth : lineWidth + infixWidth + wordWidth - additionalSpace;
23619
+ if (!lineJustStarted && projected > effective) pushLine();
23620
+ if (!lineJustStarted) {
23621
+ line.push(infix);
23622
+ lineWidth += infixWidth;
23623
+ }
23624
+ line = line.concat(word);
23625
+ lineWidth += wordWidth;
23626
+ lineJustStarted = false;
23627
+ } else {
23628
+ if (!lineJustStarted) pushLine();
23629
+ for (const g of word) {
23630
+ const gw = this._measureWord([g], lineIndex, offset);
23631
+ if (gw > largestWordWidth) largestWordWidth = gw;
23632
+ const gProj = lineJustStarted ? gw : lineWidth + gw - additionalSpace;
23633
+ if (!lineJustStarted && gProj > effective) pushLine();
23634
+ line.push(g);
23635
+ lineWidth += gw;
23636
+ lineJustStarted = false;
23637
+ offset++;
23638
+ }
23639
+ offset -= word.length;
23640
+ }
23641
+ offset += word.length + 1;
23642
+ }
23643
+ if (line.length) graphemeLines.push(line);
23644
+ const minNeeded = Math.max(0, Math.min(largestWordWidth, effective) - additionalSpace + reservedSpace);
23645
+ if (minNeeded > this.dynamicMinWidth) this.dynamicMinWidth = minNeeded;
23646
+ return graphemeLines;
23647
+ } catch (e) {
23648
+ return origWrapLine.call(this, lineIndex, desiredWidth, ref, reservedSpace);
23649
+ }
23650
+ };
21696
23651
  }
21697
23652
  TextboxProto2.__pixldocsTextboxExtended = true;
21698
23653
  __textboxBoxExtensionsInstalled = true;
@@ -22114,7 +24069,7 @@ class PixldocsRenderer {
22114
24069
  await this.waitForCanvasScene(container, cloned, i);
22115
24070
  }
22116
24071
  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");
24072
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DJD1vq7N.js");
22118
24073
  const prepared = preparePagesForExport(
22119
24074
  cloned.pages,
22120
24075
  canvasWidth,
@@ -22331,9 +24286,9 @@ class PixldocsRenderer {
22331
24286
  }
22332
24287
  waitForCanvasScene(container, config, pageIndex, maxWaitMs = 8e3, pollMs = 50) {
22333
24288
  return new Promise((resolve) => {
22334
- var _a2, _b;
24289
+ var _a2, _b2;
22335
24290
  const start = Date.now();
22336
- const pageHasContent = (((_b = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b.length) ?? 0) > 0;
24291
+ const pageHasContent = (((_b2 = (_a2 = config.pages[pageIndex]) == null ? void 0 : _a2.children) == null ? void 0 : _b2.length) ?? 0) > 0;
22337
24292
  const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
22338
24293
  const check = () => {
22339
24294
  const fabricCanvas = this.getFabricCanvasFromContainer(container);
@@ -22408,9 +24363,9 @@ class PixldocsRenderer {
22408
24363
  return normalized;
22409
24364
  }
22410
24365
  paintPageBackground(ctx, page, width, height) {
22411
- var _a2, _b;
24366
+ var _a2, _b2;
22412
24367
  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;
24368
+ const gradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22414
24369
  ctx.clearRect(0, 0, width, height);
22415
24370
  ctx.fillStyle = backgroundColor;
22416
24371
  ctx.fillRect(0, 0, width, height);
@@ -22658,7 +24613,7 @@ class PixldocsRenderer {
22658
24613
  };
22659
24614
  const onReady = () => {
22660
24615
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
22661
- var _a2, _b;
24616
+ var _a2, _b2;
22662
24617
  try {
22663
24618
  const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
22664
24619
  await this.waitForCanvasImages(container, expectedImageIds);
@@ -22677,7 +24632,7 @@ class PixldocsRenderer {
22677
24632
  );
22678
24633
  const page = renderConfig.pages[pageIndex];
22679
24634
  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;
24635
+ const backgroundGradient = (_b2 = page == null ? void 0 : page.settings) == null ? void 0 : _b2.backgroundGradient;
22681
24636
  cleanup();
22682
24637
  resolve({
22683
24638
  svg: svgString,
@@ -22798,7 +24753,7 @@ class PixldocsRenderer {
22798
24753
  logJsonLine("[canvas-renderer][fabric-text-parity]", { stage, textboxes: sample.length, sample });
22799
24754
  }
22800
24755
  async waitForStableTextMetrics(container, config, options = {}) {
22801
- var _a2, _b, _c;
24756
+ var _a2, _b2, _c;
22802
24757
  if (typeof document !== "undefined") {
22803
24758
  void ensureFontsForResolvedConfig(config);
22804
24759
  await this.waitForRelevantFonts(config);
@@ -22840,7 +24795,7 @@ class PixldocsRenderer {
22840
24795
  }
22841
24796
  fabricInstance.getObjects().forEach(primeCharBounds);
22842
24797
  (_a2 = fabricInstance.calcOffset) == null ? void 0 : _a2.call(fabricInstance);
22843
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
24798
+ (_b2 = fabricInstance.renderAll) == null ? void 0 : _b2.call(fabricInstance);
22844
24799
  await waitForPaint();
22845
24800
  (_c = fabricInstance.renderAll) == null ? void 0 : _c.call(fabricInstance);
22846
24801
  await waitForPaint();
@@ -22881,7 +24836,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22881
24836
  };
22882
24837
  logParityJson(tag, stage, { kind: "summary", ...summary });
22883
24838
  const sample = texts.slice(0, maxItems).map((t, idx) => {
22884
- var _a2, _b;
24839
+ var _a2, _b2;
22885
24840
  const tspans = Array.from(t.querySelectorAll("tspan"));
22886
24841
  const tspanInfo = tspans.slice(0, 8).map((s) => ({
22887
24842
  x: s.getAttribute("x"),
@@ -22898,7 +24853,7 @@ function dumpSvgTextDiagnostics(svgStr, pageIndex, tag, stage, maxItems = 30) {
22898
24853
  while (cursor && !containerWidth) {
22899
24854
  containerWidth = (_a2 = cursor.getAttribute) == null ? void 0 : _a2.call(cursor, "width");
22900
24855
  cursor = cursor.parentElement;
22901
- if (cursor && ((_b = cursor.tagName) == null ? void 0 : _b.toLowerCase()) === "svg") break;
24856
+ if (cursor && ((_b2 = cursor.tagName) == null ? void 0 : _b2.toLowerCase()) === "svg") break;
22902
24857
  }
22903
24858
  return {
22904
24859
  idx,
@@ -23286,10 +25241,10 @@ function normalizeSvgExplicitColors(svg) {
23286
25241
  return normalizeGradientPaintRef(v);
23287
25242
  };
23288
25243
  const hasExplicitNonePaint = (el, attr) => {
23289
- var _a2, _b;
25244
+ var _a2, _b2;
23290
25245
  const raw = (el.getAttribute(attr) ?? ((_a2 = el.style) == null ? void 0 : _a2.getPropertyValue(attr)) ?? "").trim().toLowerCase();
23291
25246
  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();
25247
+ const rawOpacity = (el.getAttribute(`${attr}-opacity`) ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue(`${attr}-opacity`)) ?? "").trim().toLowerCase();
23293
25248
  return rawOpacity === "0" || rawOpacity === "0%" || rawOpacity === "0.0";
23294
25249
  };
23295
25250
  function walk(el, parentFill, parentStroke, parentColor) {
@@ -23356,14 +25311,14 @@ function normalizeSvgExplicitColors(svg) {
23356
25311
  function bakeGroupOpacityIntoChildren(svg) {
23357
25312
  const DRAWABLE = /* @__PURE__ */ new Set(["path", "rect", "circle", "ellipse", "polygon", "polyline", "line", "text", "tspan"]);
23358
25313
  function walkAndBake(el, inheritedOpacity) {
23359
- var _a2, _b, _c, _d;
25314
+ var _a2, _b2, _c, _d;
23360
25315
  if (isInSvgDefinitionSubtree(el)) {
23361
25316
  for (let i = 0; i < el.children.length; i++) walkAndBake(el.children[i], 1);
23362
25317
  return;
23363
25318
  }
23364
25319
  const tag = ((_a2 = el.tagName) == null ? void 0 : _a2.toLowerCase()) ?? "";
23365
25320
  const opacityAttr = parseSvgOpacity(el.getAttribute("opacity"));
23366
- const styleOpacity = parseSvgOpacity(((_b = el.style) == null ? void 0 : _b.getPropertyValue("opacity")) || null);
25321
+ const styleOpacity = parseSvgOpacity(((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("opacity")) || null);
23367
25322
  const ownOpacity = opacityAttr ?? styleOpacity ?? 1;
23368
25323
  const combinedOpacity = inheritedOpacity * ownOpacity;
23369
25324
  if (ownOpacity < 0.999 && tag !== "image") {
@@ -23886,10 +25841,10 @@ function getFirstExplicitColorFromSvg(svg) {
23886
25841
  return getGradientStopColorAsHex(svg, gradientId);
23887
25842
  };
23888
25843
  function walk(el) {
23889
- var _a2, _b;
25844
+ var _a2, _b2;
23890
25845
  if (fill && stroke) return;
23891
25846
  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"));
25847
+ const s = el.getAttribute("stroke") ?? ((_b2 = el.style) == null ? void 0 : _b2.getPropertyValue("stroke"));
23893
25848
  if (!fill) {
23894
25849
  if (isRealColor(f)) fill = f;
23895
25850
  else if (f) {
@@ -23917,8 +25872,8 @@ function isNearWhite(hex) {
23917
25872
  return r >= 250 && g >= 250 && b >= 250;
23918
25873
  }
23919
25874
  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");
25875
+ var _a2, _b2;
25876
+ 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
25877
  }
23923
25878
  function getGradientStopColorAsHex(svgRoot, gradientId, visited = /* @__PURE__ */ new Set()) {
23924
25879
  var _a2;
@@ -23982,12 +25937,12 @@ async function convertTextDecorationsToLines(svg) {
23982
25937
  else el.removeAttribute("style");
23983
25938
  };
23984
25939
  const resolveInheritedSvgValue = (el, attr, styleProp = attr) => {
23985
- var _a2, _b;
25940
+ var _a2, _b2;
23986
25941
  let current = el;
23987
25942
  while (current) {
23988
25943
  const attrValue = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
23989
25944
  if (attrValue) return attrValue;
23990
- const styleValue = (_b = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b.trim();
25945
+ const styleValue = (_b2 = getInlineStyleValue(current, styleProp)) == null ? void 0 : _b2.trim();
23991
25946
  if (styleValue) return styleValue;
23992
25947
  current = current.parentElement;
23993
25948
  }
@@ -24165,7 +26120,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
24165
26120
  }
24166
26121
  }
24167
26122
  async function rasterizeShadowMarkers(svg) {
24168
- var _a2, _b, _c, _d, _e, _f;
26123
+ var _a2, _b2, _c, _d, _e, _f;
24169
26124
  if (typeof window === "undefined" || typeof document === "undefined") return;
24170
26125
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
24171
26126
  if (markers.length === 0) return;
@@ -24189,7 +26144,7 @@ async function rasterizeShadowMarkers(svg) {
24189
26144
  const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
24190
26145
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
24191
26146
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
24192
- (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
26147
+ (_b2 = marker.parentNode) == null ? void 0 : _b2.removeChild(marker);
24193
26148
  continue;
24194
26149
  }
24195
26150
  const innerXml = restoreSourceFontsForShadowRaster(
@@ -24434,7 +26389,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24434
26389
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24435
26390
  sanitizeSvgTreeForPdf(svgToDraw);
24436
26391
  try {
24437
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-BBY4avH9.js");
26392
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DJD1vq7N.js");
24438
26393
  try {
24439
26394
  await logTextMeasurementDiagnostic(svgToDraw);
24440
26395
  } catch {
@@ -24450,7 +26405,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24450
26405
  }
24451
26406
  }
24452
26407
  function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundColor, backgroundGradient) {
24453
- var _a2, _b;
26408
+ var _a2, _b2;
24454
26409
  if (backgroundGradient && ((_a2 = backgroundGradient.stops) == null ? void 0 : _a2.length) >= 2) {
24455
26410
  const grad = backgroundGradient;
24456
26411
  const colorStops = grad.stops.map((s) => {
@@ -24505,7 +26460,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24505
26460
  doc.fill({ key: patternKey, matrix: doc.Matrix(1, 0, 0, 1, 0, 0) });
24506
26461
  });
24507
26462
  } catch {
24508
- const fallback = ((_b = colorStops[0]) == null ? void 0 : _b.color) || [255, 255, 255];
26463
+ const fallback = ((_b2 = colorStops[0]) == null ? void 0 : _b2.color) || [255, 255, 255];
24509
26464
  pdf.setFillColor(fallback[0], fallback[1], fallback[2]);
24510
26465
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
24511
26466
  }
@@ -24518,7 +26473,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
24518
26473
  }
24519
26474
  }
24520
26475
  async function assemblePdfFromSvgs(svgResults, options = {}) {
24521
- var _a2, _b;
26476
+ var _a2, _b2;
24522
26477
  if (svgResults.length === 0) throw new Error("No pages to export");
24523
26478
  const { title, stripPageBackground } = options;
24524
26479
  const firstPage = svgResults[0];
@@ -24551,7 +26506,7 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
24551
26506
  const pageOrientation = page.width > page.height ? "landscape" : "portrait";
24552
26507
  pdf.addPage([page.width, page.height], pageOrientation);
24553
26508
  }
24554
- const hasGradient = !!((_b = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b.length);
26509
+ const hasGradient = !!((_b2 = (_a2 = page.backgroundGradient) == null ? void 0 : _a2.stops) == null ? void 0 : _b2.length);
24555
26510
  drawPageBackground(pdf, i, page.width, page.height, page.backgroundColor, page.backgroundGradient);
24556
26511
  const shouldStripBg = stripPageBackground ?? hasGradient;
24557
26512
  const textMode = options.textMode ?? (options.outlineText === true ? "pixel-perfect" : "selectable");
@@ -24834,4 +26789,4 @@ export {
24834
26789
  buildTeaserBlurFlatKeys as y,
24835
26790
  collectFontDescriptorsFromConfig as z
24836
26791
  };
24837
- //# sourceMappingURL=index-DtyVze4W.js.map
26792
+ //# sourceMappingURL=index-C5mQ2kl5.js.map