@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.
@@ -1027,7 +1027,7 @@ const defaultProjectSettings = {
1027
1027
  snapToGrid: false,
1028
1028
  gridSize: 20,
1029
1029
  snapToGuides: true,
1030
- snapThreshold: 5
1030
+ snapThreshold: 8
1031
1031
  };
1032
1032
  const defaultPageSettings = {
1033
1033
  backgroundColor: "#ffffff"
@@ -5755,7 +5755,7 @@ function getObjectSnapPoints(obj) {
5755
5755
  }
5756
5756
  function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapToGuides, snapThreshold) {
5757
5757
  if (!snapToGuides) return { guides: [], snapDx: 0, snapDy: 0 };
5758
- const threshold = snapThreshold || 5;
5758
+ const threshold = snapThreshold || 8;
5759
5759
  const newGuides = [];
5760
5760
  const horizontalSnaps = [];
5761
5761
  const verticalSnaps = [];
@@ -5817,13 +5817,115 @@ function calculateSnapGuides(movingObj, canvas, canvasWidth, canvasHeight, snapT
5817
5817
  verticalSnaps.sort((a, b) => a.distance - b.distance);
5818
5818
  const bestSnap = verticalSnaps[0];
5819
5819
  snapDx = bestSnap.delta;
5820
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5820
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5821
5821
  }
5822
5822
  if (horizontalSnaps.length > 0) {
5823
5823
  horizontalSnaps.sort((a, b) => a.distance - b.distance);
5824
5824
  const bestSnap = horizontalSnaps[0];
5825
5825
  snapDy = bestSnap.delta;
5826
- newGuides.push({ ...bestSnap.guide, distance: Math.round(bestSnap.distance) });
5826
+ newGuides.push({ ...bestSnap.guide, kind: "alignment" });
5827
+ }
5828
+ const projectedLeft = moving.left + snapDx;
5829
+ const projectedRight = moving.right + snapDx;
5830
+ moving.centerX + snapDx;
5831
+ const projectedTop = moving.top + snapDy;
5832
+ const projectedBottom = moving.bottom + snapDy;
5833
+ moving.centerY + snapDy;
5834
+ if (snapDx === 0) {
5835
+ let bestPair = null;
5836
+ for (const a of allObjects) {
5837
+ const A = getObjectSnapPoints(a);
5838
+ if (A.right > projectedLeft) continue;
5839
+ if (A.bottom < projectedTop || A.top > projectedBottom) continue;
5840
+ for (const b of allObjects) {
5841
+ if (b === a) continue;
5842
+ const B = getObjectSnapPoints(b);
5843
+ if (B.left < projectedRight) continue;
5844
+ if (B.bottom < projectedTop || B.top > projectedBottom) continue;
5845
+ const gapL = projectedLeft - A.right;
5846
+ const gapR = B.left - projectedRight;
5847
+ const diff = gapR - gapL;
5848
+ const absDiff = Math.abs(diff);
5849
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5850
+ bestPair = { A, B, delta: diff / 2, absDiff };
5851
+ }
5852
+ }
5853
+ }
5854
+ if (bestPair) {
5855
+ snapDx = bestPair.delta;
5856
+ const newLeft = moving.left + snapDx;
5857
+ const newRight = moving.right + snapDx;
5858
+ const equalGap = Math.round(newLeft - bestPair.A.right);
5859
+ const bracketY = (Math.max(bestPair.A.top, projectedTop, bestPair.B.top) + Math.min(bestPair.A.bottom, projectedBottom, bestPair.B.bottom)) / 2;
5860
+ if (equalGap > 0) {
5861
+ newGuides.push({
5862
+ type: "horizontal",
5863
+ position: bracketY,
5864
+ kind: "gap",
5865
+ gap: equalGap,
5866
+ start: bestPair.A.right,
5867
+ end: newLeft,
5868
+ bracketAt: bracketY
5869
+ });
5870
+ newGuides.push({
5871
+ type: "horizontal",
5872
+ position: bracketY,
5873
+ kind: "gap",
5874
+ gap: equalGap,
5875
+ start: newRight,
5876
+ end: bestPair.B.left,
5877
+ bracketAt: bracketY
5878
+ });
5879
+ }
5880
+ }
5881
+ }
5882
+ if (snapDy === 0) {
5883
+ let bestPair = null;
5884
+ for (const a of allObjects) {
5885
+ const A = getObjectSnapPoints(a);
5886
+ if (A.bottom > projectedTop) continue;
5887
+ if (A.right < projectedLeft || A.left > projectedRight) continue;
5888
+ for (const b of allObjects) {
5889
+ if (b === a) continue;
5890
+ const B = getObjectSnapPoints(b);
5891
+ if (B.top < projectedBottom) continue;
5892
+ if (B.right < projectedLeft || B.left > projectedRight) continue;
5893
+ const gapT = projectedTop - A.bottom;
5894
+ const gapB = B.top - projectedBottom;
5895
+ const diff = gapB - gapT;
5896
+ const absDiff = Math.abs(diff);
5897
+ if (absDiff < threshold && (!bestPair || absDiff < bestPair.absDiff)) {
5898
+ bestPair = { A, B, delta: diff / 2, absDiff };
5899
+ }
5900
+ }
5901
+ }
5902
+ if (bestPair) {
5903
+ snapDy = bestPair.delta;
5904
+ const newTop = moving.top + snapDy;
5905
+ const newBottom = moving.bottom + snapDy;
5906
+ const equalGap = Math.round(newTop - bestPair.A.bottom);
5907
+ const bracketX = (Math.max(bestPair.A.left, projectedLeft, bestPair.B.left) + Math.min(bestPair.A.right, projectedRight, bestPair.B.right)) / 2;
5908
+ if (equalGap > 0) {
5909
+ newGuides.push({
5910
+ type: "vertical",
5911
+ position: bracketX,
5912
+ kind: "gap",
5913
+ gap: equalGap,
5914
+ start: bestPair.A.bottom,
5915
+ end: newTop,
5916
+ bracketAt: bracketX
5917
+ });
5918
+ newGuides.push({
5919
+ type: "vertical",
5920
+ position: bracketX,
5921
+ kind: "gap",
5922
+ gap: equalGap,
5923
+ start: newBottom,
5924
+ end: bestPair.B.top,
5925
+ bracketAt: bracketX
5926
+ });
5927
+ }
5928
+ }
5827
5929
  }
5828
5930
  return { guides: newGuides, snapDx, snapDy };
5829
5931
  }
@@ -9957,6 +10059,7 @@ const PageCanvas = react.forwardRef(
9957
10059
  const hasClearedCachesBeforeFirstSyncRef = react.useRef(false);
9958
10060
  const [guides, setGuides] = react.useState([]);
9959
10061
  const [gridResizeLabel, setGridResizeLabel] = react.useState(null);
10062
+ const [hoverBounds, setHoverBounds] = react.useState(null);
9960
10063
  const [rotationLabel, setRotationLabel] = react.useState(null);
9961
10064
  const [sizeLabel, setSizeLabel] = react.useState(null);
9962
10065
  const [ready, setReady] = react.useState(false);
@@ -11298,6 +11401,26 @@ const PageCanvas = react.forwardRef(
11298
11401
  if (editLockRef.current) return;
11299
11402
  const t = opt.target;
11300
11403
  const tid = t ? getObjectId(t) : null;
11404
+ try {
11405
+ const activeIds = new Set(selectedIdsRef.current);
11406
+ const isHoveringSelected = !!(tid && activeIds.has(tid));
11407
+ if (t && tid && tid !== "__background__" && !isHoveringSelected) {
11408
+ const outer = t.group && !t.group.__pixldocsGroupSelection ? t.group : t;
11409
+ outer.setCoords();
11410
+ const br = outer.getBoundingRect();
11411
+ setHoverBounds({
11412
+ left: br.left,
11413
+ top: br.top,
11414
+ width: br.width,
11415
+ height: br.height,
11416
+ angle: 0
11417
+ });
11418
+ } else {
11419
+ setHoverBounds(null);
11420
+ }
11421
+ } catch {
11422
+ setHoverBounds(null);
11423
+ }
11301
11424
  if (t && tid && tid !== "__background__") return;
11302
11425
  try {
11303
11426
  const pointer = fabricCanvas.getPointer(opt.e);
@@ -11310,6 +11433,9 @@ const PageCanvas = react.forwardRef(
11310
11433
  fabricCanvas.defaultCursor = "default";
11311
11434
  }
11312
11435
  });
11436
+ fabricCanvas.on("mouse:out", () => {
11437
+ setHoverBounds(null);
11438
+ });
11313
11439
  fabricCanvas.on("mouse:down", (ev) => {
11314
11440
  if (fabricCanvas._currentTransform) {
11315
11441
  lockEdits();
@@ -11673,6 +11799,7 @@ const PageCanvas = react.forwardRef(
11673
11799
  const snapTarget = fabricCanvas.getActiveObject() ?? obj;
11674
11800
  const { guides: newGuides, snapDx, snapDy } = calculateSnapGuidesCallback(snapTarget);
11675
11801
  setGuides(newGuides);
11802
+ setHoverBounds(null);
11676
11803
  if (snapDx !== 0 || snapDy !== 0) {
11677
11804
  snapTarget.set({ left: (snapTarget.left ?? 0) + snapDx, top: (snapTarget.top ?? 0) + snapDy });
11678
11805
  }
@@ -15160,6 +15287,59 @@ const PageCanvas = react.forwardRef(
15160
15287
  ),
15161
15288
  sectionsOverlay,
15162
15289
  groupBoundsOverlay,
15290
+ canvas.projectSettings.showGrid && (canvas.projectSettings.gridSize ?? 0) > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
15291
+ "svg",
15292
+ {
15293
+ className: "absolute inset-0 pointer-events-none",
15294
+ style: { width: scaledWidth, height: scaledHeight },
15295
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15296
+ children: [
15297
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx(
15298
+ "pattern",
15299
+ {
15300
+ id: `pixldocs-grid-${pageId}`,
15301
+ width: canvas.projectSettings.gridSize,
15302
+ height: canvas.projectSettings.gridSize,
15303
+ patternUnits: "userSpaceOnUse",
15304
+ children: /* @__PURE__ */ jsxRuntime.jsx(
15305
+ "path",
15306
+ {
15307
+ d: `M ${canvas.projectSettings.gridSize} 0 L 0 0 0 ${canvas.projectSettings.gridSize}`,
15308
+ fill: "none",
15309
+ stroke: "currentColor",
15310
+ strokeWidth: 0.5,
15311
+ opacity: 0.18,
15312
+ className: "text-foreground"
15313
+ }
15314
+ )
15315
+ }
15316
+ ) }),
15317
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: canvasWidth, height: canvasHeight, fill: `url(#pixldocs-grid-${pageId})` })
15318
+ ]
15319
+ }
15320
+ ),
15321
+ hoverBounds && !guides.length && /* @__PURE__ */ jsxRuntime.jsx(
15322
+ "svg",
15323
+ {
15324
+ className: "absolute inset-0 pointer-events-none",
15325
+ style: { width: scaledWidth, height: scaledHeight },
15326
+ viewBox: `0 0 ${canvasWidth} ${canvasHeight}`,
15327
+ children: /* @__PURE__ */ jsxRuntime.jsx(
15328
+ "rect",
15329
+ {
15330
+ x: hoverBounds.left,
15331
+ y: hoverBounds.top,
15332
+ width: hoverBounds.width,
15333
+ height: hoverBounds.height,
15334
+ fill: "none",
15335
+ stroke: "hsl(256 80% 58%)",
15336
+ strokeWidth: 1,
15337
+ strokeDasharray: "0",
15338
+ opacity: 0.65
15339
+ }
15340
+ )
15341
+ }
15342
+ ),
15163
15343
  guides.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
15164
15344
  "svg",
15165
15345
  {
@@ -15169,11 +15349,46 @@ const PageCanvas = react.forwardRef(
15169
15349
  children: (() => {
15170
15350
  const seen = /* @__PURE__ */ new Set();
15171
15351
  return guides.filter((guide) => {
15352
+ if (guide.kind === "gap") return true;
15172
15353
  const key = `${guide.type}-${guide.position.toFixed(1)}`;
15173
15354
  if (seen.has(key)) return false;
15174
15355
  seen.add(key);
15175
15356
  return true;
15176
15357
  }).map((guide, i) => {
15358
+ if (guide.kind === "gap" && guide.gap != null && guide.start != null && guide.end != null) {
15359
+ const gapColor = "#ec4899";
15360
+ const label = `${guide.gap}`;
15361
+ const labelW2 = Math.max(22, label.length * 7 + 8);
15362
+ if (guide.type === "horizontal") {
15363
+ const y = guide.bracketAt ?? guide.position;
15364
+ const x1 = guide.start;
15365
+ const x2 = guide.end;
15366
+ const mid = (x1 + x2) / 2;
15367
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15368
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y, x2, y2: y, stroke: gapColor, strokeWidth: 1 }),
15369
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1, y1: y - 4, x2: x1, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15370
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x2, y1: y - 4, x2, y2: y + 4, stroke: gapColor, strokeWidth: 1 }),
15371
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${mid}, ${y - 12})`, children: [
15372
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15373
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
15374
+ ] })
15375
+ ] }, i);
15376
+ } else {
15377
+ const x = guide.bracketAt ?? guide.position;
15378
+ const y1 = guide.start;
15379
+ const y2 = guide.end;
15380
+ const mid = (y1 + y2) / 2;
15381
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
15382
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x, y1, x2: x, y2, stroke: gapColor, strokeWidth: 1 }),
15383
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1, x2: x + 4, y2: y1, stroke: gapColor, strokeWidth: 1 }),
15384
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: x - 4, y1: y2, x2: x + 4, y2, stroke: gapColor, strokeWidth: 1 }),
15385
+ /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${x + 12}, ${mid})`, children: [
15386
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: -labelW2 / 2, y: -9, width: labelW2, height: 16, rx: 4, fill: gapColor }),
15387
+ /* @__PURE__ */ jsxRuntime.jsx("text", { x: 0, y: 3, textAnchor: "middle", fill: "#fff", fontSize: 10, fontFamily: "system-ui, sans-serif", fontWeight: 600, children: label })
15388
+ ] })
15389
+ ] }, i);
15390
+ }
15391
+ }
15177
15392
  const isElementRelative = guide.start !== void 0 && guide.end !== void 0;
15178
15393
  const isActive2 = guide.active === true;
15179
15394
  const strokeColor = isActive2 ? "#22c55e" : isElementRelative ? "#f43f5e" : "#3b82f6";
@@ -21168,9 +21383,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
21168
21383
  }
21169
21384
  return svgString;
21170
21385
  }
21171
- const resolvedPackageVersion = "0.5.240";
21386
+ const resolvedPackageVersion = "0.5.241";
21172
21387
  const PACKAGE_VERSION = resolvedPackageVersion;
21173
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.240";
21388
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.241";
21174
21389
  const roundParityValue = (value) => {
21175
21390
  if (typeof value !== "number") return value;
21176
21391
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -21917,7 +22132,7 @@ class PixldocsRenderer {
21917
22132
  await this.waitForCanvasScene(container, cloned, i);
21918
22133
  }
21919
22134
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
21920
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-D1yCN3sm.cjs"));
22135
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BQhOl3Zh.cjs"));
21921
22136
  const prepared = preparePagesForExport(
21922
22137
  cloned.pages,
21923
22138
  canvasWidth,
@@ -24237,7 +24452,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
24237
24452
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
24238
24453
  sanitizeSvgTreeForPdf(svgToDraw);
24239
24454
  try {
24240
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-D1yCN3sm.cjs"));
24455
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BQhOl3Zh.cjs"));
24241
24456
  try {
24242
24457
  await logTextMeasurementDiagnostic(svgToDraw);
24243
24458
  } catch {
@@ -24634,4 +24849,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
24634
24849
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
24635
24850
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
24636
24851
  exports.warmTemplateFromForm = warmTemplateFromForm;
24637
- //# sourceMappingURL=index-DBOS1ouN.cjs.map
24852
+ //# sourceMappingURL=index-BRI84Wy6.cjs.map