@almadar/ui 2.52.0 → 2.54.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
@@ -22086,6 +22086,26 @@ var CodeBlock = React125__default.memo(
22086
22086
  const codeRef = useRef(null);
22087
22087
  const savedScrollLeftRef = useRef(0);
22088
22088
  const [copied, setCopied] = useState(false);
22089
+ const [editableValue, setEditableValue] = useState(code);
22090
+ const [editableTextareaKey, setEditableTextareaKey] = useState(0);
22091
+ const lastPropCodeRef = useRef(code);
22092
+ const editableTextareaRef = useRef(null);
22093
+ const editableOverlayRef = useRef(null);
22094
+ useEffect(() => {
22095
+ if (code !== lastPropCodeRef.current) {
22096
+ lastPropCodeRef.current = code;
22097
+ setEditableValue(code);
22098
+ setEditableTextareaKey((k) => k + 1);
22099
+ }
22100
+ }, [code]);
22101
+ const handleEditableScroll = useCallback(() => {
22102
+ const ta = editableTextareaRef.current;
22103
+ const ov = editableOverlayRef.current;
22104
+ if (ta && ov) {
22105
+ ov.scrollTop = ta.scrollTop;
22106
+ ov.scrollLeft = ta.scrollLeft;
22107
+ }
22108
+ }, []);
22089
22109
  const isFoldable = foldableProp ?? (language === "orb" || language === "json");
22090
22110
  const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
22091
22111
  const foldRegions = useMemo(
@@ -22246,33 +22266,106 @@ var CodeBlock = React125__default.memo(
22246
22266
  }
22247
22267
  ),
22248
22268
  editable ? (
22249
- /* GAP-51: editable mode composes the Textarea atom. Plain text editing,
22250
- no Prism highlighting overlay (follow-up). The textarea is uncontrolled
22251
- on the value side: we pass `code` as the initial value and forward
22252
- every keystroke via onChange the consumer is responsible for
22253
- debouncing and re-deriving `code` only after the user stops typing
22254
- so the cursor doesn't fight a re-render. */
22255
- /* @__PURE__ */ jsx(
22256
- Textarea,
22269
+ /* GAP-77: editable mode = transparent Textarea on top + Prism-highlighted
22270
+ overlay underneath. The textarea is uncontrolled (defaultValue + key)
22271
+ to avoid cursor jumps; the overlay reads `editableValue` which is
22272
+ mirrored from the textarea via onChange. Both elements share IDENTICAL
22273
+ font / line-height / padding so the highlighted text aligns with the
22274
+ textarea's invisible glyphs.
22275
+
22276
+ Scroll sync: the overlay has `pointer-events: none` and the textarea
22277
+ scrolls; `handleEditableScroll` keeps the overlay's scroll matched. */
22278
+ /* @__PURE__ */ jsxs(
22279
+ Box,
22257
22280
  {
22258
- defaultValue: code,
22259
- onChange: (e) => onChange?.(e.target.value),
22260
- spellCheck: false,
22261
22281
  style: {
22262
- fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
22263
- fontSize: "13px",
22264
- lineHeight: "1.5",
22282
+ position: "relative",
22265
22283
  backgroundColor: "#1e1e1e",
22266
- color: "#e6e6e6",
22267
22284
  borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem",
22268
- border: "none",
22269
- padding: "1rem",
22270
- resize: "none",
22271
22285
  minHeight: "160px",
22272
22286
  maxHeight,
22273
- width: "100%",
22274
- outline: "none"
22275
- }
22287
+ overflow: "hidden"
22288
+ },
22289
+ children: [
22290
+ /* @__PURE__ */ jsx(
22291
+ "div",
22292
+ {
22293
+ ref: editableOverlayRef,
22294
+ "aria-hidden": true,
22295
+ style: {
22296
+ position: "absolute",
22297
+ inset: 0,
22298
+ overflow: "hidden",
22299
+ pointerEvents: "none",
22300
+ maxHeight
22301
+ },
22302
+ children: /* @__PURE__ */ jsx(
22303
+ SyntaxHighlighter,
22304
+ {
22305
+ PreTag: "div",
22306
+ language,
22307
+ style: activeStyle,
22308
+ customStyle: {
22309
+ backgroundColor: "transparent",
22310
+ borderRadius: 0,
22311
+ padding: "1rem",
22312
+ margin: 0,
22313
+ whiteSpace: "pre",
22314
+ minWidth: "100%",
22315
+ minHeight: "160px",
22316
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
22317
+ fontSize: "13px",
22318
+ lineHeight: "1.5"
22319
+ },
22320
+ codeTagProps: {
22321
+ style: {
22322
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
22323
+ fontSize: "13px",
22324
+ lineHeight: "1.5"
22325
+ }
22326
+ },
22327
+ children: editableValue || " "
22328
+ }
22329
+ )
22330
+ }
22331
+ ),
22332
+ /* @__PURE__ */ jsx(
22333
+ Textarea,
22334
+ {
22335
+ ref: editableTextareaRef,
22336
+ defaultValue: code,
22337
+ onChange: (e) => {
22338
+ setEditableValue(e.target.value);
22339
+ onChange?.(e.target.value);
22340
+ },
22341
+ onScroll: handleEditableScroll,
22342
+ spellCheck: false,
22343
+ style: {
22344
+ position: "relative",
22345
+ zIndex: 1,
22346
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
22347
+ fontSize: "13px",
22348
+ lineHeight: "1.5",
22349
+ backgroundColor: "transparent",
22350
+ color: "transparent",
22351
+ caretColor: "#e6e6e6",
22352
+ borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem",
22353
+ border: "none",
22354
+ padding: "1rem",
22355
+ resize: "none",
22356
+ minHeight: "160px",
22357
+ maxHeight,
22358
+ width: "100%",
22359
+ height: "100%",
22360
+ outline: "none",
22361
+ whiteSpace: "pre",
22362
+ overflowWrap: "normal",
22363
+ overflow: "auto"
22364
+ }
22365
+ },
22366
+ editableTextareaKey
22367
+ )
22368
+ ]
22276
22369
  }
22277
22370
  )
22278
22371
  ) : /* @__PURE__ */ jsx(
@@ -48403,7 +48496,7 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
48403
48496
  onBlur: (e) => handlePropChange(propName, e.target.value)
48404
48497
  }
48405
48498
  ) : /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-[11px] text-muted-foreground", children: [
48406
- displayValue || (ps.types?.join(" | ") ?? "string"),
48499
+ displayValue || "\u2014",
48407
48500
  ps.required ? " *" : ""
48408
48501
  ] })
48409
48502
  ] }, propName);
@@ -49094,545 +49187,103 @@ AvlCosmicZoom.displayName = "AvlCosmicZoom";
49094
49187
 
49095
49188
  // components/organisms/avl/AvlOrbitalsCosmicZoom.tsx
49096
49189
  init_avl_schema_parser();
49097
- init_Box();
49098
- init_Typography();
49099
- var UNIT_DISPLAY_W = 240;
49100
- var UNIT_DISPLAY_H = 160;
49101
- function layoutOrbitals(count, containerW, containerH) {
49102
- if (count === 0) return [];
49103
- if (count === 1) return [{ cx: containerW / 2, cy: containerH / 2 }];
49104
- const cols = Math.min(count, Math.ceil(Math.sqrt(count)));
49105
- const rows = Math.ceil(count / cols);
49106
- const cellW = containerW / (cols + 0.3);
49107
- const cellH = containerH / (rows + 0.3);
49108
- const originX = (containerW - cols * cellW) / 2 + cellW / 2;
49109
- const originY = (containerH - rows * cellH) / 2 + cellH / 2;
49110
- return Array.from({ length: count }, (_, i) => ({
49111
- cx: originX + i % cols * cellW,
49112
- cy: originY + Math.floor(i / cols) * cellH
49113
- }));
49114
- }
49115
- var avlOczWireId = 0;
49116
- var EventWireOverlay = ({
49117
- orbitalViews,
49118
- crossLinks,
49119
- color,
49120
- animated,
49121
- containerW,
49122
- containerH
49190
+ init_avl_zoom_state();
49191
+ init_types();
49192
+ var SWIM_GUTTER2 = 120;
49193
+ var CENTER_W2 = 360;
49194
+ var AvlTraitScene = ({
49195
+ data,
49196
+ color = "var(--color-primary)",
49197
+ onTransitionClick
49123
49198
  }) => {
49124
- const ids = React125__default.useMemo(() => {
49125
- avlOczWireId += 1;
49126
- return { arrow: `avl-ocz-wire-${avlOczWireId}-arrow` };
49127
- }, []);
49128
- const posMap = useMemo(() => {
49129
- const m = /* @__PURE__ */ new Map();
49130
- for (const ov of orbitalViews) m.set(ov.name, { cx: ov.cx, cy: ov.cy });
49131
- return m;
49132
- }, [orbitalViews]);
49133
- const wiresByPair = useMemo(() => {
49134
- const map = /* @__PURE__ */ new Map();
49135
- for (const link of crossLinks) {
49136
- const key = `${link.emitterOrbital}__${link.listenerOrbital}`;
49137
- const arr = map.get(key) ?? [];
49138
- arr.push(link);
49139
- map.set(key, arr);
49140
- }
49141
- return map;
49142
- }, [crossLinks]);
49143
- const orbitalR = UNIT_DISPLAY_W * 0.38;
49144
- return /* @__PURE__ */ jsxs(
49145
- "svg",
49146
- {
49147
- style: {
49148
- position: "absolute",
49149
- inset: 0,
49150
- width: "100%",
49151
- height: "100%",
49152
- pointerEvents: "none",
49153
- overflow: "visible"
49154
- },
49155
- viewBox: `0 0 ${containerW} ${containerH}`,
49156
- preserveAspectRatio: "xMidYMid meet",
49157
- children: [
49158
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
49159
- "marker",
49160
- {
49161
- id: ids.arrow,
49162
- markerWidth: "8",
49163
- markerHeight: "6",
49164
- refX: "7",
49165
- refY: "3",
49166
- orient: "auto",
49167
- markerUnits: "strokeWidth",
49168
- children: /* @__PURE__ */ jsx("path", { d: "M0,0 L8,3 L0,6 Z", fill: color, opacity: 0.6 })
49169
- }
49170
- ) }),
49171
- animated && /* @__PURE__ */ jsx("style", { children: `
49172
- @keyframes avl-ocz-flow {
49173
- from { stroke-dashoffset: 20; }
49174
- to { stroke-dashoffset: 0; }
49175
- }
49176
- ` }),
49177
- Array.from(wiresByPair.entries()).map(
49178
- ([pairKey, links]) => links.map((link, wireIdx) => {
49179
- const fromPos = posMap.get(link.emitterOrbital);
49180
- const toPos = posMap.get(link.listenerOrbital);
49181
- if (!fromPos || !toPos) return null;
49182
- const dx = toPos.cx - fromPos.cx;
49183
- const dy = toPos.cy - fromPos.cy;
49184
- const dist = Math.sqrt(dx * dx + dy * dy) || 1;
49185
- const nx = dx / dist;
49186
- const ny = dy / dist;
49187
- const x1 = fromPos.cx + nx * orbitalR;
49188
- const y1 = fromPos.cy + ny * orbitalR;
49189
- const x2 = toPos.cx - nx * (orbitalR + 6);
49190
- const y2 = toPos.cy - ny * (orbitalR + 6);
49191
- const offset = 25 + wireIdx * 18;
49192
- const { cpx, cpy } = curveControlPoint(x1, y1, x2, y2, offset);
49193
- const pathD = `M${x1},${y1} Q${cpx},${cpy} ${x2},${y2}`;
49194
- const t = 0.5;
49195
- const lx = (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * cpx + t * t * x2;
49196
- const ly = (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * cpy + t * t * y2;
49197
- const labelW = link.eventName.length * 5.5 + 12;
49198
- return /* @__PURE__ */ jsxs("g", { children: [
49199
- /* @__PURE__ */ jsx(
49200
- "path",
49201
- {
49202
- d: pathD,
49203
- fill: "none",
49204
- stroke: color,
49205
- strokeWidth: 1.2,
49206
- strokeDasharray: animated ? "6 4" : "4 3",
49207
- markerEnd: `url(#${ids.arrow})`,
49208
- opacity: 0.5,
49209
- style: animated ? { animation: "avl-ocz-flow 1.5s linear infinite" } : void 0
49210
- }
49211
- ),
49212
- /* @__PURE__ */ jsx(
49213
- "rect",
49214
- {
49215
- x: lx - labelW / 2,
49216
- y: ly - 8,
49217
- width: labelW,
49218
- height: 14,
49219
- rx: 3,
49220
- fill: "var(--color-background, #fff)",
49221
- stroke: color,
49222
- strokeWidth: 0.5,
49223
- opacity: 0.9
49224
- }
49225
- ),
49226
- /* @__PURE__ */ jsx(
49227
- "text",
49228
- {
49229
- x: lx,
49230
- y: ly + 3,
49231
- textAnchor: "middle",
49232
- fill: color,
49233
- fontSize: 7,
49234
- fontWeight: 600,
49235
- fontFamily: "monospace",
49236
- opacity: 0.8,
49237
- children: link.eventName
49238
- }
49239
- )
49240
- ] }, `${pairKey}-${wireIdx}`);
49241
- })
49242
- )
49243
- ]
49244
- }
49245
- );
49246
- };
49247
- var InfoPanel = ({ view, crossLinks, color }) => {
49248
- const emitsOut = crossLinks.filter((l) => l.emitterOrbital === view.name);
49249
- const listensIn = crossLinks.filter((l) => l.listenerOrbital === view.name);
49250
- return /* @__PURE__ */ jsxs(
49251
- Box,
49252
- {
49253
- position: "absolute",
49254
- rounded: "lg",
49255
- paddingX: "md",
49256
- paddingY: "sm",
49257
- bg: "overlay",
49258
- style: {
49259
- left: view.cx - 120,
49260
- top: view.cy + UNIT_DISPLAY_H / 2 + 4,
49261
- width: 240,
49262
- border: `1px solid ${color}`,
49263
- zIndex: 20,
49264
- pointerEvents: "none"
49265
- },
49266
- children: [
49267
- /* @__PURE__ */ jsx(Typography, { weight: "semibold", style: { marginBottom: 4, color }, children: view.name }),
49268
- /* @__PURE__ */ jsxs(Text, { variant: "small", style: { opacity: 0.7, color }, children: [
49269
- "Entity: ",
49270
- view.entityName,
49271
- " (",
49272
- view.fieldCount,
49273
- " fields, ",
49274
- view.persistence,
49275
- ")"
49276
- ] }),
49277
- /* @__PURE__ */ jsxs(Text, { variant: "small", style: { opacity: 0.7, color }, children: [
49278
- "Traits: ",
49279
- view.traits.map((t) => t.name).join(", ") || "none"
49280
- ] }),
49281
- view.pages.length > 0 && /* @__PURE__ */ jsxs(Text, { variant: "small", style: { opacity: 0.7, color }, children: [
49282
- "Pages: ",
49283
- view.pages.map((p2) => p2.name).join(", ")
49284
- ] }),
49285
- emitsOut.length > 0 && /* @__PURE__ */ jsxs(Text, { variant: "small", style: { opacity: 0.7, color }, children: [
49286
- "Emits \u2192 ",
49287
- emitsOut.map((l) => `${l.eventName} \u2192 ${l.listenerOrbital}`).join(", ")
49288
- ] }),
49289
- listensIn.length > 0 && /* @__PURE__ */ jsxs(Text, { variant: "small", style: { opacity: 0.7, color }, children: [
49290
- "Listens \u2190 ",
49291
- listensIn.map((l) => `${l.eventName} \u2190 ${l.emitterOrbital}`).join(", ")
49292
- ] })
49293
- ]
49294
- }
49295
- );
49296
- };
49297
- var AvlOrbitalsCosmicZoom = ({
49298
- schema: schemaProp,
49299
- className,
49300
- color = "var(--color-primary, #4A90D9)",
49301
- animated = true,
49302
- width = "100%",
49303
- height = 450,
49304
- highlightedOrbital,
49305
- onOrbitalSelect,
49306
- minZoom = 0.4,
49307
- maxZoom = 3
49308
- }) => {
49309
- const parsedSchema = useMemo(() => {
49310
- if (typeof schemaProp === "string") return JSON.parse(schemaProp);
49311
- return schemaProp;
49312
- }, [schemaProp]);
49313
- const { orbitals, crossLinks } = useMemo(
49314
- () => parseApplicationLevel(parsedSchema),
49315
- [parsedSchema]
49316
- );
49317
- const containerW = typeof width === "number" ? width : 800;
49318
- const containerH = typeof height === "number" ? height : 450;
49319
- const positions = useMemo(
49320
- () => layoutOrbitals(orbitals.length, containerW, containerH),
49321
- [orbitals.length, containerW, containerH]
49322
- );
49323
- const orbitalViews = useMemo(
49324
- () => orbitals.map((o, i) => ({
49325
- name: o.name,
49326
- entityName: o.entityName,
49327
- fieldCount: o.fieldCount,
49328
- persistence: o.persistence || "persistent",
49329
- traits: o.traitNames.map((n) => ({ name: n })),
49330
- pages: o.pageNames.map((n) => ({ name: n })),
49331
- cx: positions[i]?.cx ?? 0,
49332
- cy: positions[i]?.cy ?? 0
49333
- })),
49334
- [orbitals, positions]
49335
- );
49336
- const [selected, setSelected] = useState(null);
49337
- const handleSelect = useCallback(
49338
- (name) => {
49339
- setSelected((prev) => prev === name ? null : name);
49340
- onOrbitalSelect?.(name);
49341
- },
49342
- [onOrbitalSelect]
49343
- );
49344
- const selectedView = orbitalViews.find((o) => o.name === selected);
49345
- const [zoom, setZoom] = useState(1);
49346
- const [pan, setPan] = useState({ x: 0, y: 0 });
49347
- const dragStateRef = useRef(null);
49348
- const transformWrapperRef = useRef(null);
49349
- const clampZoom = useCallback(
49350
- (z) => Math.max(minZoom, Math.min(maxZoom, z)),
49351
- [minZoom, maxZoom]
49352
- );
49353
- const handlePointerDown = useCallback((e) => {
49354
- if (e.target.closest("[data-orbital-tile]")) return;
49355
- dragStateRef.current = {
49356
- startX: e.clientX,
49357
- startY: e.clientY,
49358
- panX: pan.x,
49359
- panY: pan.y
49360
- };
49361
- e.target.setPointerCapture(e.pointerId);
49362
- }, [pan]);
49363
- const handlePointerMove = useCallback((e) => {
49364
- const drag = dragStateRef.current;
49365
- if (!drag) return;
49366
- setPan({
49367
- x: drag.panX + (e.clientX - drag.startX),
49368
- y: drag.panY + (e.clientY - drag.startY)
49369
- });
49370
- }, []);
49371
- const handlePointerUp = useCallback((e) => {
49372
- if (!dragStateRef.current) return;
49373
- dragStateRef.current = null;
49374
- try {
49375
- e.target.releasePointerCapture(e.pointerId);
49376
- } catch {
49377
- }
49378
- }, []);
49379
- const panRef = useRef(pan);
49380
- const zoomRef = useRef(zoom);
49381
- useEffect(() => {
49382
- panRef.current = pan;
49383
- }, [pan]);
49384
- useEffect(() => {
49385
- zoomRef.current = zoom;
49386
- }, [zoom]);
49387
- useEffect(() => {
49388
- const wrapper = transformWrapperRef.current;
49389
- if (!wrapper) return;
49390
- const wheelListener = (e) => {
49391
- e.preventDefault();
49392
- const rect = wrapper.getBoundingClientRect();
49393
- const cursorX = e.clientX - rect.left;
49394
- const cursorY = e.clientY - rect.top;
49395
- const currentZoom = zoomRef.current;
49396
- const currentPan = panRef.current;
49397
- const worldX = (cursorX - currentPan.x) / currentZoom;
49398
- const worldY = (cursorY - currentPan.y) / currentZoom;
49399
- const delta = e.deltaY > 0 ? -0.1 : 0.1;
49400
- const nextZoom = clampZoom(currentZoom * (1 + delta));
49401
- const nextPanX = cursorX - worldX * nextZoom;
49402
- const nextPanY = cursorY - worldY * nextZoom;
49403
- setZoom(nextZoom);
49404
- setPan({ x: nextPanX, y: nextPanY });
49405
- };
49406
- wrapper.addEventListener("wheel", wheelListener, { passive: false });
49407
- return () => wrapper.removeEventListener("wheel", wheelListener);
49408
- }, [clampZoom]);
49409
- const zoomIn = useCallback(() => setZoom((z) => clampZoom(z * 1.2)), [clampZoom]);
49410
- const zoomOut = useCallback(() => setZoom((z) => clampZoom(z / 1.2)), [clampZoom]);
49411
- const resetZoom = useCallback(() => {
49412
- setZoom(1);
49413
- setPan({ x: 0, y: 0 });
49414
- }, []);
49415
- return /* @__PURE__ */ jsxs(
49416
- Box,
49417
- {
49418
- className,
49419
- position: "relative",
49420
- overflow: "visible",
49421
- style: { width, height: containerH },
49422
- children: [
49423
- /* @__PURE__ */ jsx(
49424
- "div",
49425
- {
49426
- ref: transformWrapperRef,
49427
- onPointerDown: handlePointerDown,
49428
- onPointerMove: handlePointerMove,
49429
- onPointerUp: handlePointerUp,
49430
- onPointerCancel: handlePointerUp,
49431
- style: {
49432
- position: "absolute",
49433
- inset: 0,
49434
- overflow: "hidden",
49435
- cursor: dragStateRef.current ? "grabbing" : "grab",
49436
- touchAction: "none"
49437
- },
49438
- children: /* @__PURE__ */ jsxs(
49439
- "div",
49440
- {
49441
- style: {
49442
- position: "absolute",
49443
- inset: 0,
49444
- transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`,
49445
- transformOrigin: "0 0"
49446
- },
49447
- children: [
49448
- /* @__PURE__ */ jsx(
49449
- EventWireOverlay,
49450
- {
49451
- orbitalViews,
49452
- crossLinks,
49453
- color,
49454
- animated,
49455
- containerW,
49456
- containerH
49457
- }
49458
- ),
49459
- orbitalViews.map((view) => {
49460
- const isHighlighted = view.name === highlightedOrbital;
49461
- return /* @__PURE__ */ jsx(
49462
- Box,
49463
- {
49464
- role: "button",
49465
- tabIndex: 0,
49466
- "data-orbital-tile": "true",
49467
- onClick: () => handleSelect(view.name),
49468
- onKeyDown: (e) => {
49469
- if (e.key === "Enter" || e.key === " ") handleSelect(view.name);
49470
- },
49471
- "aria-label": `Orbital: ${view.name}${isHighlighted ? " (highlighted)" : ""}`,
49472
- position: "absolute",
49473
- style: {
49474
- left: view.cx - UNIT_DISPLAY_W / 2,
49475
- top: view.cy - UNIT_DISPLAY_H / 2,
49476
- width: UNIT_DISPLAY_W,
49477
- height: UNIT_DISPLAY_H,
49478
- cursor: "pointer",
49479
- transition: "transform 0.2s ease, filter 0.2s ease, box-shadow 0.3s ease",
49480
- transform: selected === view.name ? "scale(1.05)" : "scale(1)",
49481
- filter: selected && selected !== view.name ? "opacity(0.5)" : "none",
49482
- // GAP-52: persistent highlight ring (independent from user selection)
49483
- boxShadow: isHighlighted ? `0 0 0 3px ${color}, 0 0 24px 4px ${color}` : "none",
49484
- borderRadius: isHighlighted ? "12px" : void 0,
49485
- zIndex: isHighlighted ? 11 : selected === view.name ? 10 : 1
49486
- },
49487
- children: /* @__PURE__ */ jsx(
49488
- AvlOrbitalUnit,
49489
- {
49490
- entityName: view.entityName,
49491
- fields: view.fieldCount,
49492
- persistence: view.persistence,
49493
- traits: view.traits,
49494
- pages: view.pages,
49495
- color,
49496
- animated: animated && (selected === view.name || isHighlighted)
49497
- }
49498
- )
49499
- },
49500
- view.name
49501
- );
49502
- }),
49503
- selectedView && /* @__PURE__ */ jsx(
49504
- InfoPanel,
49505
- {
49506
- view: selectedView,
49507
- crossLinks,
49508
- color
49509
- }
49510
- )
49511
- ]
49512
- }
49513
- )
49514
- }
49515
- ),
49516
- /* @__PURE__ */ jsxs(
49517
- Box,
49518
- {
49519
- position: "absolute",
49520
- style: {
49521
- top: 12,
49522
- right: 12,
49523
- display: "flex",
49524
- flexDirection: "column",
49525
- gap: 4,
49526
- zIndex: 30
49527
- },
49528
- children: [
49529
- /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: zoomIn, title: "Zoom in", action: "COSMIC_ZOOM_IN", children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: "sm" }) }),
49530
- /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: zoomOut, title: "Zoom out", action: "COSMIC_ZOOM_OUT", children: /* @__PURE__ */ jsx(Icon, { name: "minus", size: "sm" }) }),
49531
- /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: resetZoom, title: "Reset", action: "COSMIC_ZOOM_RESET", children: /* @__PURE__ */ jsx(Icon, { name: "maximize", size: "sm" }) })
49532
- ]
49533
- }
49534
- )
49535
- ]
49536
- }
49537
- );
49538
- };
49539
- AvlOrbitalsCosmicZoom.displayName = "AvlOrbitalsCosmicZoom";
49540
- init_types();
49541
- var SWIM_GUTTER2 = 120;
49542
- var CENTER_W2 = 360;
49543
- var AvlTraitScene = ({
49544
- data,
49545
- color = "var(--color-primary)",
49546
- onTransitionClick
49547
- }) => {
49548
- const [layout, setLayout] = useState(null);
49549
- const dataKey = useMemo(() => JSON.stringify(data), [data]);
49550
- useEffect(() => {
49551
- computeTraitLayout(data).then(setLayout).catch(console.error);
49552
- }, [dataKey]);
49553
- if (!layout) {
49554
- return /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("text", { x: 300, y: 200, textAnchor: "middle", fill: color, fontSize: 12, opacity: 0.5, children: "Computing layout..." }) });
49555
- }
49556
- const hasExternal = data.listenedEvents.length > 0 || data.emittedEvents.length > 0;
49557
- const machineOffsetX = hasExternal ? 0 : 30;
49558
- const padding = 20;
49559
- const availW = CENTER_W2 - padding * 2;
49560
- const availH = 300;
49561
- const scale = Math.min(1, availW / layout.width, availH / layout.height);
49562
- const scaledW = layout.width * scale;
49563
- const scaledH = layout.height * scale;
49564
- const offsetX = padding + (availW - scaledW) / 2;
49565
- const offsetY = 50 + (availH - scaledH) / 2;
49566
- const machineHeight = scaledH + 100;
49567
- const renderMachine = /* @__PURE__ */ jsxs("g", { children: [
49568
- /* @__PURE__ */ jsx("text", { x: CENTER_W2 / 2, y: 20, textAnchor: "middle", fill: color, fontSize: 20, fontWeight: "700", fontFamily: "inherit", children: data.name }),
49569
- /* @__PURE__ */ jsxs("text", { x: CENTER_W2 / 2, y: 38, textAnchor: "middle", fill: color, fontSize: 11, opacity: 0.5, fontFamily: "inherit", children: [
49570
- "linked to ",
49571
- data.linkedEntity
49572
- ] }),
49573
- /* @__PURE__ */ jsxs("defs", { children: [
49574
- /* @__PURE__ */ jsx("marker", { id: "traitArrowV2", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "6", markerHeight: "6", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: CONNECTION_COLORS.forward.color, opacity: 0.7 }) }),
49575
- /* @__PURE__ */ jsx("marker", { id: "traitArrowBack", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "6", markerHeight: "6", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: CONNECTION_COLORS.backward.color, opacity: 0.5 }) })
49576
- ] }),
49577
- /* @__PURE__ */ jsxs("g", { transform: `translate(${offsetX},${offsetY}) scale(${scale})`, children: [
49578
- layout.edges.map((edge) => {
49579
- const conn = edge.isSelf ? CONNECTION_COLORS.selfLoop : edge.isBackward ? CONNECTION_COLORS.backward : CONNECTION_COLORS.forward;
49580
- const marker = edge.isBackward || edge.isSelf ? "url(#traitArrowBack)" : "url(#traitArrowV2)";
49581
- return /* @__PURE__ */ jsxs("g", { children: [
49582
- /* @__PURE__ */ jsx(
49583
- "path",
49584
- {
49585
- d: edgePath(edge.points),
49586
- fill: "none",
49587
- stroke: conn.color,
49588
- strokeWidth: conn.width,
49589
- strokeDasharray: conn.dash === "none" ? void 0 : conn.dash,
49590
- opacity: 0.5,
49591
- markerEnd: marker
49592
- }
49593
- ),
49594
- /* @__PURE__ */ jsx(
49595
- AvlTransitionLane,
49596
- {
49597
- x: edge.labelX,
49598
- y: edge.labelY,
49599
- event: edge.event,
49600
- guard: edge.guardExpr,
49601
- effects: edge.effects.map((e) => ({ type: e.type })),
49602
- width: edge.labelW,
49603
- isBackward: edge.isBackward,
49604
- isSelfLoop: edge.isSelf,
49605
- color,
49606
- onTransitionClick: onTransitionClick ? () => onTransitionClick(edge.index, {
49607
- x: edge.labelX + offsetX + (hasExternal ? SWIM_GUTTER2 : machineOffsetX),
49608
- y: edge.labelY + offsetY
49609
- }) : void 0
49610
- }
49611
- )
49612
- ] }, edge.id);
49613
- }),
49614
- layout.nodes.map((node) => /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
49615
- AvlState,
49616
- {
49617
- x: node.x,
49618
- y: node.y,
49619
- width: node.width,
49620
- height: node.height,
49621
- name: node.id,
49622
- isInitial: node.isInitial,
49623
- isTerminal: node.isTerminal,
49624
- role: node.role,
49625
- transitionCount: node.transitionCount,
49626
- color
49627
- }
49628
- ) }, node.id))
49629
- ] })
49630
- ] });
49631
- if (!hasExternal) {
49632
- return /* @__PURE__ */ jsx("g", { transform: `translate(${machineOffsetX}, 0)`, children: renderMachine });
49633
- }
49634
- return /* @__PURE__ */ jsx(
49635
- AvlSwimLane,
49199
+ const [layout, setLayout] = useState(null);
49200
+ const dataKey = useMemo(() => JSON.stringify(data), [data]);
49201
+ useEffect(() => {
49202
+ computeTraitLayout(data).then(setLayout).catch(console.error);
49203
+ }, [dataKey]);
49204
+ if (!layout) {
49205
+ return /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx("text", { x: 300, y: 200, textAnchor: "middle", fill: color, fontSize: 12, opacity: 0.5, children: "Computing layout..." }) });
49206
+ }
49207
+ const hasExternal = data.listenedEvents.length > 0 || data.emittedEvents.length > 0;
49208
+ const machineOffsetX = hasExternal ? 0 : 30;
49209
+ const padding = 20;
49210
+ const availW = CENTER_W2 - padding * 2;
49211
+ const availH = 300;
49212
+ const scale = Math.min(1, availW / layout.width, availH / layout.height);
49213
+ const scaledW = layout.width * scale;
49214
+ const scaledH = layout.height * scale;
49215
+ const offsetX = padding + (availW - scaledW) / 2;
49216
+ const offsetY = 50 + (availH - scaledH) / 2;
49217
+ const machineHeight = scaledH + 100;
49218
+ const renderMachine = /* @__PURE__ */ jsxs("g", { children: [
49219
+ /* @__PURE__ */ jsx("text", { x: CENTER_W2 / 2, y: 20, textAnchor: "middle", fill: color, fontSize: 20, fontWeight: "700", fontFamily: "inherit", children: data.name }),
49220
+ /* @__PURE__ */ jsxs("text", { x: CENTER_W2 / 2, y: 38, textAnchor: "middle", fill: color, fontSize: 11, opacity: 0.5, fontFamily: "inherit", children: [
49221
+ "linked to ",
49222
+ data.linkedEntity
49223
+ ] }),
49224
+ /* @__PURE__ */ jsxs("defs", { children: [
49225
+ /* @__PURE__ */ jsx("marker", { id: "traitArrowV2", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "6", markerHeight: "6", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: CONNECTION_COLORS.forward.color, opacity: 0.7 }) }),
49226
+ /* @__PURE__ */ jsx("marker", { id: "traitArrowBack", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "6", markerHeight: "6", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: CONNECTION_COLORS.backward.color, opacity: 0.5 }) })
49227
+ ] }),
49228
+ /* @__PURE__ */ jsxs("g", { transform: `translate(${offsetX},${offsetY}) scale(${scale})`, children: [
49229
+ layout.edges.map((edge) => {
49230
+ const conn = edge.isSelf ? CONNECTION_COLORS.selfLoop : edge.isBackward ? CONNECTION_COLORS.backward : CONNECTION_COLORS.forward;
49231
+ const marker = edge.isBackward || edge.isSelf ? "url(#traitArrowBack)" : "url(#traitArrowV2)";
49232
+ return /* @__PURE__ */ jsxs("g", { children: [
49233
+ /* @__PURE__ */ jsx(
49234
+ "path",
49235
+ {
49236
+ d: edgePath(edge.points),
49237
+ fill: "none",
49238
+ stroke: conn.color,
49239
+ strokeWidth: conn.width,
49240
+ strokeDasharray: conn.dash === "none" ? void 0 : conn.dash,
49241
+ opacity: 0.5,
49242
+ markerEnd: marker
49243
+ }
49244
+ ),
49245
+ /* @__PURE__ */ jsx(
49246
+ AvlTransitionLane,
49247
+ {
49248
+ x: edge.labelX,
49249
+ y: edge.labelY,
49250
+ event: edge.event,
49251
+ guard: edge.guardExpr,
49252
+ effects: edge.effects.map((e) => ({ type: e.type })),
49253
+ width: edge.labelW,
49254
+ isBackward: edge.isBackward,
49255
+ isSelfLoop: edge.isSelf,
49256
+ color,
49257
+ onTransitionClick: onTransitionClick ? () => onTransitionClick(edge.index, {
49258
+ x: edge.labelX + offsetX + (hasExternal ? SWIM_GUTTER2 : machineOffsetX),
49259
+ y: edge.labelY + offsetY
49260
+ }) : void 0
49261
+ }
49262
+ )
49263
+ ] }, edge.id);
49264
+ }),
49265
+ layout.nodes.map((node) => /* @__PURE__ */ jsx("g", { children: /* @__PURE__ */ jsx(
49266
+ AvlState,
49267
+ {
49268
+ x: node.x,
49269
+ y: node.y,
49270
+ width: node.width,
49271
+ height: node.height,
49272
+ name: node.id,
49273
+ isInitial: node.isInitial,
49274
+ isTerminal: node.isTerminal,
49275
+ role: node.role,
49276
+ transitionCount: node.transitionCount,
49277
+ color
49278
+ }
49279
+ ) }, node.id))
49280
+ ] })
49281
+ ] });
49282
+ if (!hasExternal) {
49283
+ return /* @__PURE__ */ jsx("g", { transform: `translate(${machineOffsetX}, 0)`, children: renderMachine });
49284
+ }
49285
+ return /* @__PURE__ */ jsx(
49286
+ AvlSwimLane,
49636
49287
  {
49637
49288
  listenedEvents: data.listenedEvents,
49638
49289
  emittedEvents: data.emittedEvents,
@@ -49827,63 +49478,670 @@ var AvlTransitionScene = ({
49827
49478
  return /* @__PURE__ */ jsxs("g", { children: [
49828
49479
  /* @__PURE__ */ jsx("text", { x: SECTION_LEFT, y: secY + 12, fill: color, fontSize: 10, fontWeight: 600, opacity: 0.4, fontFamily: "inherit", children: "GUARD" }),
49829
49480
  /* @__PURE__ */ jsx(
49830
- "polygon",
49481
+ "polygon",
49482
+ {
49483
+ points: `${CONTENT_LEFT + 6},${secY + 22} ${CONTENT_LEFT + 2},${secY + 26} ${CONTENT_LEFT + 6},${secY + 30} ${CONTENT_LEFT + 10},${secY + 26}`,
49484
+ fill: "none",
49485
+ stroke: color,
49486
+ strokeWidth: 1,
49487
+ opacity: 0.5
49488
+ }
49489
+ ),
49490
+ /* @__PURE__ */ jsx("text", { x: CONTENT_LEFT + 18, y: secY + 29, fill: color, fontSize: 12, fontWeight: 400, opacity: 0.6, fontFamily: "inherit", children: data.guard.label })
49491
+ ] });
49492
+ })(),
49493
+ hasEffects && (() => {
49494
+ const secY = cardY + headerH + sectionGap + triggerH + guardH;
49495
+ return /* @__PURE__ */ jsxs("g", { children: [
49496
+ /* @__PURE__ */ jsx("text", { x: SECTION_LEFT, y: secY + 12, fill: color, fontSize: 10, fontWeight: 600, opacity: 0.4, fontFamily: "inherit", children: "EFFECTS" }),
49497
+ effects.map((eff, i) => {
49498
+ const rowY = secY + 22 + i * effectRowH;
49499
+ const effectType = mapEffectType(eff.type);
49500
+ const argsText = eff.args.length > 0 ? eff.args.join(" \xB7 ") : "";
49501
+ return /* @__PURE__ */ jsxs("g", { children: [
49502
+ /* @__PURE__ */ jsx(
49503
+ AvlEffect,
49504
+ {
49505
+ x: CONTENT_LEFT + 10,
49506
+ y: rowY + 6,
49507
+ effectType,
49508
+ size: 10,
49509
+ showBackground: true
49510
+ }
49511
+ ),
49512
+ /* @__PURE__ */ jsx("text", { x: CONTENT_LEFT + 28, y: rowY + 10, fill: color, fontSize: 12, fontWeight: 500, opacity: 0.8, fontFamily: "inherit", children: eff.type }),
49513
+ argsText && /* @__PURE__ */ jsx("text", { x: CONTENT_LEFT + 28 + eff.type.length * 7 + 8, y: rowY + 10, fill: color, fontSize: 10, fontWeight: 400, opacity: 0.5, fontFamily: "monospace", children: argsText })
49514
+ ] }, `eff-${i}`);
49515
+ })
49516
+ ] });
49517
+ })(),
49518
+ hasSlots && (() => {
49519
+ const secY = cardY + headerH + sectionGap + triggerH + guardH + effectsH;
49520
+ return /* @__PURE__ */ jsxs("g", { children: [
49521
+ /* @__PURE__ */ jsx("text", { x: SECTION_LEFT, y: secY + 12, fill: color, fontSize: 10, fontWeight: 600, opacity: 0.4, fontFamily: "inherit", children: "SLOTS" }),
49522
+ data.slotTargets.map((slot, i) => {
49523
+ const rowY = secY + 22 + i * 22;
49524
+ return /* @__PURE__ */ jsxs("g", { children: [
49525
+ /* @__PURE__ */ jsx("rect", { x: CONTENT_LEFT, y: rowY - 4, width: CARD_W - 56, height: 18, rx: 4, fill: color, fillOpacity: 0.04 }),
49526
+ /* @__PURE__ */ jsxs("text", { x: CONTENT_LEFT + 8, y: rowY + 8, fill: color, fontSize: 11, fontFamily: "inherit", children: [
49527
+ slot.name,
49528
+ ": ",
49529
+ slot.pattern
49530
+ ] })
49531
+ ] }, `slot-${i}`);
49532
+ })
49533
+ ] });
49534
+ })()
49535
+ ] });
49536
+ };
49537
+ AvlTransitionScene.displayName = "AvlTransitionScene";
49538
+
49539
+ // components/organisms/avl/AvlOrbitalsCosmicZoom.tsx
49540
+ init_Box();
49541
+ init_Stack();
49542
+ init_Typography();
49543
+ var UNIT_DISPLAY_W = 240;
49544
+ var UNIT_DISPLAY_H = 160;
49545
+ function layoutOrbitals(count, containerW, containerH) {
49546
+ if (count === 0) return [];
49547
+ if (count === 1) return [{ cx: containerW / 2, cy: containerH / 2 }];
49548
+ const cols = Math.min(count, Math.ceil(Math.sqrt(count)));
49549
+ const rows = Math.ceil(count / cols);
49550
+ const cellW = containerW / (cols + 0.3);
49551
+ const cellH = containerH / (rows + 0.3);
49552
+ const originX = (containerW - cols * cellW) / 2 + cellW / 2;
49553
+ const originY = (containerH - rows * cellH) / 2 + cellH / 2;
49554
+ return Array.from({ length: count }, (_, i) => ({
49555
+ cx: originX + i % cols * cellW,
49556
+ cy: originY + Math.floor(i / cols) * cellH
49557
+ }));
49558
+ }
49559
+ var avlOczWireId = 0;
49560
+ var EventWireOverlay = ({
49561
+ orbitalViews,
49562
+ crossLinks,
49563
+ color,
49564
+ animated,
49565
+ containerW,
49566
+ containerH
49567
+ }) => {
49568
+ const ids = React125__default.useMemo(() => {
49569
+ avlOczWireId += 1;
49570
+ return { arrow: `avl-ocz-wire-${avlOczWireId}-arrow` };
49571
+ }, []);
49572
+ const posMap = useMemo(() => {
49573
+ const m = /* @__PURE__ */ new Map();
49574
+ for (const ov of orbitalViews) m.set(ov.name, { cx: ov.cx, cy: ov.cy });
49575
+ return m;
49576
+ }, [orbitalViews]);
49577
+ const wiresByPair = useMemo(() => {
49578
+ const map = /* @__PURE__ */ new Map();
49579
+ for (const link of crossLinks) {
49580
+ const key = `${link.emitterOrbital}__${link.listenerOrbital}`;
49581
+ const arr = map.get(key) ?? [];
49582
+ arr.push(link);
49583
+ map.set(key, arr);
49584
+ }
49585
+ return map;
49586
+ }, [crossLinks]);
49587
+ const orbitalR = UNIT_DISPLAY_W * 0.38;
49588
+ return /* @__PURE__ */ jsxs(
49589
+ "svg",
49590
+ {
49591
+ style: {
49592
+ position: "absolute",
49593
+ inset: 0,
49594
+ width: "100%",
49595
+ height: "100%",
49596
+ pointerEvents: "none",
49597
+ overflow: "visible"
49598
+ },
49599
+ viewBox: `0 0 ${containerW} ${containerH}`,
49600
+ preserveAspectRatio: "xMidYMid meet",
49601
+ children: [
49602
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
49603
+ "marker",
49604
+ {
49605
+ id: ids.arrow,
49606
+ markerWidth: "8",
49607
+ markerHeight: "6",
49608
+ refX: "7",
49609
+ refY: "3",
49610
+ orient: "auto",
49611
+ markerUnits: "strokeWidth",
49612
+ children: /* @__PURE__ */ jsx("path", { d: "M0,0 L8,3 L0,6 Z", fill: color, opacity: 0.6 })
49613
+ }
49614
+ ) }),
49615
+ animated && /* @__PURE__ */ jsx("style", { children: `
49616
+ @keyframes avl-ocz-flow {
49617
+ from { stroke-dashoffset: 20; }
49618
+ to { stroke-dashoffset: 0; }
49619
+ }
49620
+ ` }),
49621
+ Array.from(wiresByPair.entries()).map(
49622
+ ([pairKey, links]) => links.map((link, wireIdx) => {
49623
+ const fromPos = posMap.get(link.emitterOrbital);
49624
+ const toPos = posMap.get(link.listenerOrbital);
49625
+ if (!fromPos || !toPos) return null;
49626
+ const dx = toPos.cx - fromPos.cx;
49627
+ const dy = toPos.cy - fromPos.cy;
49628
+ const dist = Math.sqrt(dx * dx + dy * dy) || 1;
49629
+ const nx = dx / dist;
49630
+ const ny = dy / dist;
49631
+ const x1 = fromPos.cx + nx * orbitalR;
49632
+ const y1 = fromPos.cy + ny * orbitalR;
49633
+ const x2 = toPos.cx - nx * (orbitalR + 6);
49634
+ const y2 = toPos.cy - ny * (orbitalR + 6);
49635
+ const offset = 25 + wireIdx * 18;
49636
+ const { cpx, cpy } = curveControlPoint(x1, y1, x2, y2, offset);
49637
+ const pathD = `M${x1},${y1} Q${cpx},${cpy} ${x2},${y2}`;
49638
+ const t = 0.5;
49639
+ const lx = (1 - t) * (1 - t) * x1 + 2 * (1 - t) * t * cpx + t * t * x2;
49640
+ const ly = (1 - t) * (1 - t) * y1 + 2 * (1 - t) * t * cpy + t * t * y2;
49641
+ const labelW = link.eventName.length * 5.5 + 12;
49642
+ return /* @__PURE__ */ jsxs("g", { children: [
49643
+ /* @__PURE__ */ jsx(
49644
+ "path",
49645
+ {
49646
+ d: pathD,
49647
+ fill: "none",
49648
+ stroke: color,
49649
+ strokeWidth: 1.2,
49650
+ strokeDasharray: animated ? "6 4" : "4 3",
49651
+ markerEnd: `url(#${ids.arrow})`,
49652
+ opacity: 0.5,
49653
+ style: animated ? { animation: "avl-ocz-flow 1.5s linear infinite" } : void 0
49654
+ }
49655
+ ),
49656
+ /* @__PURE__ */ jsx(
49657
+ "rect",
49658
+ {
49659
+ x: lx - labelW / 2,
49660
+ y: ly - 8,
49661
+ width: labelW,
49662
+ height: 14,
49663
+ rx: 3,
49664
+ fill: "var(--color-background, #fff)",
49665
+ stroke: color,
49666
+ strokeWidth: 0.5,
49667
+ opacity: 0.9
49668
+ }
49669
+ ),
49670
+ /* @__PURE__ */ jsx(
49671
+ "text",
49672
+ {
49673
+ x: lx,
49674
+ y: ly + 3,
49675
+ textAnchor: "middle",
49676
+ fill: color,
49677
+ fontSize: 7,
49678
+ fontWeight: 600,
49679
+ fontFamily: "monospace",
49680
+ opacity: 0.8,
49681
+ children: link.eventName
49682
+ }
49683
+ )
49684
+ ] }, `${pairKey}-${wireIdx}`);
49685
+ })
49686
+ )
49687
+ ]
49688
+ }
49689
+ );
49690
+ };
49691
+ var AvlOrbitalsCosmicZoom = ({
49692
+ schema: schemaProp,
49693
+ className,
49694
+ color = "var(--color-primary, #4A90D9)",
49695
+ animated = true,
49696
+ width = "100%",
49697
+ height = 450,
49698
+ highlightedOrbital,
49699
+ onOrbitalSelect,
49700
+ minZoom = 0.4,
49701
+ maxZoom = 3
49702
+ }) => {
49703
+ const parsedSchema = useMemo(() => {
49704
+ if (typeof schemaProp === "string") return JSON.parse(schemaProp);
49705
+ return schemaProp;
49706
+ }, [schemaProp]);
49707
+ const { orbitals, crossLinks } = useMemo(
49708
+ () => parseApplicationLevel(parsedSchema),
49709
+ [parsedSchema]
49710
+ );
49711
+ const containerW = typeof width === "number" ? width : 800;
49712
+ const containerH = typeof height === "number" ? height : 450;
49713
+ const positions = useMemo(
49714
+ () => layoutOrbitals(orbitals.length, containerW, containerH),
49715
+ [orbitals.length, containerW, containerH]
49716
+ );
49717
+ const orbitalViews = useMemo(
49718
+ () => orbitals.map((o, i) => ({
49719
+ name: o.name,
49720
+ entityName: o.entityName,
49721
+ fieldCount: o.fieldCount,
49722
+ persistence: o.persistence || "persistent",
49723
+ traits: o.traitNames.map((n) => ({ name: n })),
49724
+ pages: o.pageNames.map((n) => ({ name: n })),
49725
+ cx: positions[i]?.cx ?? 0,
49726
+ cy: positions[i]?.cy ?? 0
49727
+ })),
49728
+ [orbitals, positions]
49729
+ );
49730
+ const [state, dispatch] = useReducer(zoomReducer, initialZoomState);
49731
+ const drilledForHighlightRef = useRef(false);
49732
+ useEffect(() => {
49733
+ if (!highlightedOrbital) return;
49734
+ if (drilledForHighlightRef.current) return;
49735
+ drilledForHighlightRef.current = true;
49736
+ dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: highlightedOrbital, targetPosition: { x: 0, y: 0 } });
49737
+ Promise.resolve().then(() => dispatch({ type: "ANIMATION_COMPLETE" }));
49738
+ }, [highlightedOrbital]);
49739
+ const breadcrumbs = useMemo(() => getBreadcrumbs(state), [state]);
49740
+ const handleSelect = useCallback(
49741
+ (name) => {
49742
+ dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: name, targetPosition: { x: 0, y: 0 } });
49743
+ Promise.resolve().then(() => dispatch({ type: "ANIMATION_COMPLETE" }));
49744
+ onOrbitalSelect?.(name);
49745
+ },
49746
+ [onOrbitalSelect]
49747
+ );
49748
+ const handleTraitSelect = useCallback((traitName) => {
49749
+ dispatch({ type: "ZOOM_INTO_TRAIT", trait: traitName, targetPosition: { x: 0, y: 0 } });
49750
+ Promise.resolve().then(() => dispatch({ type: "ANIMATION_COMPLETE" }));
49751
+ }, []);
49752
+ const handleTransitionSelect = useCallback((transitionIndex) => {
49753
+ dispatch({ type: "ZOOM_INTO_TRANSITION", transitionIndex, targetPosition: { x: 0, y: 0 } });
49754
+ Promise.resolve().then(() => dispatch({ type: "ANIMATION_COMPLETE" }));
49755
+ }, []);
49756
+ const handleZoomOut = useCallback(() => {
49757
+ dispatch({ type: "ZOOM_OUT" });
49758
+ Promise.resolve().then(() => dispatch({ type: "ANIMATION_COMPLETE" }));
49759
+ }, []);
49760
+ const handleBreadcrumbClick = useCallback((targetLevel) => {
49761
+ const order = ["application", "orbital", "trait", "transition"];
49762
+ const currentIdx = order.indexOf(state.level);
49763
+ const targetIdx = order.indexOf(targetLevel);
49764
+ const steps = currentIdx - targetIdx;
49765
+ for (let i = 0; i < steps; i++) {
49766
+ dispatch({ type: "ZOOM_OUT" });
49767
+ }
49768
+ Promise.resolve().then(() => dispatch({ type: "ANIMATION_COMPLETE" }));
49769
+ }, [state.level]);
49770
+ useEffect(() => {
49771
+ if (state.level === "application") return;
49772
+ const onKey = (e) => {
49773
+ if (e.key === "Escape") handleZoomOut();
49774
+ };
49775
+ window.addEventListener("keydown", onKey);
49776
+ return () => window.removeEventListener("keydown", onKey);
49777
+ }, [handleZoomOut, state.level]);
49778
+ const orbitalLevelData = useMemo(() => {
49779
+ if (!state.selectedOrbital) return null;
49780
+ return parseOrbitalLevel(parsedSchema, state.selectedOrbital);
49781
+ }, [parsedSchema, state.selectedOrbital]);
49782
+ const traitLevelData = useMemo(() => {
49783
+ if (!state.selectedOrbital || !state.selectedTrait) return null;
49784
+ return parseTraitLevel(parsedSchema, state.selectedOrbital, state.selectedTrait);
49785
+ }, [parsedSchema, state.selectedOrbital, state.selectedTrait]);
49786
+ const transitionLevelData = useMemo(() => {
49787
+ if (!state.selectedOrbital || !state.selectedTrait || state.selectedTransition === null) return null;
49788
+ return parseTransitionLevel(
49789
+ parsedSchema,
49790
+ state.selectedOrbital,
49791
+ state.selectedTrait,
49792
+ state.selectedTransition
49793
+ );
49794
+ }, [parsedSchema, state.selectedOrbital, state.selectedTrait, state.selectedTransition]);
49795
+ const [zoom, setZoom] = useState(1);
49796
+ const [pan, setPan] = useState({ x: 0, y: 0 });
49797
+ const dragStateRef = useRef(null);
49798
+ const transformWrapperRef = useRef(null);
49799
+ const clampZoom = useCallback(
49800
+ (z) => Math.max(minZoom, Math.min(maxZoom, z)),
49801
+ [minZoom, maxZoom]
49802
+ );
49803
+ const handlePointerDown = useCallback((e) => {
49804
+ if (e.target.closest("[data-orbital-tile]")) return;
49805
+ dragStateRef.current = {
49806
+ startX: e.clientX,
49807
+ startY: e.clientY,
49808
+ panX: pan.x,
49809
+ panY: pan.y
49810
+ };
49811
+ e.target.setPointerCapture(e.pointerId);
49812
+ }, [pan]);
49813
+ const handlePointerMove = useCallback((e) => {
49814
+ const drag = dragStateRef.current;
49815
+ if (!drag) return;
49816
+ setPan({
49817
+ x: drag.panX + (e.clientX - drag.startX),
49818
+ y: drag.panY + (e.clientY - drag.startY)
49819
+ });
49820
+ }, []);
49821
+ const handlePointerUp = useCallback((e) => {
49822
+ if (!dragStateRef.current) return;
49823
+ dragStateRef.current = null;
49824
+ try {
49825
+ e.target.releasePointerCapture(e.pointerId);
49826
+ } catch {
49827
+ }
49828
+ }, []);
49829
+ const panRef = useRef(pan);
49830
+ const zoomRef = useRef(zoom);
49831
+ useEffect(() => {
49832
+ panRef.current = pan;
49833
+ }, [pan]);
49834
+ useEffect(() => {
49835
+ zoomRef.current = zoom;
49836
+ }, [zoom]);
49837
+ useEffect(() => {
49838
+ const wrapper = transformWrapperRef.current;
49839
+ if (!wrapper) return;
49840
+ const wheelListener = (e) => {
49841
+ e.preventDefault();
49842
+ const rect = wrapper.getBoundingClientRect();
49843
+ const cursorX = e.clientX - rect.left;
49844
+ const cursorY = e.clientY - rect.top;
49845
+ const currentZoom = zoomRef.current;
49846
+ const currentPan = panRef.current;
49847
+ const worldX = (cursorX - currentPan.x) / currentZoom;
49848
+ const worldY = (cursorY - currentPan.y) / currentZoom;
49849
+ const delta = e.deltaY > 0 ? -0.1 : 0.1;
49850
+ const nextZoom = clampZoom(currentZoom * (1 + delta));
49851
+ const nextPanX = cursorX - worldX * nextZoom;
49852
+ const nextPanY = cursorY - worldY * nextZoom;
49853
+ setZoom(nextZoom);
49854
+ setPan({ x: nextPanX, y: nextPanY });
49855
+ };
49856
+ wrapper.addEventListener("wheel", wheelListener, { passive: false });
49857
+ return () => wrapper.removeEventListener("wheel", wheelListener);
49858
+ }, [clampZoom]);
49859
+ const zoomIn = useCallback(() => setZoom((z) => clampZoom(z * 1.2)), [clampZoom]);
49860
+ const zoomOut = useCallback(() => setZoom((z) => clampZoom(z / 1.2)), [clampZoom]);
49861
+ const resetZoom = useCallback(() => {
49862
+ setZoom(1);
49863
+ setPan({ x: 0, y: 0 });
49864
+ }, []);
49865
+ return /* @__PURE__ */ jsxs(
49866
+ Box,
49867
+ {
49868
+ className,
49869
+ position: "relative",
49870
+ overflow: "visible",
49871
+ style: { width, height: containerH },
49872
+ children: [
49873
+ /* @__PURE__ */ jsx(
49874
+ Box,
49831
49875
  {
49832
- points: `${CONTENT_LEFT + 6},${secY + 22} ${CONTENT_LEFT + 2},${secY + 26} ${CONTENT_LEFT + 6},${secY + 30} ${CONTENT_LEFT + 10},${secY + 26}`,
49833
- fill: "none",
49834
- stroke: color,
49835
- strokeWidth: 1,
49836
- opacity: 0.5
49876
+ position: "absolute",
49877
+ style: {
49878
+ top: 12,
49879
+ left: 12,
49880
+ zIndex: 30,
49881
+ background: "var(--color-card, rgba(255,255,255,0.92))",
49882
+ padding: "4px 12px",
49883
+ borderRadius: 6,
49884
+ border: `1px solid ${color}`
49885
+ },
49886
+ children: /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "center", children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxs(React125__default.Fragment, { children: [
49887
+ i > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", style: { opacity: 0.5, color }, children: "/" }),
49888
+ i < breadcrumbs.length - 1 ? /* @__PURE__ */ jsx(
49889
+ Box,
49890
+ {
49891
+ as: "span",
49892
+ onClick: () => handleBreadcrumbClick(crumb.level),
49893
+ style: { cursor: "pointer" },
49894
+ children: /* @__PURE__ */ jsx(
49895
+ Typography,
49896
+ {
49897
+ variant: "small",
49898
+ style: { color, textDecoration: "underline" },
49899
+ children: crumb.label
49900
+ }
49901
+ )
49902
+ }
49903
+ ) : /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "bold", style: { color }, children: crumb.label })
49904
+ ] }, crumb.level)) })
49837
49905
  }
49838
49906
  ),
49839
- /* @__PURE__ */ jsx("text", { x: CONTENT_LEFT + 18, y: secY + 29, fill: color, fontSize: 12, fontWeight: 400, opacity: 0.6, fontFamily: "inherit", children: data.guard.label })
49840
- ] });
49841
- })(),
49842
- hasEffects && (() => {
49843
- const secY = cardY + headerH + sectionGap + triggerH + guardH;
49844
- return /* @__PURE__ */ jsxs("g", { children: [
49845
- /* @__PURE__ */ jsx("text", { x: SECTION_LEFT, y: secY + 12, fill: color, fontSize: 10, fontWeight: 600, opacity: 0.4, fontFamily: "inherit", children: "EFFECTS" }),
49846
- effects.map((eff, i) => {
49847
- const rowY = secY + 22 + i * effectRowH;
49848
- const effectType = mapEffectType(eff.type);
49849
- const argsText = eff.args.length > 0 ? eff.args.join(" \xB7 ") : "";
49850
- return /* @__PURE__ */ jsxs("g", { children: [
49851
- /* @__PURE__ */ jsx(
49852
- AvlEffect,
49907
+ state.level !== "application" && /* @__PURE__ */ jsx(
49908
+ Box,
49909
+ {
49910
+ position: "absolute",
49911
+ style: {
49912
+ bottom: 12,
49913
+ right: 12,
49914
+ zIndex: 30,
49915
+ background: "var(--color-card, rgba(255,255,255,0.85))",
49916
+ padding: "2px 8px",
49917
+ borderRadius: 4,
49918
+ opacity: 0.8
49919
+ },
49920
+ children: /* @__PURE__ */ jsx(Typography, { variant: "small", style: { color }, children: "Press Esc to zoom out" })
49921
+ }
49922
+ ),
49923
+ state.level === "application" && /* @__PURE__ */ jsxs(Fragment, { children: [
49924
+ /* @__PURE__ */ jsx(
49925
+ "div",
49926
+ {
49927
+ ref: transformWrapperRef,
49928
+ onPointerDown: handlePointerDown,
49929
+ onPointerMove: handlePointerMove,
49930
+ onPointerUp: handlePointerUp,
49931
+ onPointerCancel: handlePointerUp,
49932
+ style: {
49933
+ position: "absolute",
49934
+ inset: 0,
49935
+ overflow: "hidden",
49936
+ cursor: dragStateRef.current ? "grabbing" : "grab",
49937
+ touchAction: "none"
49938
+ },
49939
+ children: /* @__PURE__ */ jsxs(
49940
+ "div",
49941
+ {
49942
+ style: {
49943
+ position: "absolute",
49944
+ inset: 0,
49945
+ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`,
49946
+ transformOrigin: "0 0"
49947
+ },
49948
+ children: [
49949
+ /* @__PURE__ */ jsx(
49950
+ EventWireOverlay,
49951
+ {
49952
+ orbitalViews,
49953
+ crossLinks,
49954
+ color,
49955
+ animated,
49956
+ containerW,
49957
+ containerH
49958
+ }
49959
+ ),
49960
+ orbitalViews.map((view) => {
49961
+ const isHighlighted = view.name === highlightedOrbital;
49962
+ return /* @__PURE__ */ jsx(
49963
+ Box,
49964
+ {
49965
+ role: "button",
49966
+ tabIndex: 0,
49967
+ "data-orbital-tile": "true",
49968
+ onClick: () => handleSelect(view.name),
49969
+ onKeyDown: (e) => {
49970
+ if (e.key === "Enter" || e.key === " ") handleSelect(view.name);
49971
+ },
49972
+ "aria-label": `Orbital: ${view.name}${isHighlighted ? " (highlighted)" : ""}`,
49973
+ position: "absolute",
49974
+ style: {
49975
+ left: view.cx - UNIT_DISPLAY_W / 2,
49976
+ top: view.cy - UNIT_DISPLAY_H / 2,
49977
+ width: UNIT_DISPLAY_W,
49978
+ height: UNIT_DISPLAY_H,
49979
+ cursor: "pointer",
49980
+ transition: "transform 0.2s ease, filter 0.2s ease, box-shadow 0.3s ease",
49981
+ // GAP-52: persistent highlight ring (independent from user selection)
49982
+ boxShadow: isHighlighted ? `0 0 0 3px ${color}, 0 0 24px 4px ${color}` : "none",
49983
+ borderRadius: isHighlighted ? "12px" : void 0,
49984
+ zIndex: isHighlighted ? 11 : 1
49985
+ },
49986
+ children: /* @__PURE__ */ jsx(
49987
+ AvlOrbitalUnit,
49988
+ {
49989
+ entityName: view.entityName,
49990
+ fields: view.fieldCount,
49991
+ persistence: view.persistence,
49992
+ traits: view.traits,
49993
+ pages: view.pages,
49994
+ color,
49995
+ animated: animated && isHighlighted
49996
+ }
49997
+ )
49998
+ },
49999
+ view.name
50000
+ );
50001
+ })
50002
+ ]
50003
+ }
50004
+ )
50005
+ }
50006
+ ),
50007
+ /* @__PURE__ */ jsxs(
50008
+ Box,
50009
+ {
50010
+ position: "absolute",
50011
+ style: {
50012
+ top: 12,
50013
+ right: 12,
50014
+ display: "flex",
50015
+ flexDirection: "column",
50016
+ gap: 4,
50017
+ zIndex: 30
50018
+ },
50019
+ children: [
50020
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: zoomIn, title: "Zoom in", action: "COSMIC_ZOOM_IN", children: /* @__PURE__ */ jsx(Icon, { name: "plus", size: "sm" }) }),
50021
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: zoomOut, title: "Zoom out", action: "COSMIC_ZOOM_OUT", children: /* @__PURE__ */ jsx(Icon, { name: "minus", size: "sm" }) }),
50022
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", onClick: resetZoom, title: "Reset", action: "COSMIC_ZOOM_RESET", children: /* @__PURE__ */ jsx(Icon, { name: "maximize", size: "sm" }) })
50023
+ ]
50024
+ }
50025
+ )
50026
+ ] }),
50027
+ state.level === "orbital" && orbitalLevelData && /* @__PURE__ */ jsxs(
50028
+ Box,
50029
+ {
50030
+ position: "absolute",
50031
+ style: {
50032
+ inset: 0,
50033
+ paddingTop: 56,
50034
+ paddingBottom: 24,
50035
+ paddingLeft: 24,
50036
+ paddingRight: 24,
50037
+ display: "flex",
50038
+ alignItems: "stretch",
50039
+ justifyContent: "center",
50040
+ gap: 24
50041
+ },
50042
+ children: [
50043
+ /* @__PURE__ */ jsx(Box, { style: { flex: 1, maxWidth: 720, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx(
50044
+ AvlOrbitalUnit,
50045
+ {
50046
+ entityName: orbitalLevelData.entity.name,
50047
+ fields: orbitalLevelData.entity.fields.length,
50048
+ persistence: orbitalLevelData.entity.persistence || "persistent",
50049
+ traits: orbitalLevelData.traits.map((t) => ({ name: t.name })),
50050
+ pages: orbitalLevelData.pages.map((p2) => ({ name: p2.name })),
50051
+ color,
50052
+ animated
50053
+ }
50054
+ ) }),
50055
+ /* @__PURE__ */ jsxs(
50056
+ Box,
50057
+ {
50058
+ style: {
50059
+ width: 220,
50060
+ padding: 12,
50061
+ display: "flex",
50062
+ flexDirection: "column",
50063
+ gap: 8,
50064
+ borderLeft: `1px solid ${color}`,
50065
+ opacity: 0.95
50066
+ },
50067
+ children: [
50068
+ /* @__PURE__ */ jsxs(Typography, { variant: "small", weight: "semibold", style: { color, marginBottom: 4 }, children: [
50069
+ "Traits (",
50070
+ orbitalLevelData.traits.length,
50071
+ ")"
50072
+ ] }),
50073
+ orbitalLevelData.traits.length === 0 && /* @__PURE__ */ jsx(Text, { variant: "small", style: { opacity: 0.6, color }, children: "No traits" }),
50074
+ orbitalLevelData.traits.map((trait) => /* @__PURE__ */ jsx(
50075
+ Button,
50076
+ {
50077
+ variant: "ghost",
50078
+ size: "sm",
50079
+ onClick: () => handleTraitSelect(trait.name),
50080
+ action: "COSMIC_DRILL_TRAIT",
50081
+ children: trait.name
50082
+ },
50083
+ trait.name
50084
+ )),
50085
+ orbitalLevelData.pages.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
50086
+ /* @__PURE__ */ jsxs(Typography, { variant: "small", weight: "semibold", style: { color, marginTop: 12 }, children: [
50087
+ "Pages (",
50088
+ orbitalLevelData.pages.length,
50089
+ ")"
50090
+ ] }),
50091
+ orbitalLevelData.pages.map((page) => /* @__PURE__ */ jsx(Text, { variant: "small", style: { opacity: 0.7, color }, children: page.name }, page.name))
50092
+ ] })
50093
+ ]
50094
+ }
50095
+ )
50096
+ ]
50097
+ }
50098
+ ),
50099
+ state.level === "trait" && traitLevelData && /* @__PURE__ */ jsx(
50100
+ Box,
50101
+ {
50102
+ position: "absolute",
50103
+ style: {
50104
+ inset: 0,
50105
+ paddingTop: 56,
50106
+ paddingBottom: 24,
50107
+ paddingLeft: 24,
50108
+ paddingRight: 24
50109
+ },
50110
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 600 400", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
50111
+ AvlTraitScene,
49853
50112
  {
49854
- x: CONTENT_LEFT + 10,
49855
- y: rowY + 6,
49856
- effectType,
49857
- size: 10,
49858
- showBackground: true
50113
+ data: traitLevelData,
50114
+ color,
50115
+ onTransitionClick: (idx) => handleTransitionSelect(idx)
49859
50116
  }
49860
- ),
49861
- /* @__PURE__ */ jsx("text", { x: CONTENT_LEFT + 28, y: rowY + 10, fill: color, fontSize: 12, fontWeight: 500, opacity: 0.8, fontFamily: "inherit", children: eff.type }),
49862
- argsText && /* @__PURE__ */ jsx("text", { x: CONTENT_LEFT + 28 + eff.type.length * 7 + 8, y: rowY + 10, fill: color, fontSize: 10, fontWeight: 400, opacity: 0.5, fontFamily: "monospace", children: argsText })
49863
- ] }, `eff-${i}`);
49864
- })
49865
- ] });
49866
- })(),
49867
- hasSlots && (() => {
49868
- const secY = cardY + headerH + sectionGap + triggerH + guardH + effectsH;
49869
- return /* @__PURE__ */ jsxs("g", { children: [
49870
- /* @__PURE__ */ jsx("text", { x: SECTION_LEFT, y: secY + 12, fill: color, fontSize: 10, fontWeight: 600, opacity: 0.4, fontFamily: "inherit", children: "SLOTS" }),
49871
- data.slotTargets.map((slot, i) => {
49872
- const rowY = secY + 22 + i * 22;
49873
- return /* @__PURE__ */ jsxs("g", { children: [
49874
- /* @__PURE__ */ jsx("rect", { x: CONTENT_LEFT, y: rowY - 4, width: CARD_W - 56, height: 18, rx: 4, fill: color, fillOpacity: 0.04 }),
49875
- /* @__PURE__ */ jsxs("text", { x: CONTENT_LEFT + 8, y: rowY + 8, fill: color, fontSize: 11, fontFamily: "inherit", children: [
49876
- slot.name,
49877
- ": ",
49878
- slot.pattern
49879
- ] })
49880
- ] }, `slot-${i}`);
49881
- })
49882
- ] });
49883
- })()
49884
- ] });
50117
+ ) })
50118
+ }
50119
+ ),
50120
+ state.level === "transition" && transitionLevelData && /* @__PURE__ */ jsx(
50121
+ Box,
50122
+ {
50123
+ position: "absolute",
50124
+ style: {
50125
+ inset: 0,
50126
+ paddingTop: 56,
50127
+ paddingBottom: 24,
50128
+ paddingLeft: 24,
50129
+ paddingRight: 24
50130
+ },
50131
+ children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 600 400", style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
50132
+ AvlTransitionScene,
50133
+ {
50134
+ data: transitionLevelData,
50135
+ color
50136
+ }
50137
+ ) })
50138
+ }
50139
+ )
50140
+ ]
50141
+ }
50142
+ );
49885
50143
  };
49886
- AvlTransitionScene.displayName = "AvlTransitionScene";
50144
+ AvlOrbitalsCosmicZoom.displayName = "AvlOrbitalsCosmicZoom";
49887
50145
  var AvlClickTarget = ({
49888
50146
  x,
49889
50147
  y,