@almadar/ui 2.41.0 → 2.43.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
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { OrbitControls, Grid as Grid$1, Stars, Sparkles, Html, RoundedBox } from '@react-three/drei';
3
3
  import * as React124 from 'react';
4
- import React124__default, { createContext, useState, useRef, useCallback, useEffect, useLayoutEffect, lazy, useContext, useMemo, Suspense, useSyncExternalStore, useId, forwardRef, useImperativeHandle, useReducer, Component } from 'react';
4
+ import React124__default, { createContext, useState, useRef, useCallback, useEffect, useMemo, useLayoutEffect, lazy, useContext, Suspense, useSyncExternalStore, useId, forwardRef, useImperativeHandle, useReducer, Component } from 'react';
5
5
  import { useThree, useFrame, Canvas } from '@react-three/fiber';
6
6
  import * as THREE6 from 'three';
7
7
  import { MathUtils, Vector3, Quaternion, QuadraticBezierCurve3 } from 'three';
@@ -44,7 +44,7 @@ import langDiff from 'react-syntax-highlighter/dist/esm/languages/prism/diff';
44
44
  import langToml from 'react-syntax-highlighter/dist/esm/languages/prism/toml';
45
45
  import langGo from 'react-syntax-highlighter/dist/esm/languages/prism/go';
46
46
  import langGraphql from 'react-syntax-highlighter/dist/esm/languages/prism/graphql';
47
- import { schemaToIR, getPage, isCircuitEvent } from '@almadar/core';
47
+ import { FieldTypeSchema, schemaToIR, getPage, isCircuitEvent } from '@almadar/core';
48
48
  import '@tanstack/react-query';
49
49
  import { StateMachineManager, createContextFromBindings, interpolateValue, EffectExecutor } from '@almadar/runtime';
50
50
 
@@ -2914,7 +2914,7 @@ function EventBusProvider({ children, debug: debug2 = false }) {
2914
2914
  }
2915
2915
  };
2916
2916
  }, [debug2]);
2917
- const contextValue = useMemo(
2917
+ const contextValue2 = useMemo(
2918
2918
  () => ({
2919
2919
  emit,
2920
2920
  on,
@@ -2927,12 +2927,12 @@ function EventBusProvider({ children, debug: debug2 = false }) {
2927
2927
  [emit, on, once, hasListeners, onAny, getSelectedEntity, clearSelectedEntity]
2928
2928
  );
2929
2929
  useEffect(() => {
2930
- setGlobalEventBus(contextValue);
2930
+ setGlobalEventBus(contextValue2);
2931
2931
  return () => {
2932
2932
  setGlobalEventBus(null);
2933
2933
  };
2934
- }, [contextValue]);
2935
- return /* @__PURE__ */ jsx(EventBusContext.Provider, { value: contextValue, children });
2934
+ }, [contextValue2]);
2935
+ return /* @__PURE__ */ jsx(EventBusContext.Provider, { value: contextValue2, children });
2936
2936
  }
2937
2937
  var EventBusContext;
2938
2938
  var init_EventBusProvider = __esm({
@@ -13229,7 +13229,7 @@ var ThemeProvider = ({
13229
13229
  const newMode = resolvedMode === "dark" ? "light" : "dark";
13230
13230
  setMode(newMode);
13231
13231
  }, [resolvedMode, setMode]);
13232
- const contextValue = useMemo(
13232
+ const contextValue2 = useMemo(
13233
13233
  () => ({
13234
13234
  theme,
13235
13235
  mode,
@@ -13251,7 +13251,7 @@ var ThemeProvider = ({
13251
13251
  appliedTheme
13252
13252
  ]
13253
13253
  );
13254
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: contextValue, children });
13254
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: contextValue2, children });
13255
13255
  };
13256
13256
  function useTheme() {
13257
13257
  const context = useContext(ThemeContext);
@@ -13275,25 +13275,92 @@ function useTheme() {
13275
13275
  var store = /* @__PURE__ */ new Map();
13276
13276
  var storeListeners = /* @__PURE__ */ new Set();
13277
13277
  var watchCallbacks = /* @__PURE__ */ new Map();
13278
- function advance(entityType, data) {
13279
- const prev = store.get(entityType);
13280
- const oldData = prev?.data ?? [];
13281
- store.set(entityType, { data, version: (prev?.version ?? 0) + 1 });
13278
+ function extractId(record) {
13279
+ const r2 = record;
13280
+ return String(r2.id ?? r2._id ?? r2.key ?? "");
13281
+ }
13282
+ function materialize(snap) {
13283
+ return snap.ids.map((id) => snap.entities.get(id));
13284
+ }
13285
+ function notifyListeners(entityType, prev) {
13282
13286
  for (const listener of storeListeners) {
13283
13287
  listener();
13284
13288
  }
13285
13289
  const cbs = watchCallbacks.get(entityType);
13286
13290
  if (cbs) {
13291
+ const oldData = prev ? materialize(prev) : [];
13292
+ const cur = store.get(entityType);
13293
+ const newData = cur ? materialize(cur) : [];
13287
13294
  for (const cb of cbs) {
13288
13295
  try {
13289
- cb(oldData, data);
13296
+ cb(oldData, newData);
13290
13297
  } catch {
13291
13298
  }
13292
13299
  }
13293
13300
  }
13294
13301
  }
13302
+ function setAll(entityType, records) {
13303
+ const entities = /* @__PURE__ */ new Map();
13304
+ const ids = [];
13305
+ for (const r2 of records) {
13306
+ const rec = r2;
13307
+ const id = extractId(rec);
13308
+ if (id) {
13309
+ entities.set(id, rec);
13310
+ ids.push(id);
13311
+ }
13312
+ }
13313
+ const prev = store.get(entityType);
13314
+ store.set(entityType, { entities, ids, version: (prev?.version ?? 0) + 1 });
13315
+ notifyListeners(entityType, prev);
13316
+ }
13317
+ function upsertOne(entityType, record) {
13318
+ const id = extractId(record);
13319
+ if (!id) return;
13320
+ const prev = store.get(entityType);
13321
+ const snapshot = prev ? { entities: new Map(prev.entities), ids: [...prev.ids], version: prev.version } : { entities: /* @__PURE__ */ new Map(), ids: [], version: 0 };
13322
+ snapshot.entities.set(id, record);
13323
+ if (!snapshot.ids.includes(id)) snapshot.ids.push(id);
13324
+ snapshot.version++;
13325
+ store.set(entityType, snapshot);
13326
+ notifyListeners(entityType, prev);
13327
+ }
13328
+ function addOne(entityType, record) {
13329
+ upsertOne(entityType, record);
13330
+ }
13331
+ function updateOne(entityType, id, changes) {
13332
+ const prev = store.get(entityType);
13333
+ if (!prev?.entities.has(id)) return;
13334
+ const snapshot = {
13335
+ entities: new Map(prev.entities),
13336
+ ids: [...prev.ids],
13337
+ version: prev.version
13338
+ };
13339
+ snapshot.entities.set(id, { ...snapshot.entities.get(id), ...changes });
13340
+ snapshot.version++;
13341
+ store.set(entityType, snapshot);
13342
+ notifyListeners(entityType, prev);
13343
+ }
13344
+ function removeOne(entityType, id) {
13345
+ const prev = store.get(entityType);
13346
+ if (!prev) return;
13347
+ const snapshot = {
13348
+ entities: new Map(prev.entities),
13349
+ ids: prev.ids.filter((i) => i !== id),
13350
+ version: prev.version
13351
+ };
13352
+ snapshot.entities.delete(id);
13353
+ snapshot.version++;
13354
+ store.set(entityType, snapshot);
13355
+ notifyListeners(entityType, prev);
13356
+ }
13295
13357
  function getSnapshot(entityType) {
13296
- return store.get(entityType)?.data ?? [];
13358
+ const snap = store.get(entityType);
13359
+ if (!snap) return [];
13360
+ return materialize(snap);
13361
+ }
13362
+ function getById(entityType, id) {
13363
+ return store.get(entityType)?.entities.get(id) ?? null;
13297
13364
  }
13298
13365
  function getVersion(entityType) {
13299
13366
  return store.get(entityType)?.version ?? 0;
@@ -13317,15 +13384,21 @@ function useEntityRef(entityType) {
13317
13384
  }, [entityType]);
13318
13385
  return useSyncExternalStore(subscribeToStore, getSnapshotStable, () => []);
13319
13386
  }
13320
- var EntityStoreContext = createContext({
13321
- advance,
13322
- getSnapshot
13323
- });
13387
+ var contextValue = {
13388
+ setAll,
13389
+ upsertOne,
13390
+ addOne,
13391
+ updateOne,
13392
+ removeOne,
13393
+ getSnapshot,
13394
+ getById
13395
+ };
13396
+ var EntityStoreContext = createContext(contextValue);
13324
13397
  function useEntityStore() {
13325
13398
  return useContext(EntityStoreContext);
13326
13399
  }
13327
13400
  function EntityStoreProvider({ children }) {
13328
- return /* @__PURE__ */ jsx(EntityStoreContext.Provider, { value: { advance, getSnapshot }, children });
13401
+ return /* @__PURE__ */ jsx(EntityStoreContext.Provider, { value: contextValue, children });
13329
13402
  }
13330
13403
 
13331
13404
  // providers/OrbitalProvider.tsx
@@ -13401,13 +13474,13 @@ function SelectionProvider({
13401
13474
  unsubCancel();
13402
13475
  };
13403
13476
  }, [eventBus, setSelected, clearSelection, debug2]);
13404
- const contextValue = {
13477
+ const contextValue2 = {
13405
13478
  selected,
13406
13479
  setSelected,
13407
13480
  clearSelection,
13408
13481
  isSelected
13409
13482
  };
13410
- return /* @__PURE__ */ jsx(SelectionContext.Provider, { value: contextValue, children });
13483
+ return /* @__PURE__ */ jsx(SelectionContext.Provider, { value: contextValue2, children });
13411
13484
  }
13412
13485
  var DEFAULT_SLOTS = {
13413
13486
  main: null,
@@ -13556,8 +13629,8 @@ function useUISlotManager() {
13556
13629
  var UISlotContext = createContext(null);
13557
13630
  function UISlotProvider({ children }) {
13558
13631
  const slotManager = useUISlotManager();
13559
- const contextValue = useMemo(() => slotManager, [slotManager]);
13560
- return /* @__PURE__ */ jsx(UISlotContext.Provider, { value: contextValue, children });
13632
+ const contextValue2 = useMemo(() => slotManager, [slotManager]);
13633
+ return /* @__PURE__ */ jsx(UISlotContext.Provider, { value: contextValue2, children });
13561
13634
  }
13562
13635
  function useUISlots() {
13563
13636
  const context = useContext(UISlotContext);
@@ -20060,13 +20133,13 @@ function getState() {
20060
20133
  }
20061
20134
  return { checks: /* @__PURE__ */ new Map(), transitions: [], bridgeHealth: null, listeners: /* @__PURE__ */ new Set() };
20062
20135
  }
20063
- function notifyListeners() {
20136
+ function notifyListeners2() {
20064
20137
  getState().listeners.forEach((l) => l());
20065
20138
  exposeOnWindow();
20066
20139
  }
20067
20140
  function registerCheck(id, label, status = "pending", details) {
20068
20141
  getState().checks.set(id, { id, label, status, details, updatedAt: Date.now() });
20069
- notifyListeners();
20142
+ notifyListeners2();
20070
20143
  }
20071
20144
  function getAllChecks() {
20072
20145
  return Array.from(getState().checks.values());
@@ -20110,7 +20183,7 @@ function recordTransition(trace) {
20110
20183
  failedEffects.map((e) => `${e.type}: ${e.error}`).join("; ")
20111
20184
  );
20112
20185
  }
20113
- notifyListeners();
20186
+ notifyListeners2();
20114
20187
  }
20115
20188
  function getTransitions2() {
20116
20189
  return [...getState().transitions];
@@ -20143,7 +20216,7 @@ function recordServerResponse(orbitalName, event, response) {
20143
20216
  response.error
20144
20217
  );
20145
20218
  }
20146
- notifyListeners();
20219
+ notifyListeners2();
20147
20220
  }
20148
20221
  function getBridgeHealth() {
20149
20222
  const bh = getState().bridgeHealth;
@@ -21759,6 +21832,42 @@ var orbStyleOverrides = {
21759
21832
  "orb-op-async": { color: ORB_COLORS.dark.async }
21760
21833
  };
21761
21834
  var orbStyle = { ...dark, ...orbStyleOverrides };
21835
+ function computeFoldRegions(code) {
21836
+ const lines = code.split("\n");
21837
+ const regions = [];
21838
+ const stack = [];
21839
+ for (let i = 0; i < lines.length; i++) {
21840
+ const line = lines[i];
21841
+ let inString = false;
21842
+ for (let j = 0; j < line.length; j++) {
21843
+ const ch = line[j];
21844
+ if (ch === "\\" && inString) {
21845
+ j++;
21846
+ continue;
21847
+ }
21848
+ if (ch === '"') {
21849
+ inString = !inString;
21850
+ continue;
21851
+ }
21852
+ if (inString) continue;
21853
+ if (ch === "{" || ch === "[") {
21854
+ stack.push({ line: i, bracket: ch });
21855
+ } else if (ch === "}" || ch === "]") {
21856
+ const open = stack.pop();
21857
+ if (open && open.line < i) {
21858
+ regions.push({
21859
+ start: open.line,
21860
+ end: i,
21861
+ closeBracket: ch
21862
+ });
21863
+ }
21864
+ }
21865
+ }
21866
+ }
21867
+ return regions.sort((a, b) => a.start - b.start);
21868
+ }
21869
+ var LINE_PROPS_FN = (n) => ({ "data-line": String(n - 1) });
21870
+ var HIDDEN_LINE_NUMBERS = { display: "none" };
21762
21871
  var CodeBlock = React124__default.memo(
21763
21872
  ({
21764
21873
  code: rawCode,
@@ -21766,6 +21875,7 @@ var CodeBlock = React124__default.memo(
21766
21875
  showCopyButton = true,
21767
21876
  showLanguageBadge = true,
21768
21877
  maxHeight = "60vh",
21878
+ foldable: foldableProp,
21769
21879
  className
21770
21880
  }) => {
21771
21881
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
@@ -21774,8 +21884,114 @@ var CodeBlock = React124__default.memo(
21774
21884
  const eventBus = useEventBus();
21775
21885
  const { t: _t } = useTranslate();
21776
21886
  const scrollRef = useRef(null);
21887
+ const codeRef = useRef(null);
21777
21888
  const savedScrollLeftRef = useRef(0);
21778
21889
  const [copied, setCopied] = useState(false);
21890
+ const isFoldable = foldableProp ?? (language === "orb" || language === "json");
21891
+ const [collapsed, setCollapsed] = useState(() => /* @__PURE__ */ new Set());
21892
+ const foldRegions = useMemo(
21893
+ () => isFoldable ? computeFoldRegions(code) : [],
21894
+ [code, isFoldable]
21895
+ );
21896
+ const foldStartMap = useMemo(() => {
21897
+ const m = /* @__PURE__ */ new Map();
21898
+ for (const r2 of foldRegions) m.set(r2.start, r2);
21899
+ return m;
21900
+ }, [foldRegions]);
21901
+ const hiddenLines = useMemo(() => {
21902
+ const h = /* @__PURE__ */ new Set();
21903
+ for (const r2 of foldRegions) {
21904
+ if (!collapsed.has(r2.start)) continue;
21905
+ for (let i = r2.start + 1; i <= r2.end; i++) h.add(i);
21906
+ }
21907
+ return h;
21908
+ }, [foldRegions, collapsed]);
21909
+ const collapsedRef = useRef(collapsed);
21910
+ collapsedRef.current = collapsed;
21911
+ const foldStartMapRef = useRef(foldStartMap);
21912
+ foldStartMapRef.current = foldStartMap;
21913
+ const toggleFold = useCallback((lineNum) => {
21914
+ setCollapsed((prev) => {
21915
+ const next = new Set(prev);
21916
+ if (next.has(lineNum)) next.delete(lineNum);
21917
+ else next.add(lineNum);
21918
+ return next;
21919
+ });
21920
+ }, []);
21921
+ const toggleFoldRef = useRef(toggleFold);
21922
+ toggleFoldRef.current = toggleFold;
21923
+ useEffect(() => {
21924
+ setCollapsed(/* @__PURE__ */ new Set());
21925
+ }, [code]);
21926
+ const highlightedElement = useMemo(
21927
+ () => /* @__PURE__ */ jsx(
21928
+ SyntaxHighlighter,
21929
+ {
21930
+ PreTag: "div",
21931
+ language,
21932
+ style: activeStyle,
21933
+ wrapLines: true,
21934
+ showLineNumbers: true,
21935
+ showInlineLineNumbers: false,
21936
+ lineNumberContainerStyle: HIDDEN_LINE_NUMBERS,
21937
+ lineProps: LINE_PROPS_FN,
21938
+ customStyle: {
21939
+ backgroundColor: "transparent",
21940
+ borderRadius: 0,
21941
+ padding: 0,
21942
+ margin: 0,
21943
+ whiteSpace: "pre",
21944
+ minWidth: "100%"
21945
+ },
21946
+ children: code
21947
+ }
21948
+ ),
21949
+ [code, language, activeStyle]
21950
+ );
21951
+ useLayoutEffect(() => {
21952
+ const container = codeRef.current;
21953
+ if (!container) return;
21954
+ container.querySelectorAll(".fold-toggle, .fold-summary").forEach((el) => el.remove());
21955
+ const lineEls = container.querySelectorAll("[data-line]");
21956
+ if (!isFoldable || foldRegions.length === 0) {
21957
+ lineEls.forEach((el) => {
21958
+ el.style.display = "";
21959
+ el.style.position = "";
21960
+ el.style.paddingLeft = "";
21961
+ });
21962
+ return;
21963
+ }
21964
+ lineEls.forEach((el) => {
21965
+ const num = parseInt(el.getAttribute("data-line") ?? "-1", 10);
21966
+ if (hiddenLines.has(num)) {
21967
+ el.style.display = "none";
21968
+ return;
21969
+ }
21970
+ el.style.display = "";
21971
+ el.style.position = "relative";
21972
+ el.style.paddingLeft = "1.2em";
21973
+ const region = foldStartMap.get(num);
21974
+ if (!region) return;
21975
+ const isCollapsed = collapsed.has(num);
21976
+ const toggle = document.createElement("span");
21977
+ toggle.className = "fold-toggle";
21978
+ toggle.textContent = isCollapsed ? "\u25B6" : "\u25BC";
21979
+ toggle.style.cssText = "position:absolute;left:0;top:0;width:1.2em;text-align:center;cursor:pointer;color:#858585;font-size:10px;user-select:none;line-height:inherit;height:100%";
21980
+ toggle.addEventListener("click", (e) => {
21981
+ e.stopPropagation();
21982
+ toggleFoldRef.current(num);
21983
+ });
21984
+ el.insertBefore(toggle, el.firstChild);
21985
+ if (isCollapsed) {
21986
+ const summary = document.createElement("span");
21987
+ summary.className = "fold-summary";
21988
+ summary.style.cssText = "color:#858585;font-style:italic";
21989
+ const count = region.end - region.start - 1;
21990
+ summary.textContent = ` ... ${count} line${count !== 1 ? "s" : ""} ${region.closeBracket}`;
21991
+ el.appendChild(summary);
21992
+ }
21993
+ });
21994
+ }, [collapsed, hiddenLines, foldStartMap, foldRegions, isFoldable]);
21779
21995
  useLayoutEffect(() => {
21780
21996
  const el = scrollRef.current;
21781
21997
  return () => {
@@ -21806,8 +22022,9 @@ var CodeBlock = React124__default.memo(
21806
22022
  eventBus.emit("UI:COPY_CODE", { language, success: false });
21807
22023
  }
21808
22024
  };
22025
+ const hasHeader = showLanguageBadge || showCopyButton;
21809
22026
  return /* @__PURE__ */ jsxs(Box, { className: `relative group ${className || ""}`, children: [
21810
- (showLanguageBadge || showCopyButton) && /* @__PURE__ */ jsxs(
22027
+ hasHeader && /* @__PURE__ */ jsxs(
21811
22028
  HStack,
21812
22029
  {
21813
22030
  justify: "between",
@@ -21842,31 +22059,14 @@ var CodeBlock = React124__default.memo(
21842
22059
  touchAction: "pan-x pan-y",
21843
22060
  contain: "paint",
21844
22061
  backgroundColor: "#1e1e1e",
21845
- borderRadius: showLanguageBadge || showCopyButton ? "0 0 0.5rem 0.5rem" : "0.5rem",
21846
- padding: "1rem"
22062
+ borderRadius: hasHeader ? "0 0 0.5rem 0.5rem" : "0.5rem"
21847
22063
  },
21848
- children: /* @__PURE__ */ jsx(
21849
- SyntaxHighlighter,
21850
- {
21851
- PreTag: "div",
21852
- language,
21853
- style: activeStyle,
21854
- customStyle: {
21855
- backgroundColor: "transparent",
21856
- borderRadius: 0,
21857
- padding: 0,
21858
- margin: 0,
21859
- whiteSpace: "pre",
21860
- minWidth: "100%"
21861
- },
21862
- children: code
21863
- }
21864
- )
22064
+ children: /* @__PURE__ */ jsx("div", { ref: codeRef, style: { padding: "1rem" }, children: highlightedElement })
21865
22065
  }
21866
22066
  )
21867
22067
  ] });
21868
22068
  },
21869
- (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight
22069
+ (prev, next) => prev.language === next.language && prev.code === next.code && prev.showCopyButton === next.showCopyButton && prev.maxHeight === next.maxHeight && prev.foldable === next.foldable
21870
22070
  );
21871
22071
  CodeBlock.displayName = "CodeBlock";
21872
22072
 
@@ -29770,27 +29970,27 @@ function useQuerySingleton(query) {
29770
29970
  store2.listeners.delete(listener);
29771
29971
  };
29772
29972
  }, [store2]);
29773
- const notifyListeners3 = useCallback(() => {
29973
+ const notifyListeners4 = useCallback(() => {
29774
29974
  store2.listeners.forEach((listener) => listener());
29775
29975
  }, [store2]);
29776
29976
  const setSearch = useCallback((value) => {
29777
29977
  store2.search = value;
29778
- notifyListeners3();
29779
- }, [store2, notifyListeners3]);
29978
+ notifyListeners4();
29979
+ }, [store2, notifyListeners4]);
29780
29980
  const setFilter = useCallback((key, value) => {
29781
29981
  store2.filters = { ...store2.filters, [key]: value };
29782
- notifyListeners3();
29783
- }, [store2, notifyListeners3]);
29982
+ notifyListeners4();
29983
+ }, [store2, notifyListeners4]);
29784
29984
  const clearFilters = useCallback(() => {
29785
29985
  store2.filters = {};
29786
29986
  store2.search = "";
29787
- notifyListeners3();
29788
- }, [store2, notifyListeners3]);
29987
+ notifyListeners4();
29988
+ }, [store2, notifyListeners4]);
29789
29989
  const setSort = useCallback((field, direction) => {
29790
29990
  store2.sortField = field;
29791
29991
  store2.sortDirection = direction;
29792
- notifyListeners3();
29793
- }, [store2, notifyListeners3]);
29992
+ notifyListeners4();
29993
+ }, [store2, notifyListeners4]);
29794
29994
  return {
29795
29995
  search: store2.search,
29796
29996
  setSearch,
@@ -39972,24 +40172,24 @@ init_cn();
39972
40172
  // lib/traitRegistry.ts
39973
40173
  var traits = /* @__PURE__ */ new Map();
39974
40174
  var listeners = /* @__PURE__ */ new Set();
39975
- function notifyListeners2() {
40175
+ function notifyListeners3() {
39976
40176
  listeners.forEach((listener) => listener());
39977
40177
  }
39978
40178
  function registerTrait(info) {
39979
40179
  traits.set(info.id, info);
39980
- notifyListeners2();
40180
+ notifyListeners3();
39981
40181
  }
39982
40182
  function updateTraitState(id, newState) {
39983
40183
  const trait = traits.get(id);
39984
40184
  if (trait) {
39985
40185
  trait.currentState = newState;
39986
40186
  trait.transitionCount++;
39987
- notifyListeners2();
40187
+ notifyListeners3();
39988
40188
  }
39989
40189
  }
39990
40190
  function unregisterTrait(id) {
39991
40191
  traits.delete(id);
39992
- notifyListeners2();
40192
+ notifyListeners3();
39993
40193
  }
39994
40194
  function getAllTraits() {
39995
40195
  return Array.from(traits.values());
@@ -41768,7 +41968,7 @@ function SequencerBoard({
41768
41968
  setPlayState("playing");
41769
41969
  setCurrentStep(0);
41770
41970
  let step = 0;
41771
- const advance2 = () => {
41971
+ const advance = () => {
41772
41972
  step++;
41773
41973
  if (step >= entity.maxSlots) {
41774
41974
  const playerSeq = slots.map((s) => s?.id);
@@ -41799,10 +41999,10 @@ function SequencerBoard({
41799
41999
  }
41800
42000
  } else {
41801
42001
  setCurrentStep(step);
41802
- timerRef.current = setTimeout(advance2, stepDurationMs);
42002
+ timerRef.current = setTimeout(advance, stepDurationMs);
41803
42003
  }
41804
42004
  };
41805
- timerRef.current = setTimeout(advance2, stepDurationMs);
42005
+ timerRef.current = setTimeout(advance, stepDurationMs);
41806
42006
  }, [canPlay, slots, entity.maxSlots, entity.solutions, stepDurationMs, playEvent, completeEvent, emit]);
41807
42007
  const machine = {
41808
42008
  name: entity.title,
@@ -45362,13 +45562,14 @@ function getToastPosition(position) {
45362
45562
  return "top-4 right-4";
45363
45563
  }
45364
45564
  }
45365
- function renderPatternChildren(children, onDismiss, parentId = "root") {
45565
+ function renderPatternChildren(children, onDismiss, parentId = "root", parentPath = "root") {
45366
45566
  if (!children || !Array.isArray(children) || children.length === 0) {
45367
45567
  return null;
45368
45568
  }
45369
45569
  return children.map((child, index) => {
45370
45570
  if (!child || typeof child !== "object") return null;
45371
45571
  const childId = `${parentId}-${index}`;
45572
+ const childPath = parentPath === "root" ? `root.children.${index}` : `${parentPath}.children.${index}`;
45372
45573
  const childContent = {
45373
45574
  id: childId,
45374
45575
  pattern: child.type,
@@ -45380,7 +45581,8 @@ function renderPatternChildren(children, onDismiss, parentId = "root") {
45380
45581
  SlotContentRenderer,
45381
45582
  {
45382
45583
  content: childContent,
45383
- onDismiss
45584
+ onDismiss,
45585
+ patternPath: childPath
45384
45586
  },
45385
45587
  childId
45386
45588
  );
@@ -45412,7 +45614,8 @@ function renderPatternProps(props, onDismiss) {
45412
45614
  }
45413
45615
  function SlotContentRenderer({
45414
45616
  content,
45415
- onDismiss
45617
+ onDismiss,
45618
+ patternPath
45416
45619
  }) {
45417
45620
  const entityProp = content.props.entity;
45418
45621
  const entityType = typeof entityProp === "string" ? entityProp : "";
@@ -45421,7 +45624,8 @@ function SlotContentRenderer({
45421
45624
  if (PatternComponent) {
45422
45625
  const childrenConfig = content.props.children;
45423
45626
  const hasChildren = PATTERNS_WITH_CHILDREN.has(content.pattern) || Array.isArray(childrenConfig) && childrenConfig.length > 0;
45424
- const renderedChildren = hasChildren ? renderPatternChildren(childrenConfig, onDismiss, content.id) : void 0;
45627
+ const myPath = patternPath ?? "root";
45628
+ const renderedChildren = hasChildren ? renderPatternChildren(childrenConfig, onDismiss, content.id, myPath) : void 0;
45425
45629
  const { children: _childrenConfig, ...restProps } = content.props;
45426
45630
  const renderedProps = renderPatternProps(restProps, onDismiss);
45427
45631
  let finalProps;
@@ -45437,6 +45641,7 @@ function SlotContentRenderer({
45437
45641
  } else {
45438
45642
  finalProps = renderedProps;
45439
45643
  }
45644
+ const acceptsChildren = PATTERNS_WITH_CHILDREN.has(content.pattern);
45440
45645
  return /* @__PURE__ */ jsx(
45441
45646
  Box,
45442
45647
  {
@@ -45445,6 +45650,9 @@ function SlotContentRenderer({
45445
45650
  "data-id": content.id,
45446
45651
  "data-node-id": content.nodeId,
45447
45652
  "data-source-trait": content.sourceTrait,
45653
+ "data-pattern-path": myPath,
45654
+ "data-pattern-type": content.pattern,
45655
+ "data-accepts-children": acceptsChildren ? "true" : void 0,
45448
45656
  children: /* @__PURE__ */ jsx(PatternComponent, { ...finalProps, children: renderedChildren })
45449
45657
  }
45450
45658
  );
@@ -45797,6 +46005,13 @@ createContext(null);
45797
46005
 
45798
46006
  // lib/api-client.ts
45799
46007
  typeof process !== "undefined" && process.env?.VITE_API_URL ? process.env.VITE_API_URL : "/api";
46008
+
46009
+ // hooks/useDraggable.ts
46010
+ init_useEventBus();
46011
+ var ALMADAR_DND_MIME = "application/x-almadar-dnd";
46012
+
46013
+ // hooks/useDropZone.ts
46014
+ init_useEventBus();
45800
46015
  typeof process !== "undefined" && process.env?.VITE_API_URL ? process.env.VITE_API_URL : "http://localhost:3000";
45801
46016
 
45802
46017
  // runtime/createClientEffectHandlers.ts
@@ -45844,13 +46059,13 @@ function EntitySchemaProvider({
45844
46059
  }
45845
46060
  return map;
45846
46061
  }, [entities]);
45847
- const contextValue = useMemo(
46062
+ const contextValue2 = useMemo(
45848
46063
  () => ({
45849
46064
  entities: entitiesMap
45850
46065
  }),
45851
46066
  [entitiesMap]
45852
46067
  );
45853
- return /* @__PURE__ */ jsx(EntitySchemaContext.Provider, { value: contextValue, children });
46068
+ return /* @__PURE__ */ jsx(EntitySchemaContext.Provider, { value: contextValue2, children });
45854
46069
  }
45855
46070
  function useEntitySchema() {
45856
46071
  const context = useContext(EntitySchemaContext);
@@ -46449,7 +46664,7 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
46449
46664
  if (responseData) {
46450
46665
  for (const [entityType, records] of Object.entries(responseData)) {
46451
46666
  if (Array.isArray(records)) {
46452
- entityStore.advance(entityType, records);
46667
+ entityStore.setAll(entityType, records);
46453
46668
  }
46454
46669
  }
46455
46670
  }
@@ -46508,7 +46723,7 @@ function TraitInitializer({ traits: traits2, orbitalNames, onNavigate }) {
46508
46723
  if (initResponseData) {
46509
46724
  for (const [entityType, records] of Object.entries(initResponseData)) {
46510
46725
  if (Array.isArray(records)) {
46511
- entityStore.advance(entityType, records);
46726
+ entityStore.setAll(entityType, records);
46512
46727
  }
46513
46728
  }
46514
46729
  }
@@ -46557,7 +46772,7 @@ function SchemaRunner({ schema, serverUrl, mockData, pageName, onNavigate }) {
46557
46772
  if (!serverUrl && mockData) {
46558
46773
  for (const [entityType, records] of Object.entries(mockData)) {
46559
46774
  if (Array.isArray(records)) {
46560
- entityStore.advance(entityType, records);
46775
+ entityStore.setAll(entityType, records);
46561
46776
  }
46562
46777
  }
46563
46778
  }
@@ -46638,6 +46853,9 @@ function OrbPreview({
46638
46853
  );
46639
46854
  }
46640
46855
  OrbPreview.displayName = "OrbPreview";
46856
+
46857
+ // components/molecules/avl/OrbPreviewNode.tsx
46858
+ init_useEventBus();
46641
46859
  var ScreenSizeContext = createContext("tablet");
46642
46860
  var PatternSelectionContext = createContext({ selected: null, select: () => {
46643
46861
  } });
@@ -46665,12 +46883,12 @@ function eventHandleStyle(source) {
46665
46883
  };
46666
46884
  }
46667
46885
  function buildOrbitalSchema(fullSchema, orbitalName) {
46668
- const orbital = fullSchema.orbitals?.find((o) => o.name === orbitalName);
46886
+ const orbital = (fullSchema.orbitals ?? []).find((o) => o.name === orbitalName);
46669
46887
  if (!orbital) return fullSchema;
46670
46888
  return { ...fullSchema, name: `${fullSchema.name}__${orbitalName}`, orbitals: [orbital] };
46671
46889
  }
46672
46890
  function buildTransitionSchema(fullSchema, orbitalName, traitName, transitionEvent, fromState, toState) {
46673
- const orbital = fullSchema.orbitals?.find((o) => o.name === orbitalName);
46891
+ const orbital = (fullSchema.orbitals ?? []).find((o) => o.name === orbitalName);
46674
46892
  if (!orbital) return fullSchema;
46675
46893
  const clonedOrbital = JSON.parse(JSON.stringify(orbital));
46676
46894
  const traits2 = clonedOrbital.traits ?? [];
@@ -46703,7 +46921,9 @@ function buildTransitionSchema(fullSchema, orbitalName, traitName, transitionEve
46703
46921
  }
46704
46922
  const targetTrait = traits2.find((t) => {
46705
46923
  if (typeof t === "string") return t === traitName;
46706
- return t.name === traitName;
46924
+ if ("name" in t) return t.name === traitName;
46925
+ if ("ref" in t) return t.ref === traitName;
46926
+ return false;
46707
46927
  });
46708
46928
  if (targetTrait) {
46709
46929
  clonedOrbital.traits = [targetTrait];
@@ -46720,6 +46940,21 @@ var SELECTION_STYLES = `
46720
46940
  outline: 2px solid var(--color-primary);
46721
46941
  outline-offset: 1px;
46722
46942
  }
46943
+ .orb-preview-live.drag-active [data-accepts-children="true"] {
46944
+ outline: 2px dashed var(--color-primary);
46945
+ outline-offset: -2px;
46946
+ transition: outline-color 0.15s, background-color 0.15s;
46947
+ }
46948
+ .orb-preview-live.drag-active [data-accepts-children="true"].drag-hover {
46949
+ outline-color: var(--color-primary);
46950
+ background-color: color-mix(in srgb, var(--color-primary) 5%, transparent);
46951
+ }
46952
+ .orb-preview-live .drop-indicator {
46953
+ height: 2px;
46954
+ background: var(--color-primary);
46955
+ border-radius: 1px;
46956
+ pointer-events: none;
46957
+ }
46723
46958
  `;
46724
46959
  var OrbPreviewNodeInner = (props) => {
46725
46960
  const data = props.data;
@@ -46779,6 +47014,87 @@ var OrbPreviewNodeInner = (props) => {
46779
47014
  select(null);
46780
47015
  }
46781
47016
  }, [data, select]);
47017
+ const eventBus = useEventBus();
47018
+ const [dragActive, setDragActive] = useState(false);
47019
+ useEffect(() => {
47020
+ const unsub1 = eventBus.on("UI:DRAG_START", (e) => {
47021
+ const kind = e.payload?.kind;
47022
+ if (kind === "pattern") setDragActive(true);
47023
+ });
47024
+ const unsub2 = eventBus.on("UI:DRAG_END", () => setDragActive(false));
47025
+ return () => {
47026
+ unsub1();
47027
+ unsub2();
47028
+ };
47029
+ }, [eventBus]);
47030
+ const handlePreviewDrop = useCallback((e) => {
47031
+ e.preventDefault();
47032
+ e.stopPropagation();
47033
+ setDragActive(false);
47034
+ contentRef.current?.querySelectorAll(".drag-hover, .drop-indicator").forEach((el2) => {
47035
+ el2.classList.remove("drag-hover");
47036
+ if (el2.classList.contains("drop-indicator")) el2.remove();
47037
+ });
47038
+ const raw = e.dataTransfer.getData(ALMADAR_DND_MIME);
47039
+ if (!raw) return;
47040
+ let payload;
47041
+ try {
47042
+ payload = JSON.parse(raw);
47043
+ } catch {
47044
+ return;
47045
+ }
47046
+ if (payload.kind !== "pattern") return;
47047
+ let el = e.target;
47048
+ while (el && el.dataset.acceptsChildren !== "true") {
47049
+ el = el.parentElement;
47050
+ if (!el || el === contentRef.current) break;
47051
+ }
47052
+ const containerPath = el?.dataset?.patternPath;
47053
+ if (!containerPath) {
47054
+ eventBus.emit("UI:PATTERN_INSERT", {
47055
+ parentPath: "root",
47056
+ patternType: payload.data.type,
47057
+ index: 0
47058
+ });
47059
+ return;
47060
+ }
47061
+ const pathChildren = el.querySelectorAll(":scope > [data-pattern-path]");
47062
+ let insertIndex = pathChildren.length;
47063
+ for (let i = 0; i < pathChildren.length; i++) {
47064
+ const rect = pathChildren[i].getBoundingClientRect();
47065
+ const style = el.firstElementChild ? getComputedStyle(el.firstElementChild) : null;
47066
+ const isVertical = style?.flexDirection !== "row";
47067
+ const mid = isVertical ? rect.top + rect.height / 2 : rect.left + rect.width / 2;
47068
+ const cursor = isVertical ? e.clientY : e.clientX;
47069
+ if (cursor < mid) {
47070
+ insertIndex = i;
47071
+ break;
47072
+ }
47073
+ }
47074
+ eventBus.emit("UI:PATTERN_INSERT", {
47075
+ parentPath: containerPath,
47076
+ patternType: payload.data.type,
47077
+ index: insertIndex
47078
+ });
47079
+ }, [eventBus]);
47080
+ const handlePreviewDragOver = useCallback((e) => {
47081
+ if (!dragActive) return;
47082
+ e.preventDefault();
47083
+ e.stopPropagation();
47084
+ e.dataTransfer.dropEffect = "copy";
47085
+ let el = e.target;
47086
+ while (el && el.dataset.acceptsChildren !== "true") {
47087
+ el = el.parentElement;
47088
+ if (!el || el === contentRef.current) break;
47089
+ }
47090
+ contentRef.current?.querySelectorAll(".drag-hover").forEach((c) => c.classList.remove("drag-hover"));
47091
+ if (el?.dataset?.acceptsChildren === "true") {
47092
+ el.classList.add("drag-hover");
47093
+ }
47094
+ }, [dragActive]);
47095
+ const handlePreviewDragLeave = useCallback((e) => {
47096
+ contentRef.current?.querySelectorAll(".drag-hover").forEach((c) => c.classList.remove("drag-hover"));
47097
+ }, []);
46782
47098
  return /* @__PURE__ */ jsxs(
46783
47099
  Box,
46784
47100
  {
@@ -46823,8 +47139,11 @@ var OrbPreviewNodeInner = (props) => {
46823
47139
  Box,
46824
47140
  {
46825
47141
  ref: contentRef,
46826
- className: "orb-preview-live nodrag",
47142
+ className: `orb-preview-live nodrag${dragActive ? " drag-active" : ""}`,
46827
47143
  onClick: handleContentClick,
47144
+ onDrop: handlePreviewDrop,
47145
+ onDragOver: handlePreviewDragOver,
47146
+ onDragLeave: handlePreviewDragLeave,
46828
47147
  children: orbitalSchema ? /* @__PURE__ */ jsx(Box, { style: { minHeight: preset.minHeight }, children: /* @__PURE__ */ jsx(
46829
47148
  OrbPreview,
46830
47149
  {
@@ -46943,7 +47262,9 @@ init_Avl3DExprTree();
46943
47262
  // components/organisms/avl/OrbInspector.tsx
46944
47263
  init_Box();
46945
47264
  init_Typography();
47265
+ init_Stack();
46946
47266
  init_types();
47267
+ init_useEventBus();
46947
47268
  function formatExpression(expr) {
46948
47269
  if (!expr) return "";
46949
47270
  if (typeof expr === "string") return expr;
@@ -46978,7 +47299,7 @@ var FIELD_TYPE_MAP = {
46978
47299
  array: "array"
46979
47300
  };
46980
47301
  function findEntity(schema, orbitalName) {
46981
- const orbital = schema.orbitals?.find((o) => o.name === orbitalName);
47302
+ const orbital = (schema.orbitals ?? []).find((o) => o.name === orbitalName);
46982
47303
  if (!orbital || typeof orbital.entity === "string") return null;
46983
47304
  const e = orbital.entity;
46984
47305
  const fields = (e.fields ?? []).map((f3) => ({
@@ -46989,7 +47310,7 @@ function findEntity(schema, orbitalName) {
46989
47310
  return { name: e.name ?? orbitalName, persistence: e.persistence ?? "runtime", fields };
46990
47311
  }
46991
47312
  function findTransition(schema, orbitalName, traitName, event) {
46992
- const orbital = schema.orbitals?.find((o) => o.name === orbitalName);
47313
+ const orbital = (schema.orbitals ?? []).find((o) => o.name === orbitalName);
46993
47314
  if (!orbital) return null;
46994
47315
  const traits2 = orbital.traits ?? [];
46995
47316
  const trait = traits2.find((t) => typeof t !== "string" && t.name === traitName);
@@ -46999,16 +47320,39 @@ function findTransition(schema, orbitalName, traitName, event) {
46999
47320
  return sm.transitions?.find((t) => t.event === event) ?? null;
47000
47321
  }
47001
47322
  function findTraits(schema, orbitalName) {
47002
- const orbital = schema.orbitals?.find((o) => o.name === orbitalName);
47323
+ const orbital = (schema.orbitals ?? []).find((o) => o.name === orbitalName);
47003
47324
  if (!orbital) return [];
47004
47325
  return (orbital.traits ?? []).filter((t) => typeof t !== "string").map((t) => ({
47005
47326
  name: t.name,
47006
47327
  stateCount: t.stateMachine?.states?.length ?? 0
47007
47328
  }));
47008
47329
  }
47330
+ function findPatternInTree(root, path) {
47331
+ if (!path || path === "root") return root;
47332
+ const parts = path.split(".");
47333
+ let current = root;
47334
+ for (const part of parts) {
47335
+ if (current === null || current === void 0 || typeof current !== "object") return null;
47336
+ const record = current;
47337
+ if (part === "children" && Array.isArray(record.children)) {
47338
+ current = record.children;
47339
+ } else if (Array.isArray(current)) {
47340
+ const idx = parseInt(part, 10);
47341
+ if (isNaN(idx) || idx < 0 || idx >= current.length) return null;
47342
+ current = current[idx];
47343
+ } else {
47344
+ current = record[part];
47345
+ }
47346
+ }
47347
+ return typeof current === "object" && current !== null ? current : null;
47348
+ }
47349
+ var FIELD_TYPE_OPTIONS = FieldTypeSchema.options.map((v) => ({ value: v, label: v }));
47350
+ var EFFECT_TYPE_OPTIONS = Object.keys(EFFECT_TYPE_TO_CATEGORY).map((v) => ({ value: v, label: v }));
47009
47351
  function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose }) {
47010
47352
  const { selected: selectedPattern } = useContext(PatternSelectionContext);
47011
47353
  const [activeTab, setActiveTab] = useState("inspector");
47354
+ const eventBus = useEventBus();
47355
+ const { t } = useTranslate();
47012
47356
  const orbitalName = node.orbitalName ?? "";
47013
47357
  const traitName = node.traitName ?? "";
47014
47358
  const transitionEvent = node.transitionEvent ?? "";
@@ -47028,25 +47372,56 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47028
47372
  return findTransition(schema, orbitalName, traitName, transitionEvent);
47029
47373
  }, [schema, orbitalName, traitName, transitionEvent]);
47030
47374
  const traits2 = useMemo(() => findTraits(schema, orbitalName), [schema, orbitalName]);
47375
+ const patternConfig = useMemo(() => {
47376
+ if (!selectedPattern || !transition) return null;
47377
+ const patternId = selectedPattern.patternId ?? "root";
47378
+ for (const eff of transition.effects ?? []) {
47379
+ if (Array.isArray(eff) && eff[0] === "render-ui" && eff[2]) {
47380
+ const found = findPatternInTree(eff[2], patternId);
47381
+ if (found) return found;
47382
+ }
47383
+ }
47384
+ return null;
47385
+ }, [selectedPattern, transition]);
47031
47386
  const orbCode = useMemo(() => {
47032
- const orbital = schema.orbitals?.find((o) => o.name === orbitalName);
47387
+ const orbital = (schema.orbitals ?? []).find((o) => o.name === orbitalName);
47033
47388
  if (!orbital) return "{}";
47034
47389
  if (isExpanded && traitName) {
47035
- const traits3 = orbital.traits ?? [];
47036
- const trait = traits3.find((t) => typeof t !== "string" && t.name === traitName);
47390
+ const traitList = orbital.traits ?? [];
47391
+ const trait = traitList.find((tr) => typeof tr !== "string" && tr.name === traitName);
47037
47392
  if (trait && typeof trait !== "string" && trait.stateMachine) {
47038
47393
  if (transitionEvent) {
47039
- const t = trait.stateMachine.transitions?.find((tx) => tx.event === transitionEvent);
47040
- if (t) return JSON.stringify(t, null, 2);
47394
+ const tx = trait.stateMachine.transitions?.find((txn) => txn.event === transitionEvent);
47395
+ if (tx) return JSON.stringify(tx, null, 2);
47041
47396
  }
47042
47397
  return JSON.stringify({ name: trait.name, stateMachine: trait.stateMachine }, null, 2);
47043
47398
  }
47044
47399
  }
47045
47400
  return JSON.stringify(orbital, null, 2);
47046
47401
  }, [schema, orbitalName, traitName, transitionEvent, isExpanded]);
47047
- const handlePropChange = useCallback((prop, value) => {
47048
- if (!editable || !onSchemaChange) return;
47049
- }, [editable, onSchemaChange]);
47402
+ const handlePropChange = useCallback((propName, value) => {
47403
+ if (!editable) return;
47404
+ eventBus.emit("UI:PROP_CHANGE", { propName, value });
47405
+ }, [editable, eventBus]);
47406
+ const handleAddField = useCallback(() => {
47407
+ eventBus.emit("UI:ADD_FIELD", {});
47408
+ }, [eventBus]);
47409
+ const handleUpdateField = useCallback((fieldName, updates) => {
47410
+ eventBus.emit("UI:UPDATE_FIELD", { fieldName, updates });
47411
+ }, [eventBus]);
47412
+ const handleRemoveField = useCallback((fieldName) => {
47413
+ eventBus.emit("UI:REMOVE_FIELD", { fieldName });
47414
+ }, [eventBus]);
47415
+ const handleGuardChange = useCallback((guardExpr) => {
47416
+ if (!editable) return;
47417
+ eventBus.emit("UI:GUARD_CHANGE", { guard: guardExpr || null });
47418
+ }, [editable, eventBus]);
47419
+ const handleAddEffect = useCallback((effectType) => {
47420
+ eventBus.emit("UI:ADD_EFFECT", { effectType });
47421
+ }, [eventBus]);
47422
+ const handleRemoveEffect = useCallback((effectIndex) => {
47423
+ eventBus.emit("UI:REMOVE_EFFECT", { effectIndex });
47424
+ }, [eventBus]);
47050
47425
  const headerTitle = selectedPattern ? selectedPattern.patternType : isExpanded ? transitionEvent || "Transition" : orbitalName;
47051
47426
  return /* @__PURE__ */ jsxs(Box, { className: "flex flex-col bg-card border-l border-border h-full", style: { width: 340 }, children: [
47052
47427
  /* @__PURE__ */ jsxs(Box, { className: "shrink-0 border-b border-border", children: [
@@ -47104,48 +47479,98 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47104
47479
  /* ── Inspector Tab ── */
47105
47480
  /* @__PURE__ */ jsxs(Fragment, { children: [
47106
47481
  selectedPattern && patternDef?.propsSchema && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47107
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "Props" }),
47108
- /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1.5", children: Object.entries(patternDef.propsSchema).slice(0, 8).map(([propName, propSchema]) => {
47482
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Props") }),
47483
+ /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1.5", children: Object.entries(patternDef.propsSchema).slice(0, 12).map(([propName, propSchema]) => {
47109
47484
  const ps = propSchema;
47485
+ const currentValue = patternConfig ? patternConfig[propName] : void 0;
47486
+ const displayValue = currentValue !== void 0 ? typeof currentValue === "object" ? JSON.stringify(currentValue) : String(currentValue) : "";
47110
47487
  return /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
47111
47488
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[11px] w-20 shrink-0 font-mono", children: propName }),
47112
47489
  editable ? /* @__PURE__ */ jsx(
47113
47490
  Input,
47114
47491
  {
47492
+ defaultValue: displayValue,
47115
47493
  placeholder: ps.types?.join(" | ") ?? "string",
47116
47494
  className: "flex-1 text-[11px] h-6",
47117
- onChange: (e) => handlePropChange(propName, e.target.value)
47495
+ onBlur: (e) => handlePropChange(propName, e.target.value)
47118
47496
  }
47119
47497
  ) : /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-[11px] text-muted-foreground", children: [
47120
- ps.types?.join(" | ") ?? "string",
47498
+ displayValue || (ps.types?.join(" | ") ?? "string"),
47121
47499
  ps.required ? " *" : ""
47122
47500
  ] })
47123
47501
  ] }, propName);
47124
47502
  }) })
47125
47503
  ] }),
47126
47504
  (selectedPattern && isEntityPattern || !selectedPattern && !isExpanded) && entity && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47127
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "Entity" }),
47505
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: t("Entity") }),
47128
47506
  /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2 mb-2", children: [
47129
47507
  /* @__PURE__ */ jsx("svg", { width: 14, height: 14, children: /* @__PURE__ */ jsx("circle", { cx: 7, cy: 7, r: 5, fill: "var(--color-primary)" }) }),
47130
47508
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-semibold text-[12px]", children: entity.name }),
47131
47509
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px]", children: entity.persistence })
47132
47510
  ] }),
47133
- /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1", children: entity.fields.map((f3) => /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
47511
+ /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1", children: entity.fields.map((f3) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
47134
47512
  /* @__PURE__ */ jsx("svg", { width: 12, height: 12, children: /* @__PURE__ */ jsx(AvlFieldType, { x: 6, y: 6, kind: FIELD_TYPE_MAP[f3.type] ?? "string", size: 4 }) }),
47135
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px] font-mono flex-1", children: f3.name }),
47136
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px]", children: f3.type }),
47137
- f3.required && /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-primary text-[9px]", children: "req" })
47138
- ] }, f3.name)) })
47513
+ editable ? /* @__PURE__ */ jsxs(Fragment, { children: [
47514
+ /* @__PURE__ */ jsx(
47515
+ Input,
47516
+ {
47517
+ defaultValue: f3.name,
47518
+ className: "flex-1 text-[11px] font-mono h-6",
47519
+ onBlur: (e) => {
47520
+ if (e.target.value !== f3.name) {
47521
+ handleUpdateField(f3.name, { name: e.target.value });
47522
+ }
47523
+ }
47524
+ }
47525
+ ),
47526
+ /* @__PURE__ */ jsx(
47527
+ Select,
47528
+ {
47529
+ value: f3.type,
47530
+ options: FIELD_TYPE_OPTIONS,
47531
+ onChange: (e) => handleUpdateField(f3.name, { type: e.target.value }),
47532
+ className: "w-20 text-[10px] h-6"
47533
+ }
47534
+ ),
47535
+ /* @__PURE__ */ jsx(
47536
+ Button,
47537
+ {
47538
+ variant: "ghost",
47539
+ size: "sm",
47540
+ onClick: () => handleRemoveField(f3.name),
47541
+ className: "shrink-0 p-0.5 h-6 w-6",
47542
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", size: "xs" })
47543
+ }
47544
+ )
47545
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
47546
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px] font-mono flex-1", children: f3.name }),
47547
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px]", children: f3.type }),
47548
+ f3.required && /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-primary text-[9px]", children: t("req") })
47549
+ ] })
47550
+ ] }, f3.name)) }),
47551
+ editable && /* @__PURE__ */ jsxs(
47552
+ Button,
47553
+ {
47554
+ variant: "ghost",
47555
+ size: "sm",
47556
+ onClick: handleAddField,
47557
+ className: "mt-2 text-[11px] w-full",
47558
+ children: [
47559
+ /* @__PURE__ */ jsx(Icon, { name: "plus", size: "xs", className: "mr-1" }),
47560
+ t("Add Field")
47561
+ ]
47562
+ }
47563
+ )
47139
47564
  ] }),
47140
47565
  !selectedPattern && !isExpanded && traits2.length > 0 && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47141
47566
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "Traits" }),
47142
- /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1", children: traits2.map((t) => /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
47143
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px] font-semibold", children: t.name }),
47567
+ /* @__PURE__ */ jsx(Box, { className: "flex flex-col gap-1", children: traits2.map((t2) => /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
47568
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px] font-semibold", children: t2.name }),
47144
47569
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-muted-foreground text-[10px]", children: [
47145
- t.stateCount,
47570
+ t2.stateCount,
47146
47571
  " states"
47147
47572
  ] })
47148
- ] }, t.name)) })
47573
+ ] }, t2.name)) })
47149
47574
  ] }),
47150
47575
  isExpanded && fromState && toState && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47151
47576
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "Transition" }),
@@ -47164,19 +47589,22 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47164
47589
  /* @__PURE__ */ jsx("svg", { width: 16, height: 16, children: /* @__PURE__ */ jsx(AvlEvent, { x: 8, y: 8, size: 12 }) }),
47165
47590
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-semibold text-[12px]", children: transitionEvent })
47166
47591
  ] }) }),
47167
- (transition?.guard ?? guard) && /* @__PURE__ */ jsx(Box, { className: "px-4 py-2 border-b border-border/40", children: /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
47592
+ (transition?.guard ?? guard ?? editable) && isExpanded && /* @__PURE__ */ jsx(Box, { className: "px-4 py-2 border-b border-border/40", children: /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
47168
47593
  /* @__PURE__ */ jsx("svg", { width: 16, height: 16, children: /* @__PURE__ */ jsx(AvlGuard, { x: 8, y: 8, size: 12 }) }),
47169
47594
  editable ? /* @__PURE__ */ jsx(
47170
47595
  Input,
47171
47596
  {
47172
47597
  defaultValue: formatExpression(transition?.guard ?? guard),
47173
- className: "flex-1 text-[11px] font-mono h-6"
47598
+ placeholder: t("Guard expression"),
47599
+ className: "flex-1 text-[11px] font-mono h-6",
47600
+ onBlur: (e) => handleGuardChange(e.target.value)
47174
47601
  }
47175
47602
  ) : /* @__PURE__ */ jsx(Typography, { variant: "small", className: "font-mono text-[11px] text-muted-foreground", children: formatExpression(transition?.guard ?? guard) })
47176
47603
  ] }) }),
47177
- effectTypes.length > 0 && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47604
+ (effectTypes.length > 0 || editable) && isExpanded && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3 border-b border-border/40", children: [
47178
47605
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: [
47179
- "Effects (",
47606
+ t("Effects"),
47607
+ " (",
47180
47608
  effectTypes.length,
47181
47609
  ")"
47182
47610
  ] }),
@@ -47184,15 +47612,26 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47184
47612
  const isKnown = KNOWN_EFFECTS.has(type);
47185
47613
  const category = EFFECT_TYPE_TO_CATEGORY[type];
47186
47614
  const catColor = category ? EFFECT_CATEGORY_COLORS[category] : void 0;
47187
- return /* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-2", children: [
47615
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
47188
47616
  /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "text-muted-foreground text-[11px] w-4 text-right shrink-0", children: [
47189
47617
  i + 1,
47190
47618
  "."
47191
47619
  ] }),
47192
47620
  isKnown && /* @__PURE__ */ jsx("svg", { width: 16, height: 16, children: /* @__PURE__ */ jsx(AvlEffect, { x: 8, y: 8, effectType: type, size: 6, showBackground: true }) }),
47193
- /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px]", style: { color: catColor?.color }, children: effectSummary(type) })
47621
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px] flex-1", style: { color: catColor?.color }, children: effectSummary(type) }),
47622
+ editable && /* @__PURE__ */ jsx(
47623
+ Button,
47624
+ {
47625
+ variant: "ghost",
47626
+ size: "sm",
47627
+ onClick: () => handleRemoveEffect(i),
47628
+ className: "shrink-0 p-0.5 h-6 w-6",
47629
+ children: /* @__PURE__ */ jsx(Icon, { name: "x", size: "xs" })
47630
+ }
47631
+ )
47194
47632
  ] }, i);
47195
- }) })
47633
+ }) }),
47634
+ editable && /* @__PURE__ */ jsx(AddEffectButton, { onAdd: handleAddEffect })
47196
47635
  ] }),
47197
47636
  patterns.length > 0 && !selectedPattern && /* @__PURE__ */ jsxs(Box, { className: "px-4 py-3", children: [
47198
47637
  /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-muted-foreground text-[10px] uppercase tracking-wider mb-2", children: "render-ui" }),
@@ -47208,6 +47647,40 @@ function OrbInspector({ node, schema, editable = false, onSchemaChange, onClose
47208
47647
  ) })
47209
47648
  ] });
47210
47649
  }
47650
+ function AddEffectButton({ onAdd }) {
47651
+ const [open, setOpen] = useState(false);
47652
+ const { t } = useTranslate();
47653
+ return /* @__PURE__ */ jsxs(Box, { className: "relative mt-2", children: [
47654
+ /* @__PURE__ */ jsxs(
47655
+ Button,
47656
+ {
47657
+ variant: "ghost",
47658
+ size: "sm",
47659
+ onClick: () => setOpen((prev) => !prev),
47660
+ className: "text-[11px] w-full",
47661
+ children: [
47662
+ /* @__PURE__ */ jsx(Icon, { name: "plus", size: "xs", className: "mr-1" }),
47663
+ t("Add Effect")
47664
+ ]
47665
+ }
47666
+ ),
47667
+ open && /* @__PURE__ */ jsx(Box, { className: "absolute z-20 top-full left-0 right-0 mt-1 bg-card border border-border rounded-md shadow-lg overflow-hidden", children: EFFECT_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsxs(
47668
+ Box,
47669
+ {
47670
+ className: "px-3 py-1.5 text-[11px] cursor-pointer hover:bg-muted/50 flex items-center gap-2",
47671
+ onClick: () => {
47672
+ onAdd(opt.value);
47673
+ setOpen(false);
47674
+ },
47675
+ children: [
47676
+ KNOWN_EFFECTS.has(opt.value) && /* @__PURE__ */ jsx("svg", { width: 14, height: 14, children: /* @__PURE__ */ jsx(AvlEffect, { x: 7, y: 7, effectType: opt.value, size: 5, showBackground: true }) }),
47677
+ /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-[11px]", children: opt.label })
47678
+ ]
47679
+ },
47680
+ opt.value
47681
+ )) })
47682
+ ] });
47683
+ }
47211
47684
  function OrbPatternTree({ config, depth }) {
47212
47685
  if (!config || typeof config !== "object") return null;
47213
47686
  const { type, children, ...props } = config;
@@ -47256,7 +47729,9 @@ function FlowCanvasInner({
47256
47729
  onLevelChange,
47257
47730
  initialOrbital,
47258
47731
  editable,
47259
- onSchemaChange
47732
+ onSchemaChange,
47733
+ onPatternDelete,
47734
+ onEventWire
47260
47735
  }) {
47261
47736
  const parsedSchema = useMemo(() => {
47262
47737
  if (typeof schemaProp === "string") return JSON.parse(schemaProp);
@@ -47335,8 +47810,15 @@ function FlowCanvasInner({
47335
47810
  setExpandedOrbital(void 0);
47336
47811
  onLevelChange?.("overview");
47337
47812
  }
47813
+ } else if (e.key === "Delete" || e.key === "Backspace") {
47814
+ const target = e.target;
47815
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) return;
47816
+ if (selectedPattern && selectedPattern.nodeData) {
47817
+ onPatternDelete?.({ patternId: selectedPattern.patternId ?? "", nodeData: selectedPattern.nodeData });
47818
+ setSelectedPattern(null);
47819
+ }
47338
47820
  }
47339
- }, [level, onLevelChange, selectedNode]);
47821
+ }, [level, onLevelChange, selectedNode, selectedPattern, onPatternDelete]);
47340
47822
  useEffect(() => {
47341
47823
  document.addEventListener("keydown", handleKeyDown);
47342
47824
  return () => document.removeEventListener("keydown", handleKeyDown);
@@ -47351,6 +47833,22 @@ function FlowCanvasInner({
47351
47833
  onLevelChange?.("overview");
47352
47834
  }
47353
47835
  }, [level, onLevelChange, selectedNode]);
47836
+ const handleConnect = useCallback((connection) => {
47837
+ if (!connection.sourceHandle?.startsWith("event-") || !onEventWire) return;
47838
+ const eventName = connection.sourceHandle.replace("event-", "");
47839
+ const sourceNode = nodes.find((n) => n.id === connection.source);
47840
+ const targetNode = nodes.find((n) => n.id === connection.target);
47841
+ if (!sourceNode || !targetNode) return;
47842
+ const srcData = sourceNode.data;
47843
+ const tgtData = targetNode.data;
47844
+ onEventWire({
47845
+ eventName,
47846
+ sourceOrbital: srcData.orbitalName ?? "",
47847
+ targetOrbital: tgtData.orbitalName ?? "",
47848
+ sourceTraitName: srcData.traitName,
47849
+ targetTraitName: tgtData.traitName
47850
+ });
47851
+ }, [nodes, onEventWire]);
47354
47852
  const screenSizeKeys = ["mobile", "tablet", "desktop"];
47355
47853
  return /* @__PURE__ */ jsx(ScreenSizeContext.Provider, { value: screenSize, children: /* @__PURE__ */ jsx(PatternSelectionContext.Provider, { value: patternSelectionValue, children: /* @__PURE__ */ jsxs(
47356
47854
  Box,
@@ -47371,6 +47869,7 @@ function FlowCanvasInner({
47371
47869
  onEdgesChange,
47372
47870
  onNodeDoubleClick: handleNodeDoubleClick,
47373
47871
  onNodeClick: handleNodeClick,
47872
+ onConnect: handleConnect,
47374
47873
  minZoom: 0.1,
47375
47874
  maxZoom: 2,
47376
47875
  fitView: true,