@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.
@@ -107,7 +107,6 @@ const createDefaultGroup = (partial) => ({
107
107
  locked: false,
108
108
  left: 0,
109
109
  top: 0,
110
- angle: 0,
111
110
  ...partial
112
111
  });
113
112
  const PAGE_BACKGROUND_ELEMENT_ID = "__pageBackground__";
@@ -830,17 +829,9 @@ function getNodeBounds(node, pageChildren, options) {
830
829
  let width;
831
830
  let height;
832
831
  if (isGroup(node) && (pageChildren == null ? void 0 : pageChildren.length) !== void 0) {
833
- const group = node;
834
- const storedWidth = Number(group.width);
835
- const storedHeight = Number(group.height);
836
- if (!isStackLayoutMode(group.layoutMode) && storedWidth > 0 && storedHeight > 0) {
837
- width = storedWidth;
838
- height = storedHeight;
839
- } else {
840
- const size = groupBoundsFromChildren(group, pageChildren ?? []);
841
- width = size.width;
842
- height = size.height;
843
- }
832
+ const size = groupBoundsFromChildren(node, pageChildren ?? []);
833
+ width = size.width;
834
+ height = size.height;
844
835
  } else {
845
836
  width = simpleWidth(node);
846
837
  height = simpleHeight(node);
@@ -10511,119 +10502,6 @@ function bakeEdgeFade(source, fade) {
10511
10502
  }
10512
10503
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10513
10504
  const SELECTION_BORDER_SCALE = 2;
10514
- const normalizeAngle180 = (angle) => {
10515
- const n = (angle % 360 + 360) % 360;
10516
- return n > 180 ? n - 360 : n;
10517
- };
10518
- 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;
10519
- const frameFromStoredGroupNode = (group, pageChildren, options) => {
10520
- const width = Number(group.width);
10521
- const height = Number(group.height);
10522
- if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) return null;
10523
- try {
10524
- const abs = getAbsoluteBounds(group, pageChildren, options);
10525
- return { left: abs.left, top: abs.top, width, height };
10526
- } catch {
10527
- return null;
10528
- }
10529
- };
10530
- const orientedFrameFromWorldPoints = (points, angle) => {
10531
- if (points.length === 0 || !Number.isFinite(angle)) return null;
10532
- const rad = -angle * Math.PI / 180;
10533
- const cos = Math.cos(rad);
10534
- const sin = Math.sin(rad);
10535
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
10536
- for (const p of points) {
10537
- const xr = p.x * cos - p.y * sin;
10538
- const yr = p.x * sin + p.y * cos;
10539
- minX = Math.min(minX, xr);
10540
- minY = Math.min(minY, yr);
10541
- maxX = Math.max(maxX, xr);
10542
- maxY = Math.max(maxY, yr);
10543
- }
10544
- if (![minX, minY, maxX, maxY].every(Number.isFinite)) return null;
10545
- const width = Math.max(1, maxX - minX);
10546
- const height = Math.max(1, maxY - minY);
10547
- const centerXRot = minX + width / 2;
10548
- const centerYRot = minY + height / 2;
10549
- const back = angle * Math.PI / 180;
10550
- const c = Math.cos(back);
10551
- const s = Math.sin(back);
10552
- const centerX = centerXRot * c - centerYRot * s;
10553
- const centerY = centerXRot * s + centerYRot * c;
10554
- return { left: centerX - width / 2, top: centerY - height / 2, width, height };
10555
- };
10556
- const collectFabricObjectWorldPoints = (objects) => {
10557
- const points = [];
10558
- for (const obj of objects) {
10559
- try {
10560
- obj.setCoords();
10561
- } catch {
10562
- }
10563
- const coords = typeof obj.getCoords === "function" ? obj.getCoords() : null;
10564
- if (Array.isArray(coords) && coords.length) {
10565
- points.push(...coords.map((p) => ({ x: p.x, y: p.y })));
10566
- continue;
10567
- }
10568
- const aC = obj.aCoords;
10569
- if (!aC) continue;
10570
- for (const key of ["tl", "tr", "br", "bl"]) {
10571
- const p = aC[key];
10572
- if (p) points.push({ x: p.x, y: p.y });
10573
- }
10574
- }
10575
- return points;
10576
- };
10577
- const collectFabricObjectWorldCenters = (objects) => {
10578
- var _a2;
10579
- const centers = [];
10580
- for (const obj of objects) {
10581
- try {
10582
- obj.setCoords();
10583
- } catch {
10584
- }
10585
- const coords = typeof obj.getCoords === "function" ? obj.getCoords() : null;
10586
- if (Array.isArray(coords) && coords.length >= 4) {
10587
- 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));
10588
- 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 });
10589
- continue;
10590
- }
10591
- try {
10592
- const center = (_a2 = obj.getCenterPoint) == null ? void 0 : _a2.call(obj);
10593
- if (center && Number.isFinite(center.x) && Number.isFinite(center.y)) centers.push({ x: center.x, y: center.y });
10594
- } catch {
10595
- }
10596
- }
10597
- return centers;
10598
- };
10599
- const logicalFrameContainsWorldCenters = (frame, angle, centers) => {
10600
- if (!isValidLogicalGroupFrame(frame) || centers.length === 0) return true;
10601
- const a = typeof angle === "number" && Number.isFinite(angle) ? normalizeAngle180(angle) : 0;
10602
- const cx = frame.left + frame.width / 2;
10603
- const cy = frame.top + frame.height / 2;
10604
- const rad = -a * Math.PI / 180;
10605
- const c = Math.cos(rad);
10606
- const s = Math.sin(rad);
10607
- const tolerance = Math.max(8, Math.min(frame.width, frame.height) * 0.08);
10608
- return centers.every((p) => {
10609
- const dx = p.x - cx;
10610
- const dy = p.y - cy;
10611
- const localX = cx + dx * c - dy * s;
10612
- const localY = cy + dx * s + dy * c;
10613
- return localX >= frame.left - tolerance && localX <= frame.left + frame.width + tolerance && localY >= frame.top - tolerance && localY <= frame.top + frame.height + tolerance;
10614
- });
10615
- };
10616
- const shouldRepairUnrotatedLogicalGroupFrame = (storedFrame, contentFrame, angle) => {
10617
- if (!isValidLogicalGroupFrame(storedFrame) || !isValidLogicalGroupFrame(contentFrame)) return false;
10618
- if (Math.abs(normalizeAngle180(typeof angle === "number" && Number.isFinite(angle) ? angle : 0)) > 0.5) return false;
10619
- const storedArea = storedFrame.width * storedFrame.height;
10620
- const contentArea = contentFrame.width * contentFrame.height;
10621
- const areaRatio = storedArea / Math.max(1, contentArea);
10622
- const dx = Math.abs(storedFrame.left + storedFrame.width / 2 - (contentFrame.left + contentFrame.width / 2));
10623
- const dy = Math.abs(storedFrame.top + storedFrame.height / 2 - (contentFrame.top + contentFrame.height / 2));
10624
- const driftTolerance = Math.max(12, Math.min(storedFrame.width, storedFrame.height) * 0.08);
10625
- return areaRatio > 1.35 || dx > driftTolerance || dy > driftTolerance;
10626
- };
10627
10505
  let ensureCanvaControlRenders = () => {
10628
10506
  };
10629
10507
  try {
@@ -11554,7 +11432,7 @@ const bakeTextboxScaleIntoTypography = (obj, sourceElement) => {
11554
11432
  };
11555
11433
  return updates;
11556
11434
  };
11557
- function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferredGroupFrame) {
11435
+ function applyWarpAwareSelectionBorders(selection) {
11558
11436
  var _a2;
11559
11437
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
11560
11438
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -11566,13 +11444,17 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11566
11444
  if (selection.__pixldocsAlignedAngle == null) {
11567
11445
  const kids = selection.getObjects();
11568
11446
  if (kids.length >= 1) {
11569
- const angleDelta = (a, b) => Math.abs(normalizeAngle180(a - b));
11447
+ const norm = (a) => {
11448
+ const n = (a % 360 + 360) % 360;
11449
+ return n > 180 ? n - 360 : n;
11450
+ };
11451
+ const angleDelta = (a, b) => Math.abs(norm(a - b));
11570
11452
  const selectionMatrix = selection.calcTransformMatrix();
11571
11453
  const worldMatrices = kids.map((k) => fabric.util.multiplyTransformMatrices(
11572
11454
  selectionMatrix,
11573
11455
  k.calcOwnMatrix()
11574
11456
  ));
11575
- const worldAngles = worldMatrices.map((m) => normalizeAngle180(fabric.util.qrDecompose(m).angle ?? 0));
11457
+ const worldAngles = worldMatrices.map((m) => norm(fabric.util.qrDecompose(m).angle ?? 0));
11576
11458
  const worldPoints = [];
11577
11459
  for (const k of kids) {
11578
11460
  try {
@@ -11617,40 +11499,15 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11617
11499
  for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
11618
11500
  buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
11619
11501
  const dominant = buckets[0];
11620
- let effectivePreferredGroupAngle = preferredGroupAngle;
11621
- let effectivePreferredGroupFrame = preferredGroupFrame;
11622
- const logicalGroupId = selection.__pixldocsGroupSelection;
11623
- if (logicalGroupId && (!(typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle)) || !isValidLogicalGroupFrame(effectivePreferredGroupFrame))) {
11624
- try {
11625
- const stateNow = useEditorStore.getState();
11626
- const pageNow = stateNow.canvas.pages.find((p) => p.id === stateNow.canvas.currentPageId);
11627
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], logicalGroupId) : null;
11628
- if (groupNode && isGroup(groupNode)) {
11629
- if (!(typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle)) && typeof groupNode.angle === "number") {
11630
- effectivePreferredGroupAngle = groupNode.angle;
11631
- }
11632
- if (!isValidLogicalGroupFrame(effectivePreferredGroupFrame)) {
11633
- effectivePreferredGroupFrame = frameFromStoredGroupNode(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []);
11634
- }
11635
- }
11636
- } catch {
11637
- }
11502
+ let targetAngle = null;
11503
+ const isLogicalGroupSelection = !!selection.__pixldocsGroupSelection;
11504
+ const frozenGroupAngle = selection.__pixldocsFrozenGroupAngle;
11505
+ if (isLogicalGroupSelection && typeof frozenGroupAngle === "number" && Math.abs(frozenGroupAngle) > 0.5) {
11506
+ targetAngle = frozenGroupAngle;
11638
11507
  }
11639
- const rawPreferredGroupAngle = typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle) ? effectivePreferredGroupAngle : typeof selection.__pixldocsGroupAngle === "number" && Number.isFinite(selection.__pixldocsGroupAngle) ? selection.__pixldocsGroupAngle : null;
11640
- const preferredAngle = rawPreferredGroupAngle != null ? normalizeAngle180(rawPreferredGroupAngle) : null;
11641
- let targetAngle = preferredAngle;
11642
- const isLogicalGroupSelection = !!logicalGroupId;
11643
11508
  if (targetAngle == null && dominant && Math.abs(dominant.angle) > 0.5 && (isLogicalGroupSelection || kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length)) {
11644
11509
  targetAngle = dominant.angle;
11645
11510
  }
11646
- if (isLogicalGroupSelection && isValidLogicalGroupFrame(effectivePreferredGroupFrame) && targetAngle != null) {
11647
- const contentFrame = orientedFrameFromWorldPoints(worldPoints, targetAngle);
11648
- if (shouldRepairUnrotatedLogicalGroupFrame(effectivePreferredGroupFrame, contentFrame, targetAngle)) {
11649
- effectivePreferredGroupFrame = contentFrame;
11650
- } else if (!logicalFrameContainsWorldCenters(effectivePreferredGroupFrame, targetAngle, collectFabricObjectWorldCenters(kids))) {
11651
- effectivePreferredGroupFrame = null;
11652
- }
11653
- }
11654
11511
  if (targetAngle != null) {
11655
11512
  const restoreKidsFromWorld = () => {
11656
11513
  const invSelection = fabric.util.invertTransform(
@@ -11689,30 +11546,11 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11689
11546
  targetAngle,
11690
11547
  worldAngles
11691
11548
  });
11692
- 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;
11693
11549
  selection.set({ angle: targetAngle, scaleX: 1, scaleY: 1, skewX: 0, skewY: 0 });
11694
- if (fixedFrame) {
11695
- selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11696
- selection.setPositionByOrigin(
11697
- new fabric.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11698
- "center",
11699
- "center"
11700
- );
11701
- }
11702
11550
  restoreKidsFromWorld();
11703
- if (!fixedFrame) {
11704
- try {
11705
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11706
- } catch {
11707
- }
11708
- }
11709
- if (fixedFrame) {
11710
- selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11711
- selection.setPositionByOrigin(
11712
- new fabric.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11713
- "center",
11714
- "center"
11715
- );
11551
+ try {
11552
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11553
+ } catch {
11716
11554
  }
11717
11555
  restoreKidsFromWorld();
11718
11556
  selection.setCoords();
@@ -11801,93 +11639,22 @@ const PageCanvas = forwardRef(
11801
11639
  const [ready, setReady] = useState(false);
11802
11640
  const [unlockRequestId, setUnlockRequestId] = useState(0);
11803
11641
  const applyLogicalGroupSelectionVisualState = useCallback((selection, groupId) => {
11804
- var _a2, _b2;
11642
+ var _a2;
11805
11643
  selection.__pixldocsGroupSelection = groupId;
11806
11644
  delete selection.__pixldocsLogicalGroupIds;
11807
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11808
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11809
- let groupAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : void 0;
11810
- const members = selection.getObjects();
11811
- if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11812
- const storedW = Number(groupNode.width);
11813
- const storedH = Number(groupNode.height);
11814
- if (storedW > 0 && storedH > 0) {
11815
- groupAngle = 0;
11816
- }
11817
- }
11818
- if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11819
- try {
11820
- const selectionMatrix = selection.calcTransformMatrix();
11821
- const worldAngles = members.map((kid) => normalizeAngle180(fabric.util.qrDecompose(
11822
- fabric.util.multiplyTransformMatrices(
11823
- selectionMatrix,
11824
- kid.calcOwnMatrix()
11825
- )
11826
- ).angle ?? 0));
11827
- const buckets = [];
11828
- for (const angle of worldAngles) {
11829
- const bucket = buckets.find((candidate) => Math.abs(normalizeAngle180(candidate.angle - angle)) <= 2);
11830
- if (bucket) bucket.count += 1;
11831
- else buckets.push({ angle, count: 1 });
11832
- }
11833
- buckets.sort((a, b) => b.count - a.count || Math.abs(b.angle) - Math.abs(a.angle));
11834
- const inferredAngle = (_a2 = buckets[0]) == null ? void 0 : _a2.angle;
11835
- if (typeof inferredAngle === "number" && Number.isFinite(inferredAngle) && Math.abs(inferredAngle) > 0.01) {
11836
- groupAngle = inferredAngle;
11837
- }
11838
- } catch {
11839
- }
11840
- }
11841
- let groupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []) : null;
11842
- let groupFrameWasInferred = false;
11843
- const memberWorldPoints = collectFabricObjectWorldPoints(members);
11844
- const memberWorldCenters = collectFabricObjectWorldCenters(members);
11845
- const contentFrame = typeof groupAngle === "number" && Number.isFinite(groupAngle) ? orientedFrameFromWorldPoints(memberWorldPoints, normalizeAngle180(groupAngle)) : null;
11846
- if (shouldRepairUnrotatedLogicalGroupFrame(groupFrame, contentFrame, groupAngle)) {
11847
- groupFrame = contentFrame;
11848
- groupFrameWasInferred = true;
11849
- } else if (groupFrame && !logicalFrameContainsWorldCenters(groupFrame, groupAngle, memberWorldCenters)) {
11850
- groupFrame = null;
11851
- }
11852
- if (!groupFrame && groupNode && isGroup(groupNode) && typeof groupAngle === "number" && Number.isFinite(groupAngle)) {
11853
- groupFrame = contentFrame ?? orientedFrameFromWorldPoints(memberWorldPoints, normalizeAngle180(groupAngle));
11854
- groupFrameWasInferred = isValidLogicalGroupFrame(groupFrame);
11855
- }
11856
- if (groupNode && isGroup(groupNode)) {
11857
- const updates = {};
11858
- if (typeof groupAngle === "number" && Number.isFinite(groupAngle)) {
11859
- const prevAngle = typeof groupNode.angle === "number" ? groupNode.angle : void 0;
11860
- if (prevAngle === void 0 || Math.abs(normalizeAngle180(prevAngle - groupAngle)) > 0.01) {
11861
- updates.angle = normalizeAngle180(groupAngle);
11862
- }
11863
- }
11864
- if (isValidLogicalGroupFrame(groupFrame)) {
11865
- const prevW = Number(groupNode.width);
11866
- const prevH = Number(groupNode.height);
11867
- if (!Number.isFinite(prevW) || Math.abs(prevW - groupFrame.width) > 0.5) updates.width = groupFrame.width;
11868
- if (!Number.isFinite(prevH) || Math.abs(prevH - groupFrame.height) > 0.5) updates.height = groupFrame.height;
11869
- if (groupFrameWasInferred) {
11870
- const storePos = absoluteToStorePosition(groupFrame.left, groupFrame.top, groupId, (pageNow == null ? void 0 : pageNow.children) ?? []);
11871
- const prevLeft = Number(groupNode.left ?? 0);
11872
- const prevTop = Number(groupNode.top ?? 0);
11873
- if (!Number.isFinite(prevLeft) || Math.abs(prevLeft - storePos.left) > 0.5) updates.left = storePos.left;
11874
- if (!Number.isFinite(prevTop) || Math.abs(prevTop - storePos.top) > 0.5) updates.top = storePos.top;
11875
- }
11876
- }
11877
- if (Object.keys(updates).length > 0) {
11878
- useEditorStore.getState().updateNode(groupId, updates, { recordHistory: false, skipLayoutRecalc: true });
11879
- }
11880
- }
11881
- if (groupAngle !== void 0) {
11882
- selection.__pixldocsGroupAngle = groupAngle;
11883
- const aligned = selection.__pixldocsAlignedAngle;
11884
- if (typeof aligned === "number" && Math.abs(normalizeAngle180(aligned - groupAngle)) > 0.01) {
11885
- delete selection.__pixldocsAlignedAngle;
11645
+ selection.hasBorders = true;
11646
+ try {
11647
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11648
+ const node = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11649
+ const ang = node && isGroup(node) ? node.angle ?? 0 : 0;
11650
+ if (Math.abs(ang) > 0.01) {
11651
+ selection.__pixldocsFrozenGroupAngle = ang;
11652
+ } else {
11653
+ delete selection.__pixldocsFrozenGroupAngle;
11886
11654
  }
11887
- } else {
11888
- delete selection.__pixldocsGroupAngle;
11655
+ } catch {
11889
11656
  }
11890
- selection.hasBorders = true;
11657
+ const members = selection.getObjects();
11891
11658
  for (const prev of suppressGroupMemberBordersRef.current) {
11892
11659
  if (members.includes(prev)) continue;
11893
11660
  const origBorders = prev.__pixldocsOrigHasBorders;
@@ -11910,7 +11677,7 @@ const PageCanvas = forwardRef(
11910
11677
  if (m.__pixldocsOrigHasControls === void 0) m.__pixldocsOrigHasControls = m.hasControls;
11911
11678
  m.hasBorders = false;
11912
11679
  m.hasControls = false;
11913
- if (m.__cropGroup || ((_b2 = m._ct) == null ? void 0 : _b2.isCropGroup)) {
11680
+ if (m.__cropGroup || ((_a2 = m._ct) == null ? void 0 : _a2.isCropGroup)) {
11914
11681
  if (m.__pixldocsOrigLockScalingX === void 0) {
11915
11682
  m.__pixldocsOrigLockScalingX = m.lockScalingX;
11916
11683
  m.__pixldocsOrigLockScalingY = m.lockScalingY;
@@ -11919,8 +11686,8 @@ const PageCanvas = forwardRef(
11919
11686
  m.lockScalingY = false;
11920
11687
  }
11921
11688
  }
11922
- applyWarpAwareSelectionBorders(selection, groupAngle, groupFrame);
11923
- }, [pageId]);
11689
+ applyWarpAwareSelectionBorders(selection);
11690
+ }, []);
11924
11691
  const pageBoundsOptions = useMemo(
11925
11692
  () => ({ pageContentWidth: canvasWidth, pageContentHeight: canvasHeight }),
11926
11693
  [canvasWidth, canvasHeight]
@@ -12022,11 +11789,7 @@ const PageCanvas = forwardRef(
12022
11789
  const activeMembers = activeBeforeRestore.getObjects();
12023
11790
  const sameMembers = activeMembers.length === members.length && members.every((member) => activeMembers.includes(member));
12024
11791
  const sameGroup = activeBeforeRestore.__pixldocsGroupSelection === groupId;
12025
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12026
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
12027
- const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
12028
- const alignedAngle = activeBeforeRestore.__pixldocsAlignedAngle;
12029
- const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
11792
+ const alreadyAligned = activeBeforeRestore.__pixldocsAlignedAngle != null;
12030
11793
  if (sameMembers && sameGroup && alreadyAligned) {
12031
11794
  ensureCanvaControlRenders(activeBeforeRestore);
12032
11795
  return;
@@ -12043,11 +11806,9 @@ const PageCanvas = forwardRef(
12043
11806
  members.forEach((m) => m.setCoords());
12044
11807
  } catch {
12045
11808
  }
12046
- if (!selection.__pixldocsGroupSelection) {
12047
- try {
12048
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
12049
- } catch {
12050
- }
11809
+ try {
11810
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11811
+ } catch {
12051
11812
  }
12052
11813
  selection.setCoords();
12053
11814
  fc.requestRenderAll();
@@ -13487,18 +13248,14 @@ const PageCanvas = forwardRef(
13487
13248
  const groupNode = findNodeById(pageChildren2, groupId);
13488
13249
  if (!groupNode) return;
13489
13250
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
13490
- const storedGroupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, pageChildren2, pageBoundsOptions) : null;
13491
- const groupFrame = storedGroupFrame ?? groupAbs;
13492
13251
  const rect = active.getBoundingRect();
13493
13252
  groupSelectionTransformStartRef.current = {
13494
13253
  groupId,
13495
13254
  selection: active,
13496
13255
  selectionLeft: rect.left,
13497
13256
  selectionTop: rect.top,
13498
- groupLeft: groupFrame.left,
13499
- groupTop: groupFrame.top,
13500
- groupWidth: groupFrame.width,
13501
- groupHeight: groupFrame.height,
13257
+ groupLeft: groupAbs.left,
13258
+ groupTop: groupAbs.top,
13502
13259
  selectionAngle: ((active.angle ?? 0) % 360 + 360) % 360
13503
13260
  };
13504
13261
  logRotDriftSelectionSnapshot("transform-start", active, {
@@ -13666,11 +13423,7 @@ const PageCanvas = forwardRef(
13666
13423
  const activeIds = active.getObjects().map((obj) => getObjectId(obj)).filter((id) => !!id && id !== "__background__");
13667
13424
  const sameMembers = activeIds.length === snapshot.memberIds.length && snapshot.memberIds.every((id) => activeIds.includes(id));
13668
13425
  const sameGroup = active.__pixldocsGroupSelection === snapshot.groupSelectionId;
13669
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13670
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], snapshot.groupSelectionId) : null;
13671
- const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
13672
- const alignedAngle = active.__pixldocsAlignedAngle;
13673
- const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
13426
+ const alreadyAligned = active.__pixldocsAlignedAngle != null;
13674
13427
  if (sameMembers && sameGroup && alreadyAligned) {
13675
13428
  ensureCanvaControlRenders(active);
13676
13429
  return;
@@ -13826,32 +13579,15 @@ const PageCanvas = forwardRef(
13826
13579
  if (oid && memberIds.has(oid)) members.push(o);
13827
13580
  }
13828
13581
  if (members.length === 0) return null;
13829
- const savedGroupAngle = typeof g.angle === "number" && Number.isFinite(g.angle) ? normalizeAngle180(g.angle) : null;
13830
- const savedGroupWidth = Number(g.width);
13831
- const savedGroupHeight = Number(g.height);
13832
- if (savedGroupAngle != null && savedGroupWidth > 0 && savedGroupHeight > 0) {
13833
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13834
- const pageChildrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
13835
- const abs = getAbsoluteBounds(g, pageChildrenNow);
13836
- const cx = abs.left + savedGroupWidth / 2;
13837
- const cy = abs.top + savedGroupHeight / 2;
13838
- const rad2 = savedGroupAngle * Math.PI / 180;
13839
- const c = Math.cos(rad2), s = Math.sin(rad2);
13840
- const rotate = (x, y) => {
13841
- const dx = x - cx;
13842
- const dy = y - cy;
13843
- return { x: cx + dx * c - dy * s, y: cy + dx * s + dy * c };
13844
- };
13845
- return {
13846
- corners: [
13847
- rotate(abs.left, abs.top),
13848
- rotate(abs.left + savedGroupWidth, abs.top),
13849
- rotate(abs.left + savedGroupWidth, abs.top + savedGroupHeight),
13850
- rotate(abs.left, abs.top + savedGroupHeight)
13851
- ],
13852
- angle: savedGroupAngle
13853
- };
13854
- }
13582
+ const TOL = 2;
13583
+ const norm = (a) => {
13584
+ const n = (a % 360 + 360) % 360;
13585
+ return n > 180 ? n - 360 : n;
13586
+ };
13587
+ const angleDelta = (a, b) => {
13588
+ const d = Math.abs(norm(a) - norm(b));
13589
+ return Math.min(d, 360 - d);
13590
+ };
13855
13591
  const worldPoints = [];
13856
13592
  for (const m of members) {
13857
13593
  (_a2 = m.setCoords) == null ? void 0 : _a2.call(m);
@@ -13862,35 +13598,37 @@ const PageCanvas = forwardRef(
13862
13598
  if (p) worldPoints.push({ x: p.x, y: p.y });
13863
13599
  }
13864
13600
  }
13865
- const getLegacyDominantAngle = () => {
13866
- var _a3;
13867
- const areaFor = (angle) => {
13868
- if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13869
- const r = -angle * Math.PI / 180;
13870
- const c = Math.cos(r), s = Math.sin(r);
13871
- let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13872
- for (const p of worldPoints) {
13873
- const xr = p.x * c - p.y * s;
13874
- const yr = p.x * s + p.y * c;
13875
- if (xr < nX) nX = xr;
13876
- if (yr < nY) nY = yr;
13877
- if (xr > xX) xX = xr;
13878
- if (yr > xY) xY = yr;
13879
- }
13880
- return Math.max(1, (xX - nX) * (xY - nY));
13881
- };
13601
+ const orientedAreaForAngle = (angle) => {
13602
+ if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13603
+ const r = -angle * Math.PI / 180;
13604
+ const c = Math.cos(r), s = Math.sin(r);
13605
+ let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13606
+ for (const p of worldPoints) {
13607
+ const xr = p.x * c - p.y * s;
13608
+ const yr = p.x * s + p.y * c;
13609
+ if (xr < nX) nX = xr;
13610
+ if (yr < nY) nY = yr;
13611
+ if (xr > xX) xX = xr;
13612
+ if (yr > xY) xY = yr;
13613
+ }
13614
+ return Math.max(1, (xX - nX) * (xY - nY));
13615
+ };
13616
+ let a0;
13617
+ const savedGroupAngle = norm(g.angle ?? 0);
13618
+ if (Math.abs(savedGroupAngle) > 0.01) {
13619
+ a0 = savedGroupAngle;
13620
+ } else {
13882
13621
  const buckets = [];
13883
13622
  for (const m of members) {
13884
- const a = normalizeAngle180(m.angle ?? 0);
13885
- const b = buckets.find((x) => Math.abs(normalizeAngle180(x.angle - a)) <= 2);
13623
+ const a = norm(m.angle ?? 0);
13624
+ const b = buckets.find((x) => angleDelta(x.angle, a) <= TOL);
13886
13625
  if (b) b.count++;
13887
13626
  else buckets.push({ angle: a, count: 1, area: Number.POSITIVE_INFINITY });
13888
13627
  }
13889
- for (const b of buckets) b.area = areaFor(b.angle);
13628
+ for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
13890
13629
  buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
13891
- return ((_a3 = buckets[0]) == null ? void 0 : _a3.angle) ?? 0;
13892
- };
13893
- const a0 = savedGroupAngle ?? getLegacyDominantAngle();
13630
+ a0 = buckets[0].angle;
13631
+ }
13894
13632
  const rad = -a0 * Math.PI / 180;
13895
13633
  const cos = Math.cos(rad), sin = Math.sin(rad);
13896
13634
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -15091,7 +14829,6 @@ const PageCanvas = forwardRef(
15091
14829
  fabricCanvas.on("object:rotating", (e) => {
15092
14830
  var _a2, _b2;
15093
14831
  markSimpleTransform(e);
15094
- prepareGroupSelectionTransformStart(e.target);
15095
14832
  didTransformRef.current = true;
15096
14833
  const tr = e.target;
15097
14834
  if (shouldLogRotDriftLiveTick(tr, "rotating")) {
@@ -15228,6 +14965,29 @@ const PageCanvas = forwardRef(
15228
14965
  } catch {
15229
14966
  }
15230
14967
  groupShiftReflowSnapshotRef.current = null;
14968
+ try {
14969
+ const t = e.target;
14970
+ if (t instanceof fabric.ActiveSelection) {
14971
+ const gid = t.__pixldocsGroupSelection;
14972
+ const delta = ((t.angle ?? 0) + 360) % 360;
14973
+ const deltaSigned = delta > 180 ? delta - 360 : delta;
14974
+ if (gid && Math.abs(deltaSigned) > 0.01) {
14975
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
14976
+ const node = pageNow ? findNodeById(pageNow.children ?? [], gid) : null;
14977
+ if (node && isGroup(node)) {
14978
+ const prev = node.angle ?? 0;
14979
+ const next = ((prev + deltaSigned) % 360 + 360) % 360;
14980
+ const nextSigned = next > 180 ? next - 360 : next;
14981
+ useEditorStore.getState().updateNode(
14982
+ gid,
14983
+ { angle: nextSigned },
14984
+ { recordHistory: false, skipLayoutRecalc: true }
14985
+ );
14986
+ }
14987
+ }
14988
+ }
14989
+ } catch {
14990
+ }
15231
14991
  lockEdits();
15232
14992
  const modifiedTarget = e.target;
15233
14993
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -15281,7 +15041,7 @@ const PageCanvas = forwardRef(
15281
15041
  const groupLeft = fabricCenterX - w / 2;
15282
15042
  const groupTop = fabricCenterY - h / 2;
15283
15043
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, movedGroupId, pageChildrenSec);
15284
- useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top, angle: fabricSectionGroup.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15044
+ useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15285
15045
  }
15286
15046
  commitHistory();
15287
15047
  unlockEditsSoon();
@@ -15424,7 +15184,7 @@ const PageCanvas = forwardRef(
15424
15184
  const groupLeft = centerX - w / 2;
15425
15185
  const groupTop = centerY - h / 2;
15426
15186
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildrenSec);
15427
- useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15187
+ useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15428
15188
  }
15429
15189
  const node = findNodeById(pageChildrenSec, groupId);
15430
15190
  if (isChildModified && node && !groupMoved) {
@@ -15453,7 +15213,7 @@ const PageCanvas = forwardRef(
15453
15213
  const groupLeft = active.originX === "center" ? centerX - w / 2 : centerX;
15454
15214
  const groupTop = active.originY === "center" ? centerY - h / 2 : centerY;
15455
15215
  const storePos = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildren3);
15456
- useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15216
+ useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top }, { recordHistory: false, skipLayoutRecalc: true });
15457
15217
  commitHistory();
15458
15218
  unlockEditsSoon();
15459
15219
  return;
@@ -15635,38 +15395,6 @@ const PageCanvas = forwardRef(
15635
15395
  360 - Math.abs(currentSelAngle - startSelAngle)
15636
15396
  );
15637
15397
  const hadRotation = isActiveSelection && activeObj && angleDelta > 0.01;
15638
- if ((hadScale || hadRotation) && activeObj instanceof fabric.ActiveSelection && activeGroupSelectionId === groupToMove.id) {
15639
- const groupTransformUpdates = {};
15640
- if (hadRotation) {
15641
- groupTransformUpdates.angle = normalizeAngle180(currentSelAngle);
15642
- activeObj.__pixldocsGroupAngle = groupTransformUpdates.angle;
15643
- }
15644
- if (hadScale && (transformStart == null ? void 0 : transformStart.groupId) === groupToMove.id) {
15645
- const center = activeObj.getCenterPoint();
15646
- const nextWidth = Math.max(1, transformStart.groupWidth * Math.abs(activeObj.scaleX ?? 1));
15647
- const nextHeight = Math.max(1, transformStart.groupHeight * Math.abs(activeObj.scaleY ?? 1));
15648
- const storePos = absoluteToStorePosition(
15649
- center.x - nextWidth / 2,
15650
- center.y - nextHeight / 2,
15651
- groupToMove.id,
15652
- pageChildren2
15653
- );
15654
- groupTransformUpdates.left = storePos.left;
15655
- groupTransformUpdates.top = storePos.top;
15656
- groupTransformUpdates.width = nextWidth;
15657
- groupTransformUpdates.height = nextHeight;
15658
- } else if (!Number.isFinite(Number(groupToMove.width)) || !Number.isFinite(Number(groupToMove.height))) {
15659
- groupTransformUpdates.width = (transformStart == null ? void 0 : transformStart.groupWidth) ?? groupAbs.width;
15660
- groupTransformUpdates.height = (transformStart == null ? void 0 : transformStart.groupHeight) ?? groupAbs.height;
15661
- }
15662
- if (Object.keys(groupTransformUpdates).length > 0) {
15663
- useEditorStore.getState().updateNode(
15664
- groupToMove.id,
15665
- groupTransformUpdates,
15666
- { recordHistory: false, skipLayoutRecalc: true }
15667
- );
15668
- }
15669
- }
15670
15398
  if (!hadScale && !hadRotation && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1)) {
15671
15399
  const { updateNode: updateNodeStore, commitHistory: commitHistoryStore, getCurrentElements } = useEditorStore.getState();
15672
15400
  const newLeft = (groupToMove.left ?? 0) + deltaX;
@@ -16302,11 +16030,9 @@ const PageCanvas = forwardRef(
16302
16030
  } catch {
16303
16031
  }
16304
16032
  fabricCanvas.setActiveObject(newSel);
16305
- if (!wasGroupSel) {
16306
- try {
16307
- (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16308
- } catch {
16309
- }
16033
+ try {
16034
+ (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16035
+ } catch {
16310
16036
  }
16311
16037
  try {
16312
16038
  for (const member of membersToReselect) member.setCoords();
@@ -16683,12 +16409,7 @@ const PageCanvas = forwardRef(
16683
16409
  const prevAS = activeBeforeSync instanceof fabric.ActiveSelection ? activeBeforeSync : null;
16684
16410
  const prevMembers = prevAS ? prevAS.getObjects() : [];
16685
16411
  const sameMembers = !!prevAS && prevMembers.length === freshMembers.length && prevMembers.every((m) => freshMembers.includes(m));
16686
- const savedSnapshotAngle = activeSelectionSnapshot.groupSelectionId ? (() => {
16687
- const groupNode = findNodeById(pageTree, activeSelectionSnapshot.groupSelectionId);
16688
- return groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
16689
- })() : null;
16690
- const prevAlignedAngle = sameMembers ? prevAS.__pixldocsAlignedAngle : null;
16691
- const alreadyAligned = sameMembers && typeof prevAlignedAngle === "number" && (savedSnapshotAngle == null || Math.abs(normalizeAngle180(prevAlignedAngle - savedSnapshotAngle)) <= 0.01);
16412
+ const alreadyAligned = sameMembers && prevAS.__pixldocsAlignedAngle != null;
16692
16413
  if (sameMembers && alreadyAligned) {
16693
16414
  try {
16694
16415
  ensureCanvaControlRenders(prevAS);
@@ -17730,10 +17451,11 @@ const PageCanvas = forwardRef(
17730
17451
  if (sameSelection && (isFlatGroupSelection || isPureSingleGroupSelection)) {
17731
17452
  if (selectedGroupSelectionId && active instanceof fabric.ActiveSelection) {
17732
17453
  if (isPureSingleGroupSelection) {
17733
- applyLogicalGroupSelectionVisualState(active, selectedGroupSelectionId);
17454
+ active.__pixldocsGroupSelection = selectedGroupSelectionId;
17455
+ delete active.__pixldocsLogicalGroupIds;
17456
+ suppressGroupMemberBordersRef.current = active.getObjects();
17734
17457
  } else {
17735
17458
  delete active.__pixldocsGroupSelection;
17736
- delete active.__pixldocsGroupAngle;
17737
17459
  active.__pixldocsLogicalGroupIds = selectedGroupIds;
17738
17460
  active.hasBorders = true;
17739
17461
  suppressGroupMemberBordersRef.current = active.getObjects().filter((m) => {
@@ -17773,10 +17495,10 @@ const PageCanvas = forwardRef(
17773
17495
  const selection = new fabric.ActiveSelection(toSelect, { canvas: fc });
17774
17496
  if (selectedGroupSelectionId) {
17775
17497
  if (isPureSingleGroupSelection) {
17776
- applyLogicalGroupSelectionVisualState(selection, selectedGroupSelectionId);
17498
+ selection.__pixldocsGroupSelection = selectedGroupSelectionId;
17499
+ suppressGroupMemberBordersRef.current = toSelect;
17777
17500
  } else {
17778
17501
  selection.__pixldocsLogicalGroupIds = selectedGroupIds;
17779
- delete selection.__pixldocsGroupAngle;
17780
17502
  selection.hasBorders = true;
17781
17503
  suppressGroupMemberBordersRef.current = toSelect.filter((m) => {
17782
17504
  const id = getObjectId(m);
@@ -25419,9 +25141,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
25419
25141
  }
25420
25142
  return svgString;
25421
25143
  }
25422
- const resolvedPackageVersion = "0.5.397";
25144
+ const resolvedPackageVersion = "0.5.399";
25423
25145
  const PACKAGE_VERSION = resolvedPackageVersion;
25424
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.397";
25146
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.399";
25425
25147
  const roundParityValue = (value) => {
25426
25148
  if (typeof value !== "number") return value;
25427
25149
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26235,7 +25957,7 @@ class PixldocsRenderer {
26235
25957
  await this.waitForCanvasScene(container, cloned, i);
26236
25958
  }
26237
25959
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26238
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-EYMFI-uk.js");
25960
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CyA0vb5f.js");
26239
25961
  const prepared = preparePagesForExport(
26240
25962
  cloned.pages,
26241
25963
  canvasWidth,
@@ -28555,7 +28277,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
28555
28277
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
28556
28278
  sanitizeSvgTreeForPdf(svgToDraw);
28557
28279
  try {
28558
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-EYMFI-uk.js");
28280
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CyA0vb5f.js");
28559
28281
  try {
28560
28282
  await logTextMeasurementDiagnostic(svgToDraw);
28561
28283
  } catch {
@@ -28955,4 +28677,4 @@ export {
28955
28677
  buildTeaserBlurFlatKeys as y,
28956
28678
  collectFontDescriptorsFromConfig as z
28957
28679
  };
28958
- //# sourceMappingURL=index-CqOZyZpi.js.map
28680
+ //# sourceMappingURL=index-yLA9pqpr.js.map