@almadar/ui 4.6.14 → 4.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.
@@ -2,7 +2,7 @@ import { clsx } from 'clsx';
2
2
  import { twMerge } from 'tailwind-merge';
3
3
  import * as React111 from 'react';
4
4
  import React111__default, { useContext, useRef, useEffect, useCallback, createContext, useState, Suspense, useMemo, lazy, useSyncExternalStore, useLayoutEffect, useId } from 'react';
5
- import { EventBusContext, SelectionContext } from '@almadar/ui/providers';
5
+ import { EventBusContext } from '@almadar/ui/providers';
6
6
  import * as LucideIcons from 'lucide-react';
7
7
  import { Loader2, X, AlertTriangle, Info, AlertCircle, CheckCircle, ChevronDown, List, Printer, ChevronRight, ChevronLeft, XCircle, Wrench, RotateCcw, Send, Code, FileText, WrapText, Check, Copy, Zap, Sword, Move, Heart, Shield, Trash2, Settings, Menu as Menu$1, Search, Bell, LogOut, ChevronUp, MoreHorizontal, Bug, ZoomOut, ZoomIn, Download, Pause, Play, Package, Calendar, Pencil, Eye, Image as Image$1, Upload, ArrowRight, ArrowLeft, Eraser, SkipForward, TrendingUp, TrendingDown, Minus, ArrowUp, ArrowDown, MoreVertical, Circle, Clock, CheckCircle2, HelpCircle, FileQuestion, Inbox, Plus, User, Filter, Star, FileWarning, Tag, DollarSign, Sun, Moon } from 'lucide-react';
8
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -4852,6 +4852,7 @@ var init_SlotsContext = __esm({
4852
4852
  "runtime/ui/SlotsContext.tsx"() {
4853
4853
  init_logger();
4854
4854
  slotLog = createLogger("almadar:ui:slot-render");
4855
+ createLogger("almadar:runtime:cross-orbital");
4855
4856
  refIds = /* @__PURE__ */ new WeakMap();
4856
4857
  nextRefId = 1;
4857
4858
  createContext({});
@@ -6266,7 +6267,7 @@ var init_MapView = __esm({
6266
6267
  shadowSize: [41, 41]
6267
6268
  });
6268
6269
  L.Marker.prototype.options.icon = defaultIcon;
6269
- const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback115, useState: useState100 } = React111__default;
6270
+ const { useEffect: useEffect66, useRef: useRef65, useCallback: useCallback115, useState: useState99 } = React111__default;
6270
6271
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
6271
6272
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
6272
6273
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -6310,7 +6311,7 @@ var init_MapView = __esm({
6310
6311
  showAttribution = true
6311
6312
  }) {
6312
6313
  const eventBus = useEventBus2();
6313
- const [clickedPosition, setClickedPosition] = useState100(null);
6314
+ const [clickedPosition, setClickedPosition] = useState99(null);
6314
6315
  const handleMapClick = useCallback115((lat, lng) => {
6315
6316
  if (showClickedPin) {
6316
6317
  setClickedPosition({ lat, lng });
@@ -17289,8 +17290,7 @@ function formatValue(value, format) {
17289
17290
  }
17290
17291
  function DataGrid({
17291
17292
  entity,
17292
- fields: fieldsProp,
17293
- columns: columnsProp,
17293
+ fields,
17294
17294
  itemActions,
17295
17295
  cols,
17296
17296
  gap = "md",
@@ -17311,7 +17311,6 @@ function DataGrid({
17311
17311
  const { t } = useTranslate();
17312
17312
  const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
17313
17313
  const [visibleCount, setVisibleCount] = useState(pageSize || Infinity);
17314
- const fields = fieldsProp ?? columnsProp ?? [];
17315
17314
  const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
17316
17315
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
17317
17316
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
@@ -17654,8 +17653,7 @@ function groupData(items, field) {
17654
17653
  }
17655
17654
  function DataList({
17656
17655
  entity,
17657
- fields: fieldsProp,
17658
- columns: columnsProp,
17656
+ fields,
17659
17657
  itemActions,
17660
17658
  gap = "none",
17661
17659
  variant = "default",
@@ -17684,7 +17682,6 @@ function DataList({
17684
17682
  const eventBus = useEventBus();
17685
17683
  const { t } = useTranslate();
17686
17684
  const [visibleCount, setVisibleCount] = React111__default.useState(pageSize || Infinity);
17687
- const fields = fieldsProp ?? columnsProp ?? [];
17688
17685
  const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
17689
17686
  const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
17690
17687
  const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
@@ -41078,87 +41075,58 @@ function useUISlotManager() {
41078
41075
  // hooks/useUIEvents.ts
41079
41076
  init_useEventBus();
41080
41077
  var UI_PREFIX = "UI:";
41081
- function useUIEvents(dispatch, validEvents, eventBusInstance) {
41078
+ function useUIEvents(dispatch, traitName, validEvents, eventBusInstance) {
41082
41079
  const defaultEventBus = useEventBus();
41083
41080
  const eventBus = eventBusInstance ?? defaultEventBus;
41084
- const validEventsKey = validEvents ? validEvents.slice().sort().join(",") : "";
41081
+ const validEventsKey = validEvents.slice().sort().join(",");
41085
41082
  const stableValidEvents = useMemo(
41086
41083
  () => validEvents,
41087
41084
  [validEventsKey]
41088
- // intentional — validEventsKey is the stable dep, not validEvents array ref
41085
+ // intentional — validEventsKey is the stable dep
41089
41086
  );
41090
41087
  useEffect(() => {
41091
41088
  const unsubscribes = [];
41092
- if (stableValidEvents) {
41093
- for (const smEvent of stableValidEvents) {
41094
- const prefixedHandler = (event) => {
41095
- dispatch(smEvent, event.payload);
41096
- };
41097
- unsubscribes.push(eventBus.on(`${UI_PREFIX}${smEvent}`, prefixedHandler));
41098
- const directHandler = (event) => {
41099
- if (event.source && event.source.fromBridge) {
41100
- return;
41101
- }
41102
- dispatch(smEvent, event.payload);
41103
- };
41104
- unsubscribes.push(eventBus.on(smEvent, directHandler));
41105
- }
41106
- }
41107
- const genericHandler = (event) => {
41108
- const eventName = event.payload?.event;
41109
- if (eventName) {
41110
- const smEvent = eventName;
41111
- if (!stableValidEvents || stableValidEvents.includes(smEvent)) {
41112
- dispatch(smEvent, event.payload);
41089
+ for (const smEvent of stableValidEvents) {
41090
+ const handler = (event) => {
41091
+ if (event.source && event.source.fromBridge) {
41092
+ return;
41113
41093
  }
41114
- }
41115
- };
41116
- unsubscribes.push(eventBus.on(`${UI_PREFIX}DISPATCH`, genericHandler));
41094
+ dispatch(smEvent, event.payload);
41095
+ };
41096
+ unsubscribes.push(
41097
+ eventBus.on(`${UI_PREFIX}${traitName}.${smEvent}`, handler)
41098
+ );
41099
+ }
41117
41100
  return () => {
41118
41101
  for (const unsub of unsubscribes) {
41119
41102
  if (typeof unsub === "function") unsub();
41120
41103
  }
41121
41104
  };
41122
- }, [eventBus, dispatch, stableValidEvents]);
41105
+ }, [eventBus, dispatch, traitName, stableValidEvents]);
41123
41106
  }
41124
- function useSelectedEntity(eventBusInstance) {
41107
+ function useTraitListens(dispatch, listens, eventBusInstance) {
41125
41108
  const defaultEventBus = useEventBus();
41126
41109
  const eventBus = eventBusInstance ?? defaultEventBus;
41127
- const selectionContext = useSelectionContext();
41128
- const [localSelected, setLocalSelected] = useState(null);
41129
- const usingContext = selectionContext !== null;
41110
+ const stableKey = listens.map((l) => `${l.sourceKey}->${l.trigger}`).sort().join("|");
41111
+ const stableListens = useMemo(
41112
+ () => listens,
41113
+ [stableKey]
41114
+ // intentional
41115
+ );
41130
41116
  useEffect(() => {
41131
- if (usingContext) return;
41132
- const handleSelect = (event) => {
41133
- const row = event.payload?.row;
41134
- if (row) {
41135
- setLocalSelected(row);
41136
- }
41137
- };
41138
- const handleDeselect = () => {
41139
- setLocalSelected(null);
41140
- };
41141
- const unsubSelect = eventBus.on("UI:SELECT", handleSelect);
41142
- const unsubView = eventBus.on("UI:VIEW", handleSelect);
41143
- const unsubDeselect = eventBus.on("UI:DESELECT", handleDeselect);
41144
- const unsubClose = eventBus.on("UI:CLOSE", handleDeselect);
41145
- const unsubCancel = eventBus.on("UI:CANCEL", handleDeselect);
41117
+ const unsubscribes = [];
41118
+ for (const spec of stableListens) {
41119
+ const handler = (event) => {
41120
+ dispatch(spec.trigger, event.payload);
41121
+ };
41122
+ unsubscribes.push(eventBus.on(`${UI_PREFIX}${spec.sourceKey}`, handler));
41123
+ }
41146
41124
  return () => {
41147
- [unsubSelect, unsubView, unsubDeselect, unsubClose, unsubCancel].forEach(
41148
- (unsub) => {
41149
- if (typeof unsub === "function") unsub();
41150
- }
41151
- );
41125
+ for (const unsub of unsubscribes) {
41126
+ if (typeof unsub === "function") unsub();
41127
+ }
41152
41128
  };
41153
- }, [eventBus, usingContext]);
41154
- if (selectionContext) {
41155
- return [selectionContext.selected, selectionContext.setSelected];
41156
- }
41157
- return [localSelected, setLocalSelected];
41158
- }
41159
- function useSelectionContext() {
41160
- const context = useContext(SelectionContext);
41161
- return context;
41129
+ }, [eventBus, dispatch, stableListens]);
41162
41130
  }
41163
41131
 
41164
41132
  // hooks/index.ts
@@ -41500,4 +41468,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
41500
41468
  });
41501
41469
  }
41502
41470
 
41503
- export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, CodeBlock, CodeExample, CodeView, CodeViewer, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_SLOTS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, DialogueBox, DialogueBubble, Divider, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GraphCanvas, GraphView, Grid, HStack, Header, Heading, HealthBar, HealthPanel, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LineChart2 as LineChart, List2 as List, LoadingState, MapView, MarkdownContent, MarketingStatCard, MasterDetail2 as MasterDetail, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, Navigation, NegotiatorBoard, NotifyListener, NumberStepper, ObjectRulePanel, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PullToRefresh, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ResourceBar, ResourceCounter, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, Table, Tabs, TagCloud, TeamCard, TeamOrganism, TerrainPalette, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, ViolationAlert, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, clearEntities, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createTranslate, createUnitAnimationState, drawSprite, generateCombatMessage, getAllEntities, getByType, getCurrentFrame, getEntity, getSingleton, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseQueryBinding, pendulum, projectileMotion, removeEntity, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, spawnEntity, springOscillator, tickAnimationState, transitionAnimation, updateEntity, updateSingleton, useAgentChat, useAuthContext, useBattleState, useCamera, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGameAudioContext, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePhysics2D, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useSelectedEntity, useSingletonEntity, useSpriteAnimations, useSwipeGesture, useTranslate, useUIEvents, useUISlotManager, useValidation };
41471
+ export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, CodeBlock, CodeExample, CodeView, CodeViewer, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_SLOTS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, DialogueBox, DialogueBubble, Divider, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GraphCanvas, GraphView, Grid, HStack, Header, Heading, HealthBar, HealthPanel, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LineChart2 as LineChart, List2 as List, LoadingState, MapView, MarkdownContent, MarketingStatCard, MasterDetail2 as MasterDetail, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, Navigation, NegotiatorBoard, NotifyListener, NumberStepper, ObjectRulePanel, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PullToRefresh, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ResourceBar, ResourceCounter, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, Table, Tabs, TagCloud, TeamCard, TeamOrganism, TerrainPalette, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, ViolationAlert, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, clearEntities, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createTranslate, createUnitAnimationState, drawSprite, generateCombatMessage, getAllEntities, getByType, getCurrentFrame, getEntity, getSingleton, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseQueryBinding, pendulum, projectileMotion, removeEntity, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, spawnEntity, springOscillator, tickAnimationState, transitionAnimation, updateEntity, updateSingleton, useAgentChat, useAuthContext, useBattleState, useCamera, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGameAudioContext, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePhysics2D, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useSingletonEntity, useSpriteAnimations, useSwipeGesture, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
@@ -57,8 +57,6 @@ export interface DataGridProps<T extends EntityRow = EntityRow> {
57
57
  entity: T | readonly T[];
58
58
  /** Field definitions for rendering each card */
59
59
  fields: readonly DataGridField[];
60
- /** Alias for fields (compiler generates `columns` for field definitions) */
61
- columns?: readonly DataGridField[];
62
60
  /** Per-item action buttons */
63
61
  itemActions?: readonly DataGridItemAction[];
64
62
  /** Number of columns (uses auto-fit if omitted) */
@@ -98,7 +96,7 @@ export interface DataGridProps<T extends EntityRow = EntityRow> {
98
96
  /** Max items to show before "Show More" button. Defaults to 0 (disabled). */
99
97
  pageSize?: number;
100
98
  }
101
- export declare function DataGrid<T extends EntityRow = EntityRow>({ entity, fields: fieldsProp, columns: columnsProp, itemActions, cols, gap, minCardWidth, className, isLoading, error, imageField, selectable, selectionEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, }: DataGridProps<T>): import("react/jsx-runtime").JSX.Element;
99
+ export declare function DataGrid<T extends EntityRow = EntityRow>({ entity, fields, itemActions, cols, gap, minCardWidth, className, isLoading, error, imageField, selectable, selectionEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, }: DataGridProps<T>): import("react/jsx-runtime").JSX.Element;
102
100
  export declare namespace DataGrid {
103
101
  var displayName: string;
104
102
  }
@@ -47,8 +47,6 @@ export interface DataListProps<T extends EntityRow = EntityRow> {
47
47
  entity: T | readonly T[];
48
48
  /** Field definitions for rendering each row */
49
49
  fields: readonly DataListField[];
50
- /** Alias for fields (compiler may generate `columns` for field definitions) */
51
- columns?: readonly DataListField[];
52
50
  /** Per-item action buttons */
53
51
  itemActions?: readonly DataListItemAction[];
54
52
  /** Gap between rows */
@@ -112,7 +110,7 @@ export interface DataListProps<T extends EntityRow = EntityRow> {
112
110
  /** Max items to show before "Show More" button. Defaults to 5. Set to 0 to disable. */
113
111
  pageSize?: number;
114
112
  }
115
- export declare function DataList<T extends EntityRow = EntityRow>({ entity, fields: fieldsProp, columns: columnsProp, itemActions, gap, variant, groupBy, senderField, currentUser, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, }: DataListProps<T>): import("react/jsx-runtime").JSX.Element;
113
+ export declare function DataList<T extends EntityRow = EntityRow>({ entity, fields, itemActions, gap, variant, groupBy, senderField, currentUser, className, isLoading, error, reorderable: _reorderable, reorderEvent: _reorderEvent, swipeLeftEvent: _swipeLeftEvent, swipeLeftActions: _swipeLeftActions, swipeRightEvent: _swipeRightEvent, swipeRightActions: _swipeRightActions, longPressEvent: _longPressEvent, infiniteScroll, loadMoreEvent, hasMore, children, pageSize, }: DataListProps<T>): import("react/jsx-runtime").JSX.Element;
116
114
  export declare namespace DataList {
117
115
  var displayName: string;
118
116
  }
@@ -232,6 +232,8 @@ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
232
232
  subtitle?: string;
233
233
  /** Shadow size override */
234
234
  shadow?: CardShadow;
235
+ /** Card content */
236
+ children?: React.ReactNode;
235
237
  }
236
238
  declare const Card: React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>>;
237
239
 
@@ -1333,87 +1333,58 @@ function useUISlotManager() {
1333
1333
  };
1334
1334
  }
1335
1335
  var UI_PREFIX = "UI:";
1336
- function useUIEvents(dispatch, validEvents, eventBusInstance) {
1336
+ function useUIEvents(dispatch, traitName, validEvents, eventBusInstance) {
1337
1337
  const defaultEventBus = useEventBus();
1338
1338
  const eventBus = eventBusInstance ?? defaultEventBus;
1339
- const validEventsKey = validEvents ? validEvents.slice().sort().join(",") : "";
1339
+ const validEventsKey = validEvents.slice().sort().join(",");
1340
1340
  const stableValidEvents = react.useMemo(
1341
1341
  () => validEvents,
1342
1342
  [validEventsKey]
1343
- // intentional — validEventsKey is the stable dep, not validEvents array ref
1343
+ // intentional — validEventsKey is the stable dep
1344
1344
  );
1345
1345
  react.useEffect(() => {
1346
1346
  const unsubscribes = [];
1347
- if (stableValidEvents) {
1348
- for (const smEvent of stableValidEvents) {
1349
- const prefixedHandler = (event) => {
1350
- dispatch(smEvent, event.payload);
1351
- };
1352
- unsubscribes.push(eventBus.on(`${UI_PREFIX}${smEvent}`, prefixedHandler));
1353
- const directHandler = (event) => {
1354
- if (event.source && event.source.fromBridge) {
1355
- return;
1356
- }
1357
- dispatch(smEvent, event.payload);
1358
- };
1359
- unsubscribes.push(eventBus.on(smEvent, directHandler));
1360
- }
1361
- }
1362
- const genericHandler = (event) => {
1363
- const eventName = event.payload?.event;
1364
- if (eventName) {
1365
- const smEvent = eventName;
1366
- if (!stableValidEvents || stableValidEvents.includes(smEvent)) {
1367
- dispatch(smEvent, event.payload);
1347
+ for (const smEvent of stableValidEvents) {
1348
+ const handler = (event) => {
1349
+ if (event.source && event.source.fromBridge) {
1350
+ return;
1368
1351
  }
1369
- }
1370
- };
1371
- unsubscribes.push(eventBus.on(`${UI_PREFIX}DISPATCH`, genericHandler));
1352
+ dispatch(smEvent, event.payload);
1353
+ };
1354
+ unsubscribes.push(
1355
+ eventBus.on(`${UI_PREFIX}${traitName}.${smEvent}`, handler)
1356
+ );
1357
+ }
1372
1358
  return () => {
1373
1359
  for (const unsub of unsubscribes) {
1374
1360
  if (typeof unsub === "function") unsub();
1375
1361
  }
1376
1362
  };
1377
- }, [eventBus, dispatch, stableValidEvents]);
1363
+ }, [eventBus, dispatch, traitName, stableValidEvents]);
1378
1364
  }
1379
- function useSelectedEntity(eventBusInstance) {
1365
+ function useTraitListens(dispatch, listens, eventBusInstance) {
1380
1366
  const defaultEventBus = useEventBus();
1381
1367
  const eventBus = eventBusInstance ?? defaultEventBus;
1382
- const selectionContext = useSelectionContext();
1383
- const [localSelected, setLocalSelected] = react.useState(null);
1384
- const usingContext = selectionContext !== null;
1368
+ const stableKey = listens.map((l) => `${l.sourceKey}->${l.trigger}`).sort().join("|");
1369
+ const stableListens = react.useMemo(
1370
+ () => listens,
1371
+ [stableKey]
1372
+ // intentional
1373
+ );
1385
1374
  react.useEffect(() => {
1386
- if (usingContext) return;
1387
- const handleSelect = (event) => {
1388
- const row = event.payload?.row;
1389
- if (row) {
1390
- setLocalSelected(row);
1391
- }
1392
- };
1393
- const handleDeselect = () => {
1394
- setLocalSelected(null);
1395
- };
1396
- const unsubSelect = eventBus.on("UI:SELECT", handleSelect);
1397
- const unsubView = eventBus.on("UI:VIEW", handleSelect);
1398
- const unsubDeselect = eventBus.on("UI:DESELECT", handleDeselect);
1399
- const unsubClose = eventBus.on("UI:CLOSE", handleDeselect);
1400
- const unsubCancel = eventBus.on("UI:CANCEL", handleDeselect);
1375
+ const unsubscribes = [];
1376
+ for (const spec of stableListens) {
1377
+ const handler = (event) => {
1378
+ dispatch(spec.trigger, event.payload);
1379
+ };
1380
+ unsubscribes.push(eventBus.on(`${UI_PREFIX}${spec.sourceKey}`, handler));
1381
+ }
1401
1382
  return () => {
1402
- [unsubSelect, unsubView, unsubDeselect, unsubClose, unsubCancel].forEach(
1403
- (unsub) => {
1404
- if (typeof unsub === "function") unsub();
1405
- }
1406
- );
1383
+ for (const unsub of unsubscribes) {
1384
+ if (typeof unsub === "function") unsub();
1385
+ }
1407
1386
  };
1408
- }, [eventBus, usingContext]);
1409
- if (selectionContext) {
1410
- return [selectionContext.selected, selectionContext.setSelected];
1411
- }
1412
- return [localSelected, setLocalSelected];
1413
- }
1414
- function useSelectionContext() {
1415
- const context = react.useContext(providers.SelectionContext);
1416
- return context;
1387
+ }, [eventBus, dispatch, stableListens]);
1417
1388
  }
1418
1389
  var queryStores = /* @__PURE__ */ new Map();
1419
1390
  function getOrCreateStore(query) {
@@ -2256,9 +2227,9 @@ exports.usePlayer = usePlayer;
2256
2227
  exports.usePreview = usePreview;
2257
2228
  exports.usePullToRefresh = usePullToRefresh;
2258
2229
  exports.useQuerySingleton = useQuerySingleton;
2259
- exports.useSelectedEntity = useSelectedEntity;
2260
2230
  exports.useSingletonEntity = useSingletonEntity;
2261
2231
  exports.useSwipeGesture = useSwipeGesture;
2232
+ exports.useTraitListens = useTraitListens;
2262
2233
  exports.useTranslate = useTranslate;
2263
2234
  exports.useUIEvents = useUIEvents;
2264
2235
  exports.useUISlotManager = useUISlotManager;
@@ -13,7 +13,7 @@ export { useDeepAgentGeneration } from './useDeepAgentGeneration';
13
13
  export { useEventBus, useEventListener, useEmitEvent } from './useEventBus';
14
14
  export type { BusEvent, BusEventSource, EventListener, Unsubscribe, EventBusContextType, } from './event-bus-types';
15
15
  export { useUISlotManager, DEFAULT_SLOTS, type UISlot, type SlotAnimation, type SlotContent, type RenderUIConfig, type SlotChangeCallback, type UISlotManager, } from './useUISlots';
16
- export { useUIEvents, useSelectedEntity } from './useUIEvents';
16
+ export { useUIEvents, useTraitListens, type TraitListenSpec } from './useUIEvents';
17
17
  export { useQuerySingleton, parseQueryBinding, type QueryState, type QuerySingletonEntity, type QuerySingletonResult, type QuerySingletonState, } from './useQuerySingleton';
18
18
  export { useEntities, useEntity as useEntityById, useEntitiesByType, useSingletonEntity, usePlayer, usePhysics, useInput, spawnEntity, updateEntity, updateSingleton, removeEntity, clearEntities, getEntity, getByType, getAllEntities, getSingleton, type Entity, } from './useEntities';
19
19
  export { useTranslate, I18nProvider, createTranslate, type TranslateFunction, type I18nContextValue, } from './useTranslate';
@@ -1,5 +1,5 @@
1
1
  import { createContext, useCallback, useState, useEffect, useMemo, useContext, useRef, useSyncExternalStore } from 'react';
2
- import { EventBusContext, SelectionContext } from '@almadar/ui/providers';
2
+ import { EventBusContext } from '@almadar/ui/providers';
3
3
  import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
4
4
 
5
5
  function useOrbitalHistory(options) {
@@ -1331,87 +1331,58 @@ function useUISlotManager() {
1331
1331
  };
1332
1332
  }
1333
1333
  var UI_PREFIX = "UI:";
1334
- function useUIEvents(dispatch, validEvents, eventBusInstance) {
1334
+ function useUIEvents(dispatch, traitName, validEvents, eventBusInstance) {
1335
1335
  const defaultEventBus = useEventBus();
1336
1336
  const eventBus = eventBusInstance ?? defaultEventBus;
1337
- const validEventsKey = validEvents ? validEvents.slice().sort().join(",") : "";
1337
+ const validEventsKey = validEvents.slice().sort().join(",");
1338
1338
  const stableValidEvents = useMemo(
1339
1339
  () => validEvents,
1340
1340
  [validEventsKey]
1341
- // intentional — validEventsKey is the stable dep, not validEvents array ref
1341
+ // intentional — validEventsKey is the stable dep
1342
1342
  );
1343
1343
  useEffect(() => {
1344
1344
  const unsubscribes = [];
1345
- if (stableValidEvents) {
1346
- for (const smEvent of stableValidEvents) {
1347
- const prefixedHandler = (event) => {
1348
- dispatch(smEvent, event.payload);
1349
- };
1350
- unsubscribes.push(eventBus.on(`${UI_PREFIX}${smEvent}`, prefixedHandler));
1351
- const directHandler = (event) => {
1352
- if (event.source && event.source.fromBridge) {
1353
- return;
1354
- }
1355
- dispatch(smEvent, event.payload);
1356
- };
1357
- unsubscribes.push(eventBus.on(smEvent, directHandler));
1358
- }
1359
- }
1360
- const genericHandler = (event) => {
1361
- const eventName = event.payload?.event;
1362
- if (eventName) {
1363
- const smEvent = eventName;
1364
- if (!stableValidEvents || stableValidEvents.includes(smEvent)) {
1365
- dispatch(smEvent, event.payload);
1345
+ for (const smEvent of stableValidEvents) {
1346
+ const handler = (event) => {
1347
+ if (event.source && event.source.fromBridge) {
1348
+ return;
1366
1349
  }
1367
- }
1368
- };
1369
- unsubscribes.push(eventBus.on(`${UI_PREFIX}DISPATCH`, genericHandler));
1350
+ dispatch(smEvent, event.payload);
1351
+ };
1352
+ unsubscribes.push(
1353
+ eventBus.on(`${UI_PREFIX}${traitName}.${smEvent}`, handler)
1354
+ );
1355
+ }
1370
1356
  return () => {
1371
1357
  for (const unsub of unsubscribes) {
1372
1358
  if (typeof unsub === "function") unsub();
1373
1359
  }
1374
1360
  };
1375
- }, [eventBus, dispatch, stableValidEvents]);
1361
+ }, [eventBus, dispatch, traitName, stableValidEvents]);
1376
1362
  }
1377
- function useSelectedEntity(eventBusInstance) {
1363
+ function useTraitListens(dispatch, listens, eventBusInstance) {
1378
1364
  const defaultEventBus = useEventBus();
1379
1365
  const eventBus = eventBusInstance ?? defaultEventBus;
1380
- const selectionContext = useSelectionContext();
1381
- const [localSelected, setLocalSelected] = useState(null);
1382
- const usingContext = selectionContext !== null;
1366
+ const stableKey = listens.map((l) => `${l.sourceKey}->${l.trigger}`).sort().join("|");
1367
+ const stableListens = useMemo(
1368
+ () => listens,
1369
+ [stableKey]
1370
+ // intentional
1371
+ );
1383
1372
  useEffect(() => {
1384
- if (usingContext) return;
1385
- const handleSelect = (event) => {
1386
- const row = event.payload?.row;
1387
- if (row) {
1388
- setLocalSelected(row);
1389
- }
1390
- };
1391
- const handleDeselect = () => {
1392
- setLocalSelected(null);
1393
- };
1394
- const unsubSelect = eventBus.on("UI:SELECT", handleSelect);
1395
- const unsubView = eventBus.on("UI:VIEW", handleSelect);
1396
- const unsubDeselect = eventBus.on("UI:DESELECT", handleDeselect);
1397
- const unsubClose = eventBus.on("UI:CLOSE", handleDeselect);
1398
- const unsubCancel = eventBus.on("UI:CANCEL", handleDeselect);
1373
+ const unsubscribes = [];
1374
+ for (const spec of stableListens) {
1375
+ const handler = (event) => {
1376
+ dispatch(spec.trigger, event.payload);
1377
+ };
1378
+ unsubscribes.push(eventBus.on(`${UI_PREFIX}${spec.sourceKey}`, handler));
1379
+ }
1399
1380
  return () => {
1400
- [unsubSelect, unsubView, unsubDeselect, unsubClose, unsubCancel].forEach(
1401
- (unsub) => {
1402
- if (typeof unsub === "function") unsub();
1403
- }
1404
- );
1381
+ for (const unsub of unsubscribes) {
1382
+ if (typeof unsub === "function") unsub();
1383
+ }
1405
1384
  };
1406
- }, [eventBus, usingContext]);
1407
- if (selectionContext) {
1408
- return [selectionContext.selected, selectionContext.setSelected];
1409
- }
1410
- return [localSelected, setLocalSelected];
1411
- }
1412
- function useSelectionContext() {
1413
- const context = useContext(SelectionContext);
1414
- return context;
1385
+ }, [eventBus, dispatch, stableListens]);
1415
1386
  }
1416
1387
  var queryStores = /* @__PURE__ */ new Map();
1417
1388
  function getOrCreateStore(query) {
@@ -2208,4 +2179,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
2208
2179
  });
2209
2180
  }
2210
2181
 
2211
- export { ALMADAR_DND_MIME, DEFAULT_SLOTS, I18nProvider, clearEntities, createTranslate, getAllEntities, getByType, getEntity, getSingleton, parseQueryBinding, removeEntity, spawnEntity, updateEntity, updateSingleton, useAgentChat, useAuthContext, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useSelectedEntity, useSingletonEntity, useSwipeGesture, useTranslate, useUIEvents, useUISlotManager, useValidation };
2182
+ export { ALMADAR_DND_MIME, DEFAULT_SLOTS, I18nProvider, clearEntities, createTranslate, getAllEntities, getByType, getEntity, getSingleton, parseQueryBinding, removeEntity, spawnEntity, updateEntity, updateSingleton, useAgentChat, useAuthContext, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useSingletonEntity, useSwipeGesture, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
@@ -1,36 +1,41 @@
1
1
  import { useEventBus } from "./useEventBus";
2
2
  import type { EventPayload } from "@almadar/core";
3
3
  /**
4
- * Hook to bridge UI events to state machine dispatch
4
+ * Hook to bridge UI events to state machine dispatch.
5
5
  *
6
6
  * @param dispatch - The state machine dispatch function
7
- * @param validEvents - Optional array of valid event names (filters which events to handle)
8
- * @param eventBusInstance - Optional event bus instance (for testing, uses hook if not provided)
7
+ * @param traitName - Owning trait name; used to construct the qualified
8
+ * subscription key `UI:{traitName}.{event}`. Required bare-key
9
+ * subscriptions were removed when the bus namespace unified on the
10
+ * listens grammar (gap #13, Phase 4 of the cross-orbital plan).
11
+ * @param validEvents - Local event names (transition triggers) the
12
+ * trait reacts to. The hook subscribes to `UI:{traitName}.{event}`
13
+ * for each entry.
14
+ * @param eventBusInstance - Optional event bus instance (for testing)
9
15
  */
10
- export declare function useUIEvents<E extends string>(dispatch: (event: E, payload?: EventPayload) => void, validEvents?: readonly E[], eventBusInstance?: ReturnType<typeof useEventBus>): void;
16
+ export declare function useUIEvents<E extends string>(dispatch: (event: E, payload?: EventPayload) => void, traitName: string, validEvents: readonly E[], eventBusInstance?: ReturnType<typeof useEventBus>): void;
11
17
  /**
12
- * Hook for selected entity tracking
13
- * Many list UIs need to track which item is selected.
18
+ * Hook to subscribe to cross-trait listens (gap #13, Phase 4 codegen).
14
19
  *
15
- * This hook uses SelectionProvider if available (preferred),
16
- * otherwise falls back to listening to events directly.
20
+ * The schema's `listens { Source EVENT -> TRIGGER }` block resolves at
21
+ * lower-time to a typed `ListenSource` enum (intra-orbital `Trait` or
22
+ * cross-orbital `Orbital.Trait`). Codegen materializes one
23
+ * `useTraitListens` call per trait, listing every `(sourceTrait,
24
+ * sourceEvent, localTrigger)` tuple from the listens block. The hook
25
+ * subscribes to the qualified bus key — `UI:Trait.EVENT` (intra-orbital)
26
+ * or `UI:Orbital.Trait.EVENT` (cross-orbital) — and dispatches the
27
+ * local trigger when the source fires.
17
28
  *
18
- * @example Using with SelectionProvider (recommended)
19
- * ```tsx
20
- * function MyPage() {
21
- * return (
22
- * <EventBusProvider>
23
- * <SelectionProvider>
24
- * <MyComponent />
25
- * </SelectionProvider>
26
- * </EventBusProvider>
27
- * );
28
- * }
29
- *
30
- * function MyComponent() {
31
- * const [selected, setSelected] = useSelectedEntity<Order>();
32
- * // selected is automatically updated when UI:VIEW/UI:SELECT events fire
33
- * }
34
- * ```
29
+ * Replaces the old bare-event cross-trait fan-out that lived inside
30
+ * `useUIEvents`. Pre-unification, `useUIEvents` subscribed to bare
31
+ * `EVENT` keys and matched on `source.trait !== own`; post-unification,
32
+ * the qualified bus key carries the source identity directly, no
33
+ * `source` filtering needed.
35
34
  */
36
- export declare function useSelectedEntity<T>(eventBusInstance?: ReturnType<typeof useEventBus>): [T | null, (entity: T | null) => void];
35
+ export interface TraitListenSpec<E extends string> {
36
+ /** Qualified `Trait.EVENT` or `Orbital.Trait.EVENT` source. */
37
+ sourceKey: string;
38
+ /** Local trigger event to dispatch when the source fires. */
39
+ trigger: E;
40
+ }
41
+ export declare function useTraitListens<E extends string>(dispatch: (event: E, payload?: EventPayload) => void, listens: readonly TraitListenSpec<E>[], eventBusInstance?: ReturnType<typeof useEventBus>): void;
@@ -703,9 +703,9 @@ function bindEventBus(eventBus) {
703
703
  log.info("bindEventBus", { hasOnAny: !!eventBus.onAny });
704
704
  exposeOnWindow();
705
705
  if (window.__orbitalVerification) {
706
- window.__orbitalVerification.sendEvent = (event, payload) => {
707
- const prefixed = event.startsWith("UI:") ? event : `UI:${event}`;
708
- log.debug("sendEvent", { event: prefixed, payloadKeys: payload ? Object.keys(payload) : [] });
706
+ window.__orbitalVerification.sendEvent = (event, payload, traitScope) => {
707
+ const prefixed = event.startsWith("UI:") ? event : traitScope ? `UI:${traitScope}.${event}` : `UI:${event}`;
708
+ log.debug("sendEvent", { event: prefixed, traitScope, payloadKeys: payload ? Object.keys(payload) : [] });
709
709
  eventBus.emit(prefixed, payload);
710
710
  };
711
711
  const eventLog = [];
package/dist/lib/index.js CHANGED
@@ -701,9 +701,9 @@ function bindEventBus(eventBus) {
701
701
  log.info("bindEventBus", { hasOnAny: !!eventBus.onAny });
702
702
  exposeOnWindow();
703
703
  if (window.__orbitalVerification) {
704
- window.__orbitalVerification.sendEvent = (event, payload) => {
705
- const prefixed = event.startsWith("UI:") ? event : `UI:${event}`;
706
- log.debug("sendEvent", { event: prefixed, payloadKeys: payload ? Object.keys(payload) : [] });
704
+ window.__orbitalVerification.sendEvent = (event, payload, traitScope) => {
705
+ const prefixed = event.startsWith("UI:") ? event : traitScope ? `UI:${traitScope}.${event}` : `UI:${event}`;
706
+ log.debug("sendEvent", { event: prefixed, traitScope, payloadKeys: payload ? Object.keys(payload) : [] });
707
707
  eventBus.emit(prefixed, payload);
708
708
  };
709
709
  const eventLog = [];