@pixldocs/canvas-renderer 0.5.398 → 0.5.400

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,20 @@ 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
- }
11656
- }
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
- if (targetAngle == null && dominant && Math.abs(dominant.angle) > 0.5 && (isLogicalGroupSelection || kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length)) {
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;
11525
+ }
11526
+ if (targetAngle == null && dominant && Math.abs(dominant.angle) > 0.5 && // Canva-style: for a *logical group* the bbox angle is authoritative
11527
+ // from the persisted group.angle (frozenGroupAngle) only. Individual
11528
+ // child rotations must NEVER drift the group's selection bbox angle.
11529
+ // The dominant-angle fallback is reserved for ad-hoc multi-selects
11530
+ // (no logical group), where matching child rotation is desirable.
11531
+ (!isLogicalGroupSelection && (kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length))) {
11662
11532
  targetAngle = dominant.angle;
11663
11533
  }
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
11534
  if (targetAngle != null) {
11673
11535
  const restoreKidsFromWorld = () => {
11674
11536
  const invSelection = fabric__namespace.util.invertTransform(
@@ -11707,30 +11569,11 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11707
11569
  targetAngle,
11708
11570
  worldAngles
11709
11571
  });
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
11572
  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
11573
  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
- );
11574
+ try {
11575
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11576
+ } catch {
11734
11577
  }
11735
11578
  restoreKidsFromWorld();
11736
11579
  selection.setCoords();
@@ -11819,66 +11662,22 @@ const PageCanvas = react.forwardRef(
11819
11662
  const [ready, setReady] = react.useState(false);
11820
11663
  const [unlockRequestId, setUnlockRequestId] = react.useState(0);
11821
11664
  const applyLogicalGroupSelectionVisualState = react.useCallback((selection, groupId) => {
11822
- var _a2, _b2;
11665
+ var _a2;
11823
11666
  selection.__pixldocsGroupSelection = groupId;
11824
11667
  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
- const memberWorldPoints = collectFabricObjectWorldPoints(members);
11861
- const memberWorldCenters = collectFabricObjectWorldCenters(members);
11862
- const contentFrame = typeof groupAngle === "number" && Number.isFinite(groupAngle) ? orientedFrameFromWorldPoints(memberWorldPoints, normalizeAngle180(groupAngle)) : null;
11863
- if (shouldRepairUnrotatedLogicalGroupFrame(groupFrame, contentFrame, groupAngle)) {
11864
- groupFrame = contentFrame;
11865
- } else if (groupFrame && !logicalFrameContainsWorldCenters(groupFrame, groupAngle, memberWorldCenters)) {
11866
- groupFrame = null;
11867
- }
11868
- if (!groupFrame && groupNode && isGroup(groupNode) && typeof groupAngle === "number" && Number.isFinite(groupAngle)) {
11869
- groupFrame = contentFrame ?? orientedFrameFromWorldPoints(memberWorldPoints, normalizeAngle180(groupAngle));
11870
- isValidLogicalGroupFrame(groupFrame);
11871
- }
11872
- if (groupAngle !== void 0) {
11873
- selection.__pixldocsGroupAngle = groupAngle;
11874
- const aligned = selection.__pixldocsAlignedAngle;
11875
- if (typeof aligned === "number" && Math.abs(normalizeAngle180(aligned - groupAngle)) > 0.01) {
11876
- delete selection.__pixldocsAlignedAngle;
11668
+ selection.hasBorders = true;
11669
+ try {
11670
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11671
+ const node = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11672
+ const ang = node && isGroup(node) ? node.angle ?? 0 : 0;
11673
+ if (Math.abs(ang) > 0.01) {
11674
+ selection.__pixldocsFrozenGroupAngle = ang;
11675
+ } else {
11676
+ delete selection.__pixldocsFrozenGroupAngle;
11877
11677
  }
11878
- } else {
11879
- delete selection.__pixldocsGroupAngle;
11678
+ } catch {
11880
11679
  }
11881
- selection.hasBorders = true;
11680
+ const members = selection.getObjects();
11882
11681
  for (const prev of suppressGroupMemberBordersRef.current) {
11883
11682
  if (members.includes(prev)) continue;
11884
11683
  const origBorders = prev.__pixldocsOrigHasBorders;
@@ -11901,7 +11700,7 @@ const PageCanvas = react.forwardRef(
11901
11700
  if (m.__pixldocsOrigHasControls === void 0) m.__pixldocsOrigHasControls = m.hasControls;
11902
11701
  m.hasBorders = false;
11903
11702
  m.hasControls = false;
11904
- if (m.__cropGroup || ((_b2 = m._ct) == null ? void 0 : _b2.isCropGroup)) {
11703
+ if (m.__cropGroup || ((_a2 = m._ct) == null ? void 0 : _a2.isCropGroup)) {
11905
11704
  if (m.__pixldocsOrigLockScalingX === void 0) {
11906
11705
  m.__pixldocsOrigLockScalingX = m.lockScalingX;
11907
11706
  m.__pixldocsOrigLockScalingY = m.lockScalingY;
@@ -11910,8 +11709,8 @@ const PageCanvas = react.forwardRef(
11910
11709
  m.lockScalingY = false;
11911
11710
  }
11912
11711
  }
11913
- applyWarpAwareSelectionBorders(selection, groupAngle, groupFrame);
11914
- }, [pageId]);
11712
+ applyWarpAwareSelectionBorders(selection);
11713
+ }, []);
11915
11714
  const pageBoundsOptions = react.useMemo(
11916
11715
  () => ({ pageContentWidth: canvasWidth, pageContentHeight: canvasHeight }),
11917
11716
  [canvasWidth, canvasHeight]
@@ -12013,11 +11812,7 @@ const PageCanvas = react.forwardRef(
12013
11812
  const activeMembers = activeBeforeRestore.getObjects();
12014
11813
  const sameMembers = activeMembers.length === members.length && members.every((member) => activeMembers.includes(member));
12015
11814
  const sameGroup = activeBeforeRestore.__pixldocsGroupSelection === groupId;
12016
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
12017
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
12018
- const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
12019
- const alignedAngle = activeBeforeRestore.__pixldocsAlignedAngle;
12020
- const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
11815
+ const alreadyAligned = activeBeforeRestore.__pixldocsAlignedAngle != null;
12021
11816
  if (sameMembers && sameGroup && alreadyAligned) {
12022
11817
  ensureCanvaControlRenders(activeBeforeRestore);
12023
11818
  return;
@@ -12034,11 +11829,9 @@ const PageCanvas = react.forwardRef(
12034
11829
  members.forEach((m) => m.setCoords());
12035
11830
  } catch {
12036
11831
  }
12037
- if (!selection.__pixldocsGroupSelection) {
12038
- try {
12039
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
12040
- } catch {
12041
- }
11832
+ try {
11833
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11834
+ } catch {
12042
11835
  }
12043
11836
  selection.setCoords();
12044
11837
  fc.requestRenderAll();
@@ -13478,18 +13271,14 @@ const PageCanvas = react.forwardRef(
13478
13271
  const groupNode = findNodeById(pageChildren2, groupId);
13479
13272
  if (!groupNode) return;
13480
13273
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
13481
- const storedGroupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, pageChildren2, pageBoundsOptions) : null;
13482
- const groupFrame = storedGroupFrame ?? groupAbs;
13483
13274
  const rect = active.getBoundingRect();
13484
13275
  groupSelectionTransformStartRef.current = {
13485
13276
  groupId,
13486
13277
  selection: active,
13487
13278
  selectionLeft: rect.left,
13488
13279
  selectionTop: rect.top,
13489
- groupLeft: groupFrame.left,
13490
- groupTop: groupFrame.top,
13491
- groupWidth: groupFrame.width,
13492
- groupHeight: groupFrame.height,
13280
+ groupLeft: groupAbs.left,
13281
+ groupTop: groupAbs.top,
13493
13282
  selectionAngle: ((active.angle ?? 0) % 360 + 360) % 360
13494
13283
  };
13495
13284
  logRotDriftSelectionSnapshot("transform-start", active, {
@@ -13657,11 +13446,7 @@ const PageCanvas = react.forwardRef(
13657
13446
  const activeIds = active.getObjects().map((obj) => getObjectId(obj)).filter((id) => !!id && id !== "__background__");
13658
13447
  const sameMembers = activeIds.length === snapshot.memberIds.length && snapshot.memberIds.every((id) => activeIds.includes(id));
13659
13448
  const sameGroup = active.__pixldocsGroupSelection === snapshot.groupSelectionId;
13660
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13661
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], snapshot.groupSelectionId) : null;
13662
- const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
13663
- const alignedAngle = active.__pixldocsAlignedAngle;
13664
- const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
13449
+ const alreadyAligned = active.__pixldocsAlignedAngle != null;
13665
13450
  if (sameMembers && sameGroup && alreadyAligned) {
13666
13451
  ensureCanvaControlRenders(active);
13667
13452
  return;
@@ -13817,32 +13602,10 @@ const PageCanvas = react.forwardRef(
13817
13602
  if (oid && memberIds.has(oid)) members.push(o);
13818
13603
  }
13819
13604
  if (members.length === 0) return null;
13820
- const savedGroupAngle = typeof g.angle === "number" && Number.isFinite(g.angle) ? normalizeAngle180(g.angle) : null;
13821
- const savedGroupWidth = Number(g.width);
13822
- const savedGroupHeight = Number(g.height);
13823
- if (savedGroupAngle != null && savedGroupWidth > 0 && savedGroupHeight > 0) {
13824
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13825
- const pageChildrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
13826
- const abs = getAbsoluteBounds(g, pageChildrenNow);
13827
- const cx = abs.left + savedGroupWidth / 2;
13828
- const cy = abs.top + savedGroupHeight / 2;
13829
- const rad2 = savedGroupAngle * Math.PI / 180;
13830
- const c = Math.cos(rad2), s = Math.sin(rad2);
13831
- const rotate = (x, y) => {
13832
- const dx = x - cx;
13833
- const dy = y - cy;
13834
- return { x: cx + dx * c - dy * s, y: cy + dx * s + dy * c };
13835
- };
13836
- return {
13837
- corners: [
13838
- rotate(abs.left, abs.top),
13839
- rotate(abs.left + savedGroupWidth, abs.top),
13840
- rotate(abs.left + savedGroupWidth, abs.top + savedGroupHeight),
13841
- rotate(abs.left, abs.top + savedGroupHeight)
13842
- ],
13843
- angle: savedGroupAngle
13844
- };
13845
- }
13605
+ const norm = (a) => {
13606
+ const n = (a % 360 + 360) % 360;
13607
+ return n > 180 ? n - 360 : n;
13608
+ };
13846
13609
  const worldPoints = [];
13847
13610
  for (const m of members) {
13848
13611
  (_a2 = m.setCoords) == null ? void 0 : _a2.call(m);
@@ -13853,35 +13616,13 @@ const PageCanvas = react.forwardRef(
13853
13616
  if (p) worldPoints.push({ x: p.x, y: p.y });
13854
13617
  }
13855
13618
  }
13856
- const getLegacyDominantAngle = () => {
13857
- var _a3;
13858
- const areaFor = (angle) => {
13859
- if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13860
- const r = -angle * Math.PI / 180;
13861
- const c = Math.cos(r), s = Math.sin(r);
13862
- let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13863
- for (const p of worldPoints) {
13864
- const xr = p.x * c - p.y * s;
13865
- const yr = p.x * s + p.y * c;
13866
- if (xr < nX) nX = xr;
13867
- if (yr < nY) nY = yr;
13868
- if (xr > xX) xX = xr;
13869
- if (yr > xY) xY = yr;
13870
- }
13871
- return Math.max(1, (xX - nX) * (xY - nY));
13872
- };
13873
- const buckets = [];
13874
- for (const m of members) {
13875
- const a = normalizeAngle180(m.angle ?? 0);
13876
- const b = buckets.find((x) => Math.abs(normalizeAngle180(x.angle - a)) <= 2);
13877
- if (b) b.count++;
13878
- else buckets.push({ angle: a, count: 1, area: Number.POSITIVE_INFINITY });
13879
- }
13880
- for (const b of buckets) b.area = areaFor(b.angle);
13881
- buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
13882
- return ((_a3 = buckets[0]) == null ? void 0 : _a3.angle) ?? 0;
13883
- };
13884
- const a0 = savedGroupAngle ?? getLegacyDominantAngle();
13619
+ let a0;
13620
+ const savedGroupAngle = norm(g.angle ?? 0);
13621
+ if (Math.abs(savedGroupAngle) > 0.01) {
13622
+ a0 = savedGroupAngle;
13623
+ } else {
13624
+ a0 = 0;
13625
+ }
13885
13626
  const rad = -a0 * Math.PI / 180;
13886
13627
  const cos = Math.cos(rad), sin = Math.sin(rad);
13887
13628
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -15082,7 +14823,6 @@ const PageCanvas = react.forwardRef(
15082
14823
  fabricCanvas.on("object:rotating", (e) => {
15083
14824
  var _a2, _b2;
15084
14825
  markSimpleTransform(e);
15085
- prepareGroupSelectionTransformStart(e.target);
15086
14826
  didTransformRef.current = true;
15087
14827
  const tr = e.target;
15088
14828
  if (shouldLogRotDriftLiveTick(tr, "rotating")) {
@@ -15219,6 +14959,29 @@ const PageCanvas = react.forwardRef(
15219
14959
  } catch {
15220
14960
  }
15221
14961
  groupShiftReflowSnapshotRef.current = null;
14962
+ try {
14963
+ const t = e.target;
14964
+ if (t instanceof fabric__namespace.ActiveSelection) {
14965
+ const gid = t.__pixldocsGroupSelection;
14966
+ const delta = ((t.angle ?? 0) + 360) % 360;
14967
+ const deltaSigned = delta > 180 ? delta - 360 : delta;
14968
+ if (gid && Math.abs(deltaSigned) > 0.01) {
14969
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
14970
+ const node = pageNow ? findNodeById(pageNow.children ?? [], gid) : null;
14971
+ if (node && isGroup(node)) {
14972
+ const prev = node.angle ?? 0;
14973
+ const next = ((prev + deltaSigned) % 360 + 360) % 360;
14974
+ const nextSigned = next > 180 ? next - 360 : next;
14975
+ useEditorStore.getState().updateNode(
14976
+ gid,
14977
+ { angle: nextSigned },
14978
+ { recordHistory: false, skipLayoutRecalc: true }
14979
+ );
14980
+ }
14981
+ }
14982
+ }
14983
+ } catch {
14984
+ }
15222
14985
  lockEdits();
15223
14986
  const modifiedTarget = e.target;
15224
14987
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -15272,7 +15035,7 @@ const PageCanvas = react.forwardRef(
15272
15035
  const groupLeft = fabricCenterX - w / 2;
15273
15036
  const groupTop = fabricCenterY - h / 2;
15274
15037
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, movedGroupId, pageChildrenSec);
15275
- useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top, angle: fabricSectionGroup.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15038
+ useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15276
15039
  }
15277
15040
  commitHistory();
15278
15041
  unlockEditsSoon();
@@ -15415,7 +15178,7 @@ const PageCanvas = react.forwardRef(
15415
15178
  const groupLeft = centerX - w / 2;
15416
15179
  const groupTop = centerY - h / 2;
15417
15180
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildrenSec);
15418
- useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15181
+ useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15419
15182
  }
15420
15183
  const node = findNodeById(pageChildrenSec, groupId);
15421
15184
  if (isChildModified && node && !groupMoved) {
@@ -15444,7 +15207,7 @@ const PageCanvas = react.forwardRef(
15444
15207
  const groupLeft = active.originX === "center" ? centerX - w / 2 : centerX;
15445
15208
  const groupTop = active.originY === "center" ? centerY - h / 2 : centerY;
15446
15209
  const storePos = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildren3);
15447
- useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15210
+ useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top }, { recordHistory: false, skipLayoutRecalc: true });
15448
15211
  commitHistory();
15449
15212
  unlockEditsSoon();
15450
15213
  return;
@@ -15626,38 +15389,6 @@ const PageCanvas = react.forwardRef(
15626
15389
  360 - Math.abs(currentSelAngle - startSelAngle)
15627
15390
  );
15628
15391
  const hadRotation = isActiveSelection && activeObj && angleDelta > 0.01;
15629
- if ((hadScale || hadRotation) && activeObj instanceof fabric__namespace.ActiveSelection && activeGroupSelectionId === groupToMove.id) {
15630
- const groupTransformUpdates = {};
15631
- if (hadRotation) {
15632
- groupTransformUpdates.angle = normalizeAngle180(currentSelAngle);
15633
- activeObj.__pixldocsGroupAngle = groupTransformUpdates.angle;
15634
- }
15635
- if (hadScale && (transformStart == null ? void 0 : transformStart.groupId) === groupToMove.id) {
15636
- const center = activeObj.getCenterPoint();
15637
- const nextWidth = Math.max(1, transformStart.groupWidth * Math.abs(activeObj.scaleX ?? 1));
15638
- const nextHeight = Math.max(1, transformStart.groupHeight * Math.abs(activeObj.scaleY ?? 1));
15639
- const storePos = absoluteToStorePosition(
15640
- center.x - nextWidth / 2,
15641
- center.y - nextHeight / 2,
15642
- groupToMove.id,
15643
- pageChildren2
15644
- );
15645
- groupTransformUpdates.left = storePos.left;
15646
- groupTransformUpdates.top = storePos.top;
15647
- groupTransformUpdates.width = nextWidth;
15648
- groupTransformUpdates.height = nextHeight;
15649
- } else if (!Number.isFinite(Number(groupToMove.width)) || !Number.isFinite(Number(groupToMove.height))) {
15650
- groupTransformUpdates.width = (transformStart == null ? void 0 : transformStart.groupWidth) ?? groupAbs.width;
15651
- groupTransformUpdates.height = (transformStart == null ? void 0 : transformStart.groupHeight) ?? groupAbs.height;
15652
- }
15653
- if (Object.keys(groupTransformUpdates).length > 0) {
15654
- useEditorStore.getState().updateNode(
15655
- groupToMove.id,
15656
- groupTransformUpdates,
15657
- { recordHistory: false, skipLayoutRecalc: true }
15658
- );
15659
- }
15660
- }
15661
15392
  if (!hadScale && !hadRotation && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1)) {
15662
15393
  const { updateNode: updateNodeStore, commitHistory: commitHistoryStore, getCurrentElements } = useEditorStore.getState();
15663
15394
  const newLeft = (groupToMove.left ?? 0) + deltaX;
@@ -16293,11 +16024,9 @@ const PageCanvas = react.forwardRef(
16293
16024
  } catch {
16294
16025
  }
16295
16026
  fabricCanvas.setActiveObject(newSel);
16296
- if (!wasGroupSel) {
16297
- try {
16298
- (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16299
- } catch {
16300
- }
16027
+ try {
16028
+ (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16029
+ } catch {
16301
16030
  }
16302
16031
  try {
16303
16032
  for (const member of membersToReselect) member.setCoords();
@@ -16674,12 +16403,7 @@ const PageCanvas = react.forwardRef(
16674
16403
  const prevAS = activeBeforeSync instanceof fabric__namespace.ActiveSelection ? activeBeforeSync : null;
16675
16404
  const prevMembers = prevAS ? prevAS.getObjects() : [];
16676
16405
  const sameMembers = !!prevAS && prevMembers.length === freshMembers.length && prevMembers.every((m) => freshMembers.includes(m));
16677
- const savedSnapshotAngle = activeSelectionSnapshot.groupSelectionId ? (() => {
16678
- const groupNode = findNodeById(pageTree, activeSelectionSnapshot.groupSelectionId);
16679
- return groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
16680
- })() : null;
16681
- const prevAlignedAngle = sameMembers ? prevAS.__pixldocsAlignedAngle : null;
16682
- const alreadyAligned = sameMembers && typeof prevAlignedAngle === "number" && (savedSnapshotAngle == null || Math.abs(normalizeAngle180(prevAlignedAngle - savedSnapshotAngle)) <= 0.01);
16406
+ const alreadyAligned = sameMembers && prevAS.__pixldocsAlignedAngle != null;
16683
16407
  if (sameMembers && alreadyAligned) {
16684
16408
  try {
16685
16409
  ensureCanvaControlRenders(prevAS);
@@ -17721,10 +17445,11 @@ const PageCanvas = react.forwardRef(
17721
17445
  if (sameSelection && (isFlatGroupSelection || isPureSingleGroupSelection)) {
17722
17446
  if (selectedGroupSelectionId && active instanceof fabric__namespace.ActiveSelection) {
17723
17447
  if (isPureSingleGroupSelection) {
17724
- applyLogicalGroupSelectionVisualState(active, selectedGroupSelectionId);
17448
+ active.__pixldocsGroupSelection = selectedGroupSelectionId;
17449
+ delete active.__pixldocsLogicalGroupIds;
17450
+ suppressGroupMemberBordersRef.current = active.getObjects();
17725
17451
  } else {
17726
17452
  delete active.__pixldocsGroupSelection;
17727
- delete active.__pixldocsGroupAngle;
17728
17453
  active.__pixldocsLogicalGroupIds = selectedGroupIds;
17729
17454
  active.hasBorders = true;
17730
17455
  suppressGroupMemberBordersRef.current = active.getObjects().filter((m) => {
@@ -17764,10 +17489,10 @@ const PageCanvas = react.forwardRef(
17764
17489
  const selection = new fabric__namespace.ActiveSelection(toSelect, { canvas: fc });
17765
17490
  if (selectedGroupSelectionId) {
17766
17491
  if (isPureSingleGroupSelection) {
17767
- applyLogicalGroupSelectionVisualState(selection, selectedGroupSelectionId);
17492
+ selection.__pixldocsGroupSelection = selectedGroupSelectionId;
17493
+ suppressGroupMemberBordersRef.current = toSelect;
17768
17494
  } else {
17769
17495
  selection.__pixldocsLogicalGroupIds = selectedGroupIds;
17770
- delete selection.__pixldocsGroupAngle;
17771
17496
  selection.hasBorders = true;
17772
17497
  suppressGroupMemberBordersRef.current = toSelect.filter((m) => {
17773
17498
  const id = getObjectId(m);
@@ -25410,9 +25135,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
25410
25135
  }
25411
25136
  return svgString;
25412
25137
  }
25413
- const resolvedPackageVersion = "0.5.398";
25138
+ const resolvedPackageVersion = "0.5.400";
25414
25139
  const PACKAGE_VERSION = resolvedPackageVersion;
25415
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.398";
25140
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.400";
25416
25141
  const roundParityValue = (value) => {
25417
25142
  if (typeof value !== "number") return value;
25418
25143
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26226,7 +25951,7 @@ class PixldocsRenderer {
26226
25951
  await this.waitForCanvasScene(container, cloned, i);
26227
25952
  }
26228
25953
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26229
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-nSxpHANQ.cjs"));
25954
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-EaqO1vVi.cjs"));
26230
25955
  const prepared = preparePagesForExport(
26231
25956
  cloned.pages,
26232
25957
  canvasWidth,
@@ -28546,7 +28271,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
28546
28271
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
28547
28272
  sanitizeSvgTreeForPdf(svgToDraw);
28548
28273
  try {
28549
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-nSxpHANQ.cjs"));
28274
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-EaqO1vVi.cjs"));
28550
28275
  try {
28551
28276
  await logTextMeasurementDiagnostic(svgToDraw);
28552
28277
  } catch {
@@ -28943,4 +28668,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
28943
28668
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
28944
28669
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
28945
28670
  exports.warmTemplateFromForm = warmTemplateFromForm;
28946
- //# sourceMappingURL=index-YhKOXBPt.cjs.map
28671
+ //# sourceMappingURL=index-CJQpuR_V.cjs.map