@pixldocs/canvas-renderer 0.5.393 → 0.5.395

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.
@@ -10520,6 +10520,10 @@ function bakeEdgeFade(source, fade) {
10520
10520
  }
10521
10521
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10522
10522
  const SELECTION_BORDER_SCALE = 2;
10523
+ const normalizeAngle180 = (angle) => {
10524
+ const n = (angle % 360 + 360) % 360;
10525
+ return n > 180 ? n - 360 : n;
10526
+ };
10523
10527
  let ensureCanvaControlRenders = () => {
10524
10528
  };
10525
10529
  try {
@@ -11450,7 +11454,7 @@ const bakeTextboxScaleIntoTypography = (obj, sourceElement) => {
11450
11454
  };
11451
11455
  return updates;
11452
11456
  };
11453
- function applyWarpAwareSelectionBorders(selection) {
11457
+ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferredGroupFrame) {
11454
11458
  var _a2;
11455
11459
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
11456
11460
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -11462,17 +11466,13 @@ function applyWarpAwareSelectionBorders(selection) {
11462
11466
  if (selection.__pixldocsAlignedAngle == null) {
11463
11467
  const kids = selection.getObjects();
11464
11468
  if (kids.length >= 1) {
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));
11469
+ const angleDelta = (a, b) => Math.abs(normalizeAngle180(a - b));
11470
11470
  const selectionMatrix = selection.calcTransformMatrix();
11471
11471
  const worldMatrices = kids.map((k) => fabric__namespace.util.multiplyTransformMatrices(
11472
11472
  selectionMatrix,
11473
11473
  k.calcOwnMatrix()
11474
11474
  ));
11475
- const worldAngles = worldMatrices.map((m) => norm(fabric__namespace.util.qrDecompose(m).angle ?? 0));
11475
+ const worldAngles = worldMatrices.map((m) => normalizeAngle180(fabric__namespace.util.qrDecompose(m).angle ?? 0));
11476
11476
  const worldPoints = [];
11477
11477
  for (const k of kids) {
11478
11478
  try {
@@ -11517,9 +11517,11 @@ function applyWarpAwareSelectionBorders(selection) {
11517
11517
  for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
11518
11518
  buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
11519
11519
  const dominant = buckets[0];
11520
- let targetAngle = null;
11520
+ const rawPreferredGroupAngle = typeof preferredGroupAngle === "number" && Number.isFinite(preferredGroupAngle) ? preferredGroupAngle : typeof selection.__pixldocsGroupAngle === "number" && Number.isFinite(selection.__pixldocsGroupAngle) ? selection.__pixldocsGroupAngle : null;
11521
+ const preferredAngle = rawPreferredGroupAngle != null ? normalizeAngle180(rawPreferredGroupAngle) : null;
11522
+ let targetAngle = preferredAngle;
11521
11523
  const isLogicalGroupSelection = !!selection.__pixldocsGroupSelection;
11522
- if (dominant && Math.abs(dominant.angle) > 0.5 && (isLogicalGroupSelection || kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length)) {
11524
+ if (targetAngle == null && dominant && Math.abs(dominant.angle) > 0.5 && (isLogicalGroupSelection || kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length)) {
11523
11525
  targetAngle = dominant.angle;
11524
11526
  }
11525
11527
  if (targetAngle != null) {
@@ -11560,11 +11562,30 @@ function applyWarpAwareSelectionBorders(selection) {
11560
11562
  targetAngle,
11561
11563
  worldAngles
11562
11564
  });
11565
+ const fixedFrame = preferredGroupFrame && Number.isFinite(preferredGroupFrame.left) && Number.isFinite(preferredGroupFrame.top) && Number.isFinite(preferredGroupFrame.width) && preferredGroupFrame.width > 0 && Number.isFinite(preferredGroupFrame.height) && preferredGroupFrame.height > 0 ? preferredGroupFrame : null;
11563
11566
  selection.set({ angle: targetAngle, scaleX: 1, scaleY: 1, skewX: 0, skewY: 0 });
11567
+ if (fixedFrame) {
11568
+ selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11569
+ selection.setPositionByOrigin(
11570
+ new fabric__namespace.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11571
+ "center",
11572
+ "center"
11573
+ );
11574
+ }
11564
11575
  restoreKidsFromWorld();
11565
- try {
11566
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11567
- } catch {
11576
+ if (!fixedFrame) {
11577
+ try {
11578
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11579
+ } catch {
11580
+ }
11581
+ }
11582
+ if (fixedFrame) {
11583
+ selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11584
+ selection.setPositionByOrigin(
11585
+ new fabric__namespace.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11586
+ "center",
11587
+ "center"
11588
+ );
11568
11589
  }
11569
11590
  restoreKidsFromWorld();
11570
11591
  selection.setCoords();
@@ -11653,9 +11674,50 @@ const PageCanvas = react.forwardRef(
11653
11674
  const [ready, setReady] = react.useState(false);
11654
11675
  const [unlockRequestId, setUnlockRequestId] = react.useState(0);
11655
11676
  const applyLogicalGroupSelectionVisualState = react.useCallback((selection, groupId) => {
11656
- var _a2;
11677
+ var _a2, _b2;
11657
11678
  selection.__pixldocsGroupSelection = groupId;
11658
11679
  delete selection.__pixldocsLogicalGroupIds;
11680
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11681
+ const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11682
+ let groupAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : void 0;
11683
+ if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11684
+ try {
11685
+ const kids = selection.getObjects();
11686
+ const selectionMatrix = selection.calcTransformMatrix();
11687
+ const worldAngles = kids.map((kid) => normalizeAngle180(fabric__namespace.util.qrDecompose(
11688
+ fabric__namespace.util.multiplyTransformMatrices(
11689
+ selectionMatrix,
11690
+ kid.calcOwnMatrix()
11691
+ )
11692
+ ).angle ?? 0));
11693
+ const buckets = [];
11694
+ for (const angle of worldAngles) {
11695
+ const bucket = buckets.find((candidate) => Math.abs(normalizeAngle180(candidate.angle - angle)) <= 2);
11696
+ if (bucket) bucket.count += 1;
11697
+ else buckets.push({ angle, count: 1 });
11698
+ }
11699
+ buckets.sort((a, b) => b.count - a.count || Math.abs(b.angle) - Math.abs(a.angle));
11700
+ const inferredAngle = (_a2 = buckets[0]) == null ? void 0 : _a2.angle;
11701
+ if (typeof inferredAngle === "number" && Number.isFinite(inferredAngle) && Math.abs(inferredAngle) > 0.01) {
11702
+ groupAngle = inferredAngle;
11703
+ useEditorStore.getState().updateNode(
11704
+ groupId,
11705
+ { angle: inferredAngle },
11706
+ { recordHistory: false, skipLayoutRecalc: true }
11707
+ );
11708
+ }
11709
+ } catch {
11710
+ }
11711
+ }
11712
+ if (groupAngle !== void 0) {
11713
+ selection.__pixldocsGroupAngle = groupAngle;
11714
+ const aligned = selection.__pixldocsAlignedAngle;
11715
+ if (typeof aligned === "number" && Math.abs(normalizeAngle180(aligned - groupAngle)) > 0.01) {
11716
+ delete selection.__pixldocsAlignedAngle;
11717
+ }
11718
+ } else {
11719
+ delete selection.__pixldocsGroupAngle;
11720
+ }
11659
11721
  selection.hasBorders = true;
11660
11722
  const members = selection.getObjects();
11661
11723
  for (const prev of suppressGroupMemberBordersRef.current) {
@@ -11680,7 +11742,7 @@ const PageCanvas = react.forwardRef(
11680
11742
  if (m.__pixldocsOrigHasControls === void 0) m.__pixldocsOrigHasControls = m.hasControls;
11681
11743
  m.hasBorders = false;
11682
11744
  m.hasControls = false;
11683
- if (m.__cropGroup || ((_a2 = m._ct) == null ? void 0 : _a2.isCropGroup)) {
11745
+ if (m.__cropGroup || ((_b2 = m._ct) == null ? void 0 : _b2.isCropGroup)) {
11684
11746
  if (m.__pixldocsOrigLockScalingX === void 0) {
11685
11747
  m.__pixldocsOrigLockScalingX = m.lockScalingX;
11686
11748
  m.__pixldocsOrigLockScalingY = m.lockScalingY;
@@ -11689,8 +11751,23 @@ const PageCanvas = react.forwardRef(
11689
11751
  m.lockScalingY = false;
11690
11752
  }
11691
11753
  }
11692
- applyWarpAwareSelectionBorders(selection);
11693
- }, []);
11754
+ const groupFrame = groupNode && isGroup(groupNode) ? (() => {
11755
+ try {
11756
+ const abs = getAbsoluteBounds(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []);
11757
+ const storedW = Number(groupNode.width);
11758
+ const storedH = Number(groupNode.height);
11759
+ return {
11760
+ left: abs.left,
11761
+ top: abs.top,
11762
+ width: Number.isFinite(storedW) && storedW > 0 ? storedW : abs.width,
11763
+ height: Number.isFinite(storedH) && storedH > 0 ? storedH : abs.height
11764
+ };
11765
+ } catch {
11766
+ return null;
11767
+ }
11768
+ })() : null;
11769
+ applyWarpAwareSelectionBorders(selection, groupAngle, groupFrame);
11770
+ }, [pageId]);
11694
11771
  const pageBoundsOptions = react.useMemo(
11695
11772
  () => ({ pageContentWidth: canvasWidth, pageContentHeight: canvasHeight }),
11696
11773
  [canvasWidth, canvasHeight]
@@ -11792,7 +11869,11 @@ const PageCanvas = react.forwardRef(
11792
11869
  const activeMembers = activeBeforeRestore.getObjects();
11793
11870
  const sameMembers = activeMembers.length === members.length && members.every((member) => activeMembers.includes(member));
11794
11871
  const sameGroup = activeBeforeRestore.__pixldocsGroupSelection === groupId;
11795
- const alreadyAligned = activeBeforeRestore.__pixldocsAlignedAngle != null;
11872
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11873
+ const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11874
+ const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
11875
+ const alignedAngle = activeBeforeRestore.__pixldocsAlignedAngle;
11876
+ const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
11796
11877
  if (sameMembers && sameGroup && alreadyAligned) {
11797
11878
  ensureCanvaControlRenders(activeBeforeRestore);
11798
11879
  return;
@@ -11809,9 +11890,11 @@ const PageCanvas = react.forwardRef(
11809
11890
  members.forEach((m) => m.setCoords());
11810
11891
  } catch {
11811
11892
  }
11812
- try {
11813
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11814
- } catch {
11893
+ if (!selection.__pixldocsGroupSelection) {
11894
+ try {
11895
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11896
+ } catch {
11897
+ }
11815
11898
  }
11816
11899
  selection.setCoords();
11817
11900
  fc.requestRenderAll();
@@ -13259,6 +13342,8 @@ const PageCanvas = react.forwardRef(
13259
13342
  selectionTop: rect.top,
13260
13343
  groupLeft: groupAbs.left,
13261
13344
  groupTop: groupAbs.top,
13345
+ groupWidth: groupAbs.width,
13346
+ groupHeight: groupAbs.height,
13262
13347
  selectionAngle: ((active.angle ?? 0) % 360 + 360) % 360
13263
13348
  };
13264
13349
  logRotDriftSelectionSnapshot("transform-start", active, {
@@ -13426,7 +13511,11 @@ const PageCanvas = react.forwardRef(
13426
13511
  const activeIds = active.getObjects().map((obj) => getObjectId(obj)).filter((id) => !!id && id !== "__background__");
13427
13512
  const sameMembers = activeIds.length === snapshot.memberIds.length && snapshot.memberIds.every((id) => activeIds.includes(id));
13428
13513
  const sameGroup = active.__pixldocsGroupSelection === snapshot.groupSelectionId;
13429
- const alreadyAligned = active.__pixldocsAlignedAngle != null;
13514
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13515
+ const groupNode = pageNow ? findNodeById(pageNow.children ?? [], snapshot.groupSelectionId) : null;
13516
+ const savedAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
13517
+ const alignedAngle = active.__pixldocsAlignedAngle;
13518
+ const alreadyAligned = typeof alignedAngle === "number" && (savedAngle == null || Math.abs(normalizeAngle180(alignedAngle - savedAngle)) <= 0.01);
13430
13519
  if (sameMembers && sameGroup && alreadyAligned) {
13431
13520
  ensureCanvaControlRenders(active);
13432
13521
  return;
@@ -13582,15 +13671,32 @@ const PageCanvas = react.forwardRef(
13582
13671
  if (oid && memberIds.has(oid)) members.push(o);
13583
13672
  }
13584
13673
  if (members.length === 0) return null;
13585
- const TOL = 2;
13586
- const norm = (a) => {
13587
- const n = (a % 360 + 360) % 360;
13588
- return n > 180 ? n - 360 : n;
13589
- };
13590
- const angleDelta = (a, b) => {
13591
- const d = Math.abs(norm(a) - norm(b));
13592
- return Math.min(d, 360 - d);
13593
- };
13674
+ const savedGroupAngle = typeof g.angle === "number" && Number.isFinite(g.angle) ? normalizeAngle180(g.angle) : null;
13675
+ const savedGroupWidth = Number(g.width);
13676
+ const savedGroupHeight = Number(g.height);
13677
+ if (savedGroupAngle != null && savedGroupWidth > 0 && savedGroupHeight > 0) {
13678
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
13679
+ const pageChildrenNow = (pageNow == null ? void 0 : pageNow.children) ?? [];
13680
+ const abs = getAbsoluteBounds(g, pageChildrenNow);
13681
+ const cx = abs.left + savedGroupWidth / 2;
13682
+ const cy = abs.top + savedGroupHeight / 2;
13683
+ const rad2 = savedGroupAngle * Math.PI / 180;
13684
+ const c = Math.cos(rad2), s = Math.sin(rad2);
13685
+ const rotate = (x, y) => {
13686
+ const dx = x - cx;
13687
+ const dy = y - cy;
13688
+ return { x: cx + dx * c - dy * s, y: cy + dx * s + dy * c };
13689
+ };
13690
+ return {
13691
+ corners: [
13692
+ rotate(abs.left, abs.top),
13693
+ rotate(abs.left + savedGroupWidth, abs.top),
13694
+ rotate(abs.left + savedGroupWidth, abs.top + savedGroupHeight),
13695
+ rotate(abs.left, abs.top + savedGroupHeight)
13696
+ ],
13697
+ angle: savedGroupAngle
13698
+ };
13699
+ }
13594
13700
  const worldPoints = [];
13595
13701
  for (const m of members) {
13596
13702
  (_a2 = m.setCoords) == null ? void 0 : _a2.call(m);
@@ -13601,31 +13707,35 @@ const PageCanvas = react.forwardRef(
13601
13707
  if (p) worldPoints.push({ x: p.x, y: p.y });
13602
13708
  }
13603
13709
  }
13604
- const orientedAreaForAngle = (angle) => {
13605
- if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13606
- const r = -angle * Math.PI / 180;
13607
- const c = Math.cos(r), s = Math.sin(r);
13608
- let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13609
- for (const p of worldPoints) {
13610
- const xr = p.x * c - p.y * s;
13611
- const yr = p.x * s + p.y * c;
13612
- if (xr < nX) nX = xr;
13613
- if (yr < nY) nY = yr;
13614
- if (xr > xX) xX = xr;
13615
- if (yr > xY) xY = yr;
13616
- }
13617
- return Math.max(1, (xX - nX) * (xY - nY));
13710
+ const getLegacyDominantAngle = () => {
13711
+ var _a3;
13712
+ const areaFor = (angle) => {
13713
+ if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13714
+ const r = -angle * Math.PI / 180;
13715
+ const c = Math.cos(r), s = Math.sin(r);
13716
+ let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13717
+ for (const p of worldPoints) {
13718
+ const xr = p.x * c - p.y * s;
13719
+ const yr = p.x * s + p.y * c;
13720
+ if (xr < nX) nX = xr;
13721
+ if (yr < nY) nY = yr;
13722
+ if (xr > xX) xX = xr;
13723
+ if (yr > xY) xY = yr;
13724
+ }
13725
+ return Math.max(1, (xX - nX) * (xY - nY));
13726
+ };
13727
+ const buckets = [];
13728
+ for (const m of members) {
13729
+ const a = normalizeAngle180(m.angle ?? 0);
13730
+ const b = buckets.find((x) => Math.abs(normalizeAngle180(x.angle - a)) <= 2);
13731
+ if (b) b.count++;
13732
+ else buckets.push({ angle: a, count: 1, area: Number.POSITIVE_INFINITY });
13733
+ }
13734
+ for (const b of buckets) b.area = areaFor(b.angle);
13735
+ buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
13736
+ return ((_a3 = buckets[0]) == null ? void 0 : _a3.angle) ?? 0;
13618
13737
  };
13619
- const buckets = [];
13620
- for (const m of members) {
13621
- const a = norm(m.angle ?? 0);
13622
- const b = buckets.find((x) => angleDelta(x.angle, a) <= TOL);
13623
- if (b) b.count++;
13624
- else buckets.push({ angle: a, count: 1, area: Number.POSITIVE_INFINITY });
13625
- }
13626
- for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
13627
- buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
13628
- const a0 = buckets[0].angle;
13738
+ const a0 = savedGroupAngle ?? getLegacyDominantAngle();
13629
13739
  const rad = -a0 * Math.PI / 180;
13630
13740
  const cos = Math.cos(rad), sin = Math.sin(rad);
13631
13741
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -14826,6 +14936,7 @@ const PageCanvas = react.forwardRef(
14826
14936
  fabricCanvas.on("object:rotating", (e) => {
14827
14937
  var _a2, _b2;
14828
14938
  markSimpleTransform(e);
14939
+ prepareGroupSelectionTransformStart(e.target);
14829
14940
  didTransformRef.current = true;
14830
14941
  const tr = e.target;
14831
14942
  if (shouldLogRotDriftLiveTick(tr, "rotating")) {
@@ -15015,7 +15126,7 @@ const PageCanvas = react.forwardRef(
15015
15126
  const groupLeft = fabricCenterX - w / 2;
15016
15127
  const groupTop = fabricCenterY - h / 2;
15017
15128
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, movedGroupId, pageChildrenSec);
15018
- useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15129
+ useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top, angle: fabricSectionGroup.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15019
15130
  }
15020
15131
  commitHistory();
15021
15132
  unlockEditsSoon();
@@ -15158,7 +15269,7 @@ const PageCanvas = react.forwardRef(
15158
15269
  const groupLeft = centerX - w / 2;
15159
15270
  const groupTop = centerY - h / 2;
15160
15271
  const storePosGroup = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildrenSec);
15161
- useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15272
+ useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15162
15273
  }
15163
15274
  const node = findNodeById(pageChildrenSec, groupId);
15164
15275
  if (isChildModified && node && !groupMoved) {
@@ -15187,7 +15298,7 @@ const PageCanvas = react.forwardRef(
15187
15298
  const groupLeft = active.originX === "center" ? centerX - w / 2 : centerX;
15188
15299
  const groupTop = active.originY === "center" ? centerY - h / 2 : centerY;
15189
15300
  const storePos = absoluteToStorePosition(groupLeft, groupTop, groupId, pageChildren3);
15190
- useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top }, { recordHistory: false, skipLayoutRecalc: true });
15301
+ useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top, angle: active.angle ?? 0 }, { recordHistory: false, skipLayoutRecalc: true });
15191
15302
  commitHistory();
15192
15303
  unlockEditsSoon();
15193
15304
  return;
@@ -15369,6 +15480,14 @@ const PageCanvas = react.forwardRef(
15369
15480
  360 - Math.abs(currentSelAngle - startSelAngle)
15370
15481
  );
15371
15482
  const hadRotation = isActiveSelection && activeObj && angleDelta > 0.01;
15483
+ if (hadRotation && activeObj instanceof fabric__namespace.ActiveSelection && activeGroupSelectionId === groupToMove.id) {
15484
+ useEditorStore.getState().updateNode(
15485
+ groupToMove.id,
15486
+ { angle: currentSelAngle },
15487
+ { recordHistory: false, skipLayoutRecalc: true }
15488
+ );
15489
+ activeObj.__pixldocsGroupAngle = currentSelAngle;
15490
+ }
15372
15491
  if (!hadScale && !hadRotation && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1)) {
15373
15492
  const { updateNode: updateNodeStore, commitHistory: commitHistoryStore, getCurrentElements } = useEditorStore.getState();
15374
15493
  const newLeft = (groupToMove.left ?? 0) + deltaX;
@@ -16004,9 +16123,11 @@ const PageCanvas = react.forwardRef(
16004
16123
  } catch {
16005
16124
  }
16006
16125
  fabricCanvas.setActiveObject(newSel);
16007
- try {
16008
- (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16009
- } catch {
16126
+ if (!wasGroupSel) {
16127
+ try {
16128
+ (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16129
+ } catch {
16130
+ }
16010
16131
  }
16011
16132
  try {
16012
16133
  for (const member of membersToReselect) member.setCoords();
@@ -16383,7 +16504,12 @@ const PageCanvas = react.forwardRef(
16383
16504
  const prevAS = activeBeforeSync instanceof fabric__namespace.ActiveSelection ? activeBeforeSync : null;
16384
16505
  const prevMembers = prevAS ? prevAS.getObjects() : [];
16385
16506
  const sameMembers = !!prevAS && prevMembers.length === freshMembers.length && prevMembers.every((m) => freshMembers.includes(m));
16386
- const alreadyAligned = sameMembers && prevAS.__pixldocsAlignedAngle != null;
16507
+ const savedSnapshotAngle = activeSelectionSnapshot.groupSelectionId ? (() => {
16508
+ const groupNode = findNodeById(pageTree, activeSelectionSnapshot.groupSelectionId);
16509
+ return groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : null;
16510
+ })() : null;
16511
+ const prevAlignedAngle = sameMembers ? prevAS.__pixldocsAlignedAngle : null;
16512
+ const alreadyAligned = sameMembers && typeof prevAlignedAngle === "number" && (savedSnapshotAngle == null || Math.abs(normalizeAngle180(prevAlignedAngle - savedSnapshotAngle)) <= 0.01);
16387
16513
  if (sameMembers && alreadyAligned) {
16388
16514
  try {
16389
16515
  ensureCanvaControlRenders(prevAS);
@@ -17425,11 +17551,10 @@ const PageCanvas = react.forwardRef(
17425
17551
  if (sameSelection && (isFlatGroupSelection || isPureSingleGroupSelection)) {
17426
17552
  if (selectedGroupSelectionId && active instanceof fabric__namespace.ActiveSelection) {
17427
17553
  if (isPureSingleGroupSelection) {
17428
- active.__pixldocsGroupSelection = selectedGroupSelectionId;
17429
- delete active.__pixldocsLogicalGroupIds;
17430
- suppressGroupMemberBordersRef.current = active.getObjects();
17554
+ applyLogicalGroupSelectionVisualState(active, selectedGroupSelectionId);
17431
17555
  } else {
17432
17556
  delete active.__pixldocsGroupSelection;
17557
+ delete active.__pixldocsGroupAngle;
17433
17558
  active.__pixldocsLogicalGroupIds = selectedGroupIds;
17434
17559
  active.hasBorders = true;
17435
17560
  suppressGroupMemberBordersRef.current = active.getObjects().filter((m) => {
@@ -17469,10 +17594,10 @@ const PageCanvas = react.forwardRef(
17469
17594
  const selection = new fabric__namespace.ActiveSelection(toSelect, { canvas: fc });
17470
17595
  if (selectedGroupSelectionId) {
17471
17596
  if (isPureSingleGroupSelection) {
17472
- selection.__pixldocsGroupSelection = selectedGroupSelectionId;
17473
- suppressGroupMemberBordersRef.current = toSelect;
17597
+ applyLogicalGroupSelectionVisualState(selection, selectedGroupSelectionId);
17474
17598
  } else {
17475
17599
  selection.__pixldocsLogicalGroupIds = selectedGroupIds;
17600
+ delete selection.__pixldocsGroupAngle;
17476
17601
  selection.hasBorders = true;
17477
17602
  suppressGroupMemberBordersRef.current = toSelect.filter((m) => {
17478
17603
  const id = getObjectId(m);
@@ -25115,9 +25240,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
25115
25240
  }
25116
25241
  return svgString;
25117
25242
  }
25118
- const resolvedPackageVersion = "0.5.393";
25243
+ const resolvedPackageVersion = "0.5.395";
25119
25244
  const PACKAGE_VERSION = resolvedPackageVersion;
25120
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.393";
25245
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.395";
25121
25246
  const roundParityValue = (value) => {
25122
25247
  if (typeof value !== "number") return value;
25123
25248
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -25931,7 +26056,7 @@ class PixldocsRenderer {
25931
26056
  await this.waitForCanvasScene(container, cloned, i);
25932
26057
  }
25933
26058
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
25934
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BWhYgu3c.cjs"));
26059
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BFZzT33a.cjs"));
25935
26060
  const prepared = preparePagesForExport(
25936
26061
  cloned.pages,
25937
26062
  canvasWidth,
@@ -28251,7 +28376,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
28251
28376
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
28252
28377
  sanitizeSvgTreeForPdf(svgToDraw);
28253
28378
  try {
28254
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BWhYgu3c.cjs"));
28379
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BFZzT33a.cjs"));
28255
28380
  try {
28256
28381
  await logTextMeasurementDiagnostic(svgToDraw);
28257
28382
  } catch {
@@ -28648,4 +28773,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
28648
28773
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
28649
28774
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
28650
28775
  exports.warmTemplateFromForm = warmTemplateFromForm;
28651
- //# sourceMappingURL=index-B0Ej1RxQ.cjs.map
28776
+ //# sourceMappingURL=index-apZqfLuE.cjs.map