@almadar/ui 6.59.0 → 6.62.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/BehaviorValueFrame-3ORZHPWQ.css +1252 -0
- package/dist/BehaviorValueFrame-QYFJEMN5.js +18 -0
- package/dist/TraitValueFrame-2WMYJOA5.css +1252 -0
- package/dist/TraitValueFrame-X6IL3UST.js +17 -0
- package/dist/Typography-KBATMU2V.js +5 -0
- package/dist/{UISlotRenderer-YNY7V2UJ.js → UISlotRenderer-NZBV5BUI.js} +12 -10
- package/dist/avl/index.cjs +32263 -28103
- package/dist/avl/index.d.ts +36 -13
- package/dist/avl/index.js +298 -6130
- package/dist/{avl-3d-context-BpBKZIbc.d.ts → avl-3d-context-Dxun3-ri.d.ts} +2 -0
- package/dist/{chunk-YYYHH5BA.js → chunk-7JRWELUC.js} +4 -6
- package/dist/{chunk-723MJUU3.js → chunk-CXQENFO2.js} +1 -1
- package/dist/chunk-D4NAA2JW.js +49 -0
- package/dist/{chunk-I5QOYW2Z.js → chunk-DXPCAQJU.js} +205 -2
- package/dist/{chunk-J755QADM.js → chunk-EDPUQ2ZJ.js} +327 -18
- package/dist/{chunk-4ZGXIW3B.js → chunk-ERDYADVZ.js} +1 -1
- package/dist/{chunk-7CVXM2KS.js → chunk-EZ5FSNOE.js} +2 -2
- package/dist/{chunk-E6MWYQVZ.js → chunk-G6EFTACU.js} +9713 -786
- package/dist/{chunk-MZIDOJMD.js → chunk-GVQOTA7T.js} +2 -1
- package/dist/{chunk-Q47ZN2DW.js → chunk-N23SVGTH.js} +83 -1
- package/dist/{chunk-KJGVLXEN.js → chunk-N4IJS5DD.js} +50 -116
- package/dist/{chunk-LZRW6GXG.js → chunk-QOV3TUAA.js} +2 -3
- package/dist/chunk-WBWXBA5H.js +434 -0
- package/dist/{chunk-V3FGTQUE.js → chunk-ZR45FGW3.js} +7 -1
- package/dist/{cn-DIql5232.d.ts → cn-CDIhHCUC.d.ts} +40 -16
- package/dist/components/index.cjs +14779 -4354
- package/dist/components/index.d.ts +200 -180
- package/dist/components/index.js +16 -17
- package/dist/context/index.cjs +9 -1
- package/dist/context/index.js +3 -1
- package/dist/hooks/index.cjs +78 -9
- package/dist/hooks/index.d.ts +43 -2
- package/dist/hooks/index.js +6 -7
- package/dist/lib/drawable/three/index.cjs +361 -27
- package/dist/lib/drawable/three/index.d.ts +2 -2
- package/dist/lib/drawable/three/index.js +28 -7
- package/dist/lib/index.cjs +117 -5
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.js +99 -6
- package/dist/locales/index.cjs +6 -9
- package/dist/locales/index.js +2 -2
- package/dist/providers/index.cjs +14137 -1311
- package/dist/providers/index.d.ts +36 -4
- package/dist/providers/index.js +37 -17
- package/dist/runtime/index.cjs +28769 -18640
- package/dist/runtime/index.d.ts +70 -3
- package/dist/runtime/index.js +15 -381
- package/dist/ssr/index.cjs +508 -134
- package/dist/ssr/index.d.cts +56 -4
- package/dist/ssr/index.d.ts +56 -4
- package/dist/ssr/index.js +446 -72
- package/dist/{verificationRegistry-B3dj1Zl8.d.ts → verificationRegistry-B5aGl7WF.d.ts} +2 -1
- package/locales/ar.json +2 -3
- package/locales/en.json +2 -3
- package/locales/sl.json +2 -3
- package/package.json +8 -8
- package/tailwind-preset.cjs +78 -8
- package/themes/_base.css +27 -0
- package/themes/_contract.md +6 -0
- package/themes/_fonts.css +4 -4
- package/themes/_foundation.css +11 -0
- package/themes/_presets.css +2 -0
- package/themes/almadar-website.css +10 -0
- package/themes/almadar.css +10 -0
- package/themes/atelier.css +10 -0
- package/themes/bauhaus.css +10 -0
- package/themes/bloomberg-dense.css +10 -0
- package/themes/clay.css +10 -0
- package/themes/corporate.css +10 -0
- package/themes/gazette.css +10 -0
- package/themes/glass.css +10 -0
- package/themes/kflow.css +5 -0
- package/themes/kiosk.css +10 -0
- package/themes/linear-clean.css +10 -0
- package/themes/material.css +10 -0
- package/themes/minimalist.css +10 -0
- package/themes/nata.css +304 -0
- package/themes/neon.css +10 -0
- package/themes/notion-editorial.css +10 -0
- package/themes/orb.css +10 -0
- package/themes/prism.css +10 -0
- package/themes/rawi.css +304 -0
- package/themes/retro.css +10 -0
- package/themes/risograph.css +10 -0
- package/themes/scandi.css +10 -0
- package/themes/solarpunk.css +10 -0
- package/themes/terminal.css +138 -128
- package/dist/Typography-LZTP72Y4.js +0 -5
- package/dist/chunk-D2E44ODB.js +0 -350
- package/dist/chunk-N7JIG57B.js +0 -138
- package/dist/chunk-QJIAJ6EH.js +0 -2256
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { LoadingState, ErrorState, UISlotComponent } from './chunk-G6EFTACU.js';
|
|
2
|
+
import { useEventBus } from './chunk-CHNC4UYE.js';
|
|
3
|
+
import { createContext, useMemo, useState, useRef, useCallback, useContext, useEffect } from 'react';
|
|
4
|
+
import { UISlotProvider, useUISlots } from '@almadar/ui/context';
|
|
5
|
+
import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
|
|
6
|
+
import { UI_SLOTS } from '@almadar/core';
|
|
7
|
+
import { createLogger } from '@almadar/logger';
|
|
8
|
+
import { useTranslate } from '@almadar/ui/hooks';
|
|
9
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
var log = createLogger("almadar:ui:plugin-host");
|
|
12
|
+
var UI_SLOT_SET = new Set(UI_SLOTS);
|
|
13
|
+
function isUISlot(value) {
|
|
14
|
+
return UI_SLOT_SET.has(value);
|
|
15
|
+
}
|
|
16
|
+
var OrbitalPluginHostContext = createContext(null);
|
|
17
|
+
function useOrbitalPluginHostContext(caller) {
|
|
18
|
+
const ctx = useContext(OrbitalPluginHostContext);
|
|
19
|
+
if (!ctx) {
|
|
20
|
+
throw new Error(`${caller} must be used within an <OrbitalPluginHost>.`);
|
|
21
|
+
}
|
|
22
|
+
return ctx;
|
|
23
|
+
}
|
|
24
|
+
function isTraitState(value) {
|
|
25
|
+
return value !== void 0 && typeof value.currentState === "string";
|
|
26
|
+
}
|
|
27
|
+
function toTraitState(runtime, orbital, trait, stateName, lastEvent) {
|
|
28
|
+
if (runtime) {
|
|
29
|
+
const raw = runtime.getState(orbital, trait);
|
|
30
|
+
if (isTraitState(raw)) return raw;
|
|
31
|
+
}
|
|
32
|
+
return { traitName: trait, currentState: stateName, previousState: null, lastEvent, context: {} };
|
|
33
|
+
}
|
|
34
|
+
function isKeymapValue(value) {
|
|
35
|
+
if (value === null || value === void 0 || typeof value !== "object" || Array.isArray(value)) return false;
|
|
36
|
+
return Object.values(value).every((entry) => Array.isArray(entry) && entry.every((k) => typeof k === "string"));
|
|
37
|
+
}
|
|
38
|
+
function findInlineTrait(schema, orbitalName, traitName) {
|
|
39
|
+
const orbital = schema.orbitals.find((o) => o.name === orbitalName);
|
|
40
|
+
if (!orbital) return void 0;
|
|
41
|
+
for (const ref of orbital.traits) {
|
|
42
|
+
if (typeof ref === "string") continue;
|
|
43
|
+
if ("ref" in ref) continue;
|
|
44
|
+
if (ref.name === traitName) return ref;
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
function readKeymapConfig(trait, knob) {
|
|
49
|
+
const declared = trait?.config?.[knob]?.default;
|
|
50
|
+
return isKeymapValue(declared) ? declared : {};
|
|
51
|
+
}
|
|
52
|
+
function traitNamesOf(orbital) {
|
|
53
|
+
const names = [];
|
|
54
|
+
for (const ref of orbital.traits) {
|
|
55
|
+
if (typeof ref === "string") {
|
|
56
|
+
names.push(ref);
|
|
57
|
+
} else if ("ref" in ref) {
|
|
58
|
+
if (ref.name) names.push(ref.name);
|
|
59
|
+
} else {
|
|
60
|
+
names.push(ref.name);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return names;
|
|
64
|
+
}
|
|
65
|
+
function useDeclaredCaptureTable(opts) {
|
|
66
|
+
const { pluginId, orbital, trait, keymapKnob = "keymap", target = "shell" } = opts;
|
|
67
|
+
const ctx = useOrbitalPluginHostContext("useDeclaredCaptureTable");
|
|
68
|
+
return useMemo(() => {
|
|
69
|
+
const schema = ctx.getPluginSchema(pluginId);
|
|
70
|
+
const inlineTrait = schema ? findInlineTrait(schema, orbital, trait) : void 0;
|
|
71
|
+
const keymap = readKeymapConfig(inlineTrait, keymapKnob);
|
|
72
|
+
const currentState = ctx.getState(pluginId, orbital, trait)?.currentState;
|
|
73
|
+
const keys = currentState !== void 0 ? keymap[currentState] ?? [] : [];
|
|
74
|
+
const entry = { mode: currentState ?? "unknown", keys: new Set(keys) };
|
|
75
|
+
const targets = Array.isArray(target) ? target : [target];
|
|
76
|
+
const table = {};
|
|
77
|
+
for (const t of targets) table[t] = entry;
|
|
78
|
+
return table;
|
|
79
|
+
}, [ctx, pluginId, orbital, trait, keymapKnob, target]);
|
|
80
|
+
}
|
|
81
|
+
function useOrbitalPluginHost() {
|
|
82
|
+
const ctx = useOrbitalPluginHostContext("useOrbitalPluginHost");
|
|
83
|
+
return { getState: ctx.getState, lastEvent: ctx.lastEvent, errors: ctx.errors };
|
|
84
|
+
}
|
|
85
|
+
function buildMockEffectHandlers(opts) {
|
|
86
|
+
const { pluginId, denySet, slotsRef, renderedSlotsRef, navigateRef } = opts;
|
|
87
|
+
const handlers = {
|
|
88
|
+
renderUI: (slot, pattern, props, priority) => {
|
|
89
|
+
if (!isUISlot(slot)) {
|
|
90
|
+
log.warn("renderUI:unknown-slot", { pluginId, slot });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (pattern === null) {
|
|
94
|
+
slotsRef.current.clearBySource(slot, pluginId);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
renderedSlotsRef.current.add(slot);
|
|
98
|
+
const { type, ...inline } = pattern;
|
|
99
|
+
slotsRef.current.render({
|
|
100
|
+
target: slot,
|
|
101
|
+
pattern: type,
|
|
102
|
+
props: { ...inline, ...props ?? {} },
|
|
103
|
+
priority,
|
|
104
|
+
sourceTrait: pluginId
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
if (denySet.has("navigate")) {
|
|
109
|
+
handlers.navigate = (path) => log.warn("navigate:denied", { pluginId, path });
|
|
110
|
+
} else if (navigateRef.current) {
|
|
111
|
+
handlers.navigate = (path) => navigateRef.current?.(path);
|
|
112
|
+
}
|
|
113
|
+
if (denySet.has("persist")) {
|
|
114
|
+
handlers.persist = async (action, entityType) => {
|
|
115
|
+
log.warn("persist:denied", { pluginId, action, entityType });
|
|
116
|
+
return void 0;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (denySet.has("call-service")) {
|
|
120
|
+
handlers.callService = async (service, action) => {
|
|
121
|
+
log.warn("call-service:denied", { pluginId, service, action });
|
|
122
|
+
return null;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return handlers;
|
|
126
|
+
}
|
|
127
|
+
function PluginRuntimeMount({
|
|
128
|
+
plugin,
|
|
129
|
+
mode,
|
|
130
|
+
transport,
|
|
131
|
+
deny,
|
|
132
|
+
navigate,
|
|
133
|
+
onTransition,
|
|
134
|
+
onDispatched,
|
|
135
|
+
onError
|
|
136
|
+
}) {
|
|
137
|
+
const bus = useEventBus();
|
|
138
|
+
const { locale } = useTranslate();
|
|
139
|
+
const slots = useUISlots();
|
|
140
|
+
const slotsRef = useRef(slots);
|
|
141
|
+
slotsRef.current = slots;
|
|
142
|
+
const renderedSlotsRef = useRef(/* @__PURE__ */ new Set());
|
|
143
|
+
const navigateRef = useRef(navigate);
|
|
144
|
+
navigateRef.current = navigate;
|
|
145
|
+
const [mockRuntime] = useState(() => {
|
|
146
|
+
if (mode !== "mock") return void 0;
|
|
147
|
+
return new OrbitalServerRuntime({
|
|
148
|
+
mode: "mock",
|
|
149
|
+
debug: false,
|
|
150
|
+
effectHandlers: buildMockEffectHandlers({
|
|
151
|
+
pluginId: plugin.id,
|
|
152
|
+
denySet: new Set(deny ?? []),
|
|
153
|
+
slotsRef,
|
|
154
|
+
renderedSlotsRef,
|
|
155
|
+
navigateRef
|
|
156
|
+
})
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
const registrationReady = useMemo(() => {
|
|
160
|
+
if (mockRuntime) return mockRuntime.register(plugin.schema);
|
|
161
|
+
if (mode === "server" && transport) return transport.register(plugin.schema);
|
|
162
|
+
return Promise.resolve();
|
|
163
|
+
}, [mockRuntime, transport, mode, plugin.schema]);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (mode === "server" && !transport) {
|
|
166
|
+
onError(plugin.id, 'OrbitalPluginHost: mode "server" requires a transport');
|
|
167
|
+
}
|
|
168
|
+
}, [mode, transport, plugin.id, onError]);
|
|
169
|
+
const teardownTimerRef = useRef(null);
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if (teardownTimerRef.current !== null) {
|
|
172
|
+
clearTimeout(teardownTimerRef.current);
|
|
173
|
+
teardownTimerRef.current = null;
|
|
174
|
+
}
|
|
175
|
+
return () => {
|
|
176
|
+
teardownTimerRef.current = setTimeout(() => {
|
|
177
|
+
teardownTimerRef.current = null;
|
|
178
|
+
if (mockRuntime) {
|
|
179
|
+
mockRuntime.unregisterAll();
|
|
180
|
+
} else if (mode === "server" && transport) {
|
|
181
|
+
void transport.unregister();
|
|
182
|
+
}
|
|
183
|
+
for (const slot of renderedSlotsRef.current) {
|
|
184
|
+
slotsRef.current.clearBySource(slot, plugin.id);
|
|
185
|
+
}
|
|
186
|
+
}, 0);
|
|
187
|
+
};
|
|
188
|
+
}, [mockRuntime, transport, mode, plugin.id]);
|
|
189
|
+
const ownOrbitals = useMemo(
|
|
190
|
+
() => new Set(plugin.schema.orbitals.map((o) => o.name)),
|
|
191
|
+
[plugin.schema]
|
|
192
|
+
);
|
|
193
|
+
const inboundBusEvents = useMemo(
|
|
194
|
+
() => new Set(plugin.inbound.map((row) => row.busEvent)),
|
|
195
|
+
[plugin.inbound]
|
|
196
|
+
);
|
|
197
|
+
const inboundTriggerNames = useMemo(
|
|
198
|
+
() => new Set(plugin.inbound.map((row) => row.trigger)),
|
|
199
|
+
[plugin.inbound]
|
|
200
|
+
);
|
|
201
|
+
const snapshotAllStates = useCallback(() => {
|
|
202
|
+
const states = [];
|
|
203
|
+
if (!mockRuntime) return states;
|
|
204
|
+
for (const orbital of plugin.schema.orbitals) {
|
|
205
|
+
for (const traitName of traitNamesOf(orbital)) {
|
|
206
|
+
const raw = mockRuntime.getState(orbital.name, traitName);
|
|
207
|
+
if (isTraitState(raw)) states.push([orbital.name, traitName, raw]);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return states;
|
|
211
|
+
}, [mockRuntime, plugin.schema]);
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
let cancelled = false;
|
|
214
|
+
void registrationReady.then(() => {
|
|
215
|
+
if (cancelled || !mockRuntime) return;
|
|
216
|
+
const states = snapshotAllStates();
|
|
217
|
+
if (states.length > 0) onDispatched(plugin.id, null, states);
|
|
218
|
+
}).catch((err) => {
|
|
219
|
+
if (!cancelled) onError(plugin.id, err instanceof Error ? err.message : String(err));
|
|
220
|
+
});
|
|
221
|
+
return () => {
|
|
222
|
+
cancelled = true;
|
|
223
|
+
};
|
|
224
|
+
}, [registrationReady, mockRuntime, plugin, onDispatched, onError, snapshotAllStates]);
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (!mockRuntime) return;
|
|
227
|
+
let timer = null;
|
|
228
|
+
const scheduleResync = () => {
|
|
229
|
+
if (timer !== null) return;
|
|
230
|
+
timer = setTimeout(() => {
|
|
231
|
+
timer = null;
|
|
232
|
+
const states = snapshotAllStates();
|
|
233
|
+
if (states.length > 0) onDispatched(plugin.id, null, states);
|
|
234
|
+
}, 0);
|
|
235
|
+
};
|
|
236
|
+
const unsub = mockRuntime.getEventBus().onAny(scheduleResync);
|
|
237
|
+
return () => {
|
|
238
|
+
unsub();
|
|
239
|
+
if (timer !== null) clearTimeout(timer);
|
|
240
|
+
};
|
|
241
|
+
}, [mockRuntime, plugin.id, onDispatched, snapshotAllStates]);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
if (!mockRuntime) return;
|
|
244
|
+
const rebroadcast = /* @__PURE__ */ new WeakSet();
|
|
245
|
+
const unsub = mockRuntime.getEventBus().onAny((event) => {
|
|
246
|
+
if (rebroadcast.has(event)) return;
|
|
247
|
+
if (inboundBusEvents.has(event.type) || inboundTriggerNames.has(event.type)) return;
|
|
248
|
+
const sourceOrbital = event.source?.orbital;
|
|
249
|
+
if (sourceOrbital === void 0 || !ownOrbitals.has(sourceOrbital)) return;
|
|
250
|
+
rebroadcast.add(event);
|
|
251
|
+
bus.emit(`UI:${event.type}`, event.payload, event.source);
|
|
252
|
+
});
|
|
253
|
+
return () => {
|
|
254
|
+
unsub();
|
|
255
|
+
};
|
|
256
|
+
}, [mockRuntime, bus, ownOrbitals, inboundBusEvents, inboundTriggerNames]);
|
|
257
|
+
const dispatch = useCallback(
|
|
258
|
+
async (row, payload) => {
|
|
259
|
+
await registrationReady;
|
|
260
|
+
let response;
|
|
261
|
+
if (mockRuntime) {
|
|
262
|
+
response = await mockRuntime.processOrbitalEvent(row.orbital, {
|
|
263
|
+
event: row.trigger,
|
|
264
|
+
payload,
|
|
265
|
+
targetTrait: row.trait,
|
|
266
|
+
locale
|
|
267
|
+
});
|
|
268
|
+
} else if (mode === "server" && transport) {
|
|
269
|
+
response = await transport.send(row.orbital, { event: row.trigger, payload, locale });
|
|
270
|
+
} else {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (!response.success) {
|
|
274
|
+
onError(plugin.id, response.error ?? `${plugin.id}: ${row.trigger} failed`);
|
|
275
|
+
}
|
|
276
|
+
if (!mockRuntime) {
|
|
277
|
+
for (const entry of response.emittedEvents ?? []) {
|
|
278
|
+
if (entry.event === row.trigger) continue;
|
|
279
|
+
if (inboundBusEvents.has(entry.event)) continue;
|
|
280
|
+
const sourceOrbital = entry.source?.orbital;
|
|
281
|
+
if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
|
|
282
|
+
bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const states = [];
|
|
287
|
+
for (const [traitName, stateName] of Object.entries(response.states)) {
|
|
288
|
+
states.push([row.orbital, traitName, toTraitState(mockRuntime, row.orbital, traitName, stateName, row.trigger)]);
|
|
289
|
+
onTransition?.(plugin.id, row.orbital, traitName, stateName);
|
|
290
|
+
}
|
|
291
|
+
onDispatched(plugin.id, row.trigger, states);
|
|
292
|
+
},
|
|
293
|
+
[registrationReady, mockRuntime, mode, transport, locale, plugin, bus, ownOrbitals, inboundBusEvents, onError, onTransition, onDispatched]
|
|
294
|
+
);
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
const unsubs = plugin.inbound.map(
|
|
297
|
+
(row) => bus.on(`UI:${row.busEvent}`, (event) => {
|
|
298
|
+
void dispatch(row, event.payload).catch((err) => {
|
|
299
|
+
onError(plugin.id, err instanceof Error ? err.message : String(err));
|
|
300
|
+
});
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
return () => {
|
|
304
|
+
for (const unsub of unsubs) unsub();
|
|
305
|
+
};
|
|
306
|
+
}, [bus, plugin, dispatch, onError]);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
function OrbitalPluginHost({
|
|
310
|
+
plugins,
|
|
311
|
+
mode = "mock",
|
|
312
|
+
transport,
|
|
313
|
+
deny,
|
|
314
|
+
navigate,
|
|
315
|
+
onTransition,
|
|
316
|
+
children
|
|
317
|
+
}) {
|
|
318
|
+
const [tick, setTick] = useState(0);
|
|
319
|
+
const [errors, setErrors] = useState({});
|
|
320
|
+
const statesRef = useRef(/* @__PURE__ */ new Map());
|
|
321
|
+
const lastEventRef = useRef(/* @__PURE__ */ new Map());
|
|
322
|
+
const pluginSchemas = useMemo(
|
|
323
|
+
() => new Map(plugins.map((plugin) => [plugin.id, plugin.schema])),
|
|
324
|
+
[plugins]
|
|
325
|
+
);
|
|
326
|
+
const handleDispatched = useCallback(
|
|
327
|
+
(pluginId, trigger, states) => {
|
|
328
|
+
if (trigger !== null) {
|
|
329
|
+
lastEventRef.current.set(pluginId, trigger);
|
|
330
|
+
}
|
|
331
|
+
let byKey = statesRef.current.get(pluginId);
|
|
332
|
+
if (!byKey) {
|
|
333
|
+
byKey = /* @__PURE__ */ new Map();
|
|
334
|
+
statesRef.current.set(pluginId, byKey);
|
|
335
|
+
}
|
|
336
|
+
for (const [orbital, trait, state] of states) {
|
|
337
|
+
byKey.set(`${orbital}.${trait}`, state);
|
|
338
|
+
}
|
|
339
|
+
setTick((t) => t + 1);
|
|
340
|
+
},
|
|
341
|
+
[]
|
|
342
|
+
);
|
|
343
|
+
const handleError = useCallback((pluginId, message) => {
|
|
344
|
+
setErrors((prev) => prev[pluginId] === message ? prev : { ...prev, [pluginId]: message });
|
|
345
|
+
}, []);
|
|
346
|
+
const contextValue = useMemo(
|
|
347
|
+
() => ({
|
|
348
|
+
getState: (pluginId, orbital, trait) => statesRef.current.get(pluginId)?.get(`${orbital}.${trait}`),
|
|
349
|
+
lastEvent: (pluginId) => lastEventRef.current.get(pluginId),
|
|
350
|
+
errors,
|
|
351
|
+
tick,
|
|
352
|
+
getPluginSchema: (pluginId) => pluginSchemas.get(pluginId)
|
|
353
|
+
}),
|
|
354
|
+
[errors, tick, pluginSchemas]
|
|
355
|
+
);
|
|
356
|
+
return /* @__PURE__ */ jsxs(OrbitalPluginHostContext.Provider, { value: contextValue, children: [
|
|
357
|
+
plugins.map((plugin) => /* @__PURE__ */ jsx(
|
|
358
|
+
PluginRuntimeMount,
|
|
359
|
+
{
|
|
360
|
+
plugin,
|
|
361
|
+
mode,
|
|
362
|
+
transport,
|
|
363
|
+
deny,
|
|
364
|
+
navigate,
|
|
365
|
+
onTransition,
|
|
366
|
+
onDispatched: handleDispatched,
|
|
367
|
+
onError: handleError
|
|
368
|
+
},
|
|
369
|
+
plugin.id
|
|
370
|
+
)),
|
|
371
|
+
children ?? null
|
|
372
|
+
] });
|
|
373
|
+
}
|
|
374
|
+
var TraitValueMountContext = createContext(null);
|
|
375
|
+
function TraitValueMountProvider({ host, children }) {
|
|
376
|
+
return /* @__PURE__ */ jsx(TraitValueMountContext.Provider, { value: host, children });
|
|
377
|
+
}
|
|
378
|
+
function initEventOf(key) {
|
|
379
|
+
return `TRAIT_VALUE_MOUNTED:${key}`;
|
|
380
|
+
}
|
|
381
|
+
function MountedInit({ event }) {
|
|
382
|
+
const bus = useEventBus();
|
|
383
|
+
useEffect(() => {
|
|
384
|
+
bus.emit(`UI:${event}`, {});
|
|
385
|
+
}, [bus, event]);
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
function TraitValueFrame({ value }) {
|
|
389
|
+
const host = useContext(TraitValueMountContext);
|
|
390
|
+
const key = useMemo(() => JSON.stringify(value), [value]);
|
|
391
|
+
const latest = useRef(value);
|
|
392
|
+
latest.current = value;
|
|
393
|
+
const [state, setState] = useState({ status: "loading" });
|
|
394
|
+
useEffect(() => {
|
|
395
|
+
const current = latest.current;
|
|
396
|
+
if (host === null) {
|
|
397
|
+
setState({ status: "error", message: `No trait-value host is configured to mount ${current.behavior}.traits.${current.trait}` });
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
let live = true;
|
|
401
|
+
setState({ status: "loading" });
|
|
402
|
+
void host.mount(current).then((result) => {
|
|
403
|
+
if (!live) return;
|
|
404
|
+
if (!result.ok) {
|
|
405
|
+
const detail = (result.errors ?? []).map((e) => `${e.code}: ${e.message}`).join("\n");
|
|
406
|
+
setState({ status: "error", message: detail.length > 0 ? `${result.error}
|
|
407
|
+
${detail}` : result.error });
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const initEvent = initEventOf(key);
|
|
411
|
+
setState({
|
|
412
|
+
status: "ready",
|
|
413
|
+
initEvent,
|
|
414
|
+
plugin: {
|
|
415
|
+
id: key,
|
|
416
|
+
schema: result.mounted.schema,
|
|
417
|
+
inbound: [{ busEvent: initEvent, orbital: result.mounted.orbital, trait: result.mounted.trait, trigger: "INIT" }]
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
return () => {
|
|
422
|
+
live = false;
|
|
423
|
+
};
|
|
424
|
+
}, [host, key]);
|
|
425
|
+
if (state.status === "loading") return /* @__PURE__ */ jsx(LoadingState, {});
|
|
426
|
+
if (state.status === "error") return /* @__PURE__ */ jsx(ErrorState, { message: state.message });
|
|
427
|
+
return /* @__PURE__ */ jsx(UISlotProvider, { children: /* @__PURE__ */ jsxs(OrbitalPluginHost, { plugins: [state.plugin], mode: host?.mode ?? "mock", transport: host?.transport, children: [
|
|
428
|
+
/* @__PURE__ */ jsx(MountedInit, { event: state.initEvent }),
|
|
429
|
+
/* @__PURE__ */ jsx(UISlotComponent, { slot: "main" })
|
|
430
|
+
] }) });
|
|
431
|
+
}
|
|
432
|
+
var TraitValueFrame_default = TraitValueFrame;
|
|
433
|
+
|
|
434
|
+
export { OrbitalPluginHost, TraitValueFrame, TraitValueFrame_default, TraitValueMountContext, TraitValueMountProvider, useDeclaredCaptureTable, useOrbitalPluginHost };
|
|
@@ -134,7 +134,13 @@ var SURFACE_VARS = [
|
|
|
134
134
|
{ cssVar: "--surface-card-image", key: "cardImage" },
|
|
135
135
|
{ cssVar: "--surface-page-image", key: "pageImage" },
|
|
136
136
|
{ cssVar: "--surface-page-image-size", key: "pageImageSize" },
|
|
137
|
-
{ cssVar: "--surface-diagram", key: "diagram" }
|
|
137
|
+
{ cssVar: "--surface-diagram", key: "diagram" },
|
|
138
|
+
{ cssVar: "--surface-edge-mask", key: "edgeMask" },
|
|
139
|
+
{ cssVar: "--surface-edge-height", key: "edgeHeight" },
|
|
140
|
+
{ cssVar: "--surface-texture-mask", key: "textureMask" },
|
|
141
|
+
{ cssVar: "--surface-texture-size", key: "textureSize" },
|
|
142
|
+
{ cssVar: "--surface-accent-image", key: "accentImage" },
|
|
143
|
+
{ cssVar: "--surface-scrim", key: "scrim" }
|
|
138
144
|
];
|
|
139
145
|
var DIAGRAM_VARS = [
|
|
140
146
|
{ cssVar: "--diagram-stroke-thin", key: "strokeThin" },
|
|
@@ -135,12 +135,14 @@ declare const BloomQuizBlock: React__default.FC<BloomQuizBlockProps>;
|
|
|
135
135
|
/**
|
|
136
136
|
* CodeRunnerPanel Organism Component
|
|
137
137
|
*
|
|
138
|
-
* Editable code block with Run/Reset buttons and
|
|
139
|
-
*
|
|
140
|
-
*
|
|
138
|
+
* Editable code block with Run/Reset buttons and a terminal output pane.
|
|
139
|
+
* Running is the program's job: Run emits the declared `runEvent` request and the
|
|
140
|
+
* program binds the result back through `output` / `running` / `error`.
|
|
141
141
|
*
|
|
142
142
|
* Event Contract:
|
|
143
|
-
* - Emits: UI
|
|
143
|
+
* - Emits: UI:<runEvent> { code, language, runId? } on Run
|
|
144
|
+
* - Emits: UI:<resetEvent> { runId? } on Reset (when declared)
|
|
145
|
+
* - Emits: UI:COPY_CODE { language, success } on Copy
|
|
144
146
|
* - entityAware: false
|
|
145
147
|
*/
|
|
146
148
|
|
|
@@ -160,16 +162,26 @@ interface CodeRunnerPanelProps extends Omit<React__default.AriaAttributes, keyof
|
|
|
160
162
|
code: string;
|
|
161
163
|
/** Programming language for syntax highlighting */
|
|
162
164
|
language: string;
|
|
163
|
-
/** Whether the panel
|
|
165
|
+
/** Whether the panel is editable and runnable (default false = read-only code block) */
|
|
164
166
|
runnable?: boolean;
|
|
165
|
-
/**
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/** Event
|
|
172
|
-
|
|
167
|
+
/** Event emitted on Run as `UI:<runEvent>` with `{ code, language, runId }`. Defaults to 'RUN_CODE'. */
|
|
168
|
+
runEvent?: EventEmit<{
|
|
169
|
+
code: string;
|
|
170
|
+
language: string;
|
|
171
|
+
runId: string;
|
|
172
|
+
}>;
|
|
173
|
+
/** Event emitted on Reset as `UI:<resetEvent>` with `{ runId }`; Reset only restores the code when absent. */
|
|
174
|
+
resetEvent?: EventEmit<{
|
|
175
|
+
runId: string;
|
|
176
|
+
}>;
|
|
177
|
+
/** Identifies this panel in its run/reset payloads (e.g. a lesson segment); empty when one panel stands alone. */
|
|
178
|
+
runId?: string;
|
|
179
|
+
/** The program's result for the last run, bound back into the panel. */
|
|
180
|
+
output?: CodeSimulationOutput | null;
|
|
181
|
+
/** True while the program is running the code. */
|
|
182
|
+
running?: boolean;
|
|
183
|
+
/** A run failure message from the program; replaces the output body. */
|
|
184
|
+
error?: string | null;
|
|
173
185
|
/** Additional CSS classes */
|
|
174
186
|
className?: string;
|
|
175
187
|
}
|
|
@@ -280,10 +292,22 @@ interface SegmentRendererProps extends Omit<React__default.AriaAttributes, keyof
|
|
|
280
292
|
/** User progress for restoring activation/reflection state */
|
|
281
293
|
userProgress?: LessonUserProgress;
|
|
282
294
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
295
|
+
* Event a runnable code block emits on Run (`{ code, language, runId }`, runId = the segment
|
|
296
|
+
* index). Omit to render runnable blocks read-only.
|
|
285
297
|
*/
|
|
286
|
-
|
|
298
|
+
runCodeEvent?: EventEmit<{
|
|
299
|
+
code: string;
|
|
300
|
+
language: string;
|
|
301
|
+
runId: string;
|
|
302
|
+
}>;
|
|
303
|
+
/** Event a runnable code block emits on Reset (`{ runId }`). */
|
|
304
|
+
resetCodeEvent?: EventEmit<{
|
|
305
|
+
runId: string;
|
|
306
|
+
}>;
|
|
307
|
+
/** The program's run results, keyed by segment runId. */
|
|
308
|
+
codeOutputs?: Record<string, CodeSimulationOutput>;
|
|
309
|
+
/** The runId the program is currently running. */
|
|
310
|
+
runningCode?: string;
|
|
287
311
|
/**
|
|
288
312
|
* Optional render slot for `visualization` segment types. When not provided,
|
|
289
313
|
* visualization segments are silently skipped. Callers can wire this to any
|