@pixldocs/canvas-renderer 0.5.398 → 0.5.399

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -125,7 +125,6 @@ const createDefaultGroup = (partial) => ({
125
125
  locked: false,
126
126
  left: 0,
127
127
  top: 0,
128
- angle: 0,
129
128
  ...partial
130
129
  });
131
130
  const PAGE_BACKGROUND_ELEMENT_ID = "__pageBackground__";
@@ -848,17 +847,9 @@ function getNodeBounds(node, pageChildren, options) {
848
847
  let width;
849
848
  let height;
850
849
  if (isGroup(node) && (pageChildren == null ? void 0 : pageChildren.length) !== void 0) {
851
- const group = node;
852
- const storedWidth = Number(group.width);
853
- const storedHeight = Number(group.height);
854
- if (!isStackLayoutMode(group.layoutMode) && storedWidth > 0 && storedHeight > 0) {
855
- width = storedWidth;
856
- height = storedHeight;
857
- } else {
858
- const size = groupBoundsFromChildren(group, pageChildren ?? []);
859
- width = size.width;
860
- height = size.height;
861
- }
850
+ const size = groupBoundsFromChildren(node, pageChildren ?? []);
851
+ width = size.width;
852
+ height = size.height;
862
853
  } else {
863
854
  width = simpleWidth(node);
864
855
  height = simpleHeight(node);
@@ -10529,119 +10520,6 @@ function bakeEdgeFade(source, fade) {
10529
10520
  }
10530
10521
  const SELECTION_PRIMARY = "hsl(217, 91%, 60%)";
10531
10522
  const SELECTION_BORDER_SCALE = 2;
10532
- const normalizeAngle180 = (angle) => {
10533
- const n = (angle % 360 + 360) % 360;
10534
- return n > 180 ? n - 360 : n;
10535
- };
10536
- const isValidLogicalGroupFrame = (frame) => !!frame && Number.isFinite(frame.left) && Number.isFinite(frame.top) && Number.isFinite(frame.width) && frame.width > 0 && Number.isFinite(frame.height) && frame.height > 0;
10537
- const frameFromStoredGroupNode = (group, pageChildren, options) => {
10538
- const width = Number(group.width);
10539
- const height = Number(group.height);
10540
- if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) return null;
10541
- try {
10542
- const abs = getAbsoluteBounds(group, pageChildren, options);
10543
- return { left: abs.left, top: abs.top, width, height };
10544
- } catch {
10545
- return null;
10546
- }
10547
- };
10548
- const orientedFrameFromWorldPoints = (points, angle) => {
10549
- if (points.length === 0 || !Number.isFinite(angle)) return null;
10550
- const rad = -angle * Math.PI / 180;
10551
- const cos = Math.cos(rad);
10552
- const sin = Math.sin(rad);
10553
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
10554
- for (const p of points) {
10555
- const xr = p.x * cos - p.y * sin;
10556
- const yr = p.x * sin + p.y * cos;
10557
- minX = Math.min(minX, xr);
10558
- minY = Math.min(minY, yr);
10559
- maxX = Math.max(maxX, xr);
10560
- maxY = Math.max(maxY, yr);
10561
- }
10562
- if (![minX, minY, maxX, maxY].every(Number.isFinite)) return null;
10563
- const width = Math.max(1, maxX - minX);
10564
- const height = Math.max(1, maxY - minY);
10565
- const centerXRot = minX + width / 2;
10566
- const centerYRot = minY + height / 2;
10567
- const back = angle * Math.PI / 180;
10568
- const c = Math.cos(back);
10569
- const s = Math.sin(back);
10570
- const centerX = centerXRot * c - centerYRot * s;
10571
- const centerY = centerXRot * s + centerYRot * c;
10572
- return { left: centerX - width / 2, top: centerY - height / 2, width, height };
10573
- };
10574
- const collectFabricObjectWorldPoints = (objects) => {
10575
- const points = [];
10576
- for (const obj of objects) {
10577
- try {
10578
- obj.setCoords();
10579
- } catch {
10580
- }
10581
- const coords = typeof obj.getCoords === "function" ? obj.getCoords() : null;
10582
- if (Array.isArray(coords) && coords.length) {
10583
- points.push(...coords.map((p) => ({ x: p.x, y: p.y })));
10584
- continue;
10585
- }
10586
- const aC = obj.aCoords;
10587
- if (!aC) continue;
10588
- for (const key of ["tl", "tr", "br", "bl"]) {
10589
- const p = aC[key];
10590
- if (p) points.push({ x: p.x, y: p.y });
10591
- }
10592
- }
10593
- return points;
10594
- };
10595
- const collectFabricObjectWorldCenters = (objects) => {
10596
- var _a2;
10597
- const centers = [];
10598
- for (const obj of objects) {
10599
- try {
10600
- obj.setCoords();
10601
- } catch {
10602
- }
10603
- const coords = typeof obj.getCoords === "function" ? obj.getCoords() : null;
10604
- if (Array.isArray(coords) && coords.length >= 4) {
10605
- const pts = coords.slice(0, 4).map((p) => ({ x: Number(p.x), y: Number(p.y) })).filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
10606
- if (pts.length) centers.push({ x: pts.reduce((sum, p) => sum + p.x, 0) / pts.length, y: pts.reduce((sum, p) => sum + p.y, 0) / pts.length });
10607
- continue;
10608
- }
10609
- try {
10610
- const center = (_a2 = obj.getCenterPoint) == null ? void 0 : _a2.call(obj);
10611
- if (center && Number.isFinite(center.x) && Number.isFinite(center.y)) centers.push({ x: center.x, y: center.y });
10612
- } catch {
10613
- }
10614
- }
10615
- return centers;
10616
- };
10617
- const logicalFrameContainsWorldCenters = (frame, angle, centers) => {
10618
- if (!isValidLogicalGroupFrame(frame) || centers.length === 0) return true;
10619
- const a = typeof angle === "number" && Number.isFinite(angle) ? normalizeAngle180(angle) : 0;
10620
- const cx = frame.left + frame.width / 2;
10621
- const cy = frame.top + frame.height / 2;
10622
- const rad = -a * Math.PI / 180;
10623
- const c = Math.cos(rad);
10624
- const s = Math.sin(rad);
10625
- const tolerance = Math.max(8, Math.min(frame.width, frame.height) * 0.08);
10626
- return centers.every((p) => {
10627
- const dx = p.x - cx;
10628
- const dy = p.y - cy;
10629
- const localX = cx + dx * c - dy * s;
10630
- const localY = cy + dx * s + dy * c;
10631
- return localX >= frame.left - tolerance && localX <= frame.left + frame.width + tolerance && localY >= frame.top - tolerance && localY <= frame.top + frame.height + tolerance;
10632
- });
10633
- };
10634
- const shouldRepairUnrotatedLogicalGroupFrame = (storedFrame, contentFrame, angle) => {
10635
- if (!isValidLogicalGroupFrame(storedFrame) || !isValidLogicalGroupFrame(contentFrame)) return false;
10636
- if (Math.abs(normalizeAngle180(typeof angle === "number" && Number.isFinite(angle) ? angle : 0)) > 0.5) return false;
10637
- const storedArea = storedFrame.width * storedFrame.height;
10638
- const contentArea = contentFrame.width * contentFrame.height;
10639
- const areaRatio = storedArea / Math.max(1, contentArea);
10640
- const dx = Math.abs(storedFrame.left + storedFrame.width / 2 - (contentFrame.left + contentFrame.width / 2));
10641
- const dy = Math.abs(storedFrame.top + storedFrame.height / 2 - (contentFrame.top + contentFrame.height / 2));
10642
- const driftTolerance = Math.max(12, Math.min(storedFrame.width, storedFrame.height) * 0.08);
10643
- return areaRatio > 1.35 || dx > driftTolerance || dy > driftTolerance;
10644
- };
10645
10523
  let ensureCanvaControlRenders = () => {
10646
10524
  };
10647
10525
  try {
@@ -11572,7 +11450,7 @@ const bakeTextboxScaleIntoTypography = (obj, sourceElement) => {
11572
11450
  };
11573
11451
  return updates;
11574
11452
  };
11575
- function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferredGroupFrame) {
11453
+ function applyWarpAwareSelectionBorders(selection) {
11576
11454
  var _a2;
11577
11455
  if (selection.__pixldocsOrigASHasBorders !== void 0) {
11578
11456
  selection.hasBorders = selection.__pixldocsOrigASHasBorders;
@@ -11584,13 +11462,17 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11584
11462
  if (selection.__pixldocsAlignedAngle == null) {
11585
11463
  const kids = selection.getObjects();
11586
11464
  if (kids.length >= 1) {
11587
- const angleDelta = (a, b) => Math.abs(normalizeAngle180(a - b));
11465
+ const norm = (a) => {
11466
+ const n = (a % 360 + 360) % 360;
11467
+ return n > 180 ? n - 360 : n;
11468
+ };
11469
+ const angleDelta = (a, b) => Math.abs(norm(a - b));
11588
11470
  const selectionMatrix = selection.calcTransformMatrix();
11589
11471
  const worldMatrices = kids.map((k) => fabric__namespace.util.multiplyTransformMatrices(
11590
11472
  selectionMatrix,
11591
11473
  k.calcOwnMatrix()
11592
11474
  ));
11593
- const worldAngles = worldMatrices.map((m) => normalizeAngle180(fabric__namespace.util.qrDecompose(m).angle ?? 0));
11475
+ const worldAngles = worldMatrices.map((m) => norm(fabric__namespace.util.qrDecompose(m).angle ?? 0));
11594
11476
  const worldPoints = [];
11595
11477
  for (const k of kids) {
11596
11478
  try {
@@ -11635,40 +11517,15 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11635
11517
  for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
11636
11518
  buckets.sort((a, b) => b.count - a.count || a.area - b.area || Math.abs(b.angle) - Math.abs(a.angle));
11637
11519
  const dominant = buckets[0];
11638
- let effectivePreferredGroupAngle = preferredGroupAngle;
11639
- let effectivePreferredGroupFrame = preferredGroupFrame;
11640
- const logicalGroupId = selection.__pixldocsGroupSelection;
11641
- if (logicalGroupId && (!(typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle)) || !isValidLogicalGroupFrame(effectivePreferredGroupFrame))) {
11642
- try {
11643
- const stateNow = useEditorStore.getState();
11644
- const pageNow = stateNow.canvas.pages.find((p) => p.id === stateNow.canvas.currentPageId);
11645
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], logicalGroupId) : null;
11646
- if (groupNode && isGroup(groupNode)) {
11647
- if (!(typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle)) && typeof groupNode.angle === "number") {
11648
- effectivePreferredGroupAngle = groupNode.angle;
11649
- }
11650
- if (!isValidLogicalGroupFrame(effectivePreferredGroupFrame)) {
11651
- effectivePreferredGroupFrame = frameFromStoredGroupNode(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []);
11652
- }
11653
- }
11654
- } catch {
11655
- }
11520
+ let targetAngle = null;
11521
+ const isLogicalGroupSelection = !!selection.__pixldocsGroupSelection;
11522
+ const frozenGroupAngle = selection.__pixldocsFrozenGroupAngle;
11523
+ if (isLogicalGroupSelection && typeof frozenGroupAngle === "number" && Math.abs(frozenGroupAngle) > 0.5) {
11524
+ targetAngle = frozenGroupAngle;
11656
11525
  }
11657
- const rawPreferredGroupAngle = typeof effectivePreferredGroupAngle === "number" && Number.isFinite(effectivePreferredGroupAngle) ? effectivePreferredGroupAngle : typeof selection.__pixldocsGroupAngle === "number" && Number.isFinite(selection.__pixldocsGroupAngle) ? selection.__pixldocsGroupAngle : null;
11658
- const preferredAngle = rawPreferredGroupAngle != null ? normalizeAngle180(rawPreferredGroupAngle) : null;
11659
- let targetAngle = preferredAngle;
11660
- const isLogicalGroupSelection = !!logicalGroupId;
11661
11526
  if (targetAngle == null && dominant && Math.abs(dominant.angle) > 0.5 && (isLogicalGroupSelection || kids.length === 1 || dominant.count >= 2 || dominant.count === kids.length)) {
11662
11527
  targetAngle = dominant.angle;
11663
11528
  }
11664
- if (isLogicalGroupSelection && isValidLogicalGroupFrame(effectivePreferredGroupFrame) && targetAngle != null) {
11665
- const contentFrame = orientedFrameFromWorldPoints(worldPoints, targetAngle);
11666
- if (shouldRepairUnrotatedLogicalGroupFrame(effectivePreferredGroupFrame, contentFrame, targetAngle)) {
11667
- effectivePreferredGroupFrame = contentFrame;
11668
- } else if (!logicalFrameContainsWorldCenters(effectivePreferredGroupFrame, targetAngle, collectFabricObjectWorldCenters(kids))) {
11669
- effectivePreferredGroupFrame = null;
11670
- }
11671
- }
11672
11529
  if (targetAngle != null) {
11673
11530
  const restoreKidsFromWorld = () => {
11674
11531
  const invSelection = fabric__namespace.util.invertTransform(
@@ -11707,30 +11564,11 @@ function applyWarpAwareSelectionBorders(selection, preferredGroupAngle, preferre
11707
11564
  targetAngle,
11708
11565
  worldAngles
11709
11566
  });
11710
- const fixedFrame = effectivePreferredGroupFrame && Number.isFinite(effectivePreferredGroupFrame.left) && Number.isFinite(effectivePreferredGroupFrame.top) && Number.isFinite(effectivePreferredGroupFrame.width) && effectivePreferredGroupFrame.width > 0 && Number.isFinite(effectivePreferredGroupFrame.height) && effectivePreferredGroupFrame.height > 0 ? effectivePreferredGroupFrame : null;
11711
11567
  selection.set({ angle: targetAngle, scaleX: 1, scaleY: 1, skewX: 0, skewY: 0 });
11712
- if (fixedFrame) {
11713
- selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11714
- selection.setPositionByOrigin(
11715
- new fabric__namespace.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11716
- "center",
11717
- "center"
11718
- );
11719
- }
11720
11568
  restoreKidsFromWorld();
11721
- if (!fixedFrame) {
11722
- try {
11723
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11724
- } catch {
11725
- }
11726
- }
11727
- if (fixedFrame) {
11728
- selection.set({ width: fixedFrame.width, height: fixedFrame.height, originX: "center", originY: "center" });
11729
- selection.setPositionByOrigin(
11730
- new fabric__namespace.Point(fixedFrame.left + fixedFrame.width / 2, fixedFrame.top + fixedFrame.height / 2),
11731
- "center",
11732
- "center"
11733
- );
11569
+ try {
11570
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11571
+ } catch {
11734
11572
  }
11735
11573
  restoreKidsFromWorld();
11736
11574
  selection.setCoords();
@@ -11819,66 +11657,22 @@ const PageCanvas = react.forwardRef(
11819
11657
  const [ready, setReady] = react.useState(false);
11820
11658
  const [unlockRequestId, setUnlockRequestId] = react.useState(0);
11821
11659
  const applyLogicalGroupSelectionVisualState = react.useCallback((selection, groupId) => {
11822
- var _a2, _b2;
11660
+ var _a2;
11823
11661
  selection.__pixldocsGroupSelection = groupId;
11824
11662
  delete selection.__pixldocsLogicalGroupIds;
11825
- const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11826
- const groupNode = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11827
- let groupAngle = groupNode && isGroup(groupNode) && typeof groupNode.angle === "number" ? groupNode.angle : void 0;
11828
- const members = selection.getObjects();
11829
- if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11830
- const storedW = Number(groupNode.width);
11831
- const storedH = Number(groupNode.height);
11832
- if (storedW > 0 && storedH > 0) {
11833
- groupAngle = 0;
11834
- }
11835
- }
11836
- if (groupAngle === void 0 && groupNode && isGroup(groupNode)) {
11837
- try {
11838
- const selectionMatrix = selection.calcTransformMatrix();
11839
- const worldAngles = members.map((kid) => normalizeAngle180(fabric__namespace.util.qrDecompose(
11840
- fabric__namespace.util.multiplyTransformMatrices(
11841
- selectionMatrix,
11842
- kid.calcOwnMatrix()
11843
- )
11844
- ).angle ?? 0));
11845
- const buckets = [];
11846
- for (const angle of worldAngles) {
11847
- const bucket = buckets.find((candidate) => Math.abs(normalizeAngle180(candidate.angle - angle)) <= 2);
11848
- if (bucket) bucket.count += 1;
11849
- else buckets.push({ angle, count: 1 });
11850
- }
11851
- buckets.sort((a, b) => b.count - a.count || Math.abs(b.angle) - Math.abs(a.angle));
11852
- const inferredAngle = (_a2 = buckets[0]) == null ? void 0 : _a2.angle;
11853
- if (typeof inferredAngle === "number" && Number.isFinite(inferredAngle) && Math.abs(inferredAngle) > 0.01) {
11854
- groupAngle = inferredAngle;
11855
- }
11856
- } catch {
11857
- }
11858
- }
11859
- let groupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, (pageNow == null ? void 0 : pageNow.children) ?? []) : null;
11860
- 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;
11663
+ selection.hasBorders = true;
11664
+ try {
11665
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
11666
+ const node = pageNow ? findNodeById(pageNow.children ?? [], groupId) : null;
11667
+ const ang = node && isGroup(node) ? node.angle ?? 0 : 0;
11668
+ if (Math.abs(ang) > 0.01) {
11669
+ selection.__pixldocsFrozenGroupAngle = ang;
11670
+ } else {
11671
+ delete selection.__pixldocsFrozenGroupAngle;
11877
11672
  }
11878
- } else {
11879
- delete selection.__pixldocsGroupAngle;
11673
+ } catch {
11880
11674
  }
11881
- selection.hasBorders = true;
11675
+ const members = selection.getObjects();
11882
11676
  for (const prev of suppressGroupMemberBordersRef.current) {
11883
11677
  if (members.includes(prev)) continue;
11884
11678
  const origBorders = prev.__pixldocsOrigHasBorders;
@@ -11901,7 +11695,7 @@ const PageCanvas = react.forwardRef(
11901
11695
  if (m.__pixldocsOrigHasControls === void 0) m.__pixldocsOrigHasControls = m.hasControls;
11902
11696
  m.hasBorders = false;
11903
11697
  m.hasControls = false;
11904
- if (m.__cropGroup || ((_b2 = m._ct) == null ? void 0 : _b2.isCropGroup)) {
11698
+ if (m.__cropGroup || ((_a2 = m._ct) == null ? void 0 : _a2.isCropGroup)) {
11905
11699
  if (m.__pixldocsOrigLockScalingX === void 0) {
11906
11700
  m.__pixldocsOrigLockScalingX = m.lockScalingX;
11907
11701
  m.__pixldocsOrigLockScalingY = m.lockScalingY;
@@ -11910,8 +11704,8 @@ const PageCanvas = react.forwardRef(
11910
11704
  m.lockScalingY = false;
11911
11705
  }
11912
11706
  }
11913
- applyWarpAwareSelectionBorders(selection, groupAngle, groupFrame);
11914
- }, [pageId]);
11707
+ applyWarpAwareSelectionBorders(selection);
11708
+ }, []);
11915
11709
  const pageBoundsOptions = react.useMemo(
11916
11710
  () => ({ pageContentWidth: canvasWidth, pageContentHeight: canvasHeight }),
11917
11711
  [canvasWidth, canvasHeight]
@@ -12013,11 +11807,7 @@ const PageCanvas = react.forwardRef(
12013
11807
  const activeMembers = activeBeforeRestore.getObjects();
12014
11808
  const sameMembers = activeMembers.length === members.length && members.every((member) => activeMembers.includes(member));
12015
11809
  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);
11810
+ const alreadyAligned = activeBeforeRestore.__pixldocsAlignedAngle != null;
12021
11811
  if (sameMembers && sameGroup && alreadyAligned) {
12022
11812
  ensureCanvaControlRenders(activeBeforeRestore);
12023
11813
  return;
@@ -12034,11 +11824,9 @@ const PageCanvas = react.forwardRef(
12034
11824
  members.forEach((m) => m.setCoords());
12035
11825
  } catch {
12036
11826
  }
12037
- if (!selection.__pixldocsGroupSelection) {
12038
- try {
12039
- (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
12040
- } catch {
12041
- }
11827
+ try {
11828
+ (_a2 = selection.triggerLayout) == null ? void 0 : _a2.call(selection);
11829
+ } catch {
12042
11830
  }
12043
11831
  selection.setCoords();
12044
11832
  fc.requestRenderAll();
@@ -13478,18 +13266,14 @@ const PageCanvas = react.forwardRef(
13478
13266
  const groupNode = findNodeById(pageChildren2, groupId);
13479
13267
  if (!groupNode) return;
13480
13268
  const groupAbs = getAbsoluteBounds(groupNode, pageChildren2);
13481
- const storedGroupFrame = groupNode && isGroup(groupNode) ? frameFromStoredGroupNode(groupNode, pageChildren2, pageBoundsOptions) : null;
13482
- const groupFrame = storedGroupFrame ?? groupAbs;
13483
13269
  const rect = active.getBoundingRect();
13484
13270
  groupSelectionTransformStartRef.current = {
13485
13271
  groupId,
13486
13272
  selection: active,
13487
13273
  selectionLeft: rect.left,
13488
13274
  selectionTop: rect.top,
13489
- groupLeft: groupFrame.left,
13490
- groupTop: groupFrame.top,
13491
- groupWidth: groupFrame.width,
13492
- groupHeight: groupFrame.height,
13275
+ groupLeft: groupAbs.left,
13276
+ groupTop: groupAbs.top,
13493
13277
  selectionAngle: ((active.angle ?? 0) % 360 + 360) % 360
13494
13278
  };
13495
13279
  logRotDriftSelectionSnapshot("transform-start", active, {
@@ -13657,11 +13441,7 @@ const PageCanvas = react.forwardRef(
13657
13441
  const activeIds = active.getObjects().map((obj) => getObjectId(obj)).filter((id) => !!id && id !== "__background__");
13658
13442
  const sameMembers = activeIds.length === snapshot.memberIds.length && snapshot.memberIds.every((id) => activeIds.includes(id));
13659
13443
  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);
13444
+ const alreadyAligned = active.__pixldocsAlignedAngle != null;
13665
13445
  if (sameMembers && sameGroup && alreadyAligned) {
13666
13446
  ensureCanvaControlRenders(active);
13667
13447
  return;
@@ -13817,32 +13597,15 @@ const PageCanvas = react.forwardRef(
13817
13597
  if (oid && memberIds.has(oid)) members.push(o);
13818
13598
  }
13819
13599
  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
- }
13600
+ const TOL = 2;
13601
+ const norm = (a) => {
13602
+ const n = (a % 360 + 360) % 360;
13603
+ return n > 180 ? n - 360 : n;
13604
+ };
13605
+ const angleDelta = (a, b) => {
13606
+ const d = Math.abs(norm(a) - norm(b));
13607
+ return Math.min(d, 360 - d);
13608
+ };
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,37 @@ 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
- };
13619
+ const orientedAreaForAngle = (angle) => {
13620
+ if (worldPoints.length === 0) return Number.POSITIVE_INFINITY;
13621
+ const r = -angle * Math.PI / 180;
13622
+ const c = Math.cos(r), s = Math.sin(r);
13623
+ let nX = Infinity, nY = Infinity, xX = -Infinity, xY = -Infinity;
13624
+ for (const p of worldPoints) {
13625
+ const xr = p.x * c - p.y * s;
13626
+ const yr = p.x * s + p.y * c;
13627
+ if (xr < nX) nX = xr;
13628
+ if (yr < nY) nY = yr;
13629
+ if (xr > xX) xX = xr;
13630
+ if (yr > xY) xY = yr;
13631
+ }
13632
+ return Math.max(1, (xX - nX) * (xY - nY));
13633
+ };
13634
+ let a0;
13635
+ const savedGroupAngle = norm(g.angle ?? 0);
13636
+ if (Math.abs(savedGroupAngle) > 0.01) {
13637
+ a0 = savedGroupAngle;
13638
+ } else {
13873
13639
  const buckets = [];
13874
13640
  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);
13641
+ const a = norm(m.angle ?? 0);
13642
+ const b = buckets.find((x) => angleDelta(x.angle, a) <= TOL);
13877
13643
  if (b) b.count++;
13878
13644
  else buckets.push({ angle: a, count: 1, area: Number.POSITIVE_INFINITY });
13879
13645
  }
13880
- for (const b of buckets) b.area = areaFor(b.angle);
13646
+ for (const b of buckets) b.area = orientedAreaForAngle(b.angle);
13881
13647
  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();
13648
+ a0 = buckets[0].angle;
13649
+ }
13885
13650
  const rad = -a0 * Math.PI / 180;
13886
13651
  const cos = Math.cos(rad), sin = Math.sin(rad);
13887
13652
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
@@ -15082,7 +14847,6 @@ const PageCanvas = react.forwardRef(
15082
14847
  fabricCanvas.on("object:rotating", (e) => {
15083
14848
  var _a2, _b2;
15084
14849
  markSimpleTransform(e);
15085
- prepareGroupSelectionTransformStart(e.target);
15086
14850
  didTransformRef.current = true;
15087
14851
  const tr = e.target;
15088
14852
  if (shouldLogRotDriftLiveTick(tr, "rotating")) {
@@ -15219,6 +14983,29 @@ const PageCanvas = react.forwardRef(
15219
14983
  } catch {
15220
14984
  }
15221
14985
  groupShiftReflowSnapshotRef.current = null;
14986
+ try {
14987
+ const t = e.target;
14988
+ if (t instanceof fabric__namespace.ActiveSelection) {
14989
+ const gid = t.__pixldocsGroupSelection;
14990
+ const delta = ((t.angle ?? 0) + 360) % 360;
14991
+ const deltaSigned = delta > 180 ? delta - 360 : delta;
14992
+ if (gid && Math.abs(deltaSigned) > 0.01) {
14993
+ const pageNow = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId);
14994
+ const node = pageNow ? findNodeById(pageNow.children ?? [], gid) : null;
14995
+ if (node && isGroup(node)) {
14996
+ const prev = node.angle ?? 0;
14997
+ const next = ((prev + deltaSigned) % 360 + 360) % 360;
14998
+ const nextSigned = next > 180 ? next - 360 : next;
14999
+ useEditorStore.getState().updateNode(
15000
+ gid,
15001
+ { angle: nextSigned },
15002
+ { recordHistory: false, skipLayoutRecalc: true }
15003
+ );
15004
+ }
15005
+ }
15006
+ }
15007
+ } catch {
15008
+ }
15222
15009
  lockEdits();
15223
15010
  const modifiedTarget = e.target;
15224
15011
  const modifiedTargetId = modifiedTarget ? getObjectId(modifiedTarget) : null;
@@ -15272,7 +15059,7 @@ const PageCanvas = react.forwardRef(
15272
15059
  const groupLeft = fabricCenterX - w / 2;
15273
15060
  const groupTop = fabricCenterY - h / 2;
15274
15061
  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 });
15062
+ useEditorStore.getState().updateNode(movedGroupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15276
15063
  }
15277
15064
  commitHistory();
15278
15065
  unlockEditsSoon();
@@ -15415,7 +15202,7 @@ const PageCanvas = react.forwardRef(
15415
15202
  const groupLeft = centerX - w / 2;
15416
15203
  const groupTop = centerY - h / 2;
15417
15204
  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 });
15205
+ useEditorStore.getState().updateNode(groupId, { left: storePosGroup.left, top: storePosGroup.top }, { recordHistory: false, skipLayoutRecalc: true });
15419
15206
  }
15420
15207
  const node = findNodeById(pageChildrenSec, groupId);
15421
15208
  if (isChildModified && node && !groupMoved) {
@@ -15444,7 +15231,7 @@ const PageCanvas = react.forwardRef(
15444
15231
  const groupLeft = active.originX === "center" ? centerX - w / 2 : centerX;
15445
15232
  const groupTop = active.originY === "center" ? centerY - h / 2 : centerY;
15446
15233
  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 });
15234
+ useEditorStore.getState().updateNode(groupId, { left: storePos.left, top: storePos.top }, { recordHistory: false, skipLayoutRecalc: true });
15448
15235
  commitHistory();
15449
15236
  unlockEditsSoon();
15450
15237
  return;
@@ -15626,38 +15413,6 @@ const PageCanvas = react.forwardRef(
15626
15413
  360 - Math.abs(currentSelAngle - startSelAngle)
15627
15414
  );
15628
15415
  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
15416
  if (!hadScale && !hadRotation && (Math.abs(deltaX) > 0.1 || Math.abs(deltaY) > 0.1)) {
15662
15417
  const { updateNode: updateNodeStore, commitHistory: commitHistoryStore, getCurrentElements } = useEditorStore.getState();
15663
15418
  const newLeft = (groupToMove.left ?? 0) + deltaX;
@@ -16293,11 +16048,9 @@ const PageCanvas = react.forwardRef(
16293
16048
  } catch {
16294
16049
  }
16295
16050
  fabricCanvas.setActiveObject(newSel);
16296
- if (!wasGroupSel) {
16297
- try {
16298
- (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16299
- } catch {
16300
- }
16051
+ try {
16052
+ (_j = newSel.triggerLayout) == null ? void 0 : _j.call(newSel);
16053
+ } catch {
16301
16054
  }
16302
16055
  try {
16303
16056
  for (const member of membersToReselect) member.setCoords();
@@ -16674,12 +16427,7 @@ const PageCanvas = react.forwardRef(
16674
16427
  const prevAS = activeBeforeSync instanceof fabric__namespace.ActiveSelection ? activeBeforeSync : null;
16675
16428
  const prevMembers = prevAS ? prevAS.getObjects() : [];
16676
16429
  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);
16430
+ const alreadyAligned = sameMembers && prevAS.__pixldocsAlignedAngle != null;
16683
16431
  if (sameMembers && alreadyAligned) {
16684
16432
  try {
16685
16433
  ensureCanvaControlRenders(prevAS);
@@ -17721,10 +17469,11 @@ const PageCanvas = react.forwardRef(
17721
17469
  if (sameSelection && (isFlatGroupSelection || isPureSingleGroupSelection)) {
17722
17470
  if (selectedGroupSelectionId && active instanceof fabric__namespace.ActiveSelection) {
17723
17471
  if (isPureSingleGroupSelection) {
17724
- applyLogicalGroupSelectionVisualState(active, selectedGroupSelectionId);
17472
+ active.__pixldocsGroupSelection = selectedGroupSelectionId;
17473
+ delete active.__pixldocsLogicalGroupIds;
17474
+ suppressGroupMemberBordersRef.current = active.getObjects();
17725
17475
  } else {
17726
17476
  delete active.__pixldocsGroupSelection;
17727
- delete active.__pixldocsGroupAngle;
17728
17477
  active.__pixldocsLogicalGroupIds = selectedGroupIds;
17729
17478
  active.hasBorders = true;
17730
17479
  suppressGroupMemberBordersRef.current = active.getObjects().filter((m) => {
@@ -17764,10 +17513,10 @@ const PageCanvas = react.forwardRef(
17764
17513
  const selection = new fabric__namespace.ActiveSelection(toSelect, { canvas: fc });
17765
17514
  if (selectedGroupSelectionId) {
17766
17515
  if (isPureSingleGroupSelection) {
17767
- applyLogicalGroupSelectionVisualState(selection, selectedGroupSelectionId);
17516
+ selection.__pixldocsGroupSelection = selectedGroupSelectionId;
17517
+ suppressGroupMemberBordersRef.current = toSelect;
17768
17518
  } else {
17769
17519
  selection.__pixldocsLogicalGroupIds = selectedGroupIds;
17770
- delete selection.__pixldocsGroupAngle;
17771
17520
  selection.hasBorders = true;
17772
17521
  suppressGroupMemberBordersRef.current = toSelect.filter((m) => {
17773
17522
  const id = getObjectId(m);
@@ -25410,9 +25159,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
25410
25159
  }
25411
25160
  return svgString;
25412
25161
  }
25413
- const resolvedPackageVersion = "0.5.398";
25162
+ const resolvedPackageVersion = "0.5.399";
25414
25163
  const PACKAGE_VERSION = resolvedPackageVersion;
25415
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.398";
25164
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.399";
25416
25165
  const roundParityValue = (value) => {
25417
25166
  if (typeof value !== "number") return value;
25418
25167
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26226,7 +25975,7 @@ class PixldocsRenderer {
26226
25975
  await this.waitForCanvasScene(container, cloned, i);
26227
25976
  }
26228
25977
  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"));
25978
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Bvm0vPj2.cjs"));
26230
25979
  const prepared = preparePagesForExport(
26231
25980
  cloned.pages,
26232
25981
  canvasWidth,
@@ -28546,7 +28295,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
28546
28295
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
28547
28296
  sanitizeSvgTreeForPdf(svgToDraw);
28548
28297
  try {
28549
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-nSxpHANQ.cjs"));
28298
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Bvm0vPj2.cjs"));
28550
28299
  try {
28551
28300
  await logTextMeasurementDiagnostic(svgToDraw);
28552
28301
  } catch {
@@ -28943,4 +28692,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
28943
28692
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
28944
28693
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
28945
28694
  exports.warmTemplateFromForm = warmTemplateFromForm;
28946
- //# sourceMappingURL=index-YhKOXBPt.cjs.map
28695
+ //# sourceMappingURL=index-BJ-DTm0r.cjs.map