@almadar/ui 5.59.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
  });
@@ -38389,6 +38385,12 @@ function getGroupColor(group, groups) {
38389
38385
  const idx = groups.indexOf(group);
38390
38386
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
38391
38387
  }
38388
+ function resolveColor2(color, el) {
38389
+ const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
38390
+ if (!m) return color;
38391
+ const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
38392
+ return resolved || (m[2]?.trim() ?? "#888888");
38393
+ }
38392
38394
  var GROUP_COLORS2; exports.GraphCanvas = void 0;
38393
38395
  var init_GraphCanvas = __esm({
38394
38396
  "components/core/molecules/GraphCanvas.tsx"() {
@@ -38418,6 +38420,7 @@ var init_GraphCanvas = __esm({
38418
38420
  draggable = true,
38419
38421
  actions,
38420
38422
  onNodeClick,
38423
+ onNodeDoubleClick,
38421
38424
  nodeClickEvent,
38422
38425
  selectedNodeId,
38423
38426
  repulsion = 800,
@@ -38437,6 +38440,19 @@ var init_GraphCanvas = __esm({
38437
38440
  const [hoveredNode, setHoveredNode] = React76.useState(null);
38438
38441
  const nodesRef = React76.useRef([]);
38439
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
+ }, []);
38440
38456
  const interactionRef = React76.useRef({
38441
38457
  mode: "none",
38442
38458
  dragNodeId: null,
@@ -38490,8 +38506,8 @@ var init_GraphCanvas = __esm({
38490
38506
  React76.useEffect(() => {
38491
38507
  const canvas = canvasRef.current;
38492
38508
  if (!canvas || propNodes.length === 0) return;
38493
- const w = canvas.width;
38494
- const h = canvas.height;
38509
+ const w = logicalW;
38510
+ const h = height;
38495
38511
  const simNodes = propNodes.map((n, idx) => {
38496
38512
  let x = n.x ?? 0;
38497
38513
  let y = n.y ?? 0;
@@ -38581,28 +38597,41 @@ var init_GraphCanvas = __esm({
38581
38597
  return () => {
38582
38598
  cancelAnimationFrame(animRef.current);
38583
38599
  };
38584
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38600
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
38585
38601
  React76.useEffect(() => {
38586
38602
  const canvas = canvasRef.current;
38587
38603
  if (!canvas) return;
38588
38604
  const ctx = canvas.getContext("2d");
38589
38605
  if (!ctx) return;
38590
- const w = canvas.width;
38591
- const h = canvas.height;
38606
+ const w = logicalW;
38607
+ const h = height;
38592
38608
  const nodes = nodesRef.current;
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);
38593
38612
  ctx.clearRect(0, 0, w, h);
38594
38613
  ctx.save();
38595
38614
  ctx.translate(offset.x, offset.y);
38596
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
+ }
38597
38624
  for (const edge of propEdges) {
38598
38625
  const source = nodes.find((n) => n.id === edge.source);
38599
38626
  const target = nodes.find((n) => n.id === edge.target);
38600
38627
  if (!source || !target) continue;
38628
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
38629
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
38601
38630
  ctx.beginPath();
38602
38631
  ctx.moveTo(source.x, source.y);
38603
38632
  ctx.lineTo(target.x, target.y);
38604
- ctx.strokeStyle = edge.color || "#88888866";
38605
- 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;
38606
38635
  ctx.stroke();
38607
38636
  if (edge.label && showLabels) {
38608
38637
  const mx = (source.x + target.x) / 2;
@@ -38613,18 +38642,20 @@ var init_GraphCanvas = __esm({
38613
38642
  ctx.fillText(edge.label, mx, my - 4);
38614
38643
  }
38615
38644
  }
38645
+ ctx.globalAlpha = 1;
38616
38646
  for (const node of nodes) {
38617
38647
  const size = node.size || 8;
38618
- const color = node.color || getGroupColor(node.group, groups);
38648
+ const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
38619
38649
  const isHovered = hoveredNode === node.id;
38620
38650
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38651
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
38621
38652
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38622
38653
  ctx.beginPath();
38623
38654
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
38624
38655
  ctx.fillStyle = color;
38625
38656
  ctx.fill();
38626
38657
  if (isSelected) {
38627
- ctx.strokeStyle = "var(--color-accent)";
38658
+ ctx.strokeStyle = accentColor;
38628
38659
  ctx.lineWidth = 3;
38629
38660
  } else {
38630
38661
  ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
@@ -38737,6 +38768,15 @@ var init_GraphCanvas = __esm({
38737
38768
  interactionRef.current.dragNodeId = null;
38738
38769
  setHoveredNode(null);
38739
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
+ );
38740
38780
  if (isLoading) {
38741
38781
  return /* @__PURE__ */ jsxRuntime.jsx(exports.LoadingState, { message: t("common.loading"), className });
38742
38782
  }
@@ -38794,15 +38834,16 @@ var init_GraphCanvas = __esm({
38794
38834
  "canvas",
38795
38835
  {
38796
38836
  ref: canvasRef,
38797
- width: 800,
38798
- height,
38837
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38838
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38799
38839
  className: "w-full cursor-grab active:cursor-grabbing",
38800
38840
  style: { height },
38801
38841
  onWheel: handleWheel,
38802
38842
  onMouseDown: handleMouseDown,
38803
38843
  onMouseMove: handleMouseMove,
38804
38844
  onMouseUp: handleMouseUp,
38805
- onMouseLeave: handleMouseLeave
38845
+ onMouseLeave: handleMouseLeave,
38846
+ onDoubleClick: handleDoubleClick
38806
38847
  }
38807
38848
  ) }),
38808
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: [
@@ -43217,24 +43258,28 @@ var init_GameCanvas3D2 = __esm({
43217
43258
  const cameraConfig = React76.useMemo(() => {
43218
43259
  const size = Math.max(
43219
43260
  gridBounds.maxX - gridBounds.minX,
43220
- gridBounds.maxZ - gridBounds.minZ
43261
+ gridBounds.maxZ - gridBounds.minZ,
43262
+ 4
43263
+ // minimum framing distance so a tiny board isn't zoomed in
43221
43264
  );
43222
- 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;
43223
43268
  switch (cameraMode) {
43224
43269
  case "isometric":
43225
43270
  return {
43226
- position: [distance, distance * 0.8, distance],
43271
+ position: [cx + d, d * 0.8, cz + d],
43227
43272
  fov: 45
43228
43273
  };
43229
43274
  case "top-down":
43230
43275
  return {
43231
- position: [0, distance * 2, 0],
43276
+ position: [cx, d * 2, cz],
43232
43277
  fov: 45
43233
43278
  };
43234
43279
  case "perspective":
43235
43280
  default:
43236
43281
  return {
43237
- position: [distance, distance, distance],
43282
+ position: [cx + d, d, cz + d],
43238
43283
  fov: 45
43239
43284
  };
43240
43285
  }
@@ -43473,7 +43518,8 @@ var init_GameCanvas3D2 = __esm({
43473
43518
  "div",
43474
43519
  {
43475
43520
  ref: containerRef,
43476
- 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" },
43477
43523
  "data-orientation": orientation,
43478
43524
  "data-camera-mode": cameraMode,
43479
43525
  "data-overlay": overlay,
@@ -43488,7 +43534,7 @@ var init_GameCanvas3D2 = __esm({
43488
43534
  near: 0.1,
43489
43535
  far: 1e3
43490
43536
  },
43491
- style: { background: backgroundColor },
43537
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
43492
43538
  onClick: (e) => {
43493
43539
  if (e.target === e.currentTarget) {
43494
43540
  eventHandlers.handleCanvasClick(e);
@@ -52851,7 +52897,7 @@ function isPatternConfig(value) {
52851
52897
  if (value instanceof Date) return false;
52852
52898
  if (typeof value === "function") return false;
52853
52899
  const record = value;
52854
- 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;
52855
52901
  }
52856
52902
  function isPlainConfigObject(value) {
52857
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
  });
@@ -38343,6 +38339,12 @@ function getGroupColor(group, groups) {
38343
38339
  const idx = groups.indexOf(group);
38344
38340
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
38345
38341
  }
38342
+ function resolveColor2(color, el) {
38343
+ const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
38344
+ if (!m) return color;
38345
+ const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
38346
+ return resolved || (m[2]?.trim() ?? "#888888");
38347
+ }
38346
38348
  var GROUP_COLORS2, GraphCanvas;
38347
38349
  var init_GraphCanvas = __esm({
38348
38350
  "components/core/molecules/GraphCanvas.tsx"() {
@@ -38372,6 +38374,7 @@ var init_GraphCanvas = __esm({
38372
38374
  draggable = true,
38373
38375
  actions,
38374
38376
  onNodeClick,
38377
+ onNodeDoubleClick,
38375
38378
  nodeClickEvent,
38376
38379
  selectedNodeId,
38377
38380
  repulsion = 800,
@@ -38391,6 +38394,19 @@ var init_GraphCanvas = __esm({
38391
38394
  const [hoveredNode, setHoveredNode] = useState(null);
38392
38395
  const nodesRef = useRef([]);
38393
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
+ }, []);
38394
38410
  const interactionRef = useRef({
38395
38411
  mode: "none",
38396
38412
  dragNodeId: null,
@@ -38444,8 +38460,8 @@ var init_GraphCanvas = __esm({
38444
38460
  useEffect(() => {
38445
38461
  const canvas = canvasRef.current;
38446
38462
  if (!canvas || propNodes.length === 0) return;
38447
- const w = canvas.width;
38448
- const h = canvas.height;
38463
+ const w = logicalW;
38464
+ const h = height;
38449
38465
  const simNodes = propNodes.map((n, idx) => {
38450
38466
  let x = n.x ?? 0;
38451
38467
  let y = n.y ?? 0;
@@ -38535,28 +38551,41 @@ var init_GraphCanvas = __esm({
38535
38551
  return () => {
38536
38552
  cancelAnimationFrame(animRef.current);
38537
38553
  };
38538
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
38554
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
38539
38555
  useEffect(() => {
38540
38556
  const canvas = canvasRef.current;
38541
38557
  if (!canvas) return;
38542
38558
  const ctx = canvas.getContext("2d");
38543
38559
  if (!ctx) return;
38544
- const w = canvas.width;
38545
- const h = canvas.height;
38560
+ const w = logicalW;
38561
+ const h = height;
38546
38562
  const nodes = nodesRef.current;
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);
38547
38566
  ctx.clearRect(0, 0, w, h);
38548
38567
  ctx.save();
38549
38568
  ctx.translate(offset.x, offset.y);
38550
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
+ }
38551
38578
  for (const edge of propEdges) {
38552
38579
  const source = nodes.find((n) => n.id === edge.source);
38553
38580
  const target = nodes.find((n) => n.id === edge.target);
38554
38581
  if (!source || !target) continue;
38582
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
38583
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
38555
38584
  ctx.beginPath();
38556
38585
  ctx.moveTo(source.x, source.y);
38557
38586
  ctx.lineTo(target.x, target.y);
38558
- ctx.strokeStyle = edge.color || "#88888866";
38559
- 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;
38560
38589
  ctx.stroke();
38561
38590
  if (edge.label && showLabels) {
38562
38591
  const mx = (source.x + target.x) / 2;
@@ -38567,18 +38596,20 @@ var init_GraphCanvas = __esm({
38567
38596
  ctx.fillText(edge.label, mx, my - 4);
38568
38597
  }
38569
38598
  }
38599
+ ctx.globalAlpha = 1;
38570
38600
  for (const node of nodes) {
38571
38601
  const size = node.size || 8;
38572
- const color = node.color || getGroupColor(node.group, groups);
38602
+ const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
38573
38603
  const isHovered = hoveredNode === node.id;
38574
38604
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
38605
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
38575
38606
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
38576
38607
  ctx.beginPath();
38577
38608
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
38578
38609
  ctx.fillStyle = color;
38579
38610
  ctx.fill();
38580
38611
  if (isSelected) {
38581
- ctx.strokeStyle = "var(--color-accent)";
38612
+ ctx.strokeStyle = accentColor;
38582
38613
  ctx.lineWidth = 3;
38583
38614
  } else {
38584
38615
  ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
@@ -38691,6 +38722,15 @@ var init_GraphCanvas = __esm({
38691
38722
  interactionRef.current.dragNodeId = null;
38692
38723
  setHoveredNode(null);
38693
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
+ );
38694
38734
  if (isLoading) {
38695
38735
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
38696
38736
  }
@@ -38748,15 +38788,16 @@ var init_GraphCanvas = __esm({
38748
38788
  "canvas",
38749
38789
  {
38750
38790
  ref: canvasRef,
38751
- width: 800,
38752
- height,
38791
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38792
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
38753
38793
  className: "w-full cursor-grab active:cursor-grabbing",
38754
38794
  style: { height },
38755
38795
  onWheel: handleWheel,
38756
38796
  onMouseDown: handleMouseDown,
38757
38797
  onMouseMove: handleMouseMove,
38758
38798
  onMouseUp: handleMouseUp,
38759
- onMouseLeave: handleMouseLeave
38799
+ onMouseLeave: handleMouseLeave,
38800
+ onDoubleClick: handleDoubleClick
38760
38801
  }
38761
38802
  ) }),
38762
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: [
@@ -43171,24 +43212,28 @@ var init_GameCanvas3D2 = __esm({
43171
43212
  const cameraConfig = useMemo(() => {
43172
43213
  const size = Math.max(
43173
43214
  gridBounds.maxX - gridBounds.minX,
43174
- gridBounds.maxZ - gridBounds.minZ
43215
+ gridBounds.maxZ - gridBounds.minZ,
43216
+ 4
43217
+ // minimum framing distance so a tiny board isn't zoomed in
43175
43218
  );
43176
- 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;
43177
43222
  switch (cameraMode) {
43178
43223
  case "isometric":
43179
43224
  return {
43180
- position: [distance, distance * 0.8, distance],
43225
+ position: [cx + d, d * 0.8, cz + d],
43181
43226
  fov: 45
43182
43227
  };
43183
43228
  case "top-down":
43184
43229
  return {
43185
- position: [0, distance * 2, 0],
43230
+ position: [cx, d * 2, cz],
43186
43231
  fov: 45
43187
43232
  };
43188
43233
  case "perspective":
43189
43234
  default:
43190
43235
  return {
43191
- position: [distance, distance, distance],
43236
+ position: [cx + d, d, cz + d],
43192
43237
  fov: 45
43193
43238
  };
43194
43239
  }
@@ -43427,7 +43472,8 @@ var init_GameCanvas3D2 = __esm({
43427
43472
  "div",
43428
43473
  {
43429
43474
  ref: containerRef,
43430
- 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" },
43431
43477
  "data-orientation": orientation,
43432
43478
  "data-camera-mode": cameraMode,
43433
43479
  "data-overlay": overlay,
@@ -43442,7 +43488,7 @@ var init_GameCanvas3D2 = __esm({
43442
43488
  near: 0.1,
43443
43489
  far: 1e3
43444
43490
  },
43445
- style: { background: backgroundColor },
43491
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
43446
43492
  onClick: (e) => {
43447
43493
  if (e.target === e.currentTarget) {
43448
43494
  eventHandlers.handleCanvasClick(e);
@@ -52805,7 +52851,7 @@ function isPatternConfig(value) {
52805
52851
  if (value instanceof Date) return false;
52806
52852
  if (typeof value === "function") return false;
52807
52853
  const record = value;
52808
- 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;
52809
52855
  }
52810
52856
  function isPlainConfigObject(value) {
52811
52857
  if (React76__default.isValidElement(value)) return false;