@almadar/ui 6.7.0 → 6.8.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.
@@ -54281,6 +54281,14 @@ function PluginRuntimeMount({
54281
54281
  () => new Set(plugin.schema.orbitals.map((o) => o.name)),
54282
54282
  [plugin.schema]
54283
54283
  );
54284
+ const inboundBusEvents = React87.useMemo(
54285
+ () => new Set(plugin.inbound.map((row) => row.busEvent)),
54286
+ [plugin.inbound]
54287
+ );
54288
+ const inboundTriggerNames = React87.useMemo(
54289
+ () => new Set(plugin.inbound.map((row) => row.trigger)),
54290
+ [plugin.inbound]
54291
+ );
54284
54292
  const snapshotAllStates = React87.useCallback(() => {
54285
54293
  const states = [];
54286
54294
  if (!mockRuntime) return states;
@@ -54322,6 +54330,21 @@ function PluginRuntimeMount({
54322
54330
  if (timer !== null) clearTimeout(timer);
54323
54331
  };
54324
54332
  }, [mockRuntime, plugin.id, onDispatched, snapshotAllStates]);
54333
+ React87.useEffect(() => {
54334
+ if (!mockRuntime) return;
54335
+ const rebroadcast = /* @__PURE__ */ new WeakSet();
54336
+ const unsub = mockRuntime.getEventBus().onAny((event) => {
54337
+ if (rebroadcast.has(event)) return;
54338
+ if (inboundBusEvents.has(event.type) || inboundTriggerNames.has(event.type)) return;
54339
+ const sourceOrbital = event.source?.orbital;
54340
+ if (sourceOrbital === void 0 || !ownOrbitals.has(sourceOrbital)) return;
54341
+ rebroadcast.add(event);
54342
+ bus.emit(`UI:${event.type}`, event.payload, event.source);
54343
+ });
54344
+ return () => {
54345
+ unsub();
54346
+ };
54347
+ }, [mockRuntime, bus, ownOrbitals, inboundBusEvents, inboundTriggerNames]);
54325
54348
  const dispatch = React87.useCallback(
54326
54349
  async (row, payload) => {
54327
54350
  await registrationReady;
@@ -54340,11 +54363,14 @@ function PluginRuntimeMount({
54340
54363
  if (!response.success) {
54341
54364
  onError(plugin.id, response.error ?? `${plugin.id}: ${row.trigger} failed`);
54342
54365
  }
54343
- for (const entry of response.emittedEvents ?? []) {
54344
- if (entry.event === row.trigger) continue;
54345
- const sourceOrbital = entry.source?.orbital;
54346
- if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
54347
- bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
54366
+ if (!mockRuntime) {
54367
+ for (const entry of response.emittedEvents ?? []) {
54368
+ if (entry.event === row.trigger) continue;
54369
+ if (inboundBusEvents.has(entry.event)) continue;
54370
+ const sourceOrbital = entry.source?.orbital;
54371
+ if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
54372
+ bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
54373
+ }
54348
54374
  }
54349
54375
  }
54350
54376
  const states = [];
@@ -54354,7 +54380,7 @@ function PluginRuntimeMount({
54354
54380
  }
54355
54381
  onDispatched(plugin.id, row.trigger, states);
54356
54382
  },
54357
- [registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, onError, onTransition, onDispatched]
54383
+ [registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, inboundBusEvents, onError, onTransition, onDispatched]
54358
54384
  );
54359
54385
  React87.useEffect(() => {
54360
54386
  const unsubs = plugin.inbound.map(
@@ -327,6 +327,34 @@ declare function BrowserPlayground({ schema, mode, initialPagePath, height, clas
327
327
  * preview has (in-memory persist, mock services); `deny` is an opt-in
328
328
  * host policy, never a default sandbox.
329
329
  *
330
+ * OUTBOUND = the runtime's internal event bus, not the direct dispatch
331
+ * response. `response.emittedEvents` (mock mode) reflects only the effects
332
+ * of the trait `dispatch()` DIRECTLY targeted — a sibling trait that reacts
333
+ * through the runtime's own cascade (`setupEventListeners`, a SEPARATE
334
+ * `processOrbitalEvent` call whose response is discarded) never shows up
335
+ * there, so its emits never reached the ambient bus (verified 2026-09-04:
336
+ * vim-mode's `Shell` relays `PLUGIN_ENABLED` -> `VimStudioBridge`'s
337
+ * `ENABLED` arm emits `STATUS` + 2x `REGISTER_COMMAND`, none of which
338
+ * reached the studio's status bar/palette). Fix: for `mode: 'mock'`, one
339
+ * `runtime.getEventBus().onAny(...)` subscription per plugin (mounted
340
+ * alongside the runtime, not per-dispatch) is the ONLY outbound path — it
341
+ * sees every emit, direct or cascaded, since every `emit` effect passes
342
+ * through that same bus. `mode: 'server'` has no local bus to subscribe to
343
+ * (the runtime lives on the remote transport), so it keeps reading
344
+ * `response.emittedEvents` for outbound — same single-hop-only limitation
345
+ * this fix removes for mock mode, unaddressed here (no `mode: 'server'`
346
+ * consumer exists yet; see `docs/Almadar_UI_Gaps.md`).
347
+ *
348
+ * RELAY RULE: the plugin's `inbound[]` is its host→plugin vocabulary — one
349
+ * `UI:<busEvent>` bus event per row, dispatching `trigger` at
350
+ * `orbital`/`trait`. Anything a plugin emits under one of those SAME
351
+ * busEvent names (or under an inbound `trigger` name) is a relay for its
352
+ * own siblings' `listens {}` (the runtime's internal event bus already
353
+ * carries that fan-out), never a fresh request back to the host, and must
354
+ * NEVER be re-broadcast — doing so re-fires the very `UI:<busEvent>`
355
+ * listener that produced it, a synchronous self-feeding loop (verified
356
+ * 2026-09-04, see `docs/Almadar_UI_Gaps.md`).
357
+ *
330
358
  * @packageDocumentation
331
359
  */
332
360
 
@@ -327,6 +327,34 @@ declare function BrowserPlayground({ schema, mode, initialPagePath, height, clas
327
327
  * preview has (in-memory persist, mock services); `deny` is an opt-in
328
328
  * host policy, never a default sandbox.
329
329
  *
330
+ * OUTBOUND = the runtime's internal event bus, not the direct dispatch
331
+ * response. `response.emittedEvents` (mock mode) reflects only the effects
332
+ * of the trait `dispatch()` DIRECTLY targeted — a sibling trait that reacts
333
+ * through the runtime's own cascade (`setupEventListeners`, a SEPARATE
334
+ * `processOrbitalEvent` call whose response is discarded) never shows up
335
+ * there, so its emits never reached the ambient bus (verified 2026-09-04:
336
+ * vim-mode's `Shell` relays `PLUGIN_ENABLED` -> `VimStudioBridge`'s
337
+ * `ENABLED` arm emits `STATUS` + 2x `REGISTER_COMMAND`, none of which
338
+ * reached the studio's status bar/palette). Fix: for `mode: 'mock'`, one
339
+ * `runtime.getEventBus().onAny(...)` subscription per plugin (mounted
340
+ * alongside the runtime, not per-dispatch) is the ONLY outbound path — it
341
+ * sees every emit, direct or cascaded, since every `emit` effect passes
342
+ * through that same bus. `mode: 'server'` has no local bus to subscribe to
343
+ * (the runtime lives on the remote transport), so it keeps reading
344
+ * `response.emittedEvents` for outbound — same single-hop-only limitation
345
+ * this fix removes for mock mode, unaddressed here (no `mode: 'server'`
346
+ * consumer exists yet; see `docs/Almadar_UI_Gaps.md`).
347
+ *
348
+ * RELAY RULE: the plugin's `inbound[]` is its host→plugin vocabulary — one
349
+ * `UI:<busEvent>` bus event per row, dispatching `trigger` at
350
+ * `orbital`/`trait`. Anything a plugin emits under one of those SAME
351
+ * busEvent names (or under an inbound `trigger` name) is a relay for its
352
+ * own siblings' `listens {}` (the runtime's internal event bus already
353
+ * carries that fan-out), never a fresh request back to the host, and must
354
+ * NEVER be re-broadcast — doing so re-fires the very `UI:<busEvent>`
355
+ * listener that produced it, a synchronous self-feeding loop (verified
356
+ * 2026-09-04, see `docs/Almadar_UI_Gaps.md`).
357
+ *
330
358
  * @packageDocumentation
331
359
  */
332
360
 
@@ -54207,6 +54207,14 @@ function PluginRuntimeMount({
54207
54207
  () => new Set(plugin.schema.orbitals.map((o) => o.name)),
54208
54208
  [plugin.schema]
54209
54209
  );
54210
+ const inboundBusEvents = useMemo(
54211
+ () => new Set(plugin.inbound.map((row) => row.busEvent)),
54212
+ [plugin.inbound]
54213
+ );
54214
+ const inboundTriggerNames = useMemo(
54215
+ () => new Set(plugin.inbound.map((row) => row.trigger)),
54216
+ [plugin.inbound]
54217
+ );
54210
54218
  const snapshotAllStates = useCallback(() => {
54211
54219
  const states = [];
54212
54220
  if (!mockRuntime) return states;
@@ -54248,6 +54256,21 @@ function PluginRuntimeMount({
54248
54256
  if (timer !== null) clearTimeout(timer);
54249
54257
  };
54250
54258
  }, [mockRuntime, plugin.id, onDispatched, snapshotAllStates]);
54259
+ useEffect(() => {
54260
+ if (!mockRuntime) return;
54261
+ const rebroadcast = /* @__PURE__ */ new WeakSet();
54262
+ const unsub = mockRuntime.getEventBus().onAny((event) => {
54263
+ if (rebroadcast.has(event)) return;
54264
+ if (inboundBusEvents.has(event.type) || inboundTriggerNames.has(event.type)) return;
54265
+ const sourceOrbital = event.source?.orbital;
54266
+ if (sourceOrbital === void 0 || !ownOrbitals.has(sourceOrbital)) return;
54267
+ rebroadcast.add(event);
54268
+ bus.emit(`UI:${event.type}`, event.payload, event.source);
54269
+ });
54270
+ return () => {
54271
+ unsub();
54272
+ };
54273
+ }, [mockRuntime, bus, ownOrbitals, inboundBusEvents, inboundTriggerNames]);
54251
54274
  const dispatch = useCallback(
54252
54275
  async (row, payload) => {
54253
54276
  await registrationReady;
@@ -54266,11 +54289,14 @@ function PluginRuntimeMount({
54266
54289
  if (!response.success) {
54267
54290
  onError(plugin.id, response.error ?? `${plugin.id}: ${row.trigger} failed`);
54268
54291
  }
54269
- for (const entry of response.emittedEvents ?? []) {
54270
- if (entry.event === row.trigger) continue;
54271
- const sourceOrbital = entry.source?.orbital;
54272
- if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
54273
- bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
54292
+ if (!mockRuntime) {
54293
+ for (const entry of response.emittedEvents ?? []) {
54294
+ if (entry.event === row.trigger) continue;
54295
+ if (inboundBusEvents.has(entry.event)) continue;
54296
+ const sourceOrbital = entry.source?.orbital;
54297
+ if (sourceOrbital !== void 0 && ownOrbitals.has(sourceOrbital)) {
54298
+ bus.emit(`UI:${entry.event}`, entry.payload, entry.source);
54299
+ }
54274
54300
  }
54275
54301
  }
54276
54302
  const states = [];
@@ -54280,7 +54306,7 @@ function PluginRuntimeMount({
54280
54306
  }
54281
54307
  onDispatched(plugin.id, row.trigger, states);
54282
54308
  },
54283
- [registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, onError, onTransition, onDispatched]
54309
+ [registrationReady, mockRuntime, mode, transport, plugin, bus, ownOrbitals, inboundBusEvents, onError, onTransition, onDispatched]
54284
54310
  );
54285
54311
  useEffect(() => {
54286
54312
  const unsubs = plugin.inbound.map(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "6.7.0",
3
+ "version": "6.8.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -122,7 +122,7 @@
122
122
  "@almadar/evaluator": "^2.44.0",
123
123
  "@almadar/logger": "^1.12.0",
124
124
  "@almadar/runtime": "^6.71.0",
125
- "@almadar/std": "^16.204.0",
125
+ "@almadar/std": "^16.205.0",
126
126
  "@almadar/syntax": "^1.16.0",
127
127
  "@dnd-kit/core": "^6.3.1",
128
128
  "@dnd-kit/sortable": "^10.0.0",