@almadar/ui 5.60.0 → 5.61.0

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.
@@ -2718,24 +2718,28 @@ var GameCanvas3D = React11.forwardRef(
2718
2718
  const cameraConfig = React11.useMemo(() => {
2719
2719
  const size = Math.max(
2720
2720
  gridBounds.maxX - gridBounds.minX,
2721
- gridBounds.maxZ - gridBounds.minZ
2721
+ gridBounds.maxZ - gridBounds.minZ,
2722
+ 4
2723
+ // minimum framing distance so a tiny board isn't zoomed in
2722
2724
  );
2723
- const distance = size * 1.5;
2725
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
2726
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
2727
+ const d = size * 1.5;
2724
2728
  switch (cameraMode) {
2725
2729
  case "isometric":
2726
2730
  return {
2727
- position: [distance, distance * 0.8, distance],
2731
+ position: [cx + d, d * 0.8, cz + d],
2728
2732
  fov: 45
2729
2733
  };
2730
2734
  case "top-down":
2731
2735
  return {
2732
- position: [0, distance * 2, 0],
2736
+ position: [cx, d * 2, cz],
2733
2737
  fov: 45
2734
2738
  };
2735
2739
  case "perspective":
2736
2740
  default:
2737
2741
  return {
2738
- position: [distance, distance, distance],
2742
+ position: [cx + d, d, cz + d],
2739
2743
  fov: 45
2740
2744
  };
2741
2745
  }
@@ -2974,7 +2978,8 @@ var GameCanvas3D = React11.forwardRef(
2974
2978
  "div",
2975
2979
  {
2976
2980
  ref: containerRef,
2977
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
2981
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
2982
+ style: { minHeight: "85vh" },
2978
2983
  "data-orientation": orientation,
2979
2984
  "data-camera-mode": cameraMode,
2980
2985
  "data-overlay": overlay,
@@ -2989,7 +2994,7 @@ var GameCanvas3D = React11.forwardRef(
2989
2994
  near: 0.1,
2990
2995
  far: 1e3
2991
2996
  },
2992
- style: { background: backgroundColor },
2997
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
2993
2998
  onClick: (e) => {
2994
2999
  if (e.target === e.currentTarget) {
2995
3000
  eventHandlers.handleCanvasClick(e);
@@ -2694,24 +2694,28 @@ var GameCanvas3D = forwardRef(
2694
2694
  const cameraConfig = useMemo(() => {
2695
2695
  const size = Math.max(
2696
2696
  gridBounds.maxX - gridBounds.minX,
2697
- gridBounds.maxZ - gridBounds.minZ
2697
+ gridBounds.maxZ - gridBounds.minZ,
2698
+ 4
2699
+ // minimum framing distance so a tiny board isn't zoomed in
2698
2700
  );
2699
- const distance = size * 1.5;
2701
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
2702
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
2703
+ const d = size * 1.5;
2700
2704
  switch (cameraMode) {
2701
2705
  case "isometric":
2702
2706
  return {
2703
- position: [distance, distance * 0.8, distance],
2707
+ position: [cx + d, d * 0.8, cz + d],
2704
2708
  fov: 45
2705
2709
  };
2706
2710
  case "top-down":
2707
2711
  return {
2708
- position: [0, distance * 2, 0],
2712
+ position: [cx, d * 2, cz],
2709
2713
  fov: 45
2710
2714
  };
2711
2715
  case "perspective":
2712
2716
  default:
2713
2717
  return {
2714
- position: [distance, distance, distance],
2718
+ position: [cx + d, d, cz + d],
2715
2719
  fov: 45
2716
2720
  };
2717
2721
  }
@@ -2950,7 +2954,8 @@ var GameCanvas3D = forwardRef(
2950
2954
  "div",
2951
2955
  {
2952
2956
  ref: containerRef,
2953
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
2957
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
2958
+ style: { minHeight: "85vh" },
2954
2959
  "data-orientation": orientation,
2955
2960
  "data-camera-mode": cameraMode,
2956
2961
  "data-overlay": overlay,
@@ -2965,7 +2970,7 @@ var GameCanvas3D = forwardRef(
2965
2970
  near: 0.1,
2966
2971
  far: 1e3
2967
2972
  },
2968
- style: { background: backgroundColor },
2973
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
2969
2974
  onClick: (e) => {
2970
2975
  if (e.target === e.currentTarget) {
2971
2976
  eventHandlers.handleCanvasClick(e);
@@ -7186,9 +7186,6 @@ var init_Skeleton = __esm({
7186
7186
  Skeleton.displayName = "Skeleton";
7187
7187
  }
7188
7188
  });
7189
- function isKnownPattern(type) {
7190
- return type in componentMapping;
7191
- }
7192
7189
  function getKnownPatterns() {
7193
7190
  return Object.keys(componentMapping);
7194
7191
  }
@@ -7284,7 +7281,6 @@ var init_slot_definitions = __esm({
7284
7281
  // renderer/index.ts
7285
7282
  var init_renderer = __esm({
7286
7283
  "renderer/index.ts"() {
7287
- init_pattern_resolver();
7288
7284
  init_slot_definitions();
7289
7285
  }
7290
7286
  });
@@ -38424,6 +38420,7 @@ var init_GraphCanvas = __esm({
38424
38420
  draggable = true,
38425
38421
  actions,
38426
38422
  onNodeClick,
38423
+ onNodeDoubleClick,
38427
38424
  nodeClickEvent,
38428
38425
  selectedNodeId,
38429
38426
  repulsion = 800,
@@ -38443,6 +38440,19 @@ var init_GraphCanvas = __esm({
38443
38440
  const [hoveredNode, setHoveredNode] = React76.useState(null);
38444
38441
  const nodesRef = React76.useRef([]);
38445
38442
  const [, forceUpdate] = React76.useState(0);
38443
+ const [logicalW, setLogicalW] = React76.useState(800);
38444
+ React76.useEffect(() => {
38445
+ const canvas = canvasRef.current;
38446
+ if (!canvas || typeof ResizeObserver === "undefined") return;
38447
+ const measure = () => {
38448
+ const width = Math.round(canvas.clientWidth);
38449
+ if (width > 0) setLogicalW(width);
38450
+ };
38451
+ measure();
38452
+ const ro = new ResizeObserver(measure);
38453
+ ro.observe(canvas);
38454
+ return () => ro.disconnect();
38455
+ }, []);
38446
38456
  const interactionRef = React76.useRef({
38447
38457
  mode: "none",
38448
38458
  dragNodeId: null,
@@ -38455,10 +38465,8 @@ var init_GraphCanvas = __esm({
38455
38465
  const canvas = canvasRef.current;
38456
38466
  if (!canvas) return null;
38457
38467
  const rect = canvas.getBoundingClientRect();
38458
- const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
38459
- const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
38460
- const screenX = (e.clientX - rect.left) * scaleX;
38461
- const screenY = (e.clientY - rect.top) * scaleY;
38468
+ const screenX = e.clientX - rect.left;
38469
+ const screenY = e.clientY - rect.top;
38462
38470
  return {
38463
38471
  screenX,
38464
38472
  screenY,
@@ -38498,8 +38506,8 @@ var init_GraphCanvas = __esm({
38498
38506
  React76.useEffect(() => {
38499
38507
  const canvas = canvasRef.current;
38500
38508
  if (!canvas || propNodes.length === 0) return;
38501
- const w = canvas.width;
38502
- const h = canvas.height;
38509
+ const w = logicalW;
38510
+ const h = height;
38503
38511
  const simNodes = propNodes.map((n, idx) => {
38504
38512
  let x = n.x ?? 0;
38505
38513
  let y = n.y ?? 0;
@@ -38589,29 +38597,41 @@ var init_GraphCanvas = __esm({
38589
38597
  return () => {
38590
38598
  cancelAnimationFrame(animRef.current);
38591
38599
  };
38592
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38600
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
38593
38601
  React76.useEffect(() => {
38594
38602
  const canvas = canvasRef.current;
38595
38603
  if (!canvas) return;
38596
38604
  const ctx = canvas.getContext("2d");
38597
38605
  if (!ctx) return;
38598
- const w = canvas.width;
38599
- const h = canvas.height;
38606
+ const w = logicalW;
38607
+ const h = height;
38600
38608
  const nodes = nodesRef.current;
38601
38609
  const accentColor = resolveColor2("var(--color-accent)", canvas);
38610
+ const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
38611
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
38602
38612
  ctx.clearRect(0, 0, w, h);
38603
38613
  ctx.save();
38604
38614
  ctx.translate(offset.x, offset.y);
38605
38615
  ctx.scale(zoom, zoom);
38616
+ const neighbors = /* @__PURE__ */ new Set();
38617
+ if (hoveredNode) {
38618
+ neighbors.add(hoveredNode);
38619
+ for (const edge of propEdges) {
38620
+ if (edge.source === hoveredNode) neighbors.add(String(edge.target));
38621
+ else if (edge.target === hoveredNode) neighbors.add(String(edge.source));
38622
+ }
38623
+ }
38606
38624
  for (const edge of propEdges) {
38607
38625
  const source = nodes.find((n) => n.id === edge.source);
38608
38626
  const target = nodes.find((n) => n.id === edge.target);
38609
38627
  if (!source || !target) continue;
38628
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
38629
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
38610
38630
  ctx.beginPath();
38611
38631
  ctx.moveTo(source.x, source.y);
38612
38632
  ctx.lineTo(target.x, target.y);
38613
- ctx.strokeStyle = edge.color || "#88888866";
38614
- ctx.lineWidth = edge.weight ? Math.max(1, edge.weight) : 1;
38633
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#88888866";
38634
+ ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
38615
38635
  ctx.stroke();
38616
38636
  if (edge.label && showLabels) {
38617
38637
  const mx = (source.x + target.x) / 2;
@@ -38622,11 +38642,13 @@ var init_GraphCanvas = __esm({
38622
38642
  ctx.fillText(edge.label, mx, my - 4);
38623
38643
  }
38624
38644
  }
38645
+ ctx.globalAlpha = 1;
38625
38646
  for (const node of nodes) {
38626
38647
  const size = node.size || 8;
38627
38648
  const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
38628
38649
  const isHovered = hoveredNode === node.id;
38629
38650
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38651
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
38630
38652
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38631
38653
  ctx.beginPath();
38632
38654
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
@@ -38746,6 +38768,15 @@ var init_GraphCanvas = __esm({
38746
38768
  interactionRef.current.dragNodeId = null;
38747
38769
  setHoveredNode(null);
38748
38770
  }, []);
38771
+ const handleDoubleClick = React76.useCallback(
38772
+ (e) => {
38773
+ const coords = toCoords(e);
38774
+ if (!coords) return;
38775
+ const node = nodeAt(coords.graphX, coords.graphY);
38776
+ if (node) onNodeDoubleClick?.(node);
38777
+ },
38778
+ [toCoords, nodeAt, onNodeDoubleClick]
38779
+ );
38749
38780
  if (isLoading) {
38750
38781
  return /* @__PURE__ */ jsxRuntime.jsx(exports.LoadingState, { message: t("common.loading"), className });
38751
38782
  }
@@ -38803,15 +38834,16 @@ var init_GraphCanvas = __esm({
38803
38834
  "canvas",
38804
38835
  {
38805
38836
  ref: canvasRef,
38806
- width: 800,
38807
- height,
38837
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38838
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38808
38839
  className: "w-full cursor-grab active:cursor-grabbing",
38809
38840
  style: { height },
38810
38841
  onWheel: handleWheel,
38811
38842
  onMouseDown: handleMouseDown,
38812
38843
  onMouseMove: handleMouseMove,
38813
38844
  onMouseUp: handleMouseUp,
38814
- onMouseLeave: handleMouseLeave
38845
+ onMouseLeave: handleMouseLeave,
38846
+ onDoubleClick: handleDoubleClick
38815
38847
  }
38816
38848
  ) }),
38817
38849
  groups.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
@@ -43226,24 +43258,28 @@ var init_GameCanvas3D2 = __esm({
43226
43258
  const cameraConfig = React76.useMemo(() => {
43227
43259
  const size = Math.max(
43228
43260
  gridBounds.maxX - gridBounds.minX,
43229
- gridBounds.maxZ - gridBounds.minZ
43261
+ gridBounds.maxZ - gridBounds.minZ,
43262
+ 4
43263
+ // minimum framing distance so a tiny board isn't zoomed in
43230
43264
  );
43231
- const distance = size * 1.5;
43265
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
43266
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
43267
+ const d = size * 1.5;
43232
43268
  switch (cameraMode) {
43233
43269
  case "isometric":
43234
43270
  return {
43235
- position: [distance, distance * 0.8, distance],
43271
+ position: [cx + d, d * 0.8, cz + d],
43236
43272
  fov: 45
43237
43273
  };
43238
43274
  case "top-down":
43239
43275
  return {
43240
- position: [0, distance * 2, 0],
43276
+ position: [cx, d * 2, cz],
43241
43277
  fov: 45
43242
43278
  };
43243
43279
  case "perspective":
43244
43280
  default:
43245
43281
  return {
43246
- position: [distance, distance, distance],
43282
+ position: [cx + d, d, cz + d],
43247
43283
  fov: 45
43248
43284
  };
43249
43285
  }
@@ -43482,7 +43518,8 @@ var init_GameCanvas3D2 = __esm({
43482
43518
  "div",
43483
43519
  {
43484
43520
  ref: containerRef,
43485
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
43521
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
43522
+ style: { minHeight: "85vh" },
43486
43523
  "data-orientation": orientation,
43487
43524
  "data-camera-mode": cameraMode,
43488
43525
  "data-overlay": overlay,
@@ -43497,7 +43534,7 @@ var init_GameCanvas3D2 = __esm({
43497
43534
  near: 0.1,
43498
43535
  far: 1e3
43499
43536
  },
43500
- style: { background: backgroundColor },
43537
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
43501
43538
  onClick: (e) => {
43502
43539
  if (e.target === e.currentTarget) {
43503
43540
  eventHandlers.handleCanvasClick(e);
@@ -52860,7 +52897,7 @@ function isPatternConfig(value) {
52860
52897
  if (value instanceof Date) return false;
52861
52898
  if (typeof value === "function") return false;
52862
52899
  const record = value;
52863
- return "type" in record && typeof record.type === "string" && isKnownPattern(record.type);
52900
+ return "type" in record && typeof record.type === "string" && patterns.getComponentForPattern(record.type) !== null;
52864
52901
  }
52865
52902
  function isPlainConfigObject(value) {
52866
52903
  if (React76__namespace.default.isValidElement(value)) return false;
@@ -7140,9 +7140,6 @@ var init_Skeleton = __esm({
7140
7140
  Skeleton.displayName = "Skeleton";
7141
7141
  }
7142
7142
  });
7143
- function isKnownPattern(type) {
7144
- return type in componentMapping;
7145
- }
7146
7143
  function getKnownPatterns() {
7147
7144
  return Object.keys(componentMapping);
7148
7145
  }
@@ -7238,7 +7235,6 @@ var init_slot_definitions = __esm({
7238
7235
  // renderer/index.ts
7239
7236
  var init_renderer = __esm({
7240
7237
  "renderer/index.ts"() {
7241
- init_pattern_resolver();
7242
7238
  init_slot_definitions();
7243
7239
  }
7244
7240
  });
@@ -38378,6 +38374,7 @@ var init_GraphCanvas = __esm({
38378
38374
  draggable = true,
38379
38375
  actions,
38380
38376
  onNodeClick,
38377
+ onNodeDoubleClick,
38381
38378
  nodeClickEvent,
38382
38379
  selectedNodeId,
38383
38380
  repulsion = 800,
@@ -38397,6 +38394,19 @@ var init_GraphCanvas = __esm({
38397
38394
  const [hoveredNode, setHoveredNode] = useState(null);
38398
38395
  const nodesRef = useRef([]);
38399
38396
  const [, forceUpdate] = useState(0);
38397
+ const [logicalW, setLogicalW] = useState(800);
38398
+ useEffect(() => {
38399
+ const canvas = canvasRef.current;
38400
+ if (!canvas || typeof ResizeObserver === "undefined") return;
38401
+ const measure = () => {
38402
+ const width = Math.round(canvas.clientWidth);
38403
+ if (width > 0) setLogicalW(width);
38404
+ };
38405
+ measure();
38406
+ const ro = new ResizeObserver(measure);
38407
+ ro.observe(canvas);
38408
+ return () => ro.disconnect();
38409
+ }, []);
38400
38410
  const interactionRef = useRef({
38401
38411
  mode: "none",
38402
38412
  dragNodeId: null,
@@ -38409,10 +38419,8 @@ var init_GraphCanvas = __esm({
38409
38419
  const canvas = canvasRef.current;
38410
38420
  if (!canvas) return null;
38411
38421
  const rect = canvas.getBoundingClientRect();
38412
- const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
38413
- const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
38414
- const screenX = (e.clientX - rect.left) * scaleX;
38415
- const screenY = (e.clientY - rect.top) * scaleY;
38422
+ const screenX = e.clientX - rect.left;
38423
+ const screenY = e.clientY - rect.top;
38416
38424
  return {
38417
38425
  screenX,
38418
38426
  screenY,
@@ -38452,8 +38460,8 @@ var init_GraphCanvas = __esm({
38452
38460
  useEffect(() => {
38453
38461
  const canvas = canvasRef.current;
38454
38462
  if (!canvas || propNodes.length === 0) return;
38455
- const w = canvas.width;
38456
- const h = canvas.height;
38463
+ const w = logicalW;
38464
+ const h = height;
38457
38465
  const simNodes = propNodes.map((n, idx) => {
38458
38466
  let x = n.x ?? 0;
38459
38467
  let y = n.y ?? 0;
@@ -38543,29 +38551,41 @@ var init_GraphCanvas = __esm({
38543
38551
  return () => {
38544
38552
  cancelAnimationFrame(animRef.current);
38545
38553
  };
38546
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38554
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
38547
38555
  useEffect(() => {
38548
38556
  const canvas = canvasRef.current;
38549
38557
  if (!canvas) return;
38550
38558
  const ctx = canvas.getContext("2d");
38551
38559
  if (!ctx) return;
38552
- const w = canvas.width;
38553
- const h = canvas.height;
38560
+ const w = logicalW;
38561
+ const h = height;
38554
38562
  const nodes = nodesRef.current;
38555
38563
  const accentColor = resolveColor2("var(--color-accent)", canvas);
38564
+ const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
38565
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
38556
38566
  ctx.clearRect(0, 0, w, h);
38557
38567
  ctx.save();
38558
38568
  ctx.translate(offset.x, offset.y);
38559
38569
  ctx.scale(zoom, zoom);
38570
+ const neighbors = /* @__PURE__ */ new Set();
38571
+ if (hoveredNode) {
38572
+ neighbors.add(hoveredNode);
38573
+ for (const edge of propEdges) {
38574
+ if (edge.source === hoveredNode) neighbors.add(String(edge.target));
38575
+ else if (edge.target === hoveredNode) neighbors.add(String(edge.source));
38576
+ }
38577
+ }
38560
38578
  for (const edge of propEdges) {
38561
38579
  const source = nodes.find((n) => n.id === edge.source);
38562
38580
  const target = nodes.find((n) => n.id === edge.target);
38563
38581
  if (!source || !target) continue;
38582
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
38583
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
38564
38584
  ctx.beginPath();
38565
38585
  ctx.moveTo(source.x, source.y);
38566
38586
  ctx.lineTo(target.x, target.y);
38567
- ctx.strokeStyle = edge.color || "#88888866";
38568
- ctx.lineWidth = edge.weight ? Math.max(1, edge.weight) : 1;
38587
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#88888866";
38588
+ ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
38569
38589
  ctx.stroke();
38570
38590
  if (edge.label && showLabels) {
38571
38591
  const mx = (source.x + target.x) / 2;
@@ -38576,11 +38596,13 @@ var init_GraphCanvas = __esm({
38576
38596
  ctx.fillText(edge.label, mx, my - 4);
38577
38597
  }
38578
38598
  }
38599
+ ctx.globalAlpha = 1;
38579
38600
  for (const node of nodes) {
38580
38601
  const size = node.size || 8;
38581
38602
  const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
38582
38603
  const isHovered = hoveredNode === node.id;
38583
38604
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38605
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
38584
38606
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38585
38607
  ctx.beginPath();
38586
38608
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
@@ -38700,6 +38722,15 @@ var init_GraphCanvas = __esm({
38700
38722
  interactionRef.current.dragNodeId = null;
38701
38723
  setHoveredNode(null);
38702
38724
  }, []);
38725
+ const handleDoubleClick = useCallback(
38726
+ (e) => {
38727
+ const coords = toCoords(e);
38728
+ if (!coords) return;
38729
+ const node = nodeAt(coords.graphX, coords.graphY);
38730
+ if (node) onNodeDoubleClick?.(node);
38731
+ },
38732
+ [toCoords, nodeAt, onNodeDoubleClick]
38733
+ );
38703
38734
  if (isLoading) {
38704
38735
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
38705
38736
  }
@@ -38757,15 +38788,16 @@ var init_GraphCanvas = __esm({
38757
38788
  "canvas",
38758
38789
  {
38759
38790
  ref: canvasRef,
38760
- width: 800,
38761
- height,
38791
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38792
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38762
38793
  className: "w-full cursor-grab active:cursor-grabbing",
38763
38794
  style: { height },
38764
38795
  onWheel: handleWheel,
38765
38796
  onMouseDown: handleMouseDown,
38766
38797
  onMouseMove: handleMouseMove,
38767
38798
  onMouseUp: handleMouseUp,
38768
- onMouseLeave: handleMouseLeave
38799
+ onMouseLeave: handleMouseLeave,
38800
+ onDoubleClick: handleDoubleClick
38769
38801
  }
38770
38802
  ) }),
38771
38803
  groups.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "px-4 py-2 border-t border-border", wrap: true, children: groups.map((group, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
@@ -43180,24 +43212,28 @@ var init_GameCanvas3D2 = __esm({
43180
43212
  const cameraConfig = useMemo(() => {
43181
43213
  const size = Math.max(
43182
43214
  gridBounds.maxX - gridBounds.minX,
43183
- gridBounds.maxZ - gridBounds.minZ
43215
+ gridBounds.maxZ - gridBounds.minZ,
43216
+ 4
43217
+ // minimum framing distance so a tiny board isn't zoomed in
43184
43218
  );
43185
- const distance = size * 1.5;
43219
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
43220
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
43221
+ const d = size * 1.5;
43186
43222
  switch (cameraMode) {
43187
43223
  case "isometric":
43188
43224
  return {
43189
- position: [distance, distance * 0.8, distance],
43225
+ position: [cx + d, d * 0.8, cz + d],
43190
43226
  fov: 45
43191
43227
  };
43192
43228
  case "top-down":
43193
43229
  return {
43194
- position: [0, distance * 2, 0],
43230
+ position: [cx, d * 2, cz],
43195
43231
  fov: 45
43196
43232
  };
43197
43233
  case "perspective":
43198
43234
  default:
43199
43235
  return {
43200
- position: [distance, distance, distance],
43236
+ position: [cx + d, d, cz + d],
43201
43237
  fov: 45
43202
43238
  };
43203
43239
  }
@@ -43436,7 +43472,8 @@ var init_GameCanvas3D2 = __esm({
43436
43472
  "div",
43437
43473
  {
43438
43474
  ref: containerRef,
43439
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
43475
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
43476
+ style: { minHeight: "85vh" },
43440
43477
  "data-orientation": orientation,
43441
43478
  "data-camera-mode": cameraMode,
43442
43479
  "data-overlay": overlay,
@@ -43451,7 +43488,7 @@ var init_GameCanvas3D2 = __esm({
43451
43488
  near: 0.1,
43452
43489
  far: 1e3
43453
43490
  },
43454
- style: { background: backgroundColor },
43491
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
43455
43492
  onClick: (e) => {
43456
43493
  if (e.target === e.currentTarget) {
43457
43494
  eventHandlers.handleCanvasClick(e);
@@ -52814,7 +52851,7 @@ function isPatternConfig(value) {
52814
52851
  if (value instanceof Date) return false;
52815
52852
  if (typeof value === "function") return false;
52816
52853
  const record = value;
52817
- return "type" in record && typeof record.type === "string" && isKnownPattern(record.type);
52854
+ return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
52818
52855
  }
52819
52856
  function isPlainConfigObject(value) {
52820
52857
  if (React76__default.isValidElement(value)) return false;