@almadar/ui 2.50.1 → 2.51.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.
@@ -22118,7 +22118,9 @@ var CodeBlock = React125__namespace.default.memo(
22118
22118
  showLanguageBadge = true,
22119
22119
  maxHeight = "60vh",
22120
22120
  foldable: foldableProp,
22121
- className
22121
+ className,
22122
+ editable = false,
22123
+ onChange
22122
22124
  }) => {
22123
22125
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
22124
22126
  const isOrb = language === "orb";
@@ -22289,7 +22291,37 @@ var CodeBlock = React125__namespace.default.memo(
22289
22291
  ]
22290
22292
  }
22291
22293
  ),
22292
- /* @__PURE__ */ jsxRuntime.jsx(
22294
+ editable ? (
22295
+ /* GAP-51: editable mode — composes the Textarea atom. Plain text editing,
22296
+ no Prism highlighting overlay (follow-up). The textarea is uncontrolled
22297
+ on the value side: we pass `code` as the initial value and forward
22298
+ every keystroke via onChange — the consumer is responsible for
22299
+ debouncing and re-deriving `code` only after the user stops typing
22300
+ so the cursor doesn't fight a re-render. */
22301
+ /* @__PURE__ */ jsxRuntime.jsx(
22302
+ Textarea,
22303
+ {
22304
+ defaultValue: code,
22305
+ onChange: (e) => onChange?.(e.target.value),
22306
+ spellCheck: false,
22307
+ style: {
22308
+ fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Mono", "Courier New", monospace',
22309
+ fontSize: "13px",
22310
+ lineHeight: "1.5",
22311
+ backgroundColor: "#1e1e1e",
22312
+ color: "#e6e6e6",
22313
+ borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem",
22314
+ border: "none",
22315
+ padding: "1rem",
22316
+ resize: "none",
22317
+ minHeight: "160px",
22318
+ maxHeight,
22319
+ width: "100%",
22320
+ outline: "none"
22321
+ }
22322
+ }
22323
+ )
22324
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
22293
22325
  "div",
22294
22326
  {
22295
22327
  ref: scrollRef,
@@ -22309,7 +22341,7 @@ var CodeBlock = React125__namespace.default.memo(
22309
22341
  )
22310
22342
  ] });
22311
22343
  },
22312
- (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable
22344
+ (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
22313
22345
  );
22314
22346
  CodeBlock.displayName = "CodeBlock";
22315
22347
 
@@ -46481,6 +46513,9 @@ function OrbitalProvider({
46481
46513
  );
46482
46514
  }
46483
46515
  OrbitalProvider.displayName = "OrbitalProvider";
46516
+
46517
+ // runtime/OrbPreview.tsx
46518
+ init_useEventBus();
46484
46519
  function useResolvedSchema(schema, pageName) {
46485
46520
  const [loading, setLoading] = React125.useState(true);
46486
46521
  const [error, setError] = React125.useState(null);
@@ -47250,7 +47285,7 @@ function SlotBridge() {
47250
47285
  }, [slots, render, clear]);
47251
47286
  return null;
47252
47287
  }
47253
- function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
47288
+ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback }) {
47254
47289
  const slotsActions = useSlotsActions();
47255
47290
  const bridge = useServerBridge();
47256
47291
  const entityStore = useEntityStore();
@@ -47291,10 +47326,11 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
47291
47326
  const fallback = setTimeout(() => {
47292
47327
  if (!initSentRef.current) {
47293
47328
  sendEvent("INIT");
47329
+ onLocalFallback?.();
47294
47330
  }
47295
47331
  }, 5e3);
47296
47332
  return () => clearTimeout(fallback);
47297
- }, [traits2, orbitalNames, sendEvent]);
47333
+ }, [traits2, orbitalNames, sendEvent, onLocalFallback]);
47298
47334
  React125.useEffect(() => {
47299
47335
  if (!bridge.connected || !orbitalNames?.length || initSentRef.current) return;
47300
47336
  initSentRef.current = true;
@@ -47340,7 +47376,7 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
47340
47376
  }, [bridge.connected, orbitalNames, bridge.sendEvent, slotsActions]);
47341
47377
  return null;
47342
47378
  }
47343
- function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate }) {
47379
+ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate, onLocalFallback }) {
47344
47380
  const { traits: traits2, allEntities, ir } = useResolvedSchema(schema, pageName);
47345
47381
  const allPageTraits = React125.useMemo(() => {
47346
47382
  if (pageName && traits2.length > 0) return traits2;
@@ -47380,7 +47416,15 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate }) {
47380
47416
  }
47381
47417
  }, [mockKey, serverUrl, mockData, entityStore]);
47382
47418
  const inner = /* @__PURE__ */ jsxRuntime.jsx(VerificationProvider, { enabled: true, children: /* @__PURE__ */ jsxRuntime.jsx(SlotsProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(EntitySchemaProvider, { entities: Array.from(allEntities.values()), children: [
47383
- /* @__PURE__ */ jsxRuntime.jsx(TraitInitializer, { traits: allPageTraits, orbitalNames: serverUrl ? orbitalNames : void 0, onNavigate }),
47419
+ /* @__PURE__ */ jsxRuntime.jsx(
47420
+ TraitInitializer,
47421
+ {
47422
+ traits: allPageTraits,
47423
+ orbitalNames: serverUrl ? orbitalNames : void 0,
47424
+ onNavigate,
47425
+ onLocalFallback
47426
+ }
47427
+ ),
47384
47428
  /* @__PURE__ */ jsxRuntime.jsx(SlotBridge, {}),
47385
47429
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "min-h-full p-4", children: /* @__PURE__ */ jsxRuntime.jsx(UISlotRenderer, { includeHud: true, hudMode: "inline", includeFloating: true }) })
47386
47430
  ] }) }) });
@@ -47397,6 +47441,16 @@ function OrbPreview({
47397
47441
  className,
47398
47442
  serverUrl
47399
47443
  }) {
47444
+ const [localFallback, setLocalFallback] = React125.useState(false);
47445
+ const eventBus = useEventBus();
47446
+ const handleLocalFallback = React125.useCallback(() => {
47447
+ if (localFallback) return;
47448
+ setLocalFallback(true);
47449
+ eventBus.emit("UI:NOTIFY", {
47450
+ message: "Preview server unreachable \u2014 running locally without server-side state.",
47451
+ severity: "warning"
47452
+ });
47453
+ }, [localFallback, eventBus]);
47400
47454
  const parseResult = React125.useMemo(() => {
47401
47455
  let parsed;
47402
47456
  if (typeof schema === "string") {
@@ -47454,13 +47508,26 @@ function OrbPreview({
47454
47508
  el.addEventListener("click", handler, true);
47455
47509
  return () => el.removeEventListener("click", handler, true);
47456
47510
  }, [pages, handleNavigate]);
47457
- return /* @__PURE__ */ jsxRuntime.jsx(
47511
+ return /* @__PURE__ */ jsxRuntime.jsxs(
47458
47512
  Box,
47459
47513
  {
47460
47514
  ref: containerRef,
47461
47515
  className: `overflow-auto border border-[var(--color-border)] rounded-[var(--radius-md)] ${className ?? ""}`,
47462
47516
  style: { height },
47463
- children: /* @__PURE__ */ jsxRuntime.jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, children: /* @__PURE__ */ jsxRuntime.jsx(UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(SchemaRunner, { schema: parsedSchema, serverUrl, mockData: effectiveMockData, pageName: currentPage, onNavigate: handleNavigate }) }) })
47517
+ children: [
47518
+ localFallback && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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." }) }),
47519
+ /* @__PURE__ */ jsxRuntime.jsx(OrbitalProvider, { initialData: effectiveMockData, skipTheme: true, verification: true, children: /* @__PURE__ */ jsxRuntime.jsx(UISlotProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
47520
+ SchemaRunner,
47521
+ {
47522
+ schema: parsedSchema,
47523
+ serverUrl,
47524
+ mockData: effectiveMockData,
47525
+ pageName: currentPage,
47526
+ onNavigate: handleNavigate,
47527
+ onLocalFallback: handleLocalFallback
47528
+ }
47529
+ ) }) })
47530
+ ]
47464
47531
  }
47465
47532
  );
47466
47533
  }
@@ -48338,6 +48405,11 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
48338
48405
  ] }),
48339
48406
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 overflow-y-auto", children: activeTab === "code" ? (
48340
48407
  /* ── Code Tab ── */
48408
+ /* GAP-51: when editable, the CodeBlock molecule renders the existing
48409
+ Textarea atom internally and forwards keystrokes via UI:CODE_CHANGE
48410
+ on the EventBus. The consumer (builder workspace) listens, debounces,
48411
+ parses via safeParseOrbitalSchema, and calls setSchema. Read-only
48412
+ consumers (editable=false) see the existing syntax-highlighted view. */
48341
48413
  /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
48342
48414
  CodeBlock,
48343
48415
  {
@@ -48345,7 +48417,9 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
48345
48417
  language: "orb",
48346
48418
  showCopyButton: true,
48347
48419
  showLanguageBadge: true,
48348
- maxHeight: "100%"
48420
+ maxHeight: "100%",
48421
+ editable,
48422
+ onChange: editable ? (code) => eventBus.emit("UI:CODE_CHANGE", { code }) : void 0
48349
48423
  }
48350
48424
  ) })
48351
48425
  ) : (
@@ -48638,6 +48712,7 @@ function FlowCanvasInner({
48638
48712
  height = 500,
48639
48713
  onNodeClick,
48640
48714
  onLevelChange,
48715
+ onOrbitalDoubleClick,
48641
48716
  initialOrbital,
48642
48717
  initialLevel,
48643
48718
  initialSelectedNode,
@@ -48714,8 +48789,16 @@ function FlowCanvasInner({
48714
48789
  setExpandedOrbital(d.orbitalName ?? node.id);
48715
48790
  setLevel("expanded");
48716
48791
  onLevelChange?.("expanded", d.orbitalName ?? node.id);
48792
+ return;
48793
+ }
48794
+ if (level === "expanded") {
48795
+ const d = node.data;
48796
+ const orbitalName = d.orbitalName ?? node.id;
48797
+ if (orbitalName && onOrbitalDoubleClick) {
48798
+ onOrbitalDoubleClick(orbitalName);
48799
+ }
48717
48800
  }
48718
- }, [level, onLevelChange, atBehaviorLevel, composeLevel]);
48801
+ }, [level, onLevelChange, onOrbitalDoubleClick, atBehaviorLevel, composeLevel]);
48719
48802
  const handleNodeClick = React125.useCallback((_, node) => {
48720
48803
  const nodeData = node.data;
48721
48804
  if (level === "expanded") {
@@ -48810,10 +48893,10 @@ function FlowCanvasInner({
48810
48893
  return /* @__PURE__ */ jsxRuntime.jsx(ScreenSizeContext.Provider, { value: screenSize, children: /* @__PURE__ */ jsxRuntime.jsx(PatternSelectionContext.Provider, { value: patternSelectionValue, children: /* @__PURE__ */ jsxRuntime.jsxs(
48811
48894
  Box,
48812
48895
  {
48813
- className: `flex ${className ?? ""}`,
48896
+ className: `flex h-full ${className ?? ""}`,
48814
48897
  style: { width, height },
48815
48898
  children: [
48816
- /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative flex-1 min-w-0", children: [
48899
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative flex-1 min-w-0 h-full", children: [
48817
48900
  /* @__PURE__ */ jsxRuntime.jsxs(
48818
48901
  react.ReactFlow,
48819
48902
  {
@@ -48830,6 +48913,7 @@ function FlowCanvasInner({
48830
48913
  minZoom: 0.1,
48831
48914
  maxZoom: 2,
48832
48915
  fitView: true,
48916
+ fitViewOptions: { padding: 0.15 },
48833
48917
  nodesDraggable: true,
48834
48918
  elementsSelectable: true,
48835
48919
  proOptions: { hideAttribution: true },
@@ -49249,7 +49333,8 @@ var AvlOrbitalsCosmicZoom = ({
49249
49333
  color = "var(--color-primary, #4A90D9)",
49250
49334
  animated = true,
49251
49335
  width = "100%",
49252
- height = 450
49336
+ height = 450,
49337
+ highlightedOrbital
49253
49338
  }) => {
49254
49339
  const parsedSchema = React125.useMemo(() => {
49255
49340
  if (typeof schemaProp === "string") return JSON.parse(schemaProp);
@@ -49303,43 +49388,49 @@ var AvlOrbitalsCosmicZoom = ({
49303
49388
  containerH
49304
49389
  }
49305
49390
  ),
49306
- orbitalViews.map((view) => /* @__PURE__ */ jsxRuntime.jsx(
49307
- Box,
49308
- {
49309
- role: "button",
49310
- tabIndex: 0,
49311
- onClick: () => handleSelect(view.name),
49312
- onKeyDown: (e) => {
49313
- if (e.key === "Enter" || e.key === " ") handleSelect(view.name);
49314
- },
49315
- "aria-label": `Orbital: ${view.name}`,
49316
- position: "absolute",
49317
- style: {
49318
- left: view.cx - UNIT_DISPLAY_W / 2,
49319
- top: view.cy - UNIT_DISPLAY_H / 2,
49320
- width: UNIT_DISPLAY_W,
49321
- height: UNIT_DISPLAY_H,
49322
- cursor: "pointer",
49323
- transition: "transform 0.2s ease, filter 0.2s ease",
49324
- transform: selected === view.name ? "scale(1.05)" : "scale(1)",
49325
- filter: selected && selected !== view.name ? "opacity(0.5)" : "none",
49326
- zIndex: selected === view.name ? 10 : 1
49391
+ orbitalViews.map((view) => {
49392
+ const isHighlighted = view.name === highlightedOrbital;
49393
+ return /* @__PURE__ */ jsxRuntime.jsx(
49394
+ Box,
49395
+ {
49396
+ role: "button",
49397
+ tabIndex: 0,
49398
+ onClick: () => handleSelect(view.name),
49399
+ onKeyDown: (e) => {
49400
+ if (e.key === "Enter" || e.key === " ") handleSelect(view.name);
49401
+ },
49402
+ "aria-label": `Orbital: ${view.name}${isHighlighted ? " (highlighted)" : ""}`,
49403
+ position: "absolute",
49404
+ style: {
49405
+ left: view.cx - UNIT_DISPLAY_W / 2,
49406
+ top: view.cy - UNIT_DISPLAY_H / 2,
49407
+ width: UNIT_DISPLAY_W,
49408
+ height: UNIT_DISPLAY_H,
49409
+ cursor: "pointer",
49410
+ transition: "transform 0.2s ease, filter 0.2s ease, box-shadow 0.3s ease",
49411
+ transform: selected === view.name ? "scale(1.05)" : "scale(1)",
49412
+ filter: selected && selected !== view.name ? "opacity(0.5)" : "none",
49413
+ // GAP-52: persistent highlight ring (independent from user selection)
49414
+ boxShadow: isHighlighted ? `0 0 0 3px ${color}, 0 0 24px 4px ${color}` : "none",
49415
+ borderRadius: isHighlighted ? "12px" : void 0,
49416
+ zIndex: isHighlighted ? 11 : selected === view.name ? 10 : 1
49417
+ },
49418
+ children: /* @__PURE__ */ jsxRuntime.jsx(
49419
+ AvlOrbitalUnit,
49420
+ {
49421
+ entityName: view.entityName,
49422
+ fields: view.fieldCount,
49423
+ persistence: view.persistence,
49424
+ traits: view.traits,
49425
+ pages: view.pages,
49426
+ color,
49427
+ animated: animated && (selected === view.name || isHighlighted)
49428
+ }
49429
+ )
49327
49430
  },
49328
- children: /* @__PURE__ */ jsxRuntime.jsx(
49329
- AvlOrbitalUnit,
49330
- {
49331
- entityName: view.entityName,
49332
- fields: view.fieldCount,
49333
- persistence: view.persistence,
49334
- traits: view.traits,
49335
- pages: view.pages,
49336
- color,
49337
- animated: animated && selected === view.name
49338
- }
49339
- )
49340
- },
49341
- view.name
49342
- )),
49431
+ view.name
49432
+ );
49433
+ }),
49343
49434
  selectedView && /* @__PURE__ */ jsxRuntime.jsx(
49344
49435
  InfoPanel,
49345
49436
  {
@@ -1210,6 +1210,16 @@ interface FlowCanvasProps {
1210
1210
  transition?: string;
1211
1211
  }) => void;
1212
1212
  onLevelChange?: (level: ViewLevel, orbital?: string) => void;
1213
+ /**
1214
+ * GAP-52: fired when the user double-clicks an orbital while ALREADY at
1215
+ * `level === 'expanded'`. Consumers (e.g. the builder workspace) use this as
1216
+ * the trigger to enter cosmic mode (`AvlOrbitalsCosmicZoom`) for the focused
1217
+ * orbital. This does NOT replace the existing overview→expanded drill —
1218
+ * that path still fires `onLevelChange('expanded', ...)` as before.
1219
+ * The callback runs unconditionally; persona / permission gating is the
1220
+ * consumer's responsibility.
1221
+ */
1222
+ onOrbitalDoubleClick?: (orbital: string) => void;
1213
1223
  initialOrbital?: string;
1214
1224
  /** Start at Level 2 (expanded) when initialOrbital is set. Default: 'overview'. */
1215
1225
  initialLevel?: ViewLevel;
@@ -1368,6 +1378,13 @@ interface AvlOrbitalsCosmicZoomProps {
1368
1378
  width?: number | string;
1369
1379
  /** Container height */
1370
1380
  height?: number | string;
1381
+ /**
1382
+ * GAP-52: name of the orbital to highlight with a persistent ring/glow.
1383
+ * Independent from user-driven selection (click). Used by the builder workspace
1384
+ * when entering cosmic mode from a focused orbital — the focused orbital is
1385
+ * highlighted while the user can still click any other orbital to select it.
1386
+ */
1387
+ highlightedOrbital?: string;
1371
1388
  }
1372
1389
  declare const AvlOrbitalsCosmicZoom: React__default.FC<AvlOrbitalsCosmicZoomProps>;
1373
1390