@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.
package/dist/avl/index.js CHANGED
@@ -12160,16 +12160,6 @@ var init_Skeleton = __esm({
12160
12160
  Skeleton.displayName = "Skeleton";
12161
12161
  }
12162
12162
  });
12163
- function isKnownPattern(type) {
12164
- return type in componentMapping;
12165
- }
12166
- var componentMapping;
12167
- var init_pattern_resolver = __esm({
12168
- "renderer/pattern-resolver.ts"() {
12169
- createLogger("almadar:ui:pattern-resolver");
12170
- componentMapping = {};
12171
- }
12172
- });
12173
12163
 
12174
12164
  // renderer/slot-definitions.ts
12175
12165
  function isPortalSlot(slot) {
@@ -12278,7 +12268,6 @@ var init_navigation = __esm({
12278
12268
  // renderer/index.ts
12279
12269
  var init_renderer = __esm({
12280
12270
  "renderer/index.ts"() {
12281
- init_pattern_resolver();
12282
12271
  init_slot_definitions();
12283
12272
  }
12284
12273
  });
@@ -13982,7 +13971,7 @@ function recordTransition(trace) {
13982
13971
  ...trace,
13983
13972
  id: `t-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
13984
13973
  };
13985
- log5.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
13974
+ log4.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
13986
13975
  getState().transitions.push(entry);
13987
13976
  if (getState().transitions.length > MAX_TRANSITIONS) {
13988
13977
  getState().transitions.shift();
@@ -14081,7 +14070,7 @@ function getTraitSnapshots() {
14081
14070
  try {
14082
14071
  snapshots.push(getter());
14083
14072
  } catch (err) {
14084
- log5.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
14073
+ log4.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
14085
14074
  }
14086
14075
  }
14087
14076
  return snapshots;
@@ -14180,10 +14169,10 @@ function updateAssetStatus(url, status) {
14180
14169
  window.__orbitalVerification.assetStatus[url] = status;
14181
14170
  }
14182
14171
  }
14183
- var log5, MAX_TRANSITIONS;
14172
+ var log4, MAX_TRANSITIONS;
14184
14173
  var init_verificationRegistry = __esm({
14185
14174
  "lib/verificationRegistry.ts"() {
14186
- log5 = createLogger("almadar:bridge");
14175
+ log4 = createLogger("almadar:bridge");
14187
14176
  MAX_TRANSITIONS = 500;
14188
14177
  exposeOnWindow();
14189
14178
  }
@@ -16114,7 +16103,7 @@ function generateDiff(oldVal, newVal) {
16114
16103
  }
16115
16104
  return diff;
16116
16105
  }
16117
- var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log6, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
16106
+ var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log5, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
16118
16107
  var init_CodeBlock = __esm({
16119
16108
  "components/core/molecules/markdown/CodeBlock.tsx"() {
16120
16109
  init_cn();
@@ -16197,7 +16186,7 @@ var init_CodeBlock = __esm({
16197
16186
  "lolo-op-async": { color: ORB_COLORS.dark.async }
16198
16187
  };
16199
16188
  loloStyle = { ...dark, ...loloStyleOverrides };
16200
- log6 = createLogger("almadar:ui:markdown-code");
16189
+ log5 = createLogger("almadar:ui:markdown-code");
16201
16190
  CODE_LANGUAGES = [
16202
16191
  "text",
16203
16192
  "json",
@@ -16461,7 +16450,7 @@ var init_CodeBlock = __esm({
16461
16450
  eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: true });
16462
16451
  setTimeout(() => setCopied(false), 2e3);
16463
16452
  } catch (err) {
16464
- log6.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
16453
+ log5.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
16465
16454
  eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: false });
16466
16455
  }
16467
16456
  };
@@ -20342,14 +20331,14 @@ function useSafeEventBus2() {
20342
20331
  } };
20343
20332
  }
20344
20333
  }
20345
- var log7, lookStyles4, ButtonGroup;
20334
+ var log6, lookStyles4, ButtonGroup;
20346
20335
  var init_ButtonGroup = __esm({
20347
20336
  "components/core/molecules/ButtonGroup.tsx"() {
20348
20337
  "use client";
20349
20338
  init_cn();
20350
20339
  init_atoms2();
20351
20340
  init_useEventBus();
20352
- log7 = createLogger("almadar:ui:button-group");
20341
+ log6 = createLogger("almadar:ui:button-group");
20353
20342
  lookStyles4 = {
20354
20343
  "right-aligned-buttons": "",
20355
20344
  "floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
@@ -20430,7 +20419,7 @@ var init_ButtonGroup = __esm({
20430
20419
  {
20431
20420
  variant: "ghost",
20432
20421
  onClick: () => {
20433
- log7.debug("Filter clicked", { field: filter.field });
20422
+ log6.debug("Filter clicked", { field: filter.field });
20434
20423
  },
20435
20424
  children: filter.label
20436
20425
  },
@@ -28092,9 +28081,9 @@ function debug(...args) {
28092
28081
  const [first, ...rest] = args;
28093
28082
  const message = typeof first === "string" ? first : "<debug>";
28094
28083
  if (rest.length === 0 && typeof first === "string") {
28095
- log8.debug(message);
28084
+ log7.debug(message);
28096
28085
  } else {
28097
- log8.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
28086
+ log7.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
28098
28087
  }
28099
28088
  }
28100
28089
  function debugGroup(label) {
@@ -28122,11 +28111,11 @@ function toLogMetaValue(v) {
28122
28111
  }
28123
28112
  return String(v);
28124
28113
  }
28125
- var NAMESPACE, log8;
28114
+ var NAMESPACE, log7;
28126
28115
  var init_debug = __esm({
28127
28116
  "lib/debug.ts"() {
28128
28117
  NAMESPACE = "almadar:ui:debug";
28129
- log8 = createLogger(NAMESPACE);
28118
+ log7 = createLogger(NAMESPACE);
28130
28119
  createLogger("almadar:ui:debug:input");
28131
28120
  createLogger("almadar:ui:debug:collision");
28132
28121
  createLogger("almadar:ui:debug:physics");
@@ -40122,6 +40111,12 @@ function getGroupColor(group, groups) {
40122
40111
  const idx = groups.indexOf(group);
40123
40112
  return GROUP_COLORS2[idx % GROUP_COLORS2.length];
40124
40113
  }
40114
+ function resolveColor2(color, el) {
40115
+ const m = /^var\((--[^,)]+)(?:,\s*([^)]+))?\)$/.exec(color.trim());
40116
+ if (!m) return color;
40117
+ const resolved = getComputedStyle(el).getPropertyValue(m[1]).trim();
40118
+ return resolved || (m[2]?.trim() ?? "#888888");
40119
+ }
40125
40120
  var GROUP_COLORS2, GraphCanvas;
40126
40121
  var init_GraphCanvas = __esm({
40127
40122
  "components/core/molecules/GraphCanvas.tsx"() {
@@ -40151,6 +40146,7 @@ var init_GraphCanvas = __esm({
40151
40146
  draggable = true,
40152
40147
  actions,
40153
40148
  onNodeClick,
40149
+ onNodeDoubleClick,
40154
40150
  nodeClickEvent,
40155
40151
  selectedNodeId,
40156
40152
  repulsion = 800,
@@ -40170,6 +40166,19 @@ var init_GraphCanvas = __esm({
40170
40166
  const [hoveredNode, setHoveredNode] = useState(null);
40171
40167
  const nodesRef = useRef([]);
40172
40168
  const [, forceUpdate] = useState(0);
40169
+ const [logicalW, setLogicalW] = useState(800);
40170
+ useEffect(() => {
40171
+ const canvas = canvasRef.current;
40172
+ if (!canvas || typeof ResizeObserver === "undefined") return;
40173
+ const measure = () => {
40174
+ const width = Math.round(canvas.clientWidth);
40175
+ if (width > 0) setLogicalW(width);
40176
+ };
40177
+ measure();
40178
+ const ro = new ResizeObserver(measure);
40179
+ ro.observe(canvas);
40180
+ return () => ro.disconnect();
40181
+ }, []);
40173
40182
  const interactionRef = useRef({
40174
40183
  mode: "none",
40175
40184
  dragNodeId: null,
@@ -40223,8 +40232,8 @@ var init_GraphCanvas = __esm({
40223
40232
  useEffect(() => {
40224
40233
  const canvas = canvasRef.current;
40225
40234
  if (!canvas || propNodes.length === 0) return;
40226
- const w = canvas.width;
40227
- const h = canvas.height;
40235
+ const w = logicalW;
40236
+ const h = height;
40228
40237
  const simNodes = propNodes.map((n, idx) => {
40229
40238
  let x = n.x ?? 0;
40230
40239
  let y = n.y ?? 0;
@@ -40314,28 +40323,41 @@ var init_GraphCanvas = __esm({
40314
40323
  return () => {
40315
40324
  cancelAnimationFrame(animRef.current);
40316
40325
  };
40317
- }, [propNodes, propEdges, layout, repulsion, linkDistance]);
40326
+ }, [propNodes, propEdges, layout, repulsion, linkDistance, logicalW, height]);
40318
40327
  useEffect(() => {
40319
40328
  const canvas = canvasRef.current;
40320
40329
  if (!canvas) return;
40321
40330
  const ctx = canvas.getContext("2d");
40322
40331
  if (!ctx) return;
40323
- const w = canvas.width;
40324
- const h = canvas.height;
40332
+ const w = logicalW;
40333
+ const h = height;
40325
40334
  const nodes = nodesRef.current;
40335
+ const accentColor = resolveColor2("var(--color-accent)", canvas);
40336
+ const dpr = typeof window !== "undefined" && window.devicePixelRatio || 1;
40337
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
40326
40338
  ctx.clearRect(0, 0, w, h);
40327
40339
  ctx.save();
40328
40340
  ctx.translate(offset.x, offset.y);
40329
40341
  ctx.scale(zoom, zoom);
40342
+ const neighbors = /* @__PURE__ */ new Set();
40343
+ if (hoveredNode) {
40344
+ neighbors.add(hoveredNode);
40345
+ for (const edge of propEdges) {
40346
+ if (edge.source === hoveredNode) neighbors.add(String(edge.target));
40347
+ else if (edge.target === hoveredNode) neighbors.add(String(edge.source));
40348
+ }
40349
+ }
40330
40350
  for (const edge of propEdges) {
40331
40351
  const source = nodes.find((n) => n.id === edge.source);
40332
40352
  const target = nodes.find((n) => n.id === edge.target);
40333
40353
  if (!source || !target) continue;
40354
+ const incident = !!hoveredNode && (edge.source === hoveredNode || edge.target === hoveredNode);
40355
+ ctx.globalAlpha = hoveredNode ? incident ? 1 : 0.12 : 1;
40334
40356
  ctx.beginPath();
40335
40357
  ctx.moveTo(source.x, source.y);
40336
40358
  ctx.lineTo(target.x, target.y);
40337
- ctx.strokeStyle = edge.color || "#88888866";
40338
- ctx.lineWidth = edge.weight ? Math.max(1, edge.weight) : 1;
40359
+ ctx.strokeStyle = incident ? accentColor : edge.color || "#88888866";
40360
+ ctx.lineWidth = incident ? 2 : edge.weight ? Math.max(1, edge.weight) : 1;
40339
40361
  ctx.stroke();
40340
40362
  if (edge.label && showLabels) {
40341
40363
  const mx = (source.x + target.x) / 2;
@@ -40346,18 +40368,20 @@ var init_GraphCanvas = __esm({
40346
40368
  ctx.fillText(edge.label, mx, my - 4);
40347
40369
  }
40348
40370
  }
40371
+ ctx.globalAlpha = 1;
40349
40372
  for (const node of nodes) {
40350
40373
  const size = node.size || 8;
40351
- const color = node.color || getGroupColor(node.group, groups);
40374
+ const color = resolveColor2(node.color || getGroupColor(node.group, groups), canvas);
40352
40375
  const isHovered = hoveredNode === node.id;
40353
40376
  const isSelected = selectedNodeId !== void 0 && node.id === selectedNodeId;
40377
+ ctx.globalAlpha = hoveredNode && !neighbors.has(node.id) ? 0.2 : 1;
40354
40378
  const radius = isSelected ? size + 4 : isHovered ? size + 2 : size;
40355
40379
  ctx.beginPath();
40356
40380
  ctx.arc(node.x, node.y, radius, 0, Math.PI * 2);
40357
40381
  ctx.fillStyle = color;
40358
40382
  ctx.fill();
40359
40383
  if (isSelected) {
40360
- ctx.strokeStyle = "var(--color-accent)";
40384
+ ctx.strokeStyle = accentColor;
40361
40385
  ctx.lineWidth = 3;
40362
40386
  } else {
40363
40387
  ctx.strokeStyle = isHovered ? "#ffffff" : "#00000020";
@@ -40470,6 +40494,15 @@ var init_GraphCanvas = __esm({
40470
40494
  interactionRef.current.dragNodeId = null;
40471
40495
  setHoveredNode(null);
40472
40496
  }, []);
40497
+ const handleDoubleClick = useCallback(
40498
+ (e) => {
40499
+ const coords = toCoords(e);
40500
+ if (!coords) return;
40501
+ const node = nodeAt(coords.graphX, coords.graphY);
40502
+ if (node) onNodeDoubleClick?.(node);
40503
+ },
40504
+ [toCoords, nodeAt, onNodeDoubleClick]
40505
+ );
40473
40506
  if (isLoading) {
40474
40507
  return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
40475
40508
  }
@@ -40527,15 +40560,16 @@ var init_GraphCanvas = __esm({
40527
40560
  "canvas",
40528
40561
  {
40529
40562
  ref: canvasRef,
40530
- width: 800,
40531
- height,
40563
+ width: Math.round(logicalW * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
40564
+ height: Math.round(height * (typeof window !== "undefined" && window.devicePixelRatio || 1)),
40532
40565
  className: "w-full cursor-grab active:cursor-grabbing",
40533
40566
  style: { height },
40534
40567
  onWheel: handleWheel,
40535
40568
  onMouseDown: handleMouseDown,
40536
40569
  onMouseMove: handleMouseMove,
40537
40570
  onMouseUp: handleMouseUp,
40538
- onMouseLeave: handleMouseLeave
40571
+ onMouseLeave: handleMouseLeave,
40572
+ onDoubleClick: handleDoubleClick
40539
40573
  }
40540
40574
  ) }),
40541
40575
  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: [
@@ -44129,11 +44163,11 @@ var init_Canvas3DErrorBoundary = __esm({
44129
44163
  "components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
44130
44164
  }
44131
44165
  });
44132
- var log9, Canvas3DErrorBoundary;
44166
+ var log8, Canvas3DErrorBoundary;
44133
44167
  var init_Canvas3DErrorBoundary2 = __esm({
44134
44168
  "components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
44135
44169
  init_Canvas3DErrorBoundary();
44136
- log9 = createLogger("almadar:ui:game:canvas3d:error-boundary");
44170
+ log8 = createLogger("almadar:ui:game:canvas3d:error-boundary");
44137
44171
  Canvas3DErrorBoundary = class extends Component {
44138
44172
  constructor(props) {
44139
44173
  super(props);
@@ -44161,8 +44195,8 @@ var init_Canvas3DErrorBoundary2 = __esm({
44161
44195
  componentDidCatch(error, errorInfo) {
44162
44196
  this.setState({ errorInfo });
44163
44197
  this.props.onError?.(error, errorInfo);
44164
- log9.error("Error caught", { error });
44165
- log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
44198
+ log8.error("Error caught", { error });
44199
+ log8.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
44166
44200
  }
44167
44201
  render() {
44168
44202
  if (this.state.hasError) {
@@ -44225,7 +44259,7 @@ function useGLTFModel(url, resourceBasePath) {
44225
44259
  setState({ model: null, isLoading: false, error: null });
44226
44260
  return;
44227
44261
  }
44228
- log10.debug("Loading", { url });
44262
+ log9.debug("Loading", { url });
44229
44263
  setState((prev) => ({ ...prev, isLoading: true, error: null }));
44230
44264
  const assetRoot = resourceBasePath || detectAssetRoot2(url);
44231
44265
  const loader = new GLTFLoader$1();
@@ -44233,7 +44267,7 @@ function useGLTFModel(url, resourceBasePath) {
44233
44267
  loader.load(
44234
44268
  url,
44235
44269
  (gltf) => {
44236
- log10.debug("Loaded", { url });
44270
+ log9.debug("Loaded", { url });
44237
44271
  setState({
44238
44272
  model: gltf.scene,
44239
44273
  isLoading: false,
@@ -44242,7 +44276,7 @@ function useGLTFModel(url, resourceBasePath) {
44242
44276
  },
44243
44277
  void 0,
44244
44278
  (err) => {
44245
- log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
44279
+ log9.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
44246
44280
  setState({
44247
44281
  model: null,
44248
44282
  isLoading: false,
@@ -44358,11 +44392,11 @@ function ModelLoader({
44358
44392
  }
44359
44393
  );
44360
44394
  }
44361
- var log10;
44395
+ var log9;
44362
44396
  var init_ModelLoader = __esm({
44363
44397
  "components/game/molecules/three/components/ModelLoader.tsx"() {
44364
44398
  "use client";
44365
- log10 = createLogger("almadar:ui:game:model-loader");
44399
+ log9 = createLogger("almadar:ui:game:model-loader");
44366
44400
  }
44367
44401
  });
44368
44402
 
@@ -44623,24 +44657,28 @@ var init_GameCanvas3D2 = __esm({
44623
44657
  const cameraConfig = useMemo(() => {
44624
44658
  const size = Math.max(
44625
44659
  gridBounds.maxX - gridBounds.minX,
44626
- gridBounds.maxZ - gridBounds.minZ
44660
+ gridBounds.maxZ - gridBounds.minZ,
44661
+ 4
44662
+ // minimum framing distance so a tiny board isn't zoomed in
44627
44663
  );
44628
- const distance = size * 1.5;
44664
+ const cx = (gridBounds.minX + gridBounds.maxX) / 2;
44665
+ const cz = (gridBounds.minZ + gridBounds.maxZ) / 2;
44666
+ const d = size * 1.5;
44629
44667
  switch (cameraMode) {
44630
44668
  case "isometric":
44631
44669
  return {
44632
- position: [distance, distance * 0.8, distance],
44670
+ position: [cx + d, d * 0.8, cz + d],
44633
44671
  fov: 45
44634
44672
  };
44635
44673
  case "top-down":
44636
44674
  return {
44637
- position: [0, distance * 2, 0],
44675
+ position: [cx, d * 2, cz],
44638
44676
  fov: 45
44639
44677
  };
44640
44678
  case "perspective":
44641
44679
  default:
44642
44680
  return {
44643
- position: [distance, distance, distance],
44681
+ position: [cx + d, d, cz + d],
44644
44682
  fov: 45
44645
44683
  };
44646
44684
  }
@@ -44879,7 +44917,8 @@ var init_GameCanvas3D2 = __esm({
44879
44917
  "div",
44880
44918
  {
44881
44919
  ref: containerRef,
44882
- className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
44920
+ className: cn("game-canvas-3d relative w-full overflow-hidden", className),
44921
+ style: { minHeight: "85vh" },
44883
44922
  "data-orientation": orientation,
44884
44923
  "data-camera-mode": cameraMode,
44885
44924
  "data-overlay": overlay,
@@ -44894,7 +44933,7 @@ var init_GameCanvas3D2 = __esm({
44894
44933
  near: 0.1,
44895
44934
  far: 1e3
44896
44935
  },
44897
- style: { background: backgroundColor },
44936
+ style: { background: backgroundColor, height: "85vh", width: "100%" },
44898
44937
  onClick: (e) => {
44899
44938
  if (e.target === e.currentTarget) {
44900
44939
  eventHandlers.handleCanvasClick(e);
@@ -48696,7 +48735,7 @@ function getAllEvents(traits2) {
48696
48735
  function EventDispatcherTab({ traits: traits2, schema }) {
48697
48736
  const eventBus = useEventBus();
48698
48737
  const { t } = useTranslate();
48699
- const [log16, setLog] = React90.useState([]);
48738
+ const [log15, setLog] = React90.useState([]);
48700
48739
  const prevStatesRef = React90.useRef(/* @__PURE__ */ new Map());
48701
48740
  React90.useEffect(() => {
48702
48741
  for (const trait of traits2) {
@@ -48760,9 +48799,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
48760
48799
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
48761
48800
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
48762
48801
  ] }),
48763
- log16.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
48802
+ log15.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
48764
48803
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
48765
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log16.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
48804
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log15.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
48766
48805
  /* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
48767
48806
  " ",
48768
48807
  /* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -53777,7 +53816,7 @@ function isPatternConfig(value) {
53777
53816
  if (value instanceof Date) return false;
53778
53817
  if (typeof value === "function") return false;
53779
53818
  const record = value;
53780
- return "type" in record && typeof record.type === "string" && isKnownPattern(record.type);
53819
+ return "type" in record && typeof record.type === "string" && getComponentForPattern$1(record.type) !== null;
53781
53820
  }
53782
53821
  function isPlainConfigObject(value) {
53783
53822
  if (React90__default.isValidElement(value)) return false;
@@ -57689,7 +57728,7 @@ function convertObjectProps(props) {
57689
57728
  function convertFnFormLambdasInProps(props) {
57690
57729
  return convertObjectProps(props);
57691
57730
  }
57692
- var log11 = createLogger("almadar:ui:effects:client-handlers");
57731
+ var log10 = createLogger("almadar:ui:effects:client-handlers");
57693
57732
  function createClientEffectHandlers(options) {
57694
57733
  const { eventBus, slotSetter, navigate, notify, callService, liveEntity } = options;
57695
57734
  return {
@@ -57698,11 +57737,11 @@ function createClientEffectHandlers(options) {
57698
57737
  eventBus.emit(prefixedEvent, payload);
57699
57738
  },
57700
57739
  persist: async () => {
57701
- log11.warn("persist is server-side only, ignored on client");
57740
+ log10.warn("persist is server-side only, ignored on client");
57702
57741
  },
57703
57742
  set: (_entityId, field, value) => {
57704
57743
  if (!liveEntity) {
57705
- log11.warn("set is server-side only, ignored on client (no live entity)");
57744
+ log10.warn("set is server-side only, ignored on client (no live entity)");
57706
57745
  return;
57707
57746
  }
57708
57747
  liveEntity[field] = value;
@@ -57734,10 +57773,10 @@ function createClientEffectHandlers(options) {
57734
57773
  slotSetter.addPattern(slot, pattern, props);
57735
57774
  },
57736
57775
  navigate: navigate ?? ((path) => {
57737
- log11.warn("No navigate handler, ignoring", { path });
57776
+ log10.warn("No navigate handler, ignoring", { path });
57738
57777
  }),
57739
57778
  notify: notify ?? ((msg, type) => {
57740
- log11.debug("notify", { type, message: msg });
57779
+ log10.debug("notify", { type, message: msg });
57741
57780
  })
57742
57781
  };
57743
57782
  }
@@ -57969,7 +58008,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
57969
58008
  };
57970
58009
  }, [traitBindings]);
57971
58010
  const executeTransitionEffects = useCallback(async (params) => {
57972
- const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log16 } = params;
58011
+ const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log15 } = params;
57973
58012
  const traitName = binding.trait.name;
57974
58013
  const linkedEntity = binding.linkedEntity || "";
57975
58014
  const entityId = payload?.entityId;
@@ -58057,7 +58096,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58057
58096
  ...handlers,
58058
58097
  set: async (targetId, field, value) => {
58059
58098
  if (baseSet) await baseSet(targetId, field, value);
58060
- log16.debug("set:write", {
58099
+ log15.debug("set:write", {
58061
58100
  traitName,
58062
58101
  field,
58063
58102
  value: JSON.stringify(value),
@@ -58097,7 +58136,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58097
58136
  const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
58098
58137
  try {
58099
58138
  await executor.executeAll(effects);
58100
- log16.debug("effects:executed", () => ({
58139
+ log15.debug("effects:executed", () => ({
58101
58140
  traitName,
58102
58141
  transition: `${previousState}->${newState}`,
58103
58142
  event: flushEvent,
@@ -58107,7 +58146,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58107
58146
  slotsTouched: Array.from(pendingSlots.keys()).join(",")
58108
58147
  }));
58109
58148
  for (const [slot, patterns] of pendingSlots) {
58110
- log16.debug("flush:slot", {
58149
+ log15.debug("flush:slot", {
58111
58150
  traitName,
58112
58151
  slot,
58113
58152
  patternCount: patterns.length,
@@ -58122,7 +58161,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
58122
58161
  });
58123
58162
  }
58124
58163
  } catch (error) {
58125
- log16.error("effects:error", {
58164
+ log15.error("effects:error", {
58126
58165
  traitName,
58127
58166
  transition: `${previousState}->${newState}`,
58128
58167
  event: flushEvent,
@@ -59117,7 +59156,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
59117
59156
  init_navigation();
59118
59157
  init_verificationRegistry();
59119
59158
  var PERF_NAMESPACE = "almadar:perf:canvas";
59120
- var log13 = createLogger(PERF_NAMESPACE);
59159
+ var log12 = createLogger(PERF_NAMESPACE);
59121
59160
  var RING_SIZE = 50;
59122
59161
  var ring = [];
59123
59162
  var writeIdx = 0;
@@ -59168,7 +59207,7 @@ function perfEnd(name, startToken, detail) {
59168
59207
  }
59169
59208
  }
59170
59209
  push({ name, durationMs, ts: endTs, detail });
59171
- log13.debug(name, () => ({ durationMs, ...detail ?? {} }));
59210
+ log12.debug(name, () => ({ durationMs, ...detail ?? {} }));
59172
59211
  }
59173
59212
  var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
59174
59213
  if (!isEnabled()) return;
@@ -59178,7 +59217,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
59178
59217
  ts: commitTime,
59179
59218
  detail: { baseDuration }
59180
59219
  });
59181
- log13.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
59220
+ log12.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
59182
59221
  };
59183
59222
 
59184
59223
  // runtime/prepareSchemaForPreview.ts
@@ -59821,7 +59860,7 @@ init_useEventBus();
59821
59860
  // components/avl/molecules/useCanvasDnd.tsx
59822
59861
  init_useEventBus();
59823
59862
  init_useAlmadarDndCollision();
59824
- var log14 = createLogger("almadar:ui:canvas-dnd");
59863
+ var log13 = createLogger("almadar:ui:canvas-dnd");
59825
59864
  function useCanvasDraggable({
59826
59865
  id,
59827
59866
  payload,
@@ -59860,7 +59899,7 @@ function defaultEmit(eventBus, drop) {
59860
59899
  if (payload.kind === "pattern") {
59861
59900
  const patternType = payload.data.type;
59862
59901
  if (typeof patternType !== "string") {
59863
- log14.warn("default-emit:pattern:missing-type");
59902
+ log13.warn("default-emit:pattern:missing-type");
59864
59903
  return;
59865
59904
  }
59866
59905
  const out = { patternType, containerNode: target.containerNode };
@@ -59869,23 +59908,23 @@ function defaultEmit(eventBus, drop) {
59869
59908
  out.index = resolved.index;
59870
59909
  }
59871
59910
  eventBus.emit("UI:PATTERN_DROP", out);
59872
- log14.info("default-emit:pattern", { patternType, level: target.level });
59911
+ log13.info("default-emit:pattern", { patternType, level: target.level });
59873
59912
  return;
59874
59913
  }
59875
59914
  if (payload.kind === "behavior") {
59876
59915
  const behaviorName = payload.data.name;
59877
59916
  if (typeof behaviorName !== "string") {
59878
- log14.warn("default-emit:behavior:missing-name");
59917
+ log13.warn("default-emit:behavior:missing-name");
59879
59918
  return;
59880
59919
  }
59881
59920
  eventBus.emit("UI:BEHAVIOR_DROP", {
59882
59921
  behaviorName,
59883
59922
  containerNode: target.containerNode
59884
59923
  });
59885
- log14.info("default-emit:behavior", { behaviorName, level: target.level });
59924
+ log13.info("default-emit:behavior", { behaviorName, level: target.level });
59886
59925
  return;
59887
59926
  }
59888
- log14.debug("default-emit:unhandled-kind", { kind: payload.kind });
59927
+ log13.debug("default-emit:unhandled-kind", { kind: payload.kind });
59889
59928
  }
59890
59929
  function CanvasDndProvider({
59891
59930
  children,
@@ -59901,9 +59940,9 @@ function CanvasDndProvider({
59901
59940
  if (payload) {
59902
59941
  setActivePayload(payload);
59903
59942
  eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
59904
- log14.info("dragStart", { id: e.active.id, kind: payload.kind });
59943
+ log13.info("dragStart", { id: e.active.id, kind: payload.kind });
59905
59944
  } else {
59906
- log14.warn("dragStart:missing-payload", { id: e.active.id });
59945
+ log13.warn("dragStart:missing-payload", { id: e.active.id });
59907
59946
  }
59908
59947
  }, [eventBus]);
59909
59948
  const handleDragEnd = React90__default.useCallback((e) => {
@@ -59913,7 +59952,7 @@ function CanvasDndProvider({
59913
59952
  const overData = e.over?.data.current;
59914
59953
  const target = overData?.target;
59915
59954
  const accepts = overData?.accepts;
59916
- log14.info("dragEnd", {
59955
+ log13.info("dragEnd", {
59917
59956
  activeId: e.active.id,
59918
59957
  overId: e.over?.id,
59919
59958
  hasPayload: !!payload,
@@ -59925,7 +59964,7 @@ function CanvasDndProvider({
59925
59964
  }
59926
59965
  if (!payload || !target) return;
59927
59966
  if (accepts && !accepts.includes(payload.kind)) {
59928
- log14.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
59967
+ log13.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
59929
59968
  return;
59930
59969
  }
59931
59970
  const activator = e.activatorEvent;
@@ -59937,7 +59976,7 @@ function CanvasDndProvider({
59937
59976
  }, [eventBus, onDrop]);
59938
59977
  const handleDragCancel = React90__default.useCallback(() => {
59939
59978
  setActivePayload(null);
59940
- log14.info("dragCancel");
59979
+ log13.info("dragCancel");
59941
59980
  }, []);
59942
59981
  return /* @__PURE__ */ jsxs(
59943
59982
  DndContext,
@@ -61752,7 +61791,7 @@ init_AvlTransitionLane();
61752
61791
  init_AvlSwimLane();
61753
61792
  init_types();
61754
61793
  init_avl_elk_layout();
61755
- var log15 = createLogger("almadar:ui:avl:trait-scene");
61794
+ var log14 = createLogger("almadar:ui:avl:trait-scene");
61756
61795
  var SWIM_GUTTER2 = 120;
61757
61796
  var CENTER_W2 = 360;
61758
61797
  var AvlTraitScene = ({
@@ -61765,7 +61804,7 @@ var AvlTraitScene = ({
61765
61804
  const dataKey = useMemo(() => JSON.stringify(data), [data]);
61766
61805
  useEffect(() => {
61767
61806
  computeTraitLayout(data).then(setLayout).catch((error) => {
61768
- log15.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
61807
+ log14.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
61769
61808
  });
61770
61809
  }, [dataKey]);
61771
61810
  if (!layout) {
@@ -55,6 +55,8 @@ export interface GraphCanvasProps {
55
55
  actions?: readonly GraphAction[];
56
56
  /** On node click */
57
57
  onNodeClick?: (node: GraphNode) => void;
58
+ /** On node double-click (e.g. to drill in) */
59
+ onNodeDoubleClick?: (node: GraphNode) => void;
58
60
  /** Node click event */
59
61
  nodeClickEvent?: EventEmit<{
60
62
  id: string;