@pixldocs/canvas-renderer 0.5.240 → 0.5.241

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.
@@ -1009,7 +1009,7 @@ const defaultProjectSettings = {
1009
1009
  snapToGrid: false,
1010
1010
  gridSize: 20,
1011
1011
  snapToGuides: true,
1012
- snapThreshold: 5
1012
+ snapThreshold: 8
1013
1013
  };
1014
1014
  const defaultPageSettings = {
1015
1015
  backgroundColor: "#ffffff"
@@ -5737,7 +5737,7 @@ function getObjectSnapPoints(obj) {
5737
5737
  }
5738
5738
  function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5739
5739
  if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };
5740
- const threshold = snapThreshold || 5;
5740
+ const threshold = snapThreshold || 8;
5741
5741
  const newGuides = [];
5742
5742
  const horizontalSnaps = [];
5743
5743
  const verticalSnaps = [];
@@ -5799,13 +5799,115 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5799
5799
  verticalSnaps.sort((a, b) => a.distance - b.distance);
5800
5800
  const bestSnap = verticalSnaps[0];
5801
5801
  snapDx = bestSnap.delta;
5802
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5802
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5803
5803
  }
5804
5804
  if (horizontalSnaps.length > 0) {
5805
5805
  horizontalSnaps.sort((a, b) => a.distance - b.distance);
5806
5806
  const bestSnap = horizontalSnaps[0];
5807
5807
  snapDy = bestSnap.delta;
5808
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5808
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5809
+ }
5810
+ const projectedLeft = moving.left + snapDx;
5811
+ const projectedRight = moving.right + snapDx;
5812
+ moving.centerX + snapDx;
5813
+ const projectedTop = moving.top + snapDy;
5814
+ const projectedBottom = moving.bottom + snapDy;
5815
+ moving.centerY + snapDy;
5816
+ if (snapDx === 0) {
5817
+ let bestPair = null;
5818
+ for (const a of allObjects) {
5819
+ const A = getObjectSnapPoints(a);
5820
+ if (A.right > projectedLeft) continue;
5821
+ if (A.bottom < projectedTop || A.top > projectedBottom) continue;
5822
+ for (const b of allObjects) {
5823
+ if (b === a) continue;
5824
+ const B = getObjectSnapPoints(b);
5825
+ if (B.left < projectedRight) continue;
5826
+ if (B.bottom < projectedTop || B.top > projectedBottom) continue;
5827
+ const gapL = projectedLeft - A.right;
5828
+ const gapR = B.left - projectedRight;
5829
+ const diff = gapR - gapL;
5830
+ const absDiff = Math.abs(diff);
5831
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5832
+ bestPair = { A, B, delta: diff / 2, absDiff };
5833
+ }
5834
+ }
5835
+ }
5836
+ if (bestPair) {
5837
+ snapDx = bestPair.delta;
5838
+ const newLeft = moving.left + snapDx;
5839
+ const newRight = moving.right + snapDx;
5840
+ const equalGap = Math.round(newLeft - bestPair.A.right);
5841
+ const bracketY = (Math.max(bestPair.A.top, projectedTop, bestPair.B.top) + Math.min(bestPair.A.bottom, projectedBottom, bestPair.B.bottom)) / 2;
5842
+ if (equalGap > 0) {
5843
+ newGuides.push({
5844
+ type: "horizontal",
5845
+ position: bracketY,
5846
+ kind: "gap",
5847
+ gap: equalGap,
5848
+ start: bestPair.A.right,
5849
+ end: newLeft,
5850
+ bracketAt: bracketY
5851
+ });
5852
+ newGuides.push({
5853
+ type: "horizontal",
5854
+ position: bracketY,
5855
+ kind: "gap",
5856
+ gap: equalGap,
5857
+ start: newRight,
5858
+ end: bestPair.B.left,
5859
+ bracketAt: bracketY
5860
+ });
5861
+ }
5862
+ }
5863
+ }
5864
+ if (snapDy === 0) {
5865
+ let bestPair = null;
5866
+ for (const a of allObjects) {
5867
+ const A = getObjectSnapPoints(a);
5868
+ if (A.bottom > projectedTop) continue;
5869
+ if (A.right < projectedLeft || A.left > projectedRight) continue;
5870
+ for (const b of allObjects) {
5871
+ if (b === a) continue;
5872
+ const B = getObjectSnapPoints(b);
5873
+ if (B.top < projectedBottom) continue;
5874
+ if (B.right < projectedLeft || B.left > projectedRight) continue;
5875
+ const gapT = projectedTop - A.bottom;
5876
+ const gapB = B.top - projectedBottom;
5877
+ const diff = gapB - gapT;
5878
+ const absDiff = Math.abs(diff);
5879
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5880
+ bestPair = { A, B, delta: diff / 2, absDiff };
5881
+ }
5882
+ }
5883
+ }
5884
+ if (bestPair) {
5885
+ snapDy = bestPair.delta;
5886
+ const newTop = moving.top + snapDy;
5887
+ const newBottom = moving.bottom + snapDy;
5888
+ const equalGap = Math.round(newTop - bestPair.A.bottom);
5889
+ const bracketX = (Math.max(bestPair.A.left, projectedLeft, bestPair.B.left) + Math.min(bestPair.A.right, projectedRight, bestPair.B.right)) / 2;
5890
+ if (equalGap > 0) {
5891
+ newGuides.push({
5892
+ type: "vertical",
5893
+ position: bracketX,
5894
+ kind: "gap",
5895
+ gap: equalGap,
5896
+ start: bestPair.A.bottom,
5897
+ end: newTop,
5898
+ bracketAt: bracketX
5899
+ });
5900
+ newGuides.push({
5901
+ type: "vertical",
5902
+ position: bracketX,
5903
+ kind: "gap",
5904
+ gap: equalGap,
5905
+ start: newBottom,
5906
+ end: bestPair.B.top,
5907
+ bracketAt: bracketX
5908
+ });
5909
+ }
5910
+ }
5809
5911
  }
5810
5912
  return { guides: newGuides, snapDx, snapDy };
5811
5913
  }
@@ -9939,6 +10041,7 @@ const PageCanvas = forwardRef(
9939
10041
  const hasClearedCachesBeforeFirstSyncRef = useRef(false);
9940
10042
  const [guides, setGuides] = useState([]);
9941
10043
  const [gridResizeLabel, setGridResizeLabel] = useState(null);
10044
+ const [hoverBounds, setHoverBounds] = useState(null);
9942
10045
  const [rotationLabel, setRotationLabel] = useState(null);
9943
10046
  const [sizeLabel, setSizeLabel] = useState(null);
9944
10047
  const [ready, setReady] = useState(false);
@@ -11280,6 +11383,26 @@ const PageCanvas = forwardRef(
11280
11383
  if (editLockRef.current) return;
11281
11384
  const t = opt.target;
11282
11385
  const tid = t ? getObjectId(t) : null;
11386
+ try {
11387
+ const activeIds = new Set(selectedIdsRef.current);
11388
+ const isHoveringSelected = !!(tid && activeIds.has(tid));
11389
+ if (t && tid && tid !== "__background__" && !isHoveringSelected) {
11390
+ const outer = t.group && !t.group.__pixldocsGroupSelection ? t.group : t;
11391
+ outer.setCoords();
11392
+ const br = outer.getBoundingRect();
11393
+ setHoverBounds({
11394
+ left: br.left,
11395
+ top: br.top,
11396
+ width: br.width,
11397
+ height: br.height,
11398
+ angle: 0
11399
+ });
11400
+ } else {
11401
+ setHoverBounds(null);
11402
+ }
11403
+ } catch {
11404
+ setHoverBounds(null);
11405
+ }
11283
11406
  if (t && tid && tid !== "__background__") return;
11284
11407
  try {
11285
11408
  const pointer = fabricCanvas.getPointer(opt.e);
@@ -11292,6 +11415,9 @@ const PageCanvas = forwardRef(
11292
11415
  fabricCanvas.defaultCursor = "default";
11293
11416
  }
11294
11417
  });
11418
+ fabricCanvas.on("mouse:out", () => {
11419
+ setHoverBounds(null);
11420
+ });
11295
11421
  fabricCanvas.on("mouse:down", (ev) => {
11296
11422
  if (fabricCanvas._currentTransform) {
11297
11423
  lockEdits();
@@ -11655,6 +11781,7 @@ const PageCanvas = forwardRef(
11655
11781
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11656
11782
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11657
11783
  setGuides(newGuides);
11784
+ setHoverBounds(null);
11658
11785
  if (snapDx !== 0 || snapDy !== 0) {
11659
11786
  snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
11660
11787
  }
@@ -15142,6 +15269,59 @@ const PageCanvas = forwardRef(
15142
15269
  ),
15143
15270
  sectionsOverlay,
15144
15271
  groupBoundsOverlay,
15272
+ canvas.projectSettings.showGrid && (canvas.projectSettings.gridSize ?? 0) > 0 && /* @__PURE__ */ jsxs(
15273
+ "svg",
15274
+ {
15275
+ className: "absolute inset-0 pointer-events-none",
15276
+ style: { width: scaledWidth, height: scaledHeight },
15277
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15278
+ children: [
15279
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
15280
+ "pattern",
15281
+ {
15282
+ id: `pixldocs-grid-${pageId}`,
15283
+ width: canvas.projectSettings.gridSize,
15284
+ height: canvas.projectSettings.gridSize,
15285
+ patternUnits: "userSpaceOnUse",
15286
+ children: /* @__PURE__ */ jsx(
15287
+ "path",
15288
+ {
15289
+ d: `M ${canvas.projectSettings.gridSize} 0 L 0 0 0 ${canvas.projectSettings.gridSize}`,
15290
+ fill: "none",
15291
+ stroke: "currentColor",
15292
+ strokeWidth: 0.5,
15293
+ opacity: 0.18,
15294
+ className: "text-foreground"
15295
+ }
15296
+ )
15297
+ }
15298
+ ) }),
15299
+ /* @__PURE__ */ jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
15300
+ ]
15301
+ }
15302
+ ),
15303
+ hoverBounds && !guides.length && /* @__PURE__ */ jsx(
15304
+ "svg",
15305
+ {
15306
+ className: "absolute inset-0 pointer-events-none",
15307
+ style: { width: scaledWidth, height: scaledHeight },
15308
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15309
+ children: /* @__PURE__ */ jsx(
15310
+ "rect",
15311
+ {
15312
+ x: hoverBounds.left,
15313
+ y: hoverBounds.top,
15314
+ width: hoverBounds.width,
15315
+ height: hoverBounds.height,
15316
+ fill: "none",
15317
+ stroke: "hsl(256 80% 58%)",
15318
+ strokeWidth: 1,
15319
+ strokeDasharray: "0",
15320
+ opacity: 0.65
15321
+ }
15322
+ )
15323
+ }
15324
+ ),
15145
15325
  guides.length > 0 && /* @__PURE__ */ jsx(
15146
15326
  "svg",
15147
15327
  {
@@ -15151,11 +15331,46 @@ const PageCanvas = forwardRef(
15151
15331
  children: (() => {
15152
15332
  const seen = /* @__PURE__ */ new Set();
15153
15333
  return guides.filter((guide) => {
15334
+ if (guide.kind === "gap") return true;
15154
15335
  const key = `${guide.type}-${guide.position.toFixed(1)}`;
15155
15336
  if (seen.has(key)) return false;
15156
15337
  seen.add(key);
15157
15338
  return true;
15158
15339
  }).map((guide, i) => {
15340
+ if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
15341
+ const gapColor = "#ec4899";
15342
+ const label = `${guide.gap}`;
15343
+ const labelW2 = Math.max(22, label.length * 7 + 8);
15344
+ if (guide.type === "horizontal") {
15345
+ const y = guide.bracketAt ?? guide.position;
15346
+ const x1 = guide.start;
15347
+ const x2 = guide.end;
15348
+ const mid = (x1 + x2) / 2;
15349
+ return /* @__PURE__ */ jsxs("g", { children: [
15350
+ /* @__PURE__ */ jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1 }),
15351
+ /* @__PURE__ */ jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15352
+ /* @__PURE__ */ jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15353
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
15354
+ /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15355
+ /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
15356
+ ] })
15357
+ ] }, i);
15358
+ } else {
15359
+ const x = guide.bracketAt ?? guide.position;
15360
+ const y1 = guide.start;
15361
+ const y2 = guide.end;
15362
+ const mid = (y1 + y2) / 2;
15363
+ return /* @__PURE__ */ jsxs("g", { children: [
15364
+ /* @__PURE__ */ jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1 }),
15365
+ /* @__PURE__ */ jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1 }),
15366
+ /* @__PURE__ */ jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1 }),
15367
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
15368
+ /* @__PURE__ */ jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15369
+ /* @__PURE__ */ jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
15370
+ ] })
15371
+ ] }, i);
15372
+ }
15373
+ }
15159
15374
  const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
15160
15375
  const isActive2 = guide.active === true;
15161
15376
  const strokeColor = isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#3b82f6";
@@ -21150,9 +21365,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21150
21365
  }
21151
21366
  return svgString;
21152
21367
  }
21153
- const resolvedPackageVersion = "0.5.240";
21368
+ const resolvedPackageVersion = "0.5.241";
21154
21369
  const PACKAGE_VERSION = resolvedPackageVersion;
21155
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.240";
21370
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.241";
21156
21371
  const roundParityValue = (value) => {
21157
21372
  if (typeof value !== "number") return value;
21158
21373
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21899,7 +22114,7 @@ class PixldocsRenderer {
21899
22114
  await this.waitForCanvasScene(container, cloned, i);
21900
22115
  }
21901
22116
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
21902
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CHVrwSi7.js");
22117
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-BBY4avH9.js");
21903
22118
  const prepared = preparePagesForExport(
21904
22119
  cloned.pages,
21905
22120
  canvasWidth,
@@ -24219,7 +24434,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24219
24434
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24220
24435
  sanitizeSvgTreeForPdf(svgToDraw);
24221
24436
  try {
24222
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CHVrwSi7.js");
24437
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-BBY4avH9.js");
24223
24438
  try {
24224
24439
  await logTextMeasurementDiagnostic(svgToDraw);
24225
24440
  } catch {
@@ -24619,4 +24834,4 @@ export {
24619
24834
  buildTeaserBlurFlatKeys as y,
24620
24835
  collectFontDescriptorsFromConfig as z
24621
24836
  };
24622
- //# sourceMappingURL=index-B2H1tQ0S.js.map
24837
+ //# sourceMappingURL=index-DtyVze4W.js.map