@almadar/ui 4.6.13 → 4.7.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 });
@@ -41078,87 +41079,58 @@ function useUISlotManager() {
41078
41079
  // hooks/useUIEvents.ts
41079
41080
  init_useEventBus();
41080
41081
  var UI_PREFIX = "UI:";
41081
- function useUIEvents(dispatch, validEvents, eventBusInstance) {
41082
+ function useUIEvents(dispatch, traitName, validEvents, eventBusInstance) {
41082
41083
  const defaultEventBus = useEventBus();
41083
41084
  const eventBus = eventBusInstance ?? defaultEventBus;
41084
- const validEventsKey = validEvents ? validEvents.slice().sort().join(",") : "";
41085
+ const validEventsKey = validEvents.slice().sort().join(",");
41085
41086
  const stableValidEvents = useMemo(
41086
41087
  () => validEvents,
41087
41088
  [validEventsKey]
41088
- // intentional — validEventsKey is the stable dep, not validEvents array ref
41089
+ // intentional — validEventsKey is the stable dep
41089
41090
  );
41090
41091
  useEffect(() => {
41091
41092
  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);
41093
+ for (const smEvent of stableValidEvents) {
41094
+ const handler = (event) => {
41095
+ if (event.source && event.source.fromBridge) {
41096
+ return;
41113
41097
  }
41114
- }
41115
- };
41116
- unsubscribes.push(eventBus.on(`${UI_PREFIX}DISPATCH`, genericHandler));
41098
+ dispatch(smEvent, event.payload);
41099
+ };
41100
+ unsubscribes.push(
41101
+ eventBus.on(`${UI_PREFIX}${traitName}.${smEvent}`, handler)
41102
+ );
41103
+ }
41117
41104
  return () => {
41118
41105
  for (const unsub of unsubscribes) {
41119
41106
  if (typeof unsub === "function") unsub();
41120
41107
  }
41121
41108
  };
41122
- }, [eventBus, dispatch, stableValidEvents]);
41109
+ }, [eventBus, dispatch, traitName, stableValidEvents]);
41123
41110
  }
41124
- function useSelectedEntity(eventBusInstance) {
41111
+ function useTraitListens(dispatch, listens, eventBusInstance) {
41125
41112
  const defaultEventBus = useEventBus();
41126
41113
  const eventBus = eventBusInstance ?? defaultEventBus;
41127
- const selectionContext = useSelectionContext();
41128
- const [localSelected, setLocalSelected] = useState(null);
41129
- const usingContext = selectionContext !== null;
41114
+ const stableKey = listens.map((l) => `${l.sourceKey}->${l.trigger}`).sort().join("|");
41115
+ const stableListens = useMemo(
41116
+ () => listens,
41117
+ [stableKey]
41118
+ // intentional
41119
+ );
41130
41120
  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);
41121
+ const unsubscribes = [];
41122
+ for (const spec of stableListens) {
41123
+ const handler = (event) => {
41124
+ dispatch(spec.trigger, event.payload);
41125
+ };
41126
+ unsubscribes.push(eventBus.on(`${UI_PREFIX}${spec.sourceKey}`, handler));
41127
+ }
41146
41128
  return () => {
41147
- [unsubSelect, unsubView, unsubDeselect, unsubClose, unsubCancel].forEach(
41148
- (unsub) => {
41149
- if (typeof unsub === "function") unsub();
41150
- }
41151
- );
41129
+ for (const unsub of unsubscribes) {
41130
+ if (typeof unsub === "function") unsub();
41131
+ }
41152
41132
  };
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;
41133
+ }, [eventBus, dispatch, stableListens]);
41162
41134
  }
41163
41135
 
41164
41136
  // hooks/index.ts
@@ -41500,4 +41472,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
41500
41472
  });
41501
41473
  }
41502
41474
 
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 };
41475
+ 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 };
@@ -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 = [];
@@ -245,6 +245,8 @@ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
245
245
  subtitle?: string;
246
246
  /** Shadow size override */
247
247
  shadow?: CardShadow;
248
+ /** Card content */
249
+ children?: React.ReactNode;
248
250
  }
249
251
  declare const Card: React.ForwardRefExoticComponent<CardProps & React.RefAttributes<HTMLDivElement>>;
250
252
 
@@ -1772,6 +1772,7 @@ var init_SlotsContext = __esm({
1772
1772
  "runtime/ui/SlotsContext.tsx"() {
1773
1773
  init_logger();
1774
1774
  slotLog = createLogger("almadar:ui:slot-render");
1775
+ createLogger("almadar:runtime:cross-orbital");
1775
1776
  refIds = /* @__PURE__ */ new WeakMap();
1776
1777
  nextRefId = 1;
1777
1778
  React116.createContext({});
@@ -9057,9 +9058,9 @@ function bindEventBus(eventBus) {
9057
9058
  log2.info("bindEventBus", { hasOnAny: !!eventBus.onAny });
9058
9059
  exposeOnWindow();
9059
9060
  if (window.__orbitalVerification) {
9060
- window.__orbitalVerification.sendEvent = (event, payload) => {
9061
- const prefixed = event.startsWith("UI:") ? event : `UI:${event}`;
9062
- log2.debug("sendEvent", { event: prefixed, payloadKeys: payload ? Object.keys(payload) : [] });
9061
+ window.__orbitalVerification.sendEvent = (event, payload, traitScope) => {
9062
+ const prefixed = event.startsWith("UI:") ? event : traitScope ? `UI:${traitScope}.${event}` : `UI:${event}`;
9063
+ log2.debug("sendEvent", { event: prefixed, traitScope, payloadKeys: payload ? Object.keys(payload) : [] });
9063
9064
  eventBus.emit(prefixed, payload);
9064
9065
  };
9065
9066
  const eventLog = [];
@@ -1727,6 +1727,7 @@ var init_SlotsContext = __esm({
1727
1727
  "runtime/ui/SlotsContext.tsx"() {
1728
1728
  init_logger();
1729
1729
  slotLog = createLogger("almadar:ui:slot-render");
1730
+ createLogger("almadar:runtime:cross-orbital");
1730
1731
  refIds = /* @__PURE__ */ new WeakMap();
1731
1732
  nextRefId = 1;
1732
1733
  createContext({});
@@ -9012,9 +9013,9 @@ function bindEventBus(eventBus) {
9012
9013
  log2.info("bindEventBus", { hasOnAny: !!eventBus.onAny });
9013
9014
  exposeOnWindow();
9014
9015
  if (window.__orbitalVerification) {
9015
- window.__orbitalVerification.sendEvent = (event, payload) => {
9016
- const prefixed = event.startsWith("UI:") ? event : `UI:${event}`;
9017
- log2.debug("sendEvent", { event: prefixed, payloadKeys: payload ? Object.keys(payload) : [] });
9016
+ window.__orbitalVerification.sendEvent = (event, payload, traitScope) => {
9017
+ const prefixed = event.startsWith("UI:") ? event : traitScope ? `UI:${traitScope}.${event}` : `UI:${event}`;
9018
+ log2.debug("sendEvent", { event: prefixed, traitScope, payloadKeys: payload ? Object.keys(payload) : [] });
9018
9019
  eventBus.emit(prefixed, payload);
9019
9020
  };
9020
9021
  const eventLog = [];
@@ -42,6 +42,14 @@ export interface OrbPreviewProps {
42
42
  className?: string;
43
43
  /** Server URL for dual execution (e.g. "/api/orbitals"). When set, events are forwarded to the server. */
44
44
  serverUrl?: string;
45
+ /**
46
+ * Initial page path to render (e.g. `/deals`). Resolves against the
47
+ * schema's `pages[]` to seed `currentPage` so the right orbital's traits
48
+ * mount on first render. Without this the playground falls back to the
49
+ * schema's first page binding (gap #13 Phase 5). runtime-verify uses this
50
+ * to walk DealCreate, NoteCompose, etc. on their owning pages.
51
+ */
52
+ initialPagePath?: string;
45
53
  }
46
54
  /**
47
55
  * Renders a live preview of an Orbital schema.
@@ -56,7 +64,7 @@ export interface OrbPreviewProps {
56
64
  * <OrbPreview schema={schema} serverUrl="/api/orbitals" />
57
65
  * ```
58
66
  */
59
- export declare function OrbPreview({ schema, mockData, autoMock, height, className, serverUrl, }: OrbPreviewProps): React.ReactElement;
67
+ export declare function OrbPreview({ schema, mockData, autoMock, height, className, serverUrl, initialPagePath, }: OrbPreviewProps): React.ReactElement;
60
68
  export declare namespace OrbPreview {
61
69
  var displayName: string;
62
70
  }