@almadar/ui 5.101.0 → 5.103.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.cjs +238 -40
- package/dist/avl/index.js +240 -42
- package/dist/components/index.cjs +89 -19
- package/dist/components/index.js +74 -8
- package/dist/hooks/index.cjs +71 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +69 -2
- package/dist/hooks/useSharedEntityStore.d.ts +53 -0
- package/dist/hooks/useTraitStateMachine.d.ts +32 -1
- package/dist/runtime/index.cjs +242 -44
- package/dist/runtime/index.js +244 -46
- package/package.json +2 -2
package/dist/runtime/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
11
11
|
import { createPortal } from 'react-dom';
|
|
12
12
|
import { useTranslate } from '@almadar/ui/hooks';
|
|
13
13
|
import { useUISlots, UISlotProvider, useTheme } from '@almadar/ui/context';
|
|
14
|
-
import { evaluateGuard,
|
|
14
|
+
import { evaluateGuard, createMinimalContext, executeEffects, evaluate } from '@almadar/evaluator';
|
|
15
15
|
import { Link, Outlet, useLocation } from 'react-router-dom';
|
|
16
16
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
|
17
17
|
import SyntaxHighlighter from 'react-syntax-highlighter/dist/esm/prism-light.js';
|
|
@@ -37,7 +37,7 @@ import ReactMarkdown from 'react-markdown';
|
|
|
37
37
|
import remarkGfm from 'remark-gfm';
|
|
38
38
|
import remarkMath from 'remark-math';
|
|
39
39
|
import rehypeKatex from 'rehype-katex';
|
|
40
|
-
import { isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, isEntityCall, isInlineTrait } from '@almadar/core';
|
|
40
|
+
import { isCircuitEvent, schemaToIR, getPage, clearSchemaCache as clearSchemaCache$1, isEntityCall, walkSExpr, mergeEntityFrame, isInlineTrait } from '@almadar/core';
|
|
41
41
|
import { DndContext, pointerWithin, rectIntersection, closestCorners, useSensors, useSensor, PointerSensor, KeyboardSensor, useDroppable } from '@dnd-kit/core';
|
|
42
42
|
import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
43
43
|
import { CSS } from '@dnd-kit/utilities';
|
|
@@ -7685,7 +7685,7 @@ function recordTransition(trace) {
|
|
|
7685
7685
|
...trace,
|
|
7686
7686
|
id: `t-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
|
|
7687
7687
|
};
|
|
7688
|
-
|
|
7688
|
+
log4.debug("transition:recorded", { trait: trace.traitName, from: trace.from, to: trace.to, event: trace.event, effectCount: trace.effects.length });
|
|
7689
7689
|
getState().transitions.push(entry);
|
|
7690
7690
|
if (getState().transitions.length > MAX_TRANSITIONS) {
|
|
7691
7691
|
getState().transitions.shift();
|
|
@@ -7787,7 +7787,7 @@ function getTraitSnapshots() {
|
|
|
7787
7787
|
try {
|
|
7788
7788
|
snapshots.push(getter());
|
|
7789
7789
|
} catch (err) {
|
|
7790
|
-
|
|
7790
|
+
log4.error("traitSnapshot getter failed", { trait: traitName, err: String(err) });
|
|
7791
7791
|
}
|
|
7792
7792
|
}
|
|
7793
7793
|
return snapshots;
|
|
@@ -7886,10 +7886,10 @@ function updateAssetStatus(url, status) {
|
|
|
7886
7886
|
window.__orbitalVerification.assetStatus[url] = status;
|
|
7887
7887
|
}
|
|
7888
7888
|
}
|
|
7889
|
-
var
|
|
7889
|
+
var log4, MAX_TRANSITIONS;
|
|
7890
7890
|
var init_verificationRegistry = __esm({
|
|
7891
7891
|
"lib/verificationRegistry.ts"() {
|
|
7892
|
-
|
|
7892
|
+
log4 = createLogger("almadar:bridge");
|
|
7893
7893
|
MAX_TRANSITIONS = 500;
|
|
7894
7894
|
exposeOnWindow();
|
|
7895
7895
|
}
|
|
@@ -17497,7 +17497,7 @@ var init_avl_elk_layout = __esm({
|
|
|
17497
17497
|
elk = new ELK();
|
|
17498
17498
|
}
|
|
17499
17499
|
});
|
|
17500
|
-
var
|
|
17500
|
+
var log5, SWIM_GUTTER, CENTER_W, BehaviorView;
|
|
17501
17501
|
var init_BehaviorView = __esm({
|
|
17502
17502
|
"components/avl/molecules/BehaviorView.tsx"() {
|
|
17503
17503
|
"use client";
|
|
@@ -17506,7 +17506,7 @@ var init_BehaviorView = __esm({
|
|
|
17506
17506
|
init_AvlSwimLane();
|
|
17507
17507
|
init_avl_atom_types();
|
|
17508
17508
|
init_avl_elk_layout();
|
|
17509
|
-
|
|
17509
|
+
log5 = createLogger("almadar:ui:avl:behavior-view");
|
|
17510
17510
|
SWIM_GUTTER = 120;
|
|
17511
17511
|
CENTER_W = 360;
|
|
17512
17512
|
BehaviorView = ({ data }) => {
|
|
@@ -17517,7 +17517,7 @@ var init_BehaviorView = __esm({
|
|
|
17517
17517
|
const dataKey = useMemo(() => JSON.stringify(traitData), [traitData]);
|
|
17518
17518
|
useEffect(() => {
|
|
17519
17519
|
if (!traitData) return;
|
|
17520
|
-
computeTraitLayout(traitData).then(setLayout).catch((err) =>
|
|
17520
|
+
computeTraitLayout(traitData).then(setLayout).catch((err) => log5.error("compute-trait-layout-failed", { error: err instanceof Error ? err : String(err) }));
|
|
17521
17521
|
}, [dataKey]);
|
|
17522
17522
|
if (!traitData) {
|
|
17523
17523
|
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-[var(--color-border)] bg-[var(--color-card)] p-4 text-center text-[var(--color-muted-foreground)] text-sm", children: t("avl.noTraitData") });
|
|
@@ -18069,7 +18069,7 @@ function generateDiff(oldVal, newVal) {
|
|
|
18069
18069
|
}
|
|
18070
18070
|
return diff;
|
|
18071
18071
|
}
|
|
18072
|
-
var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle,
|
|
18072
|
+
var orbStyleOverrides, orbStyle, loloStyleOverrides, loloStyle, log6, CODE_LANGUAGES, CODE_LANGUAGE_SET, DIFF_STYLES, DIFF_STYLE_FALLBACK, LINE_PROPS_FN, HIDDEN_LINE_NUMBERS, CodeBlock;
|
|
18073
18073
|
var init_CodeBlock = __esm({
|
|
18074
18074
|
"components/core/molecules/markdown/CodeBlock.tsx"() {
|
|
18075
18075
|
init_cn();
|
|
@@ -18152,7 +18152,7 @@ var init_CodeBlock = __esm({
|
|
|
18152
18152
|
"lolo-op-async": { color: ORB_COLORS.dark.async }
|
|
18153
18153
|
};
|
|
18154
18154
|
loloStyle = { ...dark, ...loloStyleOverrides };
|
|
18155
|
-
|
|
18155
|
+
log6 = createLogger("almadar:ui:markdown-code");
|
|
18156
18156
|
CODE_LANGUAGES = [
|
|
18157
18157
|
"text",
|
|
18158
18158
|
"json",
|
|
@@ -18420,7 +18420,7 @@ var init_CodeBlock = __esm({
|
|
|
18420
18420
|
eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: true });
|
|
18421
18421
|
setTimeout(() => setCopied(false), 2e3);
|
|
18422
18422
|
} catch (err) {
|
|
18423
|
-
|
|
18423
|
+
log6.error("Failed to copy code", { error: err instanceof Error ? err : String(err) });
|
|
18424
18424
|
eventBus.emit("UI:COPY_CODE", { language: activeLanguage, success: false });
|
|
18425
18425
|
}
|
|
18426
18426
|
};
|
|
@@ -27285,14 +27285,14 @@ function useSafeEventBus3() {
|
|
|
27285
27285
|
} };
|
|
27286
27286
|
}
|
|
27287
27287
|
}
|
|
27288
|
-
var
|
|
27288
|
+
var log7, lookStyles5, ButtonGroup;
|
|
27289
27289
|
var init_ButtonGroup = __esm({
|
|
27290
27290
|
"components/core/molecules/ButtonGroup.tsx"() {
|
|
27291
27291
|
"use client";
|
|
27292
27292
|
init_cn();
|
|
27293
27293
|
init_atoms();
|
|
27294
27294
|
init_useEventBus();
|
|
27295
|
-
|
|
27295
|
+
log7 = createLogger("almadar:ui:button-group");
|
|
27296
27296
|
lookStyles5 = {
|
|
27297
27297
|
"right-aligned-buttons": "",
|
|
27298
27298
|
"floating-bar": "fixed bottom-section left-1/2 -translate-x-1/2 shadow-elevation-toast bg-card p-card-sm rounded-container",
|
|
@@ -27373,7 +27373,7 @@ var init_ButtonGroup = __esm({
|
|
|
27373
27373
|
{
|
|
27374
27374
|
variant: "ghost",
|
|
27375
27375
|
onClick: () => {
|
|
27376
|
-
|
|
27376
|
+
log7.debug("Filter clicked", { field: filter.field });
|
|
27377
27377
|
},
|
|
27378
27378
|
children: filter.label
|
|
27379
27379
|
},
|
|
@@ -28282,9 +28282,9 @@ function debug(...args) {
|
|
|
28282
28282
|
const [first, ...rest] = args;
|
|
28283
28283
|
const message = typeof first === "string" ? first : "<debug>";
|
|
28284
28284
|
if (rest.length === 0 && typeof first === "string") {
|
|
28285
|
-
|
|
28285
|
+
log8.debug(message);
|
|
28286
28286
|
} else {
|
|
28287
|
-
|
|
28287
|
+
log8.debug(message, { args: rest.length > 0 ? formatArgs(rest) : formatArgs([first]) });
|
|
28288
28288
|
}
|
|
28289
28289
|
}
|
|
28290
28290
|
function debugGroup(label) {
|
|
@@ -28312,11 +28312,11 @@ function toLogMetaValue(v) {
|
|
|
28312
28312
|
}
|
|
28313
28313
|
return String(v);
|
|
28314
28314
|
}
|
|
28315
|
-
var NAMESPACE,
|
|
28315
|
+
var NAMESPACE, log8;
|
|
28316
28316
|
var init_debug = __esm({
|
|
28317
28317
|
"lib/debug.ts"() {
|
|
28318
28318
|
NAMESPACE = "almadar:ui:debug";
|
|
28319
|
-
|
|
28319
|
+
log8 = createLogger(NAMESPACE);
|
|
28320
28320
|
createLogger("almadar:ui:debug:input");
|
|
28321
28321
|
createLogger("almadar:ui:debug:collision");
|
|
28322
28322
|
createLogger("almadar:ui:debug:physics");
|
|
@@ -30474,12 +30474,12 @@ var init_MapView = __esm({
|
|
|
30474
30474
|
shadowSize: [41, 41]
|
|
30475
30475
|
});
|
|
30476
30476
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
30477
|
-
const { useEffect: useEffect67, useRef:
|
|
30477
|
+
const { useEffect: useEffect67, useRef: useRef66, useCallback: useCallback98, useState: useState97 } = React96__default;
|
|
30478
30478
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
30479
30479
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
30480
30480
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
30481
30481
|
const map = useMap();
|
|
30482
|
-
const prevRef =
|
|
30482
|
+
const prevRef = useRef66({ centerLat, centerLng, zoom });
|
|
30483
30483
|
useEffect67(() => {
|
|
30484
30484
|
const prev = prevRef.current;
|
|
30485
30485
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
@@ -43741,7 +43741,7 @@ function getAllEvents(traits2) {
|
|
|
43741
43741
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
43742
43742
|
const eventBus = useEventBus();
|
|
43743
43743
|
const { t } = useTranslate();
|
|
43744
|
-
const [
|
|
43744
|
+
const [log10, setLog] = React96.useState([]);
|
|
43745
43745
|
const prevStatesRef = React96.useRef(/* @__PURE__ */ new Map());
|
|
43746
43746
|
React96.useEffect(() => {
|
|
43747
43747
|
for (const trait of traits2) {
|
|
@@ -43805,9 +43805,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
43805
43805
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
43806
43806
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
43807
43807
|
] }),
|
|
43808
|
-
|
|
43808
|
+
log10.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
43809
43809
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
43810
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children:
|
|
43810
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: log10.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
43811
43811
|
/* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
43812
43812
|
" ",
|
|
43813
43813
|
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -47517,7 +47517,67 @@ var init_UISlotRenderer = __esm({
|
|
|
47517
47517
|
|
|
47518
47518
|
// hooks/index.ts
|
|
47519
47519
|
init_useEventBus();
|
|
47520
|
-
var log2 = createLogger("almadar:ui:
|
|
47520
|
+
var log2 = createLogger("almadar:ui:shared-entity-store");
|
|
47521
|
+
var EMPTY_ENTITY_STATE = {};
|
|
47522
|
+
function createSharedEntityStore() {
|
|
47523
|
+
const states = /* @__PURE__ */ new Map();
|
|
47524
|
+
const subscribers2 = /* @__PURE__ */ new Map();
|
|
47525
|
+
const getSnapshot3 = (entityId) => states.get(entityId) ?? EMPTY_ENTITY_STATE;
|
|
47526
|
+
const subscribe2 = (entityId, callback) => {
|
|
47527
|
+
let set = subscribers2.get(entityId);
|
|
47528
|
+
if (!set) {
|
|
47529
|
+
set = /* @__PURE__ */ new Set();
|
|
47530
|
+
subscribers2.set(entityId, set);
|
|
47531
|
+
}
|
|
47532
|
+
set.add(callback);
|
|
47533
|
+
return () => {
|
|
47534
|
+
const current = subscribers2.get(entityId);
|
|
47535
|
+
if (!current) return;
|
|
47536
|
+
current.delete(callback);
|
|
47537
|
+
if (current.size === 0) {
|
|
47538
|
+
subscribers2.delete(entityId);
|
|
47539
|
+
}
|
|
47540
|
+
};
|
|
47541
|
+
};
|
|
47542
|
+
const commit = (entityId, nextState) => {
|
|
47543
|
+
states.set(entityId, nextState);
|
|
47544
|
+
const set = subscribers2.get(entityId);
|
|
47545
|
+
if (!set) return;
|
|
47546
|
+
set.forEach((callback) => {
|
|
47547
|
+
try {
|
|
47548
|
+
callback();
|
|
47549
|
+
} catch (error) {
|
|
47550
|
+
log2.error("Shared entity subscriber error", {
|
|
47551
|
+
entityId,
|
|
47552
|
+
error: error instanceof Error ? error : String(error)
|
|
47553
|
+
});
|
|
47554
|
+
}
|
|
47555
|
+
});
|
|
47556
|
+
};
|
|
47557
|
+
const seed = (entityId, initialState) => {
|
|
47558
|
+
if (!states.has(entityId)) {
|
|
47559
|
+
states.set(entityId, initialState);
|
|
47560
|
+
}
|
|
47561
|
+
};
|
|
47562
|
+
return { getSnapshot: getSnapshot3, subscribe: subscribe2, commit, seed };
|
|
47563
|
+
}
|
|
47564
|
+
function useSharedEntityStore() {
|
|
47565
|
+
const storeRef = useRef(null);
|
|
47566
|
+
if (storeRef.current === null) {
|
|
47567
|
+
storeRef.current = createSharedEntityStore();
|
|
47568
|
+
}
|
|
47569
|
+
return storeRef.current;
|
|
47570
|
+
}
|
|
47571
|
+
function runTickFrame(entityId, orderedWriters, store) {
|
|
47572
|
+
let scratch = store.getSnapshot(entityId);
|
|
47573
|
+
for (const writer of orderedWriters) {
|
|
47574
|
+
const writes = writer(scratch);
|
|
47575
|
+
scratch = mergeEntityFrame(scratch, writes);
|
|
47576
|
+
}
|
|
47577
|
+
store.commit(entityId, scratch);
|
|
47578
|
+
return scratch;
|
|
47579
|
+
}
|
|
47580
|
+
var log3 = createLogger("almadar:ui:effects:client-handlers");
|
|
47521
47581
|
function createClientEffectHandlers(options) {
|
|
47522
47582
|
const { eventBus, slotSetter, navigate, notify, callService, liveEntity } = options;
|
|
47523
47583
|
return {
|
|
@@ -47526,12 +47586,12 @@ function createClientEffectHandlers(options) {
|
|
|
47526
47586
|
eventBus.emit(prefixedEvent, payload);
|
|
47527
47587
|
},
|
|
47528
47588
|
persist: async () => {
|
|
47529
|
-
|
|
47589
|
+
log3.warn("persist is server-side only, ignored on client");
|
|
47530
47590
|
},
|
|
47531
47591
|
// @almadar/runtime EffectHandlers.set types value:unknown — should be FieldValue (upstream fix queued)
|
|
47532
47592
|
set: ((_entityId, field, value) => {
|
|
47533
47593
|
if (!liveEntity) {
|
|
47534
|
-
|
|
47594
|
+
log3.warn("set is server-side only, ignored on client (no live entity)");
|
|
47535
47595
|
return;
|
|
47536
47596
|
}
|
|
47537
47597
|
liveEntity[field] = value;
|
|
@@ -47563,10 +47623,10 @@ function createClientEffectHandlers(options) {
|
|
|
47563
47623
|
slotSetter.addPattern(slot, pattern, props);
|
|
47564
47624
|
},
|
|
47565
47625
|
navigate: navigate ?? ((path) => {
|
|
47566
|
-
|
|
47626
|
+
log3.warn("No navigate handler, ignoring", { path });
|
|
47567
47627
|
}),
|
|
47568
47628
|
notify: notify ?? ((msg, type) => {
|
|
47569
|
-
|
|
47629
|
+
log3.debug("notify", { type, message: msg });
|
|
47570
47630
|
})
|
|
47571
47631
|
};
|
|
47572
47632
|
}
|
|
@@ -47737,11 +47797,101 @@ function toTraitDefinition(binding) {
|
|
|
47737
47797
|
function normalizeEventKey(eventKey) {
|
|
47738
47798
|
return eventKey.startsWith("UI:") ? eventKey.slice(3) : eventKey;
|
|
47739
47799
|
}
|
|
47800
|
+
var SHARED_ENTITY_WRITE_OPS = /* @__PURE__ */ new Set(["set"]);
|
|
47801
|
+
var SHARED_ENTITY_RENDER_OPS = /* @__PURE__ */ new Set(["render-ui", "render"]);
|
|
47802
|
+
function collectAllTraitEffects(trait) {
|
|
47803
|
+
return [
|
|
47804
|
+
...trait.ticks.flatMap((t) => t.effects),
|
|
47805
|
+
...trait.transitions.flatMap((t) => t.effects)
|
|
47806
|
+
];
|
|
47807
|
+
}
|
|
47808
|
+
function effectsCallOp(effects, ops) {
|
|
47809
|
+
for (const effect of effects) {
|
|
47810
|
+
let found = false;
|
|
47811
|
+
walkSExpr(effect, (node) => {
|
|
47812
|
+
if (!found && Array.isArray(node) && typeof node[0] === "string" && ops.has(node[0])) {
|
|
47813
|
+
found = true;
|
|
47814
|
+
}
|
|
47815
|
+
});
|
|
47816
|
+
if (found) return true;
|
|
47817
|
+
}
|
|
47818
|
+
return false;
|
|
47819
|
+
}
|
|
47820
|
+
function createSharedEntityWriter(binding, tick, traitStatesRef, emit) {
|
|
47821
|
+
return (scratch) => {
|
|
47822
|
+
const traitName = binding.trait.name;
|
|
47823
|
+
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
47824
|
+
if (tick.appliesTo.length > 0 && !tick.appliesTo.includes(currentState)) return [];
|
|
47825
|
+
const scratchEntity = { ...scratch };
|
|
47826
|
+
const writes = [];
|
|
47827
|
+
const ctx = createMinimalContext(scratchEntity, {}, currentState);
|
|
47828
|
+
const declaredDefaults = collectDeclaredConfigDefaults(binding.trait);
|
|
47829
|
+
const callSiteConfig = binding.config;
|
|
47830
|
+
if (declaredDefaults || callSiteConfig) {
|
|
47831
|
+
ctx.config = { ...declaredDefaults ?? {}, ...callSiteConfig ?? {} };
|
|
47832
|
+
}
|
|
47833
|
+
ctx.mutateEntity = (changes) => {
|
|
47834
|
+
for (const [field, value] of Object.entries(changes)) {
|
|
47835
|
+
const fieldValue = value;
|
|
47836
|
+
scratchEntity[field] = fieldValue;
|
|
47837
|
+
writes.push({ field, value: fieldValue });
|
|
47838
|
+
}
|
|
47839
|
+
};
|
|
47840
|
+
ctx.emit = (event, payload) => {
|
|
47841
|
+
emit(event, payload);
|
|
47842
|
+
};
|
|
47843
|
+
if (tick.guard !== void 0 && !evaluateGuard(tick.guard, ctx)) {
|
|
47844
|
+
tickLog.debug("guard-blocked", { traitName, tick: tick.name, state: currentState });
|
|
47845
|
+
return [];
|
|
47846
|
+
}
|
|
47847
|
+
executeEffects(tick.effects, ctx);
|
|
47848
|
+
return writes;
|
|
47849
|
+
};
|
|
47850
|
+
}
|
|
47740
47851
|
function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
47741
47852
|
const eventBus = useEventBus();
|
|
47742
47853
|
const { entities } = useEntitySchema();
|
|
47743
47854
|
const traitConfigsByName = options?.traitConfigsByName;
|
|
47744
47855
|
const orbitalsByTrait = options?.orbitalsByTrait;
|
|
47856
|
+
const sharedEntityStore = useSharedEntityStore();
|
|
47857
|
+
const sharedGroups = useMemo(() => {
|
|
47858
|
+
const groups = /* @__PURE__ */ new Map();
|
|
47859
|
+
for (const binding of traitBindings) {
|
|
47860
|
+
const linkedEntityName = binding.linkedEntity ?? binding.trait.linkedEntity;
|
|
47861
|
+
if (!linkedEntityName) continue;
|
|
47862
|
+
const entityDef = entities.get(linkedEntityName);
|
|
47863
|
+
if (!entityDef?.shared) continue;
|
|
47864
|
+
const orbitalName = orbitalsByTrait?.[binding.trait.name] ?? "";
|
|
47865
|
+
const storeKey = `${orbitalName}::${linkedEntityName}`;
|
|
47866
|
+
let group = groups.get(storeKey);
|
|
47867
|
+
if (!group) {
|
|
47868
|
+
let defaults;
|
|
47869
|
+
for (const field of entityDef.fields) {
|
|
47870
|
+
if (field.default !== void 0) {
|
|
47871
|
+
(defaults ?? (defaults = {}))[field.name] = field.default;
|
|
47872
|
+
}
|
|
47873
|
+
}
|
|
47874
|
+
group = { storeKey, entityName: linkedEntityName, writerBindings: [], renderBindings: [], defaults };
|
|
47875
|
+
groups.set(storeKey, group);
|
|
47876
|
+
}
|
|
47877
|
+
const allEffects = collectAllTraitEffects(binding.trait);
|
|
47878
|
+
if (effectsCallOp(allEffects, SHARED_ENTITY_WRITE_OPS)) group.writerBindings.push(binding);
|
|
47879
|
+
if (effectsCallOp(allEffects, SHARED_ENTITY_RENDER_OPS)) group.renderBindings.push(binding);
|
|
47880
|
+
}
|
|
47881
|
+
return groups;
|
|
47882
|
+
}, [traitBindings, entities, orbitalsByTrait]);
|
|
47883
|
+
for (const group of sharedGroups.values()) {
|
|
47884
|
+
if (group.defaults) sharedEntityStore.seed(group.storeKey, group.defaults);
|
|
47885
|
+
}
|
|
47886
|
+
const sharedKeyByTraitNameRef = useRef(/* @__PURE__ */ new Map());
|
|
47887
|
+
useEffect(() => {
|
|
47888
|
+
const map = /* @__PURE__ */ new Map();
|
|
47889
|
+
for (const group of sharedGroups.values()) {
|
|
47890
|
+
for (const binding of group.writerBindings) map.set(binding.trait.name, group.storeKey);
|
|
47891
|
+
for (const binding of group.renderBindings) map.set(binding.trait.name, group.storeKey);
|
|
47892
|
+
}
|
|
47893
|
+
sharedKeyByTraitNameRef.current = map;
|
|
47894
|
+
}, [sharedGroups]);
|
|
47745
47895
|
const manager = useMemo(() => {
|
|
47746
47896
|
const traitDefs = traitBindings.map(toTraitDefinition);
|
|
47747
47897
|
const m = new StateMachineManager(traitDefs);
|
|
@@ -47924,14 +48074,21 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
47924
48074
|
};
|
|
47925
48075
|
}, [traitBindings]);
|
|
47926
48076
|
const executeTransitionEffects = useCallback(async (params) => {
|
|
47927
|
-
const { binding, previousState, newState, payload, flushEvent, syncOnly, log:
|
|
48077
|
+
const { binding, previousState, newState, payload, flushEvent, syncOnly, log: log10 } = params;
|
|
47928
48078
|
const traitName = binding.trait.name;
|
|
47929
48079
|
const linkedEntity = binding.linkedEntity || "";
|
|
47930
48080
|
const entityId = payload?.entityId;
|
|
47931
|
-
|
|
47932
|
-
|
|
47933
|
-
|
|
47934
|
-
|
|
48081
|
+
const sharedKey = sharedKeyByTraitNameRef.current.get(traitName);
|
|
48082
|
+
let liveEntity;
|
|
48083
|
+
if (sharedKey !== void 0) {
|
|
48084
|
+
liveEntity = { ...sharedEntityStore.getSnapshot(sharedKey) };
|
|
48085
|
+
} else {
|
|
48086
|
+
let existing = traitFieldStatesRef.current.get(traitName);
|
|
48087
|
+
if (!existing) {
|
|
48088
|
+
existing = {};
|
|
48089
|
+
traitFieldStatesRef.current.set(traitName, existing);
|
|
48090
|
+
}
|
|
48091
|
+
liveEntity = existing;
|
|
47935
48092
|
}
|
|
47936
48093
|
const effects = syncOnly ? params.effects.filter(
|
|
47937
48094
|
(e) => Array.isArray(e) && SYNC_TICK_OPERATORS.has(String(e[0]))
|
|
@@ -48013,7 +48170,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
48013
48170
|
...handlers,
|
|
48014
48171
|
set: async (targetId, field, value) => {
|
|
48015
48172
|
if (baseSet) await baseSet(targetId, field, value);
|
|
48016
|
-
|
|
48173
|
+
log10.debug("set:write", {
|
|
48017
48174
|
traitName,
|
|
48018
48175
|
field,
|
|
48019
48176
|
value: JSON.stringify(value),
|
|
@@ -48053,17 +48210,20 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
48053
48210
|
const executor = new EffectExecutor({ handlers: trackingHandlers, bindings: bindingCtx, context: effectContext });
|
|
48054
48211
|
try {
|
|
48055
48212
|
await executor.executeAll(effects);
|
|
48056
|
-
|
|
48213
|
+
if (sharedKey !== void 0) {
|
|
48214
|
+
sharedEntityStore.commit(sharedKey, liveEntity);
|
|
48215
|
+
}
|
|
48216
|
+
log10.debug("effects:executed", () => ({
|
|
48057
48217
|
traitName,
|
|
48058
48218
|
transition: `${previousState}->${newState}`,
|
|
48059
48219
|
event: flushEvent,
|
|
48060
48220
|
effectCount: effects.length,
|
|
48061
48221
|
emitted: emittedDuringExec.join(","),
|
|
48062
|
-
entityAfter: JSON.stringify(
|
|
48222
|
+
entityAfter: JSON.stringify(liveEntity ?? {}),
|
|
48063
48223
|
slotsTouched: Array.from(pendingSlots.keys()).join(",")
|
|
48064
48224
|
}));
|
|
48065
48225
|
for (const [slot, patterns] of pendingSlots) {
|
|
48066
|
-
|
|
48226
|
+
log10.debug("flush:slot", {
|
|
48067
48227
|
traitName,
|
|
48068
48228
|
slot,
|
|
48069
48229
|
patternCount: patterns.length,
|
|
@@ -48078,7 +48238,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
48078
48238
|
});
|
|
48079
48239
|
}
|
|
48080
48240
|
} catch (error) {
|
|
48081
|
-
|
|
48241
|
+
log10.error("effects:error", {
|
|
48082
48242
|
traitName,
|
|
48083
48243
|
transition: `${previousState}->${newState}`,
|
|
48084
48244
|
event: flushEvent,
|
|
@@ -48087,14 +48247,16 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
48087
48247
|
});
|
|
48088
48248
|
}
|
|
48089
48249
|
return emittedDuringExec;
|
|
48090
|
-
}, [eventBus, flushSlot]);
|
|
48250
|
+
}, [eventBus, flushSlot, sharedEntityStore]);
|
|
48091
48251
|
const runTickEffects = useCallback((tick, binding) => {
|
|
48092
48252
|
const traitName = binding.trait.name;
|
|
48093
48253
|
const currentState = traitStatesRef.current.get(traitName)?.currentState ?? "";
|
|
48094
48254
|
if (tick.appliesTo.length > 0 && !tick.appliesTo.includes(currentState)) return;
|
|
48095
48255
|
if (tick.guard !== void 0) {
|
|
48256
|
+
const sharedKey = sharedKeyByTraitNameRef.current.get(traitName);
|
|
48257
|
+
const entity = sharedKey !== void 0 ? sharedEntityStore.getSnapshot(sharedKey) : traitFieldStatesRef.current.get(traitName) ?? {};
|
|
48096
48258
|
const guardCtx = {
|
|
48097
|
-
entity
|
|
48259
|
+
entity,
|
|
48098
48260
|
payload: {},
|
|
48099
48261
|
state: currentState
|
|
48100
48262
|
};
|
|
@@ -48119,10 +48281,46 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
48119
48281
|
syncOnly: true,
|
|
48120
48282
|
log: tickLog
|
|
48121
48283
|
});
|
|
48122
|
-
}, [executeTransitionEffects]);
|
|
48284
|
+
}, [executeTransitionEffects, sharedEntityStore]);
|
|
48285
|
+
const emitFromSharedWriter = useCallback((event, payload) => {
|
|
48286
|
+
const prefixedEvent = event.startsWith("UI:") ? event : `UI:${event}`;
|
|
48287
|
+
eventBus.emit(prefixedEvent, payload);
|
|
48288
|
+
}, [eventBus]);
|
|
48123
48289
|
useEffect(() => {
|
|
48124
48290
|
const scheduler = createTickScheduler();
|
|
48291
|
+
const writerTraitNames = /* @__PURE__ */ new Set();
|
|
48292
|
+
for (const group of sharedGroups.values()) {
|
|
48293
|
+
const ticksByInterval = /* @__PURE__ */ new Map();
|
|
48294
|
+
for (const binding of group.writerBindings) {
|
|
48295
|
+
writerTraitNames.add(binding.trait.name);
|
|
48296
|
+
for (const tick of binding.trait.ticks ?? []) {
|
|
48297
|
+
const intervalKey = String(tick.interval);
|
|
48298
|
+
const entries = ticksByInterval.get(intervalKey) ?? [];
|
|
48299
|
+
entries.push({ binding, tick });
|
|
48300
|
+
ticksByInterval.set(intervalKey, entries);
|
|
48301
|
+
}
|
|
48302
|
+
}
|
|
48303
|
+
for (const entries of ticksByInterval.values()) {
|
|
48304
|
+
const interval = entries[0].tick.interval;
|
|
48305
|
+
const onDue = () => {
|
|
48306
|
+
const writers = entries.map(
|
|
48307
|
+
({ binding, tick }) => createSharedEntityWriter(binding, tick, traitStatesRef, emitFromSharedWriter)
|
|
48308
|
+
);
|
|
48309
|
+
runTickFrame(group.storeKey, writers, sharedEntityStore);
|
|
48310
|
+
};
|
|
48311
|
+
if (interval === "frame") {
|
|
48312
|
+
scheduler.add(0, onDue);
|
|
48313
|
+
} else if (typeof interval === "number") {
|
|
48314
|
+
scheduler.add(interval, onDue);
|
|
48315
|
+
} else if (isValidCronExpression(interval)) {
|
|
48316
|
+
scheduler.addCron(interval, onDue);
|
|
48317
|
+
} else {
|
|
48318
|
+
scheduler.add(parseDurationString(interval), onDue);
|
|
48319
|
+
}
|
|
48320
|
+
}
|
|
48321
|
+
}
|
|
48125
48322
|
for (const binding of traitBindings) {
|
|
48323
|
+
if (writerTraitNames.has(binding.trait.name)) continue;
|
|
48126
48324
|
for (const tick of binding.trait.ticks ?? []) {
|
|
48127
48325
|
if (tick.interval === "frame") {
|
|
48128
48326
|
scheduler.add(0, () => runTickEffects(tick, binding));
|
|
@@ -48136,7 +48334,7 @@ function useTraitStateMachine(traitBindings, uiSlots, options) {
|
|
|
48136
48334
|
}
|
|
48137
48335
|
}
|
|
48138
48336
|
return () => scheduler.stopAll();
|
|
48139
|
-
}, [traitBindings, runTickEffects]);
|
|
48337
|
+
}, [traitBindings, runTickEffects, sharedGroups, sharedEntityStore, emitFromSharedWriter]);
|
|
48140
48338
|
const processEventQueued = useCallback(async (eventKey, payload) => {
|
|
48141
48339
|
const normalizedEvent = normalizeEventKey(eventKey);
|
|
48142
48340
|
const bindings = traitBindingsRef.current;
|
|
@@ -48656,7 +48854,7 @@ function buildOrbitalsByTrait(schema, resolvedPages = []) {
|
|
|
48656
48854
|
// runtime/OrbPreview.tsx
|
|
48657
48855
|
init_verificationRegistry();
|
|
48658
48856
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
48659
|
-
var
|
|
48857
|
+
var log9 = createLogger(PERF_NAMESPACE);
|
|
48660
48858
|
var RING_SIZE = 50;
|
|
48661
48859
|
var ring = [];
|
|
48662
48860
|
var writeIdx = 0;
|
|
@@ -48711,7 +48909,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
48711
48909
|
}
|
|
48712
48910
|
}
|
|
48713
48911
|
push({ name, durationMs, ts: endTs, detail });
|
|
48714
|
-
|
|
48912
|
+
log9.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
48715
48913
|
}
|
|
48716
48914
|
function perfTime(name, fn, detail) {
|
|
48717
48915
|
const t = perfStart(name);
|
|
@@ -48729,7 +48927,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
48729
48927
|
ts: commitTime,
|
|
48730
48928
|
detail: { baseDuration }
|
|
48731
48929
|
});
|
|
48732
|
-
|
|
48930
|
+
log9.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
48733
48931
|
};
|
|
48734
48932
|
function getPerfSnapshot() {
|
|
48735
48933
|
if (ring.length < RING_SIZE) return ring.slice();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almadar/ui",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.103.0",
|
|
4
4
|
"description": "React UI components, hooks, and providers for Almadar",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"lint": "eslint --no-warn-ignored --max-warnings 0 ."
|
|
124
124
|
},
|
|
125
125
|
"dependencies": {
|
|
126
|
-
"@almadar/core": "^10.
|
|
126
|
+
"@almadar/core": "^10.16.0",
|
|
127
127
|
"@almadar/evaluator": ">=2.9.2",
|
|
128
128
|
"@almadar/logger": "^1.3.0",
|
|
129
129
|
"@almadar/patterns": ">=2.35.0",
|