@pixldocs/canvas-renderer 0.5.397 → 0.5.399

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.
@@ -125,7 +125,6 @@ const createDefaultGroup = (partial) => ({
125
125
  locked: false,
126
126
  left: 0,
127
127
  top: 0,
128
- angle: 0,
129
128
  ...partial
130
129
  });
131
130
  const PAGE_BACKGROUND_ELEMENT_ID = "__pageBackground__";
@@ -848,17 +847,9 @@ function getNodeBounds(node, pageChildren, options) {
848
847
  let width;
849
848
  let height;
850
849
  if (isGroup(node) && (pageChildren == null ? void 0 : pageChildren.length) !== void 0) {
851
- const group = node;
852
- const storedWidth = Number(group.width);
853
- const storedHeight = Number(group.height);
854
- if (!isStackLayoutMode(group.layoutMode) && storedWidth > 0 && storedHeight > 0) {
855
- width = storedWidth;
856
- height = storedHeight;
857
- } else {
858
- const size = groupBoundsFromChildren(group, pageChildren ?? []);
859
- width = size.width;
860
- height = size.height;
861
- }
850
+ const size = groupBoundsFromChildren(node, pageChildren ?? []);
851
+ width = size.width;
852
+ height = size.height;
862
853
  } else {
863
854
  width = simpleWidth(node);
864
855
  height = simpleHeight(node);
@@ -10529,119 +10520,6 @@ function bakeEdgeFade(source, fade) {
10529
10520
  }
10530
10521
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10531
10522
  const SELECTION_BORDER_SCALE = 2;
10532
- const normalizeAngle180 = (angle) => {
10533
- const n = (angle % 360 + 360) % 360;
10534
- return n > 180 ? n - 360 : n;
10535
- };
10536
- const isValidLogicalGroupFrame = (frame) => !!frame && Number.isFinite(frame.left) && Number.isFinite(frame.top) && Number.isFinite(frame.width) && frame.width > 0 && Number.isFinite(frame.height) && frame.height > 0;
10537
- const frameFromStoredGroupNode = (group, pageChildren, options) => {
10538
- const width = Number(group.width);
10539
- const height = Number(group.height);
10540
- if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) return null;
10541
- try {
10542
- const abs = getAbsoluteBounds(group, pageChildren, options);
10543
- return { left: abs.left, top: abs.top, width, height };
10544
- } catch {
10545
- return null;
10546
- }
10547
- };
10548
- const orientedFrameFromWorldPoints = (points, angle) => {
10549
- if (points.length === 0 || !Number.isFinite(angle)) return null;
10550
- const rad = -angle * Math.PI / 180;
10551
- const cos = Math.cos(rad);
10552
- const sin = Math.sin(rad);
10553
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
10554
- for (const p of points) {
10555
- const xr = p.x * cos - p.y * sin;
10556
- const yr = p.x * sin + p.y * cos;
10557
- minX = Math.min(minX, xr);
10558
- minY = Math.min(minY, yr);
10559
- maxX = Math.max(maxX, xr);
10560
- maxY = Math.max(maxY, yr);
10561
- }
10562
- if (![minX, minY, maxX, maxY].every(Number.isFinite)) return null;
10563
- const width = Math.max(1, maxX - minX);
10564
- const height = Math.max(1, maxY - minY);
10565
- const centerXRot = minX + width / 2;
10566
- const centerYRot = minY + height / 2;
10567
- const back = angle * Math.PI / 180;
10568
- const c = Math.cos(back);
10569
- const s = Math.sin(back);
10570
- const centerX = centerXRot * c - centerYRot * s;
10571
- const centerY = centerXRot * s + centerYRot * c;
10572
- return { left: centerX - width / 2, top: centerY - height / 2, width, height };
10573
- };
10574
- const collectFabricObjectWorldPoints = (objects) => {
10575
- const points = [];
10576
- for (const obj of objects) {
10577
- try {
10578
- obj.setCoords();
10579
- } catch {
10580
- }
10581
- const coords = typeof obj.getCoords === "function" ? obj.getCoords() : null;
10582
- if (Array.isArray(coords) && coords.length) {
10583
- points.push(...coords.map((p) => ({ x: p.x, y: p.y })));
10584
- continue;
10585
- }
10586
- const aC = obj.aCoords;
10587
- if (!aC) continue;
10588
- for (const key of ["tl", "tr", "br", "bl"]) {
10589
- const p = aC[key];
10590
- if (p) points.push({ x: p.x, y: p.y });
10591
- }
10592
- }
10593
- return points;
10594
- };
10595
- const collectFabricObjectWorldCenters = (objects) => {
10596
- var _a2;
10597
- const centers = [];
10598
- for (const obj of objects) {
10599
- try {
10600
- obj.setCoords();
10601
- } catch {
10602
- }
10603
- const coords = typeof obj.getCoords === "function" ? obj.getCoords() : null;
10604
- if (Array.isArray(coords) && coords.length >= 4) {
10605
- const pts = coords.slice(0, 4).map((p) => ({ x: Number(p.x), y: Number(p.y) })).filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
10606
- if (pts.length) centers.push({ x: pts.reduce((sum, p) => sum + p.x, 0) / pts.length, y: pts.reduce((sum, p) => sum + p.y, 0) / pts.length });
10607
- continue;
10608
- }
10609
- try {
10610
- const center = (_a2 = obj.getCenterPoint) == null ? void 0 : _a2.call(obj);
10611
- if (center && Number.isFinite(center.x) && Number.isFinite(center.y)) centers.push({ x: center.x, y: center.y });
10612
- } catch {
10613
- }
10614
- }
10615
- return centers;
10616
- };
10617
- const logicalFrameContainsWorldCenters = (frame, angle, centers) => {
10618
- if (!isValidLogicalGroupFrame(frame) || centers.length === 0) return true;
10619
- const a = typeof angle === "number" && Number.isFinite(angle) ? normalizeAngle180(angle) : 0;
10620
- const cx = frame.left + frame.width / 2;
10621
- const cy = frame.top + frame.height / 2;
10622
- const rad = -a * Math.PI / 180;
10623
- const c = Math.cos(rad);
10624
- const s = Math.sin(rad);
10625
- const tolerance = Math.max(8, Math.min(frame.width, frame.height) * 0.08);
10626
- return centers.every((p) => {
10627
- const dx = p.x - cx;
10628
- const dy = p.y - cy;
10629
- const localX = cx + dx * c - dy * s;
10630
- const localY = cy + dx * s + dy * c;
10631
- return localX >= frame.left - tolerance && localX <= frame.left + frame.width + tolerance && localY >= frame.top - tolerance && localY <= frame.top + frame.height + tolerance;
10632
- });
10633
- };
10634
- const shouldRepairUnrotatedLogicalGroupFrame = (storedFrame, contentFrame, angle) => {
10635
- if (!isValidLogicalGroupFrame(storedFrame) || !isValidLogicalGroupFrame(contentFrame)) return false;
10636
- if (Math.abs(normalizeAngle180(typeof angle === "number" && Number.isFinite(angle) ? angle : 0)) > 0.5) return false;
10637
- const storedArea = storedFrame.width * storedFrame.height;
10638
- const contentArea = contentFrame.width * contentFrame.height;
10639
- const areaRatio = storedArea / Math.max(1, contentArea);
10640
- const dx = Math.abs(storedFrame.left + storedFrame.width / 2 - (contentFrame.left + contentFrame.width / 2));
10641
- const dy = Math.abs(storedFrame.top + storedFrame.height / 2 - (contentFrame.top + contentFrame.height / 2));
10642
- const driftTolerance = Math.max(12, Math.min(storedFrame.width, storedFrame.height) * 0.08);
10643
- return areaRatio > 1.35 || dx > driftTolerance || dy > driftTolerance;
10644
- };
10645
10523
  let ensureCanvaControlRenders = () => {
10646
10524
  };
10647
10525
  try {
@@ -11572,7 +11450,7 @@ const bakeTextboxScaleIntoTypography = (obj, sourceElement) => {
11572
11450
  };
11573
11451
  return updates;
11574
11452
  };
11575
- function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferredGroupFrame) {
11453
+ function applyWarpAwareSelectionBorders(selection) {
11576
11454
  var _a2;
11577
11455
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
11578
11456
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -11584,13 +11462,17 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11584
11462
  if (selection.__pixldocsAlignedAngle == null) {
11585
11463
  const kids = selection.getObjects();
11586
11464
  if (kids.length >= 1) {
11587
- const angleDelta = (a, b) => Math.abs(normalizeAngle180(a - b));
11465
+ const norm = (a) => {
11466
+ const n = (a % 360 + 360) % 360;
11467
+ return n > 180 ? n - 360 : n;
11468
+ };
11469
+ const angleDelta = (a, b) => Math.abs(norm(a - b));
11588
11470
  const selectionMatrix = selection.calcTransformMatrix();
11589
11471
  const worldMatrices = kids.map((k) => fabric__namespace.util.multiplyTransformMatrices(
11590
11472
  selectionMatrix,
11591
11473
  k.calcOwnMatrix()
11592
11474
  ));
11593
- const worldAngles = worldMatrices.map((m) => normalizeAngle180(fabric__namespace.util.qrDecompose(m).angle ?? 0));
11475
+ const worldAngles = worldMatrices.map((m) => norm(fabric__namespace.util.qrDecompose(m).angle ?? 0));
11594
11476
  const worldPoints = [];
11595
11477
  for (const k of kids) {
11596
11478
  try {
@@ -11635,40 +11517,15 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11635
11517
  for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
11636
11518
  buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
11637
11519
  const dominant = buckets[0];
11638
- let effectivePreferredGroupAngle = preferredGroupAngle;
11639
- let effectivePreferredGroupFrame = preferredGroupFrame;
11640
- const logicalGroupId = selection.__pixldocsGroupSelection;
11641
- if (logicalGroupId && (!(typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle)) || !isValidLogicalGroupFrame(effectivePreferredGroupFrame))) {
11642
- try {
11643
- const stateNow = useEditorStore.getState();
11644
- const pageNow = stateNow.canvas.pages.find((p) => p.id === stateNow.canvas.currentPageId);
11645
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], logicalGroupId) : null;
11646
- if (groupNode && isGroup(groupNode)) {
11647
- if (!(typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle)) && typeof groupNode.angle === "number") {
11648
- effectivePreferredGroupAngle = groupNode.angle;
11649
- }
11650
- if (!isValidLogicalGroupFrame(effectivePreferredGroupFrame)) {
11651
- effectivePreferredGroupFrame = frameFromStoredGroupNode(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []);
11652
- }
11653
- }
11654
- } catch {
11655
- }
11520
+ let targetAngle = null;
11521
+ const isLogicalGroupSelection = !!selection.__pixldocsGroupSelection;
11522
+ const frozenGroupAngle = selection.__pixldocsFrozenGroupAngle;
11523
+ if (isLogicalGroupSelection && typeof frozenGroupAngle === "number" && Math.abs(frozenGroupAngle) > 0.5) {
11524
+ targetAngle = frozenGroupAngle;
11656
11525
  }
11657
- const rawPreferredGroupAngle = typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle) ? effectivePreferredGroupAngle : typeof selection.__pixldocsGroupAngle === "number" && Number.isFinite(selection.__pixldocsGroupAngle) ? selection.__pixldocsGroupAngle : null;
11658
- const preferredAngle = rawPreferredGroupAngle != null ? normalizeAngle180(rawPreferredGroupAngle) : null;
11659
- let targetAngle = preferredAngle;
11660
- const isLogicalGroupSelection = !!logicalGroupId;
11661
11526
  if (targetAngle == null && dominant && Math.abs(dominant.angle) > 0.5 && (isLogicalGroupSelection || kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length)) {
11662
11527
  targetAngle = dominant.angle;
11663
11528
  }
11664
- if (isLogicalGroupSelection && isValidLogicalGroupFrame(effectivePreferredGroupFrame) && targetAngle != null) {
11665
- const contentFrame = orientedFrameFromWorldPoints(worldPoints, targetAngle);
11666
- if (shouldRepairUnrotatedLogicalGroupFrame(effectivePreferredGroupFrame, contentFrame, targetAngle)) {
11667
- effectivePreferredGroupFrame = contentFrame;
11668
- } else if (!logicalFrameContainsWorldCenters(effectivePreferredGroupFrame, targetAngle, collectFabricObjectWorldCenters(kids))) {
11669
- effectivePreferredGroupFrame = null;
11670
- }
11671
- }
11672
11529
  if (targetAngle != null) {
11673
11530
  const restoreKidsFromWorld = () => {
11674
11531
  const invSelection = fabric__namespace.util.invertTransform(
@@ -11707,30 +11564,11 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11707
11564
  targetAngle,
11708
11565
  worldAngles
11709
11566
  });
11710
- const fixedFrame = effectivePreferredGroupFrame && Number.isFinite(effectivePreferredGroupFrame.left) && Number.isFinite(effectivePreferredGroupFrame.top) && Number.isFinite(effectivePreferredGroupFrame.width) && effectivePreferredGroupFrame.width > 0 && Number.isFinite(effectivePreferredGroupFrame.height) && effectivePreferredGroupFrame.height > 0 ? effectivePreferredGroupFrame : null;
11711
11567
  selection.set({ angle: targetAngle, scaleX: 1, scaleY: 1, skewX: 0, skewY: 0 });
11712
- if (fixedFrame) {
11713
- selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11714
- selection.setPositionByOrigin(
11715
- new fabric__namespace.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11716
- "center",
11717
- "center"
11718
- );
11719
- }
11720
11568
  restoreKidsFromWorld();
11721
- if (!fixedFrame) {
11722
- try {
11723
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11724
- } catch {
11725
- }
11726
- }
11727
- if (fixedFrame) {
11728
- selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11729
- selection.setPositionByOrigin(
11730
- new fabric__namespace.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11731
- "center",
11732
- "center"
11733
- );
11569
+ try {
11570
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11571
+ } catch {
11734
11572
  }
11735
11573
  restoreKidsFromWorld();
11736
11574
  selection.setCoords();
@@ -11819,93 +11657,22 @@ const PageCanvas = react.forwardRef(
11819
11657
  const [ready, setReady] = react.useState(false);
11820
11658
  const [unlockRequestId, setUnlockRequestId] = react.useState(0);
11821
11659
  const applyLogicalGroupSelectionVisualState = react.useCallback((selection, groupId) => {
11822
- var _a2, _b2;
11660
+ var _a2;
11823
11661
  selection.__pixldocsGroupSelection = groupId;
11824
11662
  delete selection.__pixldocsLogicalGroupIds;
11825
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11826
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11827
- let groupAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : void 0;
11828
- const members = selection.getObjects();
11829
- if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11830
- const storedW = Number(groupNode.width);
11831
- const storedH = Number(groupNode.height);
11832
- if (storedW > 0 && storedH > 0) {
11833
- groupAngle = 0;
11834
- }
11835
- }
11836
- if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11837
- try {
11838
- const selectionMatrix = selection.calcTransformMatrix();
11839
- const worldAngles = members.map((kid) => normalizeAngle180(fabric__namespace.util.qrDecompose(
11840
- fabric__namespace.util.multiplyTransformMatrices(
11841
- selectionMatrix,
11842
- kid.calcOwnMatrix()
11843
- )
11844
- ).angle ?? 0));
11845
- const buckets = [];
11846
- for (const angle of worldAngles) {
11847
- const bucket = buckets.find((candidate) => Math.abs(normalizeAngle180(candidate.angle - angle)) <= 2);
11848
- if (bucket) bucket.count += 1;
11849
- else buckets.push({ angle, count: 1 });
11850
- }
11851
- buckets.sort((a, b) => b.count - a.count || Math.abs(b.angle) - Math.abs(a.angle));
11852
- const inferredAngle = (_a2 = buckets[0]) == null ? void 0 : _a2.angle;
11853
- if (typeof inferredAngle === "number" && Number.isFinite(inferredAngle) && Math.abs(inferredAngle) > 0.01) {
11854
- groupAngle = inferredAngle;
11855
- }
11856
- } catch {
11857
- }
11858
- }
11859
- let groupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []) : null;
11860
- let groupFrameWasInferred = false;
11861
- const memberWorldPoints = collectFabricObjectWorldPoints(members);
11862
- const memberWorldCenters = collectFabricObjectWorldCenters(members);
11863
- const contentFrame = typeof groupAngle === "number" && Number.isFinite(groupAngle) ? orientedFrameFromWorldPoints(memberWorldPoints, normalizeAngle180(groupAngle)) : null;
11864
- if (shouldRepairUnrotatedLogicalGroupFrame(groupFrame, contentFrame, groupAngle)) {
11865
- groupFrame = contentFrame;
11866
- groupFrameWasInferred = true;
11867
- } else if (groupFrame && !logicalFrameContainsWorldCenters(groupFrame, groupAngle, memberWorldCenters)) {
11868
- groupFrame = null;
11869
- }
11870
- if (!groupFrame && groupNode && isGroup(groupNode) && typeof groupAngle === "number" && Number.isFinite(groupAngle)) {
11871
- groupFrame = contentFrame ?? orientedFrameFromWorldPoints(memberWorldPoints, normalizeAngle180(groupAngle));
11872
- groupFrameWasInferred = isValidLogicalGroupFrame(groupFrame);
11873
- }
11874
- if (groupNode && isGroup(groupNode)) {
11875
- const updates = {};
11876
- if (typeof groupAngle === "number" && Number.isFinite(groupAngle)) {
11877
- const prevAngle = typeof groupNode.angle === "number" ? groupNode.angle : void 0;
11878
- if (prevAngle === void 0 || Math.abs(normalizeAngle180(prevAngle - groupAngle)) > 0.01) {
11879
- updates.angle = normalizeAngle180(groupAngle);
11880
- }
11881
- }
11882
- if (isValidLogicalGroupFrame(groupFrame)) {
11883
- const prevW = Number(groupNode.width);
11884
- const prevH = Number(groupNode.height);
11885
- if (!Number.isFinite(prevW) || Math.abs(prevW - groupFrame.width) > 0.5) updates.width = groupFrame.width;
11886
- if (!Number.isFinite(prevH) || Math.abs(prevH - groupFrame.height) > 0.5) updates.height = groupFrame.height;
11887
- if (groupFrameWasInferred) {
11888
- const storePos = absoluteToStorePosition(groupFrame.left, groupFrame.top, groupId, (pageNow == null ? void 0 : pageNow.children) ?? []);
11889
- const prevLeft = Number(groupNode.left ?? 0);
11890
- const prevTop = Number(groupNode.top ?? 0);
11891
- if (!Number.isFinite(prevLeft) || Math.abs(prevLeft - storePos.left) > 0.5) updates.left = storePos.left;
11892
- if (!Number.isFinite(prevTop) || Math.abs(prevTop - storePos.top) > 0.5) updates.top = storePos.top;
11893
- }
11894
- }
11895
- if (Object.keys(updates).length > 0) {
11896
- useEditorStore.getState().updateNode(groupId, updates, { recordHistory: false, skipLayoutRecalc: true });
11897
- }
11898
- }
11899
- if (groupAngle !== void 0) {
11900
- selection.__pixldocsGroupAngle = groupAngle;
11901
- const aligned = selection.__pixldocsAlignedAngle;
11902
- if (typeof aligned === "number" && Math.abs(normalizeAngle180(aligned - groupAngle)) > 0.01) {
11903
- delete selection.__pixldocsAlignedAngle;
11663
+ selection.hasBorders = true;
11664
+ try {
11665
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11666
+ const node = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11667
+ const ang = node && isGroup(node) ? node.angle ?? 0 : 0;
11668
+ if (Math.abs(ang) > 0.01) {
11669
+ selection.__pixldocsFrozenGroupAngle = ang;
11670
+ } else {
11671
+ delete selection.__pixldocsFrozenGroupAngle;
11904
11672
  }
11905
- } else {
11906
- delete selection.__pixldocsGroupAngle;
11673
+ } catch {
11907
11674
  }
11908
- selection.hasBorders = true;
11675
+ const members = selection.getObjects();
11909
11676
  for (const prev of suppressGroupMemberBordersRef.current) {
11910
11677
  if (members.includes(prev)) continue;
11911
11678
  const origBorders = prev.__pixldocsOrigHasBorders;
@@ -11928,7 +11695,7 @@ const PageCanvas = react.forwardRef(
11928
11695
  if (m.__pixldocsOrigHasControls === void 0) m.__pixldocsOrigHasControls = m.hasControls;
11929
11696
  m.hasBorders = false;
11930
11697
  m.hasControls = false;
11931
- if (m.__cropGroup || ((_b2 = m._ct) == null ? void 0 : _b2.isCropGroup)) {
11698
+ if (m.__cropGroup || ((_a2 = m._ct) == null ? void 0 : _a2.isCropGroup)) {
11932
11699
  if (m.__pixldocsOrigLockScalingX === void 0) {
11933
11700
  m.__pixldocsOrigLockScalingX = m.lockScalingX;
11934
11701
  m.__pixldocsOrigLockScalingY = m.lockScalingY;
@@ -11937,8 +11704,8 @@ const PageCanvas = react.forwardRef(
11937
11704
  m.lockScalingY = false;
11938
11705
  }
11939
11706
  }
11940
- applyWarpAwareSelectionBorders(selection, groupAngle, groupFrame);
11941
- }, [pageId]);
11707
+ applyWarpAwareSelectionBorders(selection);
11708
+ }, []);
11942
11709
  const pageBoundsOptions = react.useMemo(
11943
11710
  () => ({ pageContentWidth: canvasWidth, pageContentHeight: canvasHeight }),
11944
11711
  [canvasWidth, canvasHeight]
@@ -12040,11 +11807,7 @@ const PageCanvas = react.forwardRef(
12040
11807
  const activeMembers = activeBeforeRestore.getObjects();
12041
11808
  const sameMembers = activeMembers.length === members.length && members.every((member) => activeMembers.includes(member));
12042
11809
  const sameGroup = activeBeforeRestore.__pixldocsGroupSelection === groupId;
12043
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12044
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
12045
- const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
12046
- const alignedAngle = activeBeforeRestore.__pixldocsAlignedAngle;
12047
- const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
11810
+ const alreadyAligned = activeBeforeRestore.__pixldocsAlignedAngle != null;
12048
11811
  if (sameMembers && sameGroup && alreadyAligned) {
12049
11812
  ensureCanvaControlRenders(activeBeforeRestore);
12050
11813
  return;
@@ -12061,11 +11824,9 @@ const PageCanvas = react.forwardRef(
12061
11824
  members.forEach((m) => m.setCoords());
12062
11825
  } catch {
12063
11826
  }
12064
- if (!selection.__pixldocsGroupSelection) {
12065
- try {
12066
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
12067
- } catch {
12068
- }
11827
+ try {
11828
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11829
+ } catch {
12069
11830
  }
12070
11831
  selection.setCoords();
12071
11832
  fc.requestRenderAll();
@@ -13505,18 +13266,14 @@ const PageCanvas = react.forwardRef(
13505
13266
  const groupNode = findNodeById(pageChildren2, groupId);
13506
13267
  if (!groupNode) return;
13507
13268
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
13508
- const storedGroupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, pageChildren2, pageBoundsOptions) : null;
13509
- const groupFrame = storedGroupFrame ?? groupAbs;
13510
13269
  const rect = active.getBoundingRect();
13511
13270
  groupSelectionTransformStartRef.current = {
13512
13271
  groupId,
13513
13272
  selection: active,
13514
13273
  selectionLeft: rect.left,
13515
13274
  selectionTop: rect.top,
13516
- groupLeft: groupFrame.left,
13517
- groupTop: groupFrame.top,
13518
- groupWidth: groupFrame.width,
13519
- groupHeight: groupFrame.height,
13275
+ groupLeft: groupAbs.left,
13276
+ groupTop: groupAbs.top,
13520
13277
  selectionAngle: ((active.angle ?? 0) % 360 + 360) % 360
13521
13278
  };
13522
13279
  logRotDriftSelectionSnapshot("transform-start", active, {
@@ -13684,11 +13441,7 @@ const PageCanvas = react.forwardRef(
13684
13441
  const activeIds = active.getObjects().map((obj) => getObjectId(obj)).filter((id) => !!id && id !== "__background__");
13685
13442
  const sameMembers = activeIds.length === snapshot.memberIds.length && snapshot.memberIds.every((id) => activeIds.includes(id));
13686
13443
  const sameGroup = active.__pixldocsGroupSelection === snapshot.groupSelectionId;
13687
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13688
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], snapshot.groupSelectionId) : null;
13689
- const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
13690
- const alignedAngle = active.__pixldocsAlignedAngle;
13691
- const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
13444
+ const alreadyAligned = active.__pixldocsAlignedAngle != null;
13692
13445
  if (sameMembers && sameGroup && alreadyAligned) {
13693
13446
  ensureCanvaControlRenders(active);
13694
13447
  return;
@@ -13844,32 +13597,15 @@ const PageCanvas = react.forwardRef(
13844
13597
  if (oid && memberIds.has(oid)) members.push(o);
13845
13598
  }
13846
13599
  if (members.length === 0) return null;
13847
- const savedGroupAngle = typeof g.angle === "number" && Number.isFinite(g.angle) ? normalizeAngle180(g.angle) : null;
13848
- const savedGroupWidth = Number(g.width);
13849
- const savedGroupHeight = Number(g.height);
13850
- if (savedGroupAngle != null && savedGroupWidth > 0 && savedGroupHeight > 0) {
13851
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13852
- const pageChildrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
13853
- const abs = getAbsoluteBounds(g, pageChildrenNow);
13854
- const cx = abs.left + savedGroupWidth / 2;
13855
- const cy = abs.top + savedGroupHeight / 2;
13856
- const rad2 = savedGroupAngle * Math.PI / 180;
13857
- const c = Math.cos(rad2), s = Math.sin(rad2);
13858
- const rotate = (x, y) => {
13859
- const dx = x - cx;
13860
- const dy = y - cy;
13861
- return { x: cx + dx * c - dy * s, y: cy + dx * s + dy * c };
13862
- };
13863
- return {
13864
- corners: [
13865
- rotate(abs.left, abs.top),
13866
- rotate(abs.left + savedGroupWidth, abs.top),
13867
- rotate(abs.left + savedGroupWidth, abs.top + savedGroupHeight),
13868
- rotate(abs.left, abs.top + savedGroupHeight)
13869
- ],
13870
- angle: savedGroupAngle
13871
- };
13872
- }
13600
+ const TOL = 2;
13601
+ const norm = (a) => {
13602
+ const n = (a % 360 + 360) % 360;
13603
+ return n > 180 ? n - 360 : n;
13604
+ };
13605
+ const angleDelta = (a, b) => {
13606
+ const d = Math.abs(norm(a) - norm(b));
13607
+ return Math.min(d, 360 - d);
13608
+ };
13873
13609
  const worldPoints = [];
13874
13610
  for (const m of members) {
13875
13611
  (_a2 = m.setCoords) == null ? void 0 : _a2.call(m);
@@ -13880,35 +13616,37 @@ const PageCanvas = react.forwardRef(
13880
13616
  if (p) worldPoints.push({ x: p.x, y: p.y });
13881
13617
  }
13882
13618
  }
13883
- const getLegacyDominantAngle = () => {
13884
- var _a3;
13885
- const areaFor = (angle) => {
13886
- if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13887
- const r = -angle * Math.PI / 180;
13888
- const c = Math.cos(r), s = Math.sin(r);
13889
- let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13890
- for (const p of worldPoints) {
13891
- const xr = p.x * c - p.y * s;
13892
- const yr = p.x * s + p.y * c;
13893
- if (xr < nX) nX = xr;
13894
- if (yr < nY) nY = yr;
13895
- if (xr > xX) xX = xr;
13896
- if (yr > xY) xY = yr;
13897
- }
13898
- return Math.max(1, (xX - nX) * (xY - nY));
13899
- };
13619
+ const orientedAreaForAngle = (angle) => {
13620
+ if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13621
+ const r = -angle * Math.PI / 180;
13622
+ const c = Math.cos(r), s = Math.sin(r);
13623
+ let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13624
+ for (const p of worldPoints) {
13625
+ const xr = p.x * c - p.y * s;
13626
+ const yr = p.x * s + p.y * c;
13627
+ if (xr < nX) nX = xr;
13628
+ if (yr < nY) nY = yr;
13629
+ if (xr > xX) xX = xr;
13630
+ if (yr > xY) xY = yr;
13631
+ }
13632
+ return Math.max(1, (xX - nX) * (xY - nY));
13633
+ };
13634
+ let a0;
13635
+ const savedGroupAngle = norm(g.angle ?? 0);
13636
+ if (Math.abs(savedGroupAngle) > 0.01) {
13637
+ a0 = savedGroupAngle;
13638
+ } else {
13900
13639
  const buckets = [];
13901
13640
  for (const m of members) {
13902
- const a = normalizeAngle180(m.angle ?? 0);
13903
- const b = buckets.find((x) => Math.abs(normalizeAngle180(x.angle - a)) <= 2);
13641
+ const a = norm(m.angle ?? 0);
13642
+ const b = buckets.find((x) => angleDelta(x.angle, a) <= TOL);
13904
13643
  if (b) b.count++;
13905
13644
  else buckets.push({ angle: a, count: 1, area: Number.POSITIVE_INFINITY });
13906
13645
  }
13907
- for (const b of buckets) b.area = areaFor(b.angle);
13646
+ for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
13908
13647
  buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
13909
- return ((_a3 = buckets[0]) == null ? void 0 : _a3.angle) ?? 0;
13910
- };
13911
- const a0 = savedGroupAngle ?? getLegacyDominantAngle();
13648
+ a0 = buckets[0].angle;
13649
+ }
13912
13650
  const rad = -a0 * Math.PI / 180;
13913
13651
  const cos = Math.cos(rad), sin = Math.sin(rad);
13914
13652
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -15109,7 +14847,6 @@ const PageCanvas = react.forwardRef(
15109
14847
  fabricCanvas.on("object:rotating", (e) => {
15110
14848
  var _a2, _b2;
15111
14849
  markSimpleTransform(e);
15112
- prepareGroupSelectionTransformStart(e.target);
15113
14850
  didTransformRef.current = true;
15114
14851
  const tr = e.target;
15115
14852
  if (shouldLogRotDriftLiveTick(tr, "rotating")) {
@@ -15246,6 +14983,29 @@ const PageCanvas = react.forwardRef(
15246
14983
  } catch {
15247
14984
  }
15248
14985
  groupShiftReflowSnapshotRef.current = null;
14986
+ try {
14987
+ const t = e.target;
14988
+ if (t instanceof fabric__namespace.ActiveSelection) {
14989
+ const gid = t.__pixldocsGroupSelection;
14990
+ const delta = ((t.angle ?? 0) + 360) % 360;
14991
+ const deltaSigned = delta > 180 ? delta - 360 : delta;
14992
+ if (gid && Math.abs(deltaSigned) > 0.01) {
14993
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
14994
+ const node = pageNow ? findNodeById(pageNow.children ?? [], gid) : null;
14995
+ if (node && isGroup(node)) {
14996
+ const prev = node.angle ?? 0;
14997
+ const next = ((prev + deltaSigned) % 360 + 360) % 360;
14998
+ const nextSigned = next > 180 ? next - 360 : next;
14999
+ useEditorStore.getState().updateNode(
15000
+ gid,
15001
+ { angle: nextSigned },
15002
+ { recordHistory: false, skipLayoutRecalc: true }
15003
+ );
15004
+ }
15005
+ }
15006
+ }
15007
+ } catch {
15008
+ }
15249
15009
  lockEdits();
15250
15010
  const modifiedTarget = e.target;
15251
15011
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -15299,7 +15059,7 @@ const PageCanvas = react.forwardRef(
15299
15059
  const groupLeft = fabricCenterX - w / 2;
15300
15060
  const groupTop = fabricCenterY - h / 2;
15301
15061
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, movedGroupId, pageChildrenSec);
15302
- useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top, angle: fabricSectionGroup.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15062
+ useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15303
15063
  }
15304
15064
  commitHistory();
15305
15065
  unlockEditsSoon();
@@ -15442,7 +15202,7 @@ const PageCanvas = react.forwardRef(
15442
15202
  const groupLeft = centerX - w / 2;
15443
15203
  const groupTop = centerY - h / 2;
15444
15204
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildrenSec);
15445
- useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15205
+ useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15446
15206
  }
15447
15207
  const node = findNodeById(pageChildrenSec, groupId);
15448
15208
  if (isChildModified && node && !groupMoved) {
@@ -15471,7 +15231,7 @@ const PageCanvas = react.forwardRef(
15471
15231
  const groupLeft = active.originX === "center" ? centerX - w / 2 : centerX;
15472
15232
  const groupTop = active.originY === "center" ? centerY - h / 2 : centerY;
15473
15233
  const storePos = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildren3);
15474
- useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15234
+ useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top }, { recordHistory: false, skipLayoutRecalc: true });
15475
15235
  commitHistory();
15476
15236
  unlockEditsSoon();
15477
15237
  return;
@@ -15653,38 +15413,6 @@ const PageCanvas = react.forwardRef(
15653
15413
  360 - Math.abs(currentSelAngle - startSelAngle)
15654
15414
  );
15655
15415
  const hadRotation = isActiveSelection && activeObj && angleDelta > 0.01;
15656
- if ((hadScale || hadRotation) && activeObj instanceof fabric__namespace.ActiveSelection && activeGroupSelectionId === groupToMove.id) {
15657
- const groupTransformUpdates = {};
15658
- if (hadRotation) {
15659
- groupTransformUpdates.angle = normalizeAngle180(currentSelAngle);
15660
- activeObj.__pixldocsGroupAngle = groupTransformUpdates.angle;
15661
- }
15662
- if (hadScale && (transformStart == null ? void 0 : transformStart.groupId) === groupToMove.id) {
15663
- const center = activeObj.getCenterPoint();
15664
- const nextWidth = Math.max(1, transformStart.groupWidth * Math.abs(activeObj.scaleX ?? 1));
15665
- const nextHeight = Math.max(1, transformStart.groupHeight * Math.abs(activeObj.scaleY ?? 1));
15666
- const storePos = absoluteToStorePosition(
15667
- center.x - nextWidth / 2,
15668
- center.y - nextHeight / 2,
15669
- groupToMove.id,
15670
- pageChildren2
15671
- );
15672
- groupTransformUpdates.left = storePos.left;
15673
- groupTransformUpdates.top = storePos.top;
15674
- groupTransformUpdates.width = nextWidth;
15675
- groupTransformUpdates.height = nextHeight;
15676
- } else if (!Number.isFinite(Number(groupToMove.width)) || !Number.isFinite(Number(groupToMove.height))) {
15677
- groupTransformUpdates.width = (transformStart == null ? void 0 : transformStart.groupWidth) ?? groupAbs.width;
15678
- groupTransformUpdates.height = (transformStart == null ? void 0 : transformStart.groupHeight) ?? groupAbs.height;
15679
- }
15680
- if (Object.keys(groupTransformUpdates).length > 0) {
15681
- useEditorStore.getState().updateNode(
15682
- groupToMove.id,
15683
- groupTransformUpdates,
15684
- { recordHistory: false, skipLayoutRecalc: true }
15685
- );
15686
- }
15687
- }
15688
15416
  if (!hadScale && !hadRotation && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1)) {
15689
15417
  const { updateNode: updateNodeStore, commitHistory: commitHistoryStore, getCurrentElements } = useEditorStore.getState();
15690
15418
  const newLeft = (groupToMove.left ?? 0) + deltaX;
@@ -16320,11 +16048,9 @@ const PageCanvas = react.forwardRef(
16320
16048
  } catch {
16321
16049
  }
16322
16050
  fabricCanvas.setActiveObject(newSel);
16323
- if (!wasGroupSel) {
16324
- try {
16325
- (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16326
- } catch {
16327
- }
16051
+ try {
16052
+ (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16053
+ } catch {
16328
16054
  }
16329
16055
  try {
16330
16056
  for (const member of membersToReselect) member.setCoords();
@@ -16701,12 +16427,7 @@ const PageCanvas = react.forwardRef(
16701
16427
  const prevAS = activeBeforeSync instanceof fabric__namespace.ActiveSelection ? activeBeforeSync : null;
16702
16428
  const prevMembers = prevAS ? prevAS.getObjects() : [];
16703
16429
  const sameMembers = !!prevAS && prevMembers.length === freshMembers.length && prevMembers.every((m) => freshMembers.includes(m));
16704
- const savedSnapshotAngle = activeSelectionSnapshot.groupSelectionId ? (() => {
16705
- const groupNode = findNodeById(pageTree, activeSelectionSnapshot.groupSelectionId);
16706
- return groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
16707
- })() : null;
16708
- const prevAlignedAngle = sameMembers ? prevAS.__pixldocsAlignedAngle : null;
16709
- const alreadyAligned = sameMembers && typeof prevAlignedAngle === "number" && (savedSnapshotAngle == null || Math.abs(normalizeAngle180(prevAlignedAngle - savedSnapshotAngle)) <= 0.01);
16430
+ const alreadyAligned = sameMembers && prevAS.__pixldocsAlignedAngle != null;
16710
16431
  if (sameMembers && alreadyAligned) {
16711
16432
  try {
16712
16433
  ensureCanvaControlRenders(prevAS);
@@ -17748,10 +17469,11 @@ const PageCanvas = react.forwardRef(
17748
17469
  if (sameSelection && (isFlatGroupSelection || isPureSingleGroupSelection)) {
17749
17470
  if (selectedGroupSelectionId && active instanceof fabric__namespace.ActiveSelection) {
17750
17471
  if (isPureSingleGroupSelection) {
17751
- applyLogicalGroupSelectionVisualState(active, selectedGroupSelectionId);
17472
+ active.__pixldocsGroupSelection = selectedGroupSelectionId;
17473
+ delete active.__pixldocsLogicalGroupIds;
17474
+ suppressGroupMemberBordersRef.current = active.getObjects();
17752
17475
  } else {
17753
17476
  delete active.__pixldocsGroupSelection;
17754
- delete active.__pixldocsGroupAngle;
17755
17477
  active.__pixldocsLogicalGroupIds = selectedGroupIds;
17756
17478
  active.hasBorders = true;
17757
17479
  suppressGroupMemberBordersRef.current = active.getObjects().filter((m) => {
@@ -17791,10 +17513,10 @@ const PageCanvas = react.forwardRef(
17791
17513
  const selection = new fabric__namespace.ActiveSelection(toSelect, { canvas: fc });
17792
17514
  if (selectedGroupSelectionId) {
17793
17515
  if (isPureSingleGroupSelection) {
17794
- applyLogicalGroupSelectionVisualState(selection, selectedGroupSelectionId);
17516
+ selection.__pixldocsGroupSelection = selectedGroupSelectionId;
17517
+ suppressGroupMemberBordersRef.current = toSelect;
17795
17518
  } else {
17796
17519
  selection.__pixldocsLogicalGroupIds = selectedGroupIds;
17797
- delete selection.__pixldocsGroupAngle;
17798
17520
  selection.hasBorders = true;
17799
17521
  suppressGroupMemberBordersRef.current = toSelect.filter((m) => {
17800
17522
  const id = getObjectId(m);
@@ -25437,9 +25159,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
25437
25159
  }
25438
25160
  return svgString;
25439
25161
  }
25440
- const resolvedPackageVersion = "0.5.397";
25162
+ const resolvedPackageVersion = "0.5.399";
25441
25163
  const PACKAGE_VERSION = resolvedPackageVersion;
25442
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.397";
25164
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.399";
25443
25165
  const roundParityValue = (value) => {
25444
25166
  if (typeof value !== "number") return value;
25445
25167
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26253,7 +25975,7 @@ class PixldocsRenderer {
26253
25975
  await this.waitForCanvasScene(container, cloned, i);
26254
25976
  }
26255
25977
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26256
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-o6mqay90.cjs"));
25978
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Bvm0vPj2.cjs"));
26257
25979
  const prepared = preparePagesForExport(
26258
25980
  cloned.pages,
26259
25981
  canvasWidth,
@@ -28573,7 +28295,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
28573
28295
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
28574
28296
  sanitizeSvgTreeForPdf(svgToDraw);
28575
28297
  try {
28576
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-o6mqay90.cjs"));
28298
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Bvm0vPj2.cjs"));
28577
28299
  try {
28578
28300
  await logTextMeasurementDiagnostic(svgToDraw);
28579
28301
  } catch {
@@ -28970,4 +28692,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
28970
28692
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
28971
28693
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
28972
28694
  exports.warmTemplateFromForm = warmTemplateFromForm;
28973
- //# sourceMappingURL=index-BgYo0VtS.cjs.map
28695
+ //# sourceMappingURL=index-BJ-DTm0r.cjs.map