@almadar/ui 2.50.1 → 2.52.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
@@ -19342,13 +19342,13 @@ var MapViewImpl = lazy(async () => {
19342
19342
  shadowSize: [41, 41]
19343
19343
  });
19344
19344
  L.Marker.prototype.options.icon = defaultIcon;
19345
- const { useEffect: useEffect87, useRef: useRef87, useCallback: useCallback124, useState: useState124 } = React125__default;
19345
+ const { useEffect: useEffect88, useRef: useRef88, useCallback: useCallback124, useState: useState124 } = React125__default;
19346
19346
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
19347
19347
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
19348
19348
  function MapUpdater({ centerLat, centerLng, zoom }) {
19349
19349
  const map = useMap();
19350
- const prevRef = useRef87({ centerLat, centerLng, zoom });
19351
- useEffect87(() => {
19350
+ const prevRef = useRef88({ centerLat, centerLng, zoom });
19351
+ useEffect88(() => {
19352
19352
  const prev = prevRef.current;
19353
19353
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
19354
19354
  map.setView([centerLat, centerLng], zoom);
@@ -19359,7 +19359,7 @@ var MapViewImpl = lazy(async () => {
19359
19359
  }
19360
19360
  function MapClickHandler({ onMapClick }) {
19361
19361
  const map = useMap();
19362
- useEffect87(() => {
19362
+ useEffect88(() => {
19363
19363
  if (!onMapClick) return;
19364
19364
  const handler = (e) => {
19365
19365
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -22072,7 +22072,9 @@ var CodeBlock = React125__default.memo(
22072
22072
  showLanguageBadge = true,
22073
22073
  maxHeight = "60vh",
22074
22074
  foldable: foldableProp,
22075
- className
22075
+ className,
22076
+ editable = false,
22077
+ onChange
22076
22078
  }) => {
22077
22079
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
22078
22080
  const isOrb = language === "orb";
@@ -22243,7 +22245,37 @@ var CodeBlock = React125__default.memo(
22243
22245
  ]
22244
22246
  }
22245
22247
  ),
22246
- /* @__PURE__ */ jsx(
22248
+ 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,
22257
+ {
22258
+ defaultValue: code,
22259
+ onChange: (e) => onChange?.(e.target.value),
22260
+ spellCheck: false,
22261
+ style: {
22262
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
22263
+ fontSize: "13px",
22264
+ lineHeight: "1.5",
22265
+ backgroundColor: "#1e1e1e",
22266
+ color: "#e6e6e6",
22267
+ borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem",
22268
+ border: "none",
22269
+ padding: "1rem",
22270
+ resize: "none",
22271
+ minHeight: "160px",
22272
+ maxHeight,
22273
+ width: "100%",
22274
+ outline: "none"
22275
+ }
22276
+ }
22277
+ )
22278
+ ) : /* @__PURE__ */ jsx(
22247
22279
  "div",
22248
22280
  {
22249
22281
  ref: scrollRef,
@@ -22263,7 +22295,7 @@ var CodeBlock = React125__default.memo(
22263
22295
  )
22264
22296
  ] });
22265
22297
  },
22266
- (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable
22298
+ (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable && prev.editable === next.editable && prev.onChange === next.onChange
22267
22299
  );
22268
22300
  CodeBlock.displayName = "CodeBlock";
22269
22301
 
@@ -46435,6 +46467,9 @@ function OrbitalProvider({
46435
46467
  );
46436
46468
  }
46437
46469
  OrbitalProvider.displayName = "OrbitalProvider";
46470
+
46471
+ // runtime/OrbPreview.tsx
46472
+ init_useEventBus();
46438
46473
  function useResolvedSchema(schema, pageName) {
46439
46474
  const [loading, setLoading] = useState(true);
46440
46475
  const [error, setError] = useState(null);
@@ -47204,7 +47239,7 @@ function SlotBridge() {
47204
47239
  }, [slots, render, clear]);
47205
47240
  return null;
47206
47241
  }
47207
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
47242
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback }) {
47208
47243
  const slotsActions = useSlotsActions();
47209
47244
  const bridge = useServerBridge();
47210
47245
  const entityStore = useEntityStore();
@@ -47245,10 +47280,11 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
47245
47280
  const fallback = setTimeout(() => {
47246
47281
  if (!initSentRef.current) {
47247
47282
  sendEvent("INIT");
47283
+ onLocalFallback?.();
47248
47284
  }
47249
47285
  }, 5e3);
47250
47286
  return () => clearTimeout(fallback);
47251
- }, [traits2, orbitalNames, sendEvent]);
47287
+ }, [traits2, orbitalNames, sendEvent, onLocalFallback]);
47252
47288
  useEffect(() => {
47253
47289
  if (!bridge.connected || !orbitalNames?.length || initSentRef.current) return;
47254
47290
  initSentRef.current = true;
@@ -47294,7 +47330,7 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
47294
47330
  }, [bridge.connected, orbitalNames, bridge.sendEvent, slotsActions]);
47295
47331
  return null;
47296
47332
  }
47297
- function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate }) {
47333
+ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLocalFallback }) {
47298
47334
  const { traits: traits2, allEntities, ir } = useResolvedSchema(schema, pageName);
47299
47335
  const allPageTraits = useMemo(() => {
47300
47336
  if (pageName && traits2.length > 0) return traits2;
@@ -47334,7 +47370,15 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate }) {
47334
47370
  }
47335
47371
  }, [mockKey, serverUrl, mockData, entityStore]);
47336
47372
  const inner = /* @__PURE__ */ jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsx(SlotsProvider, { children: /* @__PURE__ */ jsxs(EntitySchemaProvider, { entities: Array.from(allEntities.values()), children: [
47337
- /* @__PURE__ */ jsx(TraitInitializer, { traits: allPageTraits, orbitalNames: serverUrl ? orbitalNames : void 0, onNavigate }),
47373
+ /* @__PURE__ */ jsx(
47374
+ TraitInitializer,
47375
+ {
47376
+ traits: allPageTraits,
47377
+ orbitalNames: serverUrl ? orbitalNames : void 0,
47378
+ onNavigate,
47379
+ onLocalFallback
47380
+ }
47381
+ ),
47338
47382
  /* @__PURE__ */ jsx(SlotBridge, {}),
47339
47383
  /* @__PURE__ */ jsx(Box, { className: "min-h-full p-4", children: /* @__PURE__ */ jsx(UISlotRenderer, { includeHud: true, hudMode: "inline", includeFloating: true }) })
47340
47384
  ] }) }) });
@@ -47351,6 +47395,16 @@ function OrbPreview({
47351
47395
  className,
47352
47396
  serverUrl
47353
47397
  }) {
47398
+ const [localFallback, setLocalFallback] = useState(false);
47399
+ const eventBus = useEventBus();
47400
+ const handleLocalFallback = useCallback(() => {
47401
+ if (localFallback) return;
47402
+ setLocalFallback(true);
47403
+ eventBus.emit("UI:NOTIFY", {
47404
+ message: "Preview server unreachable \u2014 running locally without server-side state.",
47405
+ severity: "warning"
47406
+ });
47407
+ }, [localFallback, eventBus]);
47354
47408
  const parseResult = useMemo(() => {
47355
47409
  let parsed;
47356
47410
  if (typeof schema === "string") {
@@ -47408,13 +47462,26 @@ function OrbPreview({
47408
47462
  el.addEventListener("click", handler, true);
47409
47463
  return () => el.removeEventListener("click", handler, true);
47410
47464
  }, [pages, handleNavigate]);
47411
- return /* @__PURE__ */ jsx(
47465
+ return /* @__PURE__ */ jsxs(
47412
47466
  Box,
47413
47467
  {
47414
47468
  ref: containerRef,
47415
47469
  className: `overflow-auto border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
47416
47470
  style: { height },
47417
- children: /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(SchemaRunner, { schema: parsedSchema, serverUrl, mockData: effectiveMockData, pageName: currentPage, onNavigate: handleNavigate }) }) })
47471
+ children: [
47472
+ localFallback && /* @__PURE__ */ jsx(Box, { className: "px-3 py-2 bg-[var(--color-warning)] bg-opacity-10 border-b border-[var(--color-warning)] flex items-center gap-2", children: /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-[var(--color-warning-foreground)] flex-1", children: "Preview server unreachable \u2014 running locally. Server-side state and persistence are disabled." }) }),
47473
+ /* @__PURE__ */ jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, children: /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsx(
47474
+ SchemaRunner,
47475
+ {
47476
+ schema: parsedSchema,
47477
+ serverUrl,
47478
+ mockData: effectiveMockData,
47479
+ pageName: currentPage,
47480
+ onNavigate: handleNavigate,
47481
+ onLocalFallback: handleLocalFallback
47482
+ }
47483
+ ) }) })
47484
+ ]
47418
47485
  }
47419
47486
  );
47420
47487
  }
@@ -47662,12 +47729,18 @@ var OrbPreviewNodeInner = (props) => {
47662
47729
  el = el.parentElement;
47663
47730
  if (!el || el === contentRef.current) break;
47664
47731
  }
47732
+ const containerNode = {
47733
+ orbitalName: data.orbitalName,
47734
+ traitName: data.traitName,
47735
+ transitionEvent: data.transitionEvent
47736
+ };
47665
47737
  const containerPath = el?.dataset?.patternPath;
47666
47738
  if (!containerPath) {
47667
- eventBus.emit("UI:PATTERN_INSERT", {
47739
+ eventBus.emit("UI:PATTERN_DROP", {
47668
47740
  parentPath: "root",
47669
47741
  patternType: payload.data.type,
47670
- index: 0
47742
+ index: 0,
47743
+ containerNode
47671
47744
  });
47672
47745
  return;
47673
47746
  }
@@ -47684,12 +47757,13 @@ var OrbPreviewNodeInner = (props) => {
47684
47757
  break;
47685
47758
  }
47686
47759
  }
47687
- eventBus.emit("UI:PATTERN_INSERT", {
47760
+ eventBus.emit("UI:PATTERN_DROP", {
47688
47761
  parentPath: containerPath,
47689
47762
  patternType: payload.data.type,
47690
- index: insertIndex
47763
+ index: insertIndex,
47764
+ containerNode
47691
47765
  });
47692
- }, [eventBus]);
47766
+ }, [eventBus, data.orbitalName, data.traitName, data.transitionEvent]);
47693
47767
  const handlePreviewDragOver = useCallback((e) => {
47694
47768
  if (!e.dataTransfer.types.includes(ALMADAR_DND_MIME)) return;
47695
47769
  e.preventDefault();
@@ -48292,6 +48366,11 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
48292
48366
  ] }),
48293
48367
  /* @__PURE__ */ jsx(Box, { className: "flex-1 overflow-y-auto", children: activeTab === "code" ? (
48294
48368
  /* ── Code Tab ── */
48369
+ /* GAP-51: when editable, the CodeBlock molecule renders the existing
48370
+ Textarea atom internally and forwards keystrokes via UI:CODE_CHANGE
48371
+ on the EventBus. The consumer (builder workspace) listens, debounces,
48372
+ parses via safeParseOrbitalSchema, and calls setSchema. Read-only
48373
+ consumers (editable=false) see the existing syntax-highlighted view. */
48295
48374
  /* @__PURE__ */ jsx(Box, { className: "p-2", children: /* @__PURE__ */ jsx(
48296
48375
  CodeBlock,
48297
48376
  {
@@ -48299,7 +48378,9 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
48299
48378
  language: "orb",
48300
48379
  showCopyButton: true,
48301
48380
  showLanguageBadge: true,
48302
- maxHeight: "100%"
48381
+ maxHeight: "100%",
48382
+ editable,
48383
+ onChange: editable ? (code) => eventBus.emit("UI:CODE_CHANGE", { code }) : void 0
48303
48384
  }
48304
48385
  ) })
48305
48386
  ) : (
@@ -48592,6 +48673,8 @@ function FlowCanvasInner({
48592
48673
  height = 500,
48593
48674
  onNodeClick,
48594
48675
  onLevelChange,
48676
+ onOrbitalDoubleClick,
48677
+ cosmicEntryLevel = "expanded",
48595
48678
  initialOrbital,
48596
48679
  initialLevel,
48597
48680
  initialSelectedNode,
@@ -48665,11 +48748,24 @@ function FlowCanvasInner({
48665
48748
  }
48666
48749
  if (level === "overview") {
48667
48750
  const d = node.data;
48668
- setExpandedOrbital(d.orbitalName ?? node.id);
48751
+ const orbitalName = d.orbitalName ?? node.id;
48752
+ if (onOrbitalDoubleClick && (cosmicEntryLevel === "overview" || cosmicEntryLevel === "both")) {
48753
+ onOrbitalDoubleClick(orbitalName);
48754
+ return;
48755
+ }
48756
+ setExpandedOrbital(orbitalName);
48669
48757
  setLevel("expanded");
48670
- onLevelChange?.("expanded", d.orbitalName ?? node.id);
48758
+ onLevelChange?.("expanded", orbitalName);
48759
+ return;
48671
48760
  }
48672
- }, [level, onLevelChange, atBehaviorLevel, composeLevel]);
48761
+ if (level === "expanded") {
48762
+ const d = node.data;
48763
+ const orbitalName = d.orbitalName ?? node.id;
48764
+ if (orbitalName && onOrbitalDoubleClick && (cosmicEntryLevel === "expanded" || cosmicEntryLevel === "both")) {
48765
+ onOrbitalDoubleClick(orbitalName);
48766
+ }
48767
+ }
48768
+ }, [level, onLevelChange, onOrbitalDoubleClick, cosmicEntryLevel, atBehaviorLevel, composeLevel]);
48673
48769
  const handleNodeClick = useCallback((_, node) => {
48674
48770
  const nodeData = node.data;
48675
48771
  if (level === "expanded") {
@@ -48764,10 +48860,10 @@ function FlowCanvasInner({
48764
48860
  return /* @__PURE__ */ jsx(ScreenSizeContext.Provider, { value: screenSize, children: /* @__PURE__ */ jsx(PatternSelectionContext.Provider, { value: patternSelectionValue, children: /* @__PURE__ */ jsxs(
48765
48861
  Box,
48766
48862
  {
48767
- className: `flex ${className ?? ""}`,
48863
+ className: `flex h-full ${className ?? ""}`,
48768
48864
  style: { width, height },
48769
48865
  children: [
48770
- /* @__PURE__ */ jsxs(Box, { className: "relative flex-1 min-w-0", children: [
48866
+ /* @__PURE__ */ jsxs(Box, { className: "relative flex-1 min-w-0 h-full", children: [
48771
48867
  /* @__PURE__ */ jsxs(
48772
48868
  ReactFlow,
48773
48869
  {
@@ -48784,6 +48880,7 @@ function FlowCanvasInner({
48784
48880
  minZoom: 0.1,
48785
48881
  maxZoom: 2,
48786
48882
  fitView: true,
48883
+ fitViewOptions: { padding: 0.15 },
48787
48884
  nodesDraggable: true,
48788
48885
  elementsSelectable: true,
48789
48886
  proOptions: { hideAttribution: true },
@@ -49203,7 +49300,11 @@ var AvlOrbitalsCosmicZoom = ({
49203
49300
  color = "var(--color-primary, #4A90D9)",
49204
49301
  animated = true,
49205
49302
  width = "100%",
49206
- height = 450
49303
+ height = 450,
49304
+ highlightedOrbital,
49305
+ onOrbitalSelect,
49306
+ minZoom = 0.4,
49307
+ maxZoom = 3
49207
49308
  }) => {
49208
49309
  const parsedSchema = useMemo(() => {
49209
49310
  if (typeof schemaProp === "string") return JSON.parse(schemaProp);
@@ -49234,10 +49335,83 @@ var AvlOrbitalsCosmicZoom = ({
49234
49335
  );
49235
49336
  const [selected, setSelected] = useState(null);
49236
49337
  const handleSelect = useCallback(
49237
- (name) => setSelected((prev) => prev === name ? null : name),
49238
- []
49338
+ (name) => {
49339
+ setSelected((prev) => prev === name ? null : name);
49340
+ onOrbitalSelect?.(name);
49341
+ },
49342
+ [onOrbitalSelect]
49239
49343
  );
49240
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
+ }, []);
49241
49415
  return /* @__PURE__ */ jsxs(
49242
49416
  Box,
49243
49417
  {
@@ -49247,59 +49421,115 @@ var AvlOrbitalsCosmicZoom = ({
49247
49421
  style: { width, height: containerH },
49248
49422
  children: [
49249
49423
  /* @__PURE__ */ jsx(
49250
- EventWireOverlay,
49424
+ "div",
49251
49425
  {
49252
- orbitalViews,
49253
- crossLinks,
49254
- color,
49255
- animated,
49256
- containerW,
49257
- containerH
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
+ )
49258
49514
  }
49259
49515
  ),
49260
- orbitalViews.map((view) => /* @__PURE__ */ jsx(
49516
+ /* @__PURE__ */ jsxs(
49261
49517
  Box,
49262
49518
  {
49263
- role: "button",
49264
- tabIndex: 0,
49265
- onClick: () => handleSelect(view.name),
49266
- onKeyDown: (e) => {
49267
- if (e.key === "Enter" || e.key === " ") handleSelect(view.name);
49268
- },
49269
- "aria-label": `Orbital: ${view.name}`,
49270
49519
  position: "absolute",
49271
49520
  style: {
49272
- left: view.cx - UNIT_DISPLAY_W / 2,
49273
- top: view.cy - UNIT_DISPLAY_H / 2,
49274
- width: UNIT_DISPLAY_W,
49275
- height: UNIT_DISPLAY_H,
49276
- cursor: "pointer",
49277
- transition: "transform 0.2s ease, filter 0.2s ease",
49278
- transform: selected === view.name ? "scale(1.05)" : "scale(1)",
49279
- filter: selected && selected !== view.name ? "opacity(0.5)" : "none",
49280
- zIndex: selected === view.name ? 10 : 1
49521
+ top: 12,
49522
+ right: 12,
49523
+ display: "flex",
49524
+ flexDirection: "column",
49525
+ gap: 4,
49526
+ zIndex: 30
49281
49527
  },
49282
- children: /* @__PURE__ */ jsx(
49283
- AvlOrbitalUnit,
49284
- {
49285
- entityName: view.entityName,
49286
- fields: view.fieldCount,
49287
- persistence: view.persistence,
49288
- traits: view.traits,
49289
- pages: view.pages,
49290
- color,
49291
- animated: animated && selected === view.name
49292
- }
49293
- )
49294
- },
49295
- view.name
49296
- )),
49297
- selectedView && /* @__PURE__ */ jsx(
49298
- InfoPanel,
49299
- {
49300
- view: selectedView,
49301
- crossLinks,
49302
- color
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
+ ]
49303
49533
  }
49304
49534
  )
49305
49535
  ]
@@ -9209,7 +9209,9 @@ var CodeBlock = React90__namespace.default.memo(
9209
9209
  showLanguageBadge = true,
9210
9210
  maxHeight = "60vh",
9211
9211
  foldable: foldableProp,
9212
- className
9212
+ className,
9213
+ editable = false,
9214
+ onChange
9213
9215
  }) => {
9214
9216
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
9215
9217
  const isOrb = language === "orb";
@@ -9380,7 +9382,37 @@ var CodeBlock = React90__namespace.default.memo(
9380
9382
  ]
9381
9383
  }
9382
9384
  ),
9383
- /* @__PURE__ */ jsxRuntime.jsx(
9385
+ editable ? (
9386
+ /* GAP-51: editable mode — composes the Textarea atom. Plain text editing,
9387
+ no Prism highlighting overlay (follow-up). The textarea is uncontrolled
9388
+ on the value side: we pass `code` as the initial value and forward
9389
+ every keystroke via onChange — the consumer is responsible for
9390
+ debouncing and re-deriving `code` only after the user stops typing
9391
+ so the cursor doesn't fight a re-render. */
9392
+ /* @__PURE__ */ jsxRuntime.jsx(
9393
+ Textarea,
9394
+ {
9395
+ defaultValue: code,
9396
+ onChange: (e) => onChange?.(e.target.value),
9397
+ spellCheck: false,
9398
+ style: {
9399
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
9400
+ fontSize: "13px",
9401
+ lineHeight: "1.5",
9402
+ backgroundColor: "#1e1e1e",
9403
+ color: "#e6e6e6",
9404
+ borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem",
9405
+ border: "none",
9406
+ padding: "1rem",
9407
+ resize: "none",
9408
+ minHeight: "160px",
9409
+ maxHeight,
9410
+ width: "100%",
9411
+ outline: "none"
9412
+ }
9413
+ }
9414
+ )
9415
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
9384
9416
  "div",
9385
9417
  {
9386
9418
  ref: scrollRef,
@@ -9400,7 +9432,7 @@ var CodeBlock = React90__namespace.default.memo(
9400
9432
  )
9401
9433
  ] });
9402
9434
  },
9403
- (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable
9435
+ (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable && prev.editable === next.editable && prev.onChange === next.onChange
9404
9436
  );
9405
9437
  CodeBlock.displayName = "CodeBlock";
9406
9438
  init_Typography();
@@ -9164,7 +9164,9 @@ var CodeBlock = React90__default.memo(
9164
9164
  showLanguageBadge = true,
9165
9165
  maxHeight = "60vh",
9166
9166
  foldable: foldableProp,
9167
- className
9167
+ className,
9168
+ editable = false,
9169
+ onChange
9168
9170
  }) => {
9169
9171
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
9170
9172
  const isOrb = language === "orb";
@@ -9335,7 +9337,37 @@ var CodeBlock = React90__default.memo(
9335
9337
  ]
9336
9338
  }
9337
9339
  ),
9338
- /* @__PURE__ */ jsx(
9340
+ editable ? (
9341
+ /* GAP-51: editable mode — composes the Textarea atom. Plain text editing,
9342
+ no Prism highlighting overlay (follow-up). The textarea is uncontrolled
9343
+ on the value side: we pass `code` as the initial value and forward
9344
+ every keystroke via onChange — the consumer is responsible for
9345
+ debouncing and re-deriving `code` only after the user stops typing
9346
+ so the cursor doesn't fight a re-render. */
9347
+ /* @__PURE__ */ jsx(
9348
+ Textarea,
9349
+ {
9350
+ defaultValue: code,
9351
+ onChange: (e) => onChange?.(e.target.value),
9352
+ spellCheck: false,
9353
+ style: {
9354
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
9355
+ fontSize: "13px",
9356
+ lineHeight: "1.5",
9357
+ backgroundColor: "#1e1e1e",
9358
+ color: "#e6e6e6",
9359
+ borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem",
9360
+ border: "none",
9361
+ padding: "1rem",
9362
+ resize: "none",
9363
+ minHeight: "160px",
9364
+ maxHeight,
9365
+ width: "100%",
9366
+ outline: "none"
9367
+ }
9368
+ }
9369
+ )
9370
+ ) : /* @__PURE__ */ jsx(
9339
9371
  "div",
9340
9372
  {
9341
9373
  ref: scrollRef,
@@ -9355,7 +9387,7 @@ var CodeBlock = React90__default.memo(
9355
9387
  )
9356
9388
  ] });
9357
9389
  },
9358
- (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable
9390
+ (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable && prev.editable === next.editable && prev.onChange === next.onChange
9359
9391
  );
9360
9392
  CodeBlock.displayName = "CodeBlock";
9361
9393
  init_Typography();