@xenosystem/blocks 0.12.1 → 0.12.2

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.
@@ -1,4 +1,4 @@
1
- import * as react from 'react';
1
+ import * as React from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { PanelModule, PanelManifest, BlockDeclaration } from '@xenosystem/block-sdk';
4
4
  import { PanelTone } from '@xenosystem/components/chrome';
@@ -314,7 +314,7 @@ interface AuthGateViewProps {
314
314
  heroSrc?: string;
315
315
  now?: () => number;
316
316
  }
317
- declare function AuthGateView$1({ controller, productName, children, heroSrc, now }: AuthGateViewProps): react.JSX.Element;
317
+ declare function AuthGateView$1({ controller, productName, children, heroSrc, now }: AuthGateViewProps): React.JSX.Element;
318
318
 
319
319
  /**
320
320
  * The auth gate AS A DECLARATION — the first block written the way `xeno-apps/SPEC.md` §8d says every
@@ -1,6 +1,7 @@
1
- import { PanelHost, PanelModule, PanelManifest } from '@xenosystem/block-sdk';
1
+ import { PanelHost, PanelModule, PanelManifest, BlockDeclaration } from '@xenosystem/block-sdk';
2
2
  import { FlatRow as FlatRow$1, DropTarget, ThrottleClock, VirtualWindow, DropPosition } from '@xenosystem/tree-core';
3
3
  export { DropPosition, DropTarget } from '@xenosystem/tree-core';
4
+ import * as React from 'react';
4
5
  import { ReactElement, ReactNode } from 'react';
5
6
  import { XenoTimebase, XenoTick } from '@xenosystem/time-core';
6
7
 
@@ -1469,6 +1470,149 @@ declare function nudgeValue(current: number, direction: number, options: Numeric
1469
1470
  /** ARIA slider attributes for a numeric field (Sound ships the only accessible fields today). */
1470
1471
  declare function ariaSliderProps(field: XenoInspectorField, value: number | null): Record<string, string | number | undefined>;
1471
1472
 
1473
+ /**
1474
+ * The inspector AS A DECLARATION — the fifth block written the §8d way (auth gate, agent, account,
1475
+ * transport, inspector), and the first whose rows carry a runtime-bound unit.
1476
+ *
1477
+ * Presentation contract: input `view` (an {@link InspectorViewState} the runtime derives from the
1478
+ * controller on every draw — sections already projected, every row flag already decided), output
1479
+ * `ui` (an {@link InspectorUiIntent}) applied by the runtime to the controller's own methods. The
1480
+ * declaration composes the SHELL: search and target chip, the multi-select banner, sections with
1481
+ * their enable switch / badge / actions / add menu, one `Field` row per property with the keyframe
1482
+ * cluster leading and easing / chip / bind-token / reset trailing, the empty state.
1483
+ *
1484
+ * What a declaration cannot hold is the CELL: scrub gestures on an axis label, select-on-focus,
1485
+ * wheel and arrow nudges, a draft that commits on blur. That is `inspector/FieldWidget`, a unit the
1486
+ * runtime registers bound to its controller (see {@link inspectorRuntimeComponents}) — the same
1487
+ * shape as `slot`: the declaration names it and binds `field: { item: '' }`; the runtime supplies it.
1488
+ *
1489
+ * The code view (`InspectorPanelView`) draws the same model; the test holds the two byte-identical.
1490
+ */
1491
+
1492
+ /** One projected row — the field plus every flag the row's chrome reads, decided by the runtime. */
1493
+ interface InspectorViewField {
1494
+ id: string;
1495
+ sectionId: string;
1496
+ label: string;
1497
+ title: string;
1498
+ focused: boolean;
1499
+ bound: boolean;
1500
+ unbound: boolean;
1501
+ tokenLabel?: string;
1502
+ tokenTitle?: string;
1503
+ showKeyframes: boolean;
1504
+ animated: boolean;
1505
+ onKeyframe: boolean;
1506
+ prevDisabled: boolean;
1507
+ nextDisabled: boolean;
1508
+ keyframeLabel: string;
1509
+ keyframeOp: 'add' | 'remove';
1510
+ showEasing: boolean;
1511
+ easing: string;
1512
+ easingOptions: {
1513
+ value: string;
1514
+ label: string;
1515
+ }[];
1516
+ keyframeChip: string;
1517
+ showToken: boolean;
1518
+ tokenActionLabel: string;
1519
+ showReset: boolean;
1520
+ showInfoNote: boolean;
1521
+ infoNote?: string;
1522
+ /** The field record itself — what `inspector/FieldWidget` renders. */
1523
+ field: XenoInspectorField;
1524
+ }
1525
+ interface InspectorViewSection {
1526
+ id: string;
1527
+ title: string;
1528
+ collapsible: boolean;
1529
+ collapsed: boolean;
1530
+ hasEnabled: boolean;
1531
+ enabled?: boolean;
1532
+ badge?: string;
1533
+ actions: {
1534
+ id: string;
1535
+ sectionId: string;
1536
+ label: string;
1537
+ glyph: string;
1538
+ disabled?: boolean;
1539
+ }[];
1540
+ hasAddMenu: boolean;
1541
+ hasActions: boolean;
1542
+ addLabel: string;
1543
+ addOptions: {
1544
+ value: string;
1545
+ label: string;
1546
+ disabled?: boolean;
1547
+ group?: string;
1548
+ }[];
1549
+ showEmptyHint: boolean;
1550
+ emptyHint?: string;
1551
+ labelWidth: number;
1552
+ fields: InspectorViewField[];
1553
+ }
1554
+ /** What the runtime pushes on `view`. */
1555
+ interface InspectorViewState {
1556
+ searchShown: boolean;
1557
+ filter: string;
1558
+ hasTarget: boolean;
1559
+ targetLabel: string;
1560
+ multi: boolean;
1561
+ banner: string;
1562
+ sections: InspectorViewSection[];
1563
+ empty: boolean;
1564
+ emptyTitle: string;
1565
+ }
1566
+ type InspectorUiIntent = {
1567
+ kind: 'filter';
1568
+ value: string;
1569
+ } | {
1570
+ kind: 'toggleSectionCollapsed';
1571
+ sectionId: string;
1572
+ } | {
1573
+ kind: 'toggleSectionEnabled';
1574
+ sectionId: string;
1575
+ } | {
1576
+ kind: 'runAction';
1577
+ sectionId: string;
1578
+ actionId: string;
1579
+ } | {
1580
+ kind: 'addItem';
1581
+ sectionId: string;
1582
+ itemId: string;
1583
+ } | {
1584
+ kind: 'focusField';
1585
+ fieldId: string;
1586
+ } | {
1587
+ kind: 'contextMenu';
1588
+ fieldId: string;
1589
+ x: number;
1590
+ y: number;
1591
+ } | {
1592
+ kind: 'keyframe';
1593
+ fieldId: string;
1594
+ op: 'toggleAnim' | 'prev' | 'next' | 'add' | 'remove';
1595
+ } | {
1596
+ kind: 'setEasing';
1597
+ fieldId: string;
1598
+ easing: string;
1599
+ } | {
1600
+ kind: 'bindToken';
1601
+ fieldId: string;
1602
+ } | {
1603
+ kind: 'resetField';
1604
+ fieldId: string;
1605
+ };
1606
+ /** Derive the view state from the live controller — the runtime calls this on every draw. */
1607
+ declare function inspectorViewState(c: InspectorPanelController): InspectorViewState;
1608
+ /** Apply a `ui` intent to the controller — the runtime's half of the contract. */
1609
+ declare function applyInspectorUi(c: InspectorPanelController, intent: InspectorUiIntent): void;
1610
+ declare const inspectorViewManifest: PanelManifest;
1611
+ declare const BEHAVIOURS: {
1612
+ 'edit/inspectorPanelView': (inputs: Record<string, unknown>) => InspectorViewState;
1613
+ };
1614
+ declare const INSPECTOR_DECLARATION: BlockDeclaration;
1615
+
1472
1616
  /**
1473
1617
  * `InspectorPanelView` — the React rendering half of the Inspector panel.
1474
1618
  *
@@ -1484,12 +1628,17 @@ interface InspectorPanelViewProps {
1484
1628
  controller: InspectorPanelController;
1485
1629
  }
1486
1630
  /**
1487
- * The inspector shell over the chrome family: search, the target chip, the multi-select banner,
1488
- * sections with an enable switch / badge / actions / add menu, and one `Field` row per property with
1489
- * the keyframe cluster leading and bind-token / reset / easing trailing. The keyboard model and every
1490
- * intent stay in the controller.
1631
+ * The inspector shell over the chrome family, drawn from the SAME view state as `INSPECTOR_DECLARATION`
1632
+ * (`inspectorViewState(controller)`) — the declaration test holds the two byte-identical. The keyboard
1633
+ * model and every intent stay in the controller; the cell is `FieldWidget`, bound to it.
1491
1634
  */
1492
1635
  declare function InspectorPanelView({ controller }: InspectorPanelViewProps): ReactElement;
1636
+ /**
1637
+ * The units the runtime registers for `INSPECTOR_DECLARATION`, bound to ITS controller: the cell
1638
+ * (`inspector/FieldWidget`), the keyboard group (`inspector/Properties`), and the glyphs. A
1639
+ * declaration can name them and bind their data props; only the runtime can supply the controller.
1640
+ */
1641
+ declare function inspectorRuntimeComponents(controller: InspectorPanelController): Record<string, React.ComponentType<any>>;
1493
1642
 
1494
1643
  /**
1495
1644
  * The canonical History wire contract (union spec §3): `XenoHistoryState` in,
@@ -6461,4 +6610,4 @@ declare function TreeIcon({ name, size }: {
6461
6610
  size?: number;
6462
6611
  }): ReactNode;
6463
6612
 
6464
- export { type BlendModeGroup, type BlendModeOption, type Box, COLOR_SPACES, COMMENTS_PANEL_ID, CURVE_PANEL_ID, type ChannelId, ColorPanelController, type ColorPanelControllerOptions, type ColorPanelSerialized, ColorPanelView, type ColorPanelViewProps, type ColorSpace, type ColorStopSink, type ColorToken, CommentsController, type CommentsControllerOptions, type CommentsHostBridge, type CommentsPanelState, CommentsPanelView, type CommentsPanelViewProps, type CommentsRenderContext, type CommentsThreadView, type CommentsViewState, type ConnectRules, type ConnectVerdict, type CreateColorPanelOptions, type CreateCommentsPanelOptions, type CreateCurvePanelOptions, type CreateFindPanelOptions, type CreateHistoryPanelOptions, type CreateInspectorPanelOptions, type CreateLayersPanelOptions, type CreateNodeGraphPanelOptions, type CreateTreePanelOptions, type CreateViewportPanelOptions, CurveController, type CurveControllerOptions, type CurveDragKind, type CurveDragState, type CurveHostBridge, type CurvePanelState, CurvePanelView, type CurvePanelViewProps, type CurvePreview, type CurveRenderConfig, type CurveRenderContext, type CurveViewState, DEFAULT_BLEND_MODES, DEFAULT_CURVE_ZOOM, DEFAULT_GRAPH_ZOOM, DEFAULT_ICON_MAP, DEFAULT_ORIGINAL_LABEL, type DropInput, EASING_PRESETS, EDIT_SPACES, EMPTY_HISTORY, EMPTY_SCHEMA, EMPTY_TREE, EMPTY_TREE_SOURCE, FIND_PANEL_ID, FindController, type FindControllerOptions, type FindHostBridge, type FindPanelState, FindPanelView, type FindPanelViewProps, type FindRenderContext, type FindViewState, type FlatRow, type FlattenOptions, GENERIC_ICON, GraphController, type GraphControllerOptions, type GraphDragKind, type GraphDragState, type GraphHostBridge, type GraphPanelState, type GraphPreview, type GraphRenderConfig, type GraphRenderContext, type GraphViewState, type HistoryKeyEvent, HistoryPanelController, type HistoryPanelControllerOptions, type HistoryPanelSerialized, HistoryPanelView, type HistoryPanelViewProps, type ProjectOptions as HistoryProjectOptions, type HistoryRow, type Hsl, type Hsv, IMPLEMENTED_TREE_MODES, type InspectorKeyEvent, InspectorPanelController, type InspectorPanelControllerOptions, type InspectorPanelSerialized, InspectorPanelView, type InspectorPanelViewProps, type ProjectOptions$1 as InspectorProjectOptions, LAYER_COLUMNS, type LayerColumn, type LayersKeyEvent, LayersPanelController, type LayersPanelControllerOptions, type LayersPanelSerialized, LayersPanelView, type LayersPanelViewProps, MIXED, NODEGRAPH_PANEL_ID, NodeGraphPanelView, type NodeGraphPanelViewProps, type NumericConstraints, type OklchDisplay, type ParseResult, type ProjectedSection, type Rgba, type ScrubOptions, type SelectMods, TREE_PANEL_ID, type TangentEdit, Thumbnail, type ThumbnailProps, TreeController, type TreeControllerOptions, type TreeHostBridge, TreeIcon, type TreeKeyEvent, type TreePanelState, TreePanelView, type TreePanelViewProps, type TreeRenderContext, type TreeRow, type TreeViewState, VIEWPORT_PANEL_ID, VIEWPORT_SLOTS, VIEW_STATE_OPS, ViewportController, type ViewportControllerOptions, type ViewportHostBridge, type ViewportPanelState, ViewportPanelView, type ViewportPanelViewProps, type ViewportRenderContext, type ViewportViewState, type XenoAddMenuItem, type XenoAnchorState, type XenoColorValue, type XenoCommentAuthor, type XenoCommentFilter, type XenoCommentMessage, type XenoCommentOp, type XenoCommentReaction, type XenoCommentSort, type XenoCommentThread, type XenoCommitPolicy, type XenoContextMenuRequest, type XenoCurveChannel, type XenoCurveInterpolation, type XenoCurveKey, type XenoCurveKeyRef, type XenoCurveOp, type XenoCurvePoint, XenoCurveScale, type XenoCurveScaleGeometry, type XenoCurveScaleState, type XenoCurveSelection, type XenoCurveSet, type XenoCurveViewport, type XenoCurveZoom, type XenoEasingPreset, type XenoEdge, type XenoFieldAnim, type XenoFieldFocus, type XenoFieldOption, type XenoFieldToken, type XenoFieldType, type XenoFieldValue, type XenoFindMatch, type XenoFindNavigate, type XenoFindOptions, type XenoFindQuery, type XenoFindReplace, type XenoFindResults, type XenoFindStatus, type XenoGradient, type XenoGraph, type XenoGraphOp, XenoGraphScale, type XenoGraphScaleGeometry, type XenoGraphScaleState, type XenoGraphSelection, type XenoGraphViewport, type XenoGraphZoom, type XenoHistoryEntry, type XenoHistoryIntent, type XenoHistoryMemory, type XenoHistoryOrder, type XenoHistoryScope, type XenoHistorySnapshot, type XenoHistorySource, type XenoHistoryState, type XenoHistoryStatus, type XenoInspectorEdit, type XenoInspectorField, type XenoInspectorSchema, type XenoInspectorSection, type XenoInspectorTarget, type XenoInspectorToken, type XenoKeyframeKind, type XenoLayerAdornment, type XenoLayerCap, type XenoLayerKind, type XenoLayerNode, type XenoLayerOp, type XenoLayerTree, type XenoNavigateRequest, type XenoNode, XenoNodeIndex, type XenoPort, type XenoPortDirection, type XenoPortRef, type XenoSectionAction, type XenoTangent, type XenoTangentMode, type XenoTangentSide, type XenoThreadActivate, type XenoThumbnailRequest, type XenoThumbnailResponse, type XenoTreeAction, type XenoTreeBadge, type XenoTreeCaps, type XenoTreeChildState, type XenoTreeChildrenResponse, type XenoTreeError, type XenoTreeGlyph, type XenoTreeIcon, type XenoTreeInvalidate, type XenoTreeMode, type XenoTreeModifiers, type XenoTreeNode, type XenoTreeNodeKind, type XenoTreeOp, type XenoTreeReveal, type XenoTreeSource, type XenoTreeSourceKind, type XenoTreeSourceState, type XenoViewportClient, type XenoViewportKey, type XenoViewportPointer, type XenoViewportSlot, type XenoViewportStats, type XenoViewportSurface, allFields, allIds, anchorStateOf, appliedCount, applyPrecision, applyTangentDrag, ariaSliderProps, boxesIntersect, canConnect, canRedo, canUndo, childStateOf, clamp, clampAlpha, clampTangent, collectDescendants, colorManifest, colorPanel, commentsManifest, commentsPanel, commitPolicyOf, componentsForSpace, computeSurfaceSize, constraintsOf, countIsMeaningful, createColorPanel, createCommentsPanel, createCurvePanel, createFindPanel, createHistoryPanel, createInspectorPanel, createLayersPanel, createNodeGraphPanel, createTreePanel, createViewportPanel, createsCycle, currentRowIndex, curveManifest, curvePanel, defaultIconFor, displayOrder, distanceToEdge, easeAt, easingPreset, edgeBounds, edgeKey, edgePath, edgesToReplace, edgesTouching, evaluateChannel, fieldMatches, findField, findManifest, findPanel, findPort, findSectionOf, fitToBounds, fitValueRange, flattenVisible, focusOrder, formatAge, formatNumeric, getAncestors, graphBounds, groupMatches, hasCap, hasDraggableTangents, hasTreeCap, hexFromHsv, highlightSegments, historyManifest, historyPanel, hslToRgb, hsvFromHex, hsvToRgb, inspectorManifest, inspectorPanel, interpolateSegment, isAncestorOf, isAnimated, isCollapsed, isColorSpace, isContainer, isEmptyContainer, isFieldEditable, isFieldMixed, isJumpable, isKeyEditable, isLayerColumn, isMixedValue, isOrphaned, isTreeContainer, isValidHex, isViewStateOp, keyRefId, layersManifest, layersPanel, liveEdges, makeColorValue, matchesFilter, messagesOf, mirrorTangent, nodeBounds, nodegraphManifest, nodegraphPanel, normalizeColorInput, normalizeHistoryState, normalizeKindKey, normalizeNumeric, normalizeRanges, normalizeSchema, normalizeTree, nudgeValue, oklchToRgb, parseCssColor, parseHexColor, parseNumeric, passesFilter, patternError, portHasRoom, portRefId, positionToValue, projectRows, projectSections, rangeBetween, resolveDropTarget, resolveIconName, rgbToHex, rgbToHsl, rgbToHsv, rgbToOklch, rgbaToCssHex, roundRgb, rowIndexOf, rowIndexOfEntry, safeAddColorStop, safeAddColorStops, sameKey, sampleChannel, scrubValue, segmentsAround, solveBezierT, sortThreads, surfaceChanged, tangentsForMode, tangentsToControlPoints, toViewportCoords, totalCount, treeManifest, treePanel, usesLogScale, valueExtent, valueToPosition, valuesEqual, viewportManifest, viewportPanel, visibleEdges, walkTree, wheelZoomFactor, zoomAboutCursor, zoomTimeAboutCursor, zoomValueAboutCursor };
6613
+ export { type BlendModeGroup, type BlendModeOption, type Box, COLOR_SPACES, COMMENTS_PANEL_ID, CURVE_PANEL_ID, type ChannelId, ColorPanelController, type ColorPanelControllerOptions, type ColorPanelSerialized, ColorPanelView, type ColorPanelViewProps, type ColorSpace, type ColorStopSink, type ColorToken, CommentsController, type CommentsControllerOptions, type CommentsHostBridge, type CommentsPanelState, CommentsPanelView, type CommentsPanelViewProps, type CommentsRenderContext, type CommentsThreadView, type CommentsViewState, type ConnectRules, type ConnectVerdict, type CreateColorPanelOptions, type CreateCommentsPanelOptions, type CreateCurvePanelOptions, type CreateFindPanelOptions, type CreateHistoryPanelOptions, type CreateInspectorPanelOptions, type CreateLayersPanelOptions, type CreateNodeGraphPanelOptions, type CreateTreePanelOptions, type CreateViewportPanelOptions, CurveController, type CurveControllerOptions, type CurveDragKind, type CurveDragState, type CurveHostBridge, type CurvePanelState, CurvePanelView, type CurvePanelViewProps, type CurvePreview, type CurveRenderConfig, type CurveRenderContext, type CurveViewState, DEFAULT_BLEND_MODES, DEFAULT_CURVE_ZOOM, DEFAULT_GRAPH_ZOOM, DEFAULT_ICON_MAP, DEFAULT_ORIGINAL_LABEL, type DropInput, EASING_PRESETS, EDIT_SPACES, EMPTY_HISTORY, EMPTY_SCHEMA, EMPTY_TREE, EMPTY_TREE_SOURCE, FIND_PANEL_ID, FindController, type FindControllerOptions, type FindHostBridge, type FindPanelState, FindPanelView, type FindPanelViewProps, type FindRenderContext, type FindViewState, type FlatRow, type FlattenOptions, GENERIC_ICON, GraphController, type GraphControllerOptions, type GraphDragKind, type GraphDragState, type GraphHostBridge, type GraphPanelState, type GraphPreview, type GraphRenderConfig, type GraphRenderContext, type GraphViewState, type HistoryKeyEvent, HistoryPanelController, type HistoryPanelControllerOptions, type HistoryPanelSerialized, HistoryPanelView, type HistoryPanelViewProps, type ProjectOptions as HistoryProjectOptions, type HistoryRow, type Hsl, type Hsv, IMPLEMENTED_TREE_MODES, BEHAVIOURS as INSPECTOR_BEHAVIOURS, INSPECTOR_DECLARATION, type InspectorKeyEvent, InspectorPanelController, type InspectorPanelControllerOptions, type InspectorPanelSerialized, InspectorPanelView, type InspectorPanelViewProps, type ProjectOptions$1 as InspectorProjectOptions, type InspectorUiIntent, type InspectorViewField, type InspectorViewSection, type InspectorViewState, LAYER_COLUMNS, type LayerColumn, type LayersKeyEvent, LayersPanelController, type LayersPanelControllerOptions, type LayersPanelSerialized, LayersPanelView, type LayersPanelViewProps, MIXED, NODEGRAPH_PANEL_ID, NodeGraphPanelView, type NodeGraphPanelViewProps, type NumericConstraints, type OklchDisplay, type ParseResult, type ProjectedSection, type Rgba, type ScrubOptions, type SelectMods, TREE_PANEL_ID, type TangentEdit, Thumbnail, type ThumbnailProps, TreeController, type TreeControllerOptions, type TreeHostBridge, TreeIcon, type TreeKeyEvent, type TreePanelState, TreePanelView, type TreePanelViewProps, type TreeRenderContext, type TreeRow, type TreeViewState, VIEWPORT_PANEL_ID, VIEWPORT_SLOTS, VIEW_STATE_OPS, ViewportController, type ViewportControllerOptions, type ViewportHostBridge, type ViewportPanelState, ViewportPanelView, type ViewportPanelViewProps, type ViewportRenderContext, type ViewportViewState, type XenoAddMenuItem, type XenoAnchorState, type XenoColorValue, type XenoCommentAuthor, type XenoCommentFilter, type XenoCommentMessage, type XenoCommentOp, type XenoCommentReaction, type XenoCommentSort, type XenoCommentThread, type XenoCommitPolicy, type XenoContextMenuRequest, type XenoCurveChannel, type XenoCurveInterpolation, type XenoCurveKey, type XenoCurveKeyRef, type XenoCurveOp, type XenoCurvePoint, XenoCurveScale, type XenoCurveScaleGeometry, type XenoCurveScaleState, type XenoCurveSelection, type XenoCurveSet, type XenoCurveViewport, type XenoCurveZoom, type XenoEasingPreset, type XenoEdge, type XenoFieldAnim, type XenoFieldFocus, type XenoFieldOption, type XenoFieldToken, type XenoFieldType, type XenoFieldValue, type XenoFindMatch, type XenoFindNavigate, type XenoFindOptions, type XenoFindQuery, type XenoFindReplace, type XenoFindResults, type XenoFindStatus, type XenoGradient, type XenoGraph, type XenoGraphOp, XenoGraphScale, type XenoGraphScaleGeometry, type XenoGraphScaleState, type XenoGraphSelection, type XenoGraphViewport, type XenoGraphZoom, type XenoHistoryEntry, type XenoHistoryIntent, type XenoHistoryMemory, type XenoHistoryOrder, type XenoHistoryScope, type XenoHistorySnapshot, type XenoHistorySource, type XenoHistoryState, type XenoHistoryStatus, type XenoInspectorEdit, type XenoInspectorField, type XenoInspectorSchema, type XenoInspectorSection, type XenoInspectorTarget, type XenoInspectorToken, type XenoKeyframeKind, type XenoLayerAdornment, type XenoLayerCap, type XenoLayerKind, type XenoLayerNode, type XenoLayerOp, type XenoLayerTree, type XenoNavigateRequest, type XenoNode, XenoNodeIndex, type XenoPort, type XenoPortDirection, type XenoPortRef, type XenoSectionAction, type XenoTangent, type XenoTangentMode, type XenoTangentSide, type XenoThreadActivate, type XenoThumbnailRequest, type XenoThumbnailResponse, type XenoTreeAction, type XenoTreeBadge, type XenoTreeCaps, type XenoTreeChildState, type XenoTreeChildrenResponse, type XenoTreeError, type XenoTreeGlyph, type XenoTreeIcon, type XenoTreeInvalidate, type XenoTreeMode, type XenoTreeModifiers, type XenoTreeNode, type XenoTreeNodeKind, type XenoTreeOp, type XenoTreeReveal, type XenoTreeSource, type XenoTreeSourceKind, type XenoTreeSourceState, type XenoViewportClient, type XenoViewportKey, type XenoViewportPointer, type XenoViewportSlot, type XenoViewportStats, type XenoViewportSurface, allFields, allIds, anchorStateOf, appliedCount, applyInspectorUi, applyPrecision, applyTangentDrag, ariaSliderProps, boxesIntersect, canConnect, canRedo, canUndo, childStateOf, clamp, clampAlpha, clampTangent, collectDescendants, colorManifest, colorPanel, commentsManifest, commentsPanel, commitPolicyOf, componentsForSpace, computeSurfaceSize, constraintsOf, countIsMeaningful, createColorPanel, createCommentsPanel, createCurvePanel, createFindPanel, createHistoryPanel, createInspectorPanel, createLayersPanel, createNodeGraphPanel, createTreePanel, createViewportPanel, createsCycle, currentRowIndex, curveManifest, curvePanel, defaultIconFor, displayOrder, distanceToEdge, easeAt, easingPreset, edgeBounds, edgeKey, edgePath, edgesToReplace, edgesTouching, evaluateChannel, fieldMatches, findField, findManifest, findPanel, findPort, findSectionOf, fitToBounds, fitValueRange, flattenVisible, focusOrder, formatAge, formatNumeric, getAncestors, graphBounds, groupMatches, hasCap, hasDraggableTangents, hasTreeCap, hexFromHsv, highlightSegments, historyManifest, historyPanel, hslToRgb, hsvFromHex, hsvToRgb, inspectorManifest, inspectorPanel, inspectorRuntimeComponents, inspectorViewManifest, inspectorViewState, interpolateSegment, isAncestorOf, isAnimated, isCollapsed, isColorSpace, isContainer, isEmptyContainer, isFieldEditable, isFieldMixed, isJumpable, isKeyEditable, isLayerColumn, isMixedValue, isOrphaned, isTreeContainer, isValidHex, isViewStateOp, keyRefId, layersManifest, layersPanel, liveEdges, makeColorValue, matchesFilter, messagesOf, mirrorTangent, nodeBounds, nodegraphManifest, nodegraphPanel, normalizeColorInput, normalizeHistoryState, normalizeKindKey, normalizeNumeric, normalizeRanges, normalizeSchema, normalizeTree, nudgeValue, oklchToRgb, parseCssColor, parseHexColor, parseNumeric, passesFilter, patternError, portHasRoom, portRefId, positionToValue, projectRows, projectSections, rangeBetween, resolveDropTarget, resolveIconName, rgbToHex, rgbToHsl, rgbToHsv, rgbToOklch, rgbaToCssHex, roundRgb, rowIndexOf, rowIndexOfEntry, safeAddColorStop, safeAddColorStops, sameKey, sampleChannel, scrubValue, segmentsAround, solveBezierT, sortThreads, surfaceChanged, tangentsForMode, tangentsToControlPoints, toViewportCoords, totalCount, treeManifest, treePanel, usesLogScale, valueExtent, valueToPosition, valuesEqual, viewportManifest, viewportPanel, visibleEdges, walkTree, wheelZoomFactor, zoomAboutCursor, zoomTimeAboutCursor, zoomValueAboutCursor };
@@ -1871,8 +1871,8 @@ function constraintsOf(field) {
1871
1871
  scale: field.scale
1872
1872
  };
1873
1873
  }
1874
- function parseNumeric(text) {
1875
- const trimmed = String(text).trim();
1874
+ function parseNumeric(text2) {
1875
+ const trimmed = String(text2).trim();
1876
1876
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
1877
1877
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
1878
1878
  if (!match) return { ok: false, reason: "nan" };
@@ -1897,8 +1897,8 @@ function normalizeNumeric(value, c) {
1897
1897
  function formatNumeric(value, field) {
1898
1898
  if (!Number.isFinite(value)) return "";
1899
1899
  const precision = field.precision;
1900
- const text = typeof precision === "number" && Number.isFinite(precision) ? value.toFixed(Math.max(0, Math.floor(precision))) : String(value);
1901
- return field.unit ? `${text}${field.unit}` : text;
1900
+ const text2 = typeof precision === "number" && Number.isFinite(precision) ? value.toFixed(Math.max(0, Math.floor(precision))) : String(value);
1901
+ return field.unit ? `${text2}${field.unit}` : text2;
1902
1902
  }
1903
1903
  function usesLogScale(c) {
1904
1904
  return c.scale === "log" && typeof c.min === "number" && typeof c.max === "number" && c.min > 0 && c.max > c.min;
@@ -2232,44 +2232,44 @@ var InspectorPanelController = class {
2232
2232
  if (!field || !isFieldEditable(field)) return;
2233
2233
  const mixed = isFieldMixed(field);
2234
2234
  const value = this.getFieldValue(fieldId);
2235
- const text = mixed ? "" : field.type === "number" && typeof value === "number" ? formatNumeric(value, field) : value === void 0 || value === null ? "" : String(value);
2236
- this.drafts.set(fieldId, { text, original: value });
2235
+ const text2 = mixed ? "" : field.type === "number" && typeof value === "number" ? formatNumeric(value, field) : value === void 0 || value === null ? "" : String(value);
2236
+ this.drafts.set(fieldId, { text: text2, original: value });
2237
2237
  this.focusedFieldId = fieldId;
2238
2238
  this.emitFieldFocus(fieldId);
2239
2239
  this.notify();
2240
2240
  }
2241
2241
  /** Update the draft text (typing). Emits nothing under the `blur` policy. */
2242
- setDraft(fieldId, text) {
2242
+ setDraft(fieldId, text2) {
2243
2243
  const draft = this.drafts.get(fieldId);
2244
2244
  if (!draft) {
2245
- this.drafts.set(fieldId, { text, original: this.getFieldValue(fieldId) });
2245
+ this.drafts.set(fieldId, { text: text2, original: this.getFieldValue(fieldId) });
2246
2246
  } else {
2247
- draft.text = text;
2247
+ draft.text = text2;
2248
2248
  }
2249
2249
  const field = this.getField(fieldId);
2250
2250
  if (field && commitPolicyOf(field) === "live") {
2251
- const parsed = this.parseDraft(fieldId, text);
2251
+ const parsed = this.parseDraft(fieldId, text2);
2252
2252
  if (parsed.ok) this.emitSet(fieldId, parsed.value);
2253
2253
  }
2254
2254
  this.notify();
2255
2255
  }
2256
2256
  /** Parse a draft string into a field-appropriate value. */
2257
- parseDraft(fieldId, text) {
2257
+ parseDraft(fieldId, text2) {
2258
2258
  const field = this.getField(fieldId);
2259
2259
  if (!field) return { ok: false };
2260
2260
  if (field.type === "number") {
2261
- const parsed = parseNumeric(text);
2261
+ const parsed = parseNumeric(text2);
2262
2262
  if (!parsed.ok) return { ok: false };
2263
2263
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOf(field)) };
2264
2264
  }
2265
2265
  if (field.type === "json") {
2266
2266
  try {
2267
- return { ok: true, value: JSON.parse(text) };
2267
+ return { ok: true, value: JSON.parse(text2) };
2268
2268
  } catch {
2269
2269
  return { ok: false };
2270
2270
  }
2271
2271
  }
2272
- return { ok: true, value: text };
2272
+ return { ok: true, value: text2 };
2273
2273
  }
2274
2274
  /**
2275
2275
  * Commit the draft (Enter / blur). Invalid input reverts instead of committing garbage.
@@ -2810,6 +2810,271 @@ var InspectorPanelController = class {
2810
2810
 
2811
2811
  // src/edit/inspector/react/InspectorPanelView.tsx
2812
2812
  import { useCallback as useCallback3, useSyncExternalStore as useSyncExternalStore2 } from "react";
2813
+
2814
+ // src/edit/inspector/declaration.ts
2815
+ function inspectorViewState(c) {
2816
+ const targets = c.getTargets();
2817
+ const multi = targets.length > 1;
2818
+ const blocked = multi && !c.canEditTargets();
2819
+ const showKeyframes = c.areKeyframesShown();
2820
+ const showTokens = c.areTokensShown();
2821
+ const focusedId = c.getFocused();
2822
+ const labelWidth = c.getLabelWidth();
2823
+ const sections = c.getSections().map((p) => {
2824
+ const s = p.section;
2825
+ return {
2826
+ id: s.id,
2827
+ title: s.title,
2828
+ collapsible: !!s.collapsible,
2829
+ collapsed: p.collapsed,
2830
+ hasEnabled: s.enabled !== void 0,
2831
+ enabled: s.enabled?.value,
2832
+ badge: s.badge,
2833
+ actions: (s.actions ?? []).map((a) => ({ id: a.id, sectionId: s.id, label: a.label, glyph: a.label.slice(0, 1), disabled: a.disabled })),
2834
+ hasAddMenu: !!s.addMenu && s.addMenu.length > 0,
2835
+ hasActions: (s.actions?.length ?? 0) > 0 || !!s.addMenu && s.addMenu.length > 0,
2836
+ addLabel: `Add to ${s.title}`,
2837
+ addOptions: (s.addMenu ?? []).map((i) => ({ value: i.id, label: i.label, disabled: i.disabled, group: i.group })),
2838
+ showEmptyHint: p.empty && !!s.emptyHint,
2839
+ emptyHint: s.emptyHint,
2840
+ labelWidth,
2841
+ fields: p.fields.map((field) => {
2842
+ const anim = field.anim;
2843
+ const sk = showKeyframes && !!anim;
2844
+ const bound = !!field.token?.tokenId;
2845
+ const easing = anim?.easing ?? "";
2846
+ const easingOptions = anim?.easingOptions ?? [];
2847
+ const easingAll = easingOptions.some((o) => o.value === easing) ? easingOptions : [{ value: easing, label: easing || "\u2014" }, ...easingOptions];
2848
+ const tokenName = field.token?.tokenName ?? field.token?.tokenId ?? void 0;
2849
+ return {
2850
+ id: field.id,
2851
+ sectionId: s.id,
2852
+ label: field.label,
2853
+ title: field.description ?? field.label,
2854
+ focused: focusedId === field.id,
2855
+ bound,
2856
+ unbound: !bound,
2857
+ tokenLabel: tokenName,
2858
+ tokenTitle: bound ? `Bound to ${tokenName}` : void 0,
2859
+ showKeyframes: sk,
2860
+ animated: sk && !!anim.animated,
2861
+ onKeyframe: sk && !!anim.onKeyframe,
2862
+ prevDisabled: sk ? anim.canPrev === false : false,
2863
+ nextDisabled: sk ? anim.canNext === false : false,
2864
+ keyframeLabel: sk && anim.onKeyframe ? "Remove keyframe" : "Add keyframe",
2865
+ keyframeOp: sk && anim.onKeyframe ? "remove" : "add",
2866
+ showEasing: sk && !!anim.animated && !!anim.onKeyframe,
2867
+ easing,
2868
+ easingOptions: easingAll.map((o) => ({ value: o.value, label: o.label })),
2869
+ keyframeChip: sk ? c.keyframeChip(field.id) : "",
2870
+ showToken: showTokens && field.token?.bindable !== false,
2871
+ tokenActionLabel: bound ? "Unbind token" : "Bind token",
2872
+ showReset: field.defaultValue !== void 0 && isFieldEditable(field),
2873
+ showInfoNote: !!field.description && field.type === "info",
2874
+ infoNote: field.description,
2875
+ field
2876
+ };
2877
+ })
2878
+ };
2879
+ });
2880
+ return {
2881
+ searchShown: c.isSearchShown(),
2882
+ filter: c.getFilter(),
2883
+ hasTarget: targets.length > 0,
2884
+ targetLabel: multi ? `${targets.length} selected` : targets[0]?.label ?? targets[0]?.id ?? "",
2885
+ multi,
2886
+ banner: blocked ? `${targets.length} selected \u2014 multi-editing is disabled` : `${targets.length} selected \xB7 shared values shown`,
2887
+ sections,
2888
+ empty: sections.length === 0,
2889
+ emptyTitle: targets.length === 0 ? "Nothing selected" : "No properties"
2890
+ };
2891
+ }
2892
+ function applyInspectorUi(c, intent) {
2893
+ switch (intent.kind) {
2894
+ case "filter":
2895
+ c.setFilter(intent.value);
2896
+ break;
2897
+ case "toggleSectionCollapsed":
2898
+ c.toggleSectionCollapsed(intent.sectionId);
2899
+ break;
2900
+ case "toggleSectionEnabled":
2901
+ c.toggleSectionEnabled(intent.sectionId);
2902
+ break;
2903
+ case "runAction":
2904
+ c.runAction(intent.sectionId, intent.actionId);
2905
+ break;
2906
+ case "addItem":
2907
+ c.addItem(intent.sectionId, intent.itemId);
2908
+ break;
2909
+ case "focusField":
2910
+ c.focusField(intent.fieldId);
2911
+ break;
2912
+ case "contextMenu":
2913
+ c.requestContextMenu(intent.fieldId, intent.x, intent.y);
2914
+ break;
2915
+ case "keyframe":
2916
+ c.keyframe(intent.fieldId, intent.op);
2917
+ break;
2918
+ case "setEasing":
2919
+ c.keyframe(intent.fieldId, "setEasing", intent.easing);
2920
+ break;
2921
+ case "bindToken": {
2922
+ const bound = !!c.getSections().flatMap((p) => p.fields).find((f) => f.id === intent.fieldId)?.token?.tokenId;
2923
+ c.bindToken(intent.fieldId, bound ? null : c.getTokens()[0]?.id ?? null);
2924
+ break;
2925
+ }
2926
+ case "resetField":
2927
+ c.resetField(intent.fieldId);
2928
+ break;
2929
+ }
2930
+ }
2931
+ var inspectorViewManifest = {
2932
+ id: inspectorManifest.id,
2933
+ version: "1.0.0",
2934
+ title: "Inspector",
2935
+ category: "edit",
2936
+ layout: { slot: "side" },
2937
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The InspectorViewState the runtime derives from the controller on every draw." }],
2938
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, sectionId?, fieldId?, \u2026 } \u2014 applied by the runtime to the controller." }],
2939
+ commands: []
2940
+ };
2941
+ var EMPTY = { searchShown: true, filter: "", hasTarget: false, targetLabel: "", multi: false, banner: "", sections: [], empty: true, emptyTitle: "Nothing selected" };
2942
+ var BEHAVIOURS = {
2943
+ "edit/inspectorPanelView": (inputs) => inputs.view ?? EMPTY
2944
+ };
2945
+ var text = (b) => ({ component: "text", props: { text: b } });
2946
+ var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
2947
+ var iconButton = (glyph, label, emit, extra = {}) => ({
2948
+ component: "chrome/IconButton",
2949
+ ...extra.when ? { when: extra.when } : {},
2950
+ props: { size: { value: "sm" }, icon: { node: { component: `inspector/${glyph}` } }, label, ...extra.props ?? {} },
2951
+ on: { onClick: emit }
2952
+ });
2953
+ var INSPECTOR_DECLARATION = {
2954
+ format: "xeno.block@1",
2955
+ manifest: inspectorViewManifest,
2956
+ behaviour: "edit/inspectorPanelView",
2957
+ composition: {
2958
+ component: "chrome/Column",
2959
+ props: { className: { value: "xeno-inspector-panel" } },
2960
+ children: [
2961
+ {
2962
+ component: "chrome/Stack",
2963
+ when: { input: "view.searchShown" },
2964
+ props: { direction: { value: "row" }, gap: { value: 6 } },
2965
+ children: [
2966
+ { component: "chrome/SearchField", props: { value: { input: "view.filter" }, placeholder: { value: "Search properties" }, label: { value: "Search properties" } }, on: { onChange: ui("filter", { value: { arg: "" } }) } },
2967
+ { component: "chrome/Badge", when: { input: "view.hasTarget" }, children: [text({ input: "view.targetLabel" })] }
2968
+ ]
2969
+ },
2970
+ { component: "chrome/Stack", when: { input: "view.multi" }, props: { pad: { value: "inset" } }, children: [{ component: "chrome/Text", props: { role: { value: "caption" } }, children: [text({ input: "view.banner" })] }] },
2971
+ {
2972
+ component: "chrome/ScrollArea",
2973
+ children: [
2974
+ {
2975
+ component: "inspector/Properties",
2976
+ children: [
2977
+ {
2978
+ component: "chrome/Section",
2979
+ each: { input: "view.sections", key: "id" },
2980
+ props: {
2981
+ title: { item: "title" },
2982
+ flush: { value: true },
2983
+ collapsible: { item: "collapsible" },
2984
+ collapsed: { item: "collapsed" },
2985
+ enabled: { item: "enabled" },
2986
+ badge: { node: { component: "chrome/Badge", when: { item: "badge" }, children: [text({ item: "badge" })] } },
2987
+ actions: {
2988
+ node: {
2989
+ component: "fragment",
2990
+ when: { item: "hasActions" },
2991
+ children: [
2992
+ {
2993
+ component: "chrome/IconButton",
2994
+ each: { item: "actions", key: "id" },
2995
+ props: { size: { value: "sm" }, icon: { node: { component: "inspector/ActionGlyph", props: { glyph: { item: "glyph" } } } }, label: { item: "label" }, disabled: { item: "disabled" } },
2996
+ on: { onClick: ui("runAction", { sectionId: { item: "sectionId" }, actionId: { item: "id" } }) }
2997
+ },
2998
+ {
2999
+ component: "chrome/Select",
3000
+ when: { item: "hasAddMenu" },
3001
+ props: { size: { value: "sm" }, placeholder: { value: "\uFF0B" }, options: { item: "addOptions" }, value: { value: "" }, label: { item: "addLabel" } },
3002
+ on: { onChange: ui("addItem", { sectionId: { item: "id" }, itemId: { arg: "" } }) }
3003
+ }
3004
+ ]
3005
+ }
3006
+ }
3007
+ },
3008
+ on: {
3009
+ onCollapsedChange: ui("toggleSectionCollapsed", { sectionId: { item: "id" } }),
3010
+ onEnabledChange: ui("toggleSectionEnabled", { sectionId: { item: "id" } })
3011
+ },
3012
+ children: [
3013
+ { component: "chrome/Stack", when: { item: "showEmptyHint" }, props: { pad: { value: "inset" } }, children: [{ component: "chrome/Text", props: { role: { value: "caption" } }, children: [text({ item: "emptyHint" })] }] },
3014
+ {
3015
+ component: "chrome/FieldGroup",
3016
+ props: { labelWidth: { item: "labelWidth" } },
3017
+ children: [
3018
+ {
3019
+ component: "fragment",
3020
+ each: { item: "fields", key: "id" },
3021
+ children: [
3022
+ {
3023
+ component: "chrome/Field",
3024
+ props: {
3025
+ label: { item: "label" },
3026
+ title: { item: "title" },
3027
+ focused: { item: "focused" },
3028
+ leading: {
3029
+ node: {
3030
+ component: "fragment",
3031
+ when: { item: "showKeyframes" },
3032
+ children: [
3033
+ iconButton("StopwatchIcon", { value: "Toggle animation" }, ui("keyframe", { fieldId: { item: "id" }, op: { value: "toggleAnim" } }), { props: { active: { item: "animated" } } }),
3034
+ iconButton("PrevIcon", { value: "Previous keyframe" }, ui("keyframe", { fieldId: { item: "id" }, op: { value: "prev" } }), { when: { item: "animated" }, props: { disabled: { item: "prevDisabled" } } }),
3035
+ { component: "chrome/IconButton", when: { item: "animated" }, props: { size: { value: "sm" }, icon: { node: { component: "inspector/DiamondIcon", props: { filled: { item: "onKeyframe" } } } }, label: { item: "keyframeLabel" } }, on: { onClick: ui("keyframe", { fieldId: { item: "id" }, op: { item: "keyframeOp" } }) } },
3036
+ iconButton("NextIcon", { value: "Next keyframe" }, ui("keyframe", { fieldId: { item: "id" }, op: { value: "next" } }), { when: { item: "animated" }, props: { disabled: { item: "nextDisabled" } } })
3037
+ ]
3038
+ }
3039
+ },
3040
+ trailing: {
3041
+ node: {
3042
+ component: "fragment",
3043
+ children: [
3044
+ { component: "chrome/Select", when: { item: "showEasing" }, props: { size: { value: "sm" }, options: { item: "easingOptions" }, value: { item: "easing" }, label: { value: "Easing" } }, on: { onChange: ui("setEasing", { fieldId: { item: "id" }, easing: { arg: "" } }) } },
3045
+ { component: "chrome/Badge", when: { item: "showKeyframes" }, props: { size: { value: "sm" } }, children: [text({ item: "keyframeChip" })] },
3046
+ iconButton("LinkIcon", { item: "tokenActionLabel" }, ui("bindToken", { fieldId: { item: "id" } }), { when: { item: "showToken" }, props: { active: { item: "bound" } } }),
3047
+ iconButton("ResetIcon", { value: "Reset to default" }, ui("resetField", { fieldId: { item: "id" } }), { when: { item: "showReset" } })
3048
+ ]
3049
+ }
3050
+ }
3051
+ },
3052
+ on: {
3053
+ onLabelClick: ui("focusField", { fieldId: { item: "id" } }),
3054
+ onContextMenu: ui("contextMenu", { fieldId: { item: "id" }, x: { arg: "" }, y: { arg: "", index: 1 } })
3055
+ },
3056
+ children: [
3057
+ { component: "chrome/Badge", when: { item: "bound" }, props: { title: { item: "tokenTitle" } }, children: [text({ item: "tokenLabel" })] },
3058
+ { component: "inspector/FieldWidget", when: { item: "unbound" }, props: { field: { item: "field" } } }
3059
+ ]
3060
+ },
3061
+ { component: "chrome/Stack", when: { item: "showInfoNote" }, props: { pad: { value: "inset" } }, children: [{ component: "chrome/Text", props: { role: { value: "caption" } }, children: [text({ item: "infoNote" })] }] }
3062
+ ]
3063
+ }
3064
+ ]
3065
+ }
3066
+ ]
3067
+ },
3068
+ { component: "chrome/EmptyState", when: { input: "view.empty" }, props: { title: { input: "view.emptyTitle" } } }
3069
+ ]
3070
+ }
3071
+ ]
3072
+ }
3073
+ ]
3074
+ }
3075
+ };
3076
+
3077
+ // src/edit/inspector/react/InspectorPanelView.tsx
2813
3078
  import { Badge as Badge2, Column as Column2, EmptyState as EmptyState2, Field, FieldGroup, IconButton as IconButton3, ScrollArea as ScrollArea2, SearchField as SearchField2, Section, Select as Select2, Stack as Stack2, Text as Text3 } from "@xenosystem/components/chrome";
2814
3079
 
2815
3080
  // src/edit/inspector/react/icons.tsx
@@ -2979,7 +3244,7 @@ function NumberWidget({ field, controller, component }) {
2979
3244
  },
2980
3245
  [controller, editable, field.id, component]
2981
3246
  );
2982
- const text = component !== void 0 ? (() => {
3247
+ const text2 = component !== void 0 ? (() => {
2983
3248
  const raw = controller.getFieldValue(field.id);
2984
3249
  const v = Array.isArray(raw) ? raw[component] : void 0;
2985
3250
  return typeof v === "number" ? formatNumeric(v, field) : "";
@@ -2991,7 +3256,7 @@ function NumberWidget({ field, controller, component }) {
2991
3256
  Input2,
2992
3257
  {
2993
3258
  inputMode: "decimal",
2994
- value: text,
3259
+ value: text2,
2995
3260
  placeholder: mixed ? controller.getMixedPlaceholder() : field.placeholder,
2996
3261
  mixed,
2997
3262
  readOnly: !editable,
@@ -3180,8 +3445,20 @@ function InspectorPanelView({ controller }) {
3180
3445
  const subscribe = useCallback3((cb) => controller.subscribe(cb), [controller]);
3181
3446
  const getSnapshot = useCallback3(() => controller.getRevision(), [controller]);
3182
3447
  useSyncExternalStore2(subscribe, getSnapshot, getSnapshot);
3183
- const sections = controller.getSections();
3184
- const targets = controller.getTargets();
3448
+ const view = inspectorViewState(controller);
3449
+ return /* @__PURE__ */ jsxs5(Column2, { className: "xeno-inspector-panel", children: [
3450
+ view.searchShown ? /* @__PURE__ */ jsxs5(Stack2, { direction: "row", gap: 6, children: [
3451
+ /* @__PURE__ */ jsx6(SearchField2, { value: view.filter, onChange: (v) => controller.setFilter(v), placeholder: "Search properties", label: "Search properties" }),
3452
+ view.hasTarget ? /* @__PURE__ */ jsx6(Badge2, { children: view.targetLabel }) : null
3453
+ ] }) : null,
3454
+ view.multi ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: view.banner }) }) : null,
3455
+ /* @__PURE__ */ jsx6(ScrollArea2, { children: /* @__PURE__ */ jsxs5(InspectorProperties, { controller, children: [
3456
+ view.sections.map((section) => /* @__PURE__ */ jsx6(SectionView, { section, controller }, section.id)),
3457
+ view.empty ? /* @__PURE__ */ jsx6(EmptyState2, { title: view.emptyTitle }) : null
3458
+ ] }) })
3459
+ ] });
3460
+ }
3461
+ function InspectorProperties({ controller, children }) {
3185
3462
  const onKeyDown = useCallback3(
3186
3463
  (e) => {
3187
3464
  if (controller.handleKey({ key: e.key, ctrl: e.ctrlKey, meta: e.metaKey, shift: e.shiftKey, alt: e.altKey })) {
@@ -3191,83 +3468,66 @@ function InspectorPanelView({ controller }) {
3191
3468
  },
3192
3469
  [controller]
3193
3470
  );
3194
- const multi = targets.length > 1;
3195
- const blocked = multi && !controller.canEditTargets();
3196
- return /* @__PURE__ */ jsxs5(Column2, { className: "xeno-inspector-panel", children: [
3197
- controller.isSearchShown() ? /* @__PURE__ */ jsxs5(Stack2, { direction: "row", gap: 6, children: [
3198
- /* @__PURE__ */ jsx6(SearchField2, { value: controller.getFilter(), onChange: (v) => controller.setFilter(v), placeholder: "Search properties", label: "Search properties" }),
3199
- targets.length > 0 ? /* @__PURE__ */ jsx6(Badge2, { children: multi ? `${targets.length} selected` : targets[0]?.label ?? targets[0]?.id }) : null
3200
- ] }) : null,
3201
- multi ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: blocked ? `${targets.length} selected \u2014 multi-editing is disabled` : `${targets.length} selected \xB7 shared values shown` }) }) : null,
3202
- /* @__PURE__ */ jsx6(ScrollArea2, { children: /* @__PURE__ */ jsxs5("div", { onKeyDown, tabIndex: 0, role: "group", "aria-label": "Properties", className: "xeno-inspector-properties", children: [
3203
- sections.map((projected) => /* @__PURE__ */ jsx6(SectionView, { projected, controller }, projected.section.id)),
3204
- sections.length === 0 ? /* @__PURE__ */ jsx6(EmptyState2, { title: targets.length === 0 ? "Nothing selected" : "No properties" }) : null
3205
- ] }) })
3206
- ] });
3471
+ return /* @__PURE__ */ jsx6("div", { onKeyDown, tabIndex: 0, role: "group", "aria-label": "Properties", className: "xeno-inspector-properties", children });
3472
+ }
3473
+ function ActionGlyph({ glyph }) {
3474
+ return /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: glyph });
3207
3475
  }
3208
- function SectionView({ projected, controller }) {
3209
- const { section, fields, collapsed, empty } = projected;
3476
+ function SectionView({ section, controller }) {
3477
+ const ui2 = (intent) => applyInspectorUi(controller, intent);
3478
+ const hasActions = section.actions.length > 0 || section.hasAddMenu;
3210
3479
  return /* @__PURE__ */ jsxs5(
3211
3480
  Section,
3212
3481
  {
3213
3482
  title: section.title,
3214
3483
  flush: true,
3215
3484
  collapsible: section.collapsible,
3216
- collapsed,
3217
- onCollapsedChange: () => controller.toggleSectionCollapsed(section.id),
3218
- enabled: section.enabled?.value,
3219
- onEnabledChange: section.enabled ? () => controller.toggleSectionEnabled(section.id) : void 0,
3485
+ collapsed: section.collapsed,
3486
+ onCollapsedChange: () => ui2({ kind: "toggleSectionCollapsed", sectionId: section.id }),
3487
+ enabled: section.enabled,
3488
+ onEnabledChange: () => ui2({ kind: "toggleSectionEnabled", sectionId: section.id }),
3220
3489
  badge: section.badge ? /* @__PURE__ */ jsx6(Badge2, { children: section.badge }) : null,
3221
- actions: /* @__PURE__ */ jsxs5(Fragment3, { children: [
3222
- section.actions?.map((action) => /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: action.label.slice(0, 1) }), label: action.label, disabled: action.disabled, onClick: () => controller.runAction(section.id, action.id) }, action.id)),
3223
- section.addMenu && section.addMenu.length > 0 ? /* @__PURE__ */ jsx6(AddMenu, { section, controller }) : null
3224
- ] }),
3490
+ actions: hasActions ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3491
+ section.actions.map((action) => /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ActionGlyph, { glyph: action.glyph }), label: action.label, disabled: action.disabled, onClick: () => ui2({ kind: "runAction", sectionId: section.id, actionId: action.id }) }, action.id)),
3492
+ section.hasAddMenu ? /* @__PURE__ */ jsx6(Select2, { size: "sm", placeholder: "\uFF0B", options: section.addOptions, value: "", label: section.addLabel, onChange: (id) => ui2({ kind: "addItem", sectionId: section.id, itemId: id }) }) : null
3493
+ ] }) : null,
3225
3494
  children: [
3226
- empty && section.emptyHint ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: section.emptyHint }) }) : null,
3227
- /* @__PURE__ */ jsx6(FieldGroup, { labelWidth: controller.getLabelWidth(), children: fields.map((field) => /* @__PURE__ */ jsx6(FieldRow, { field, controller }, field.id)) })
3495
+ section.showEmptyHint ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: section.emptyHint }) }) : null,
3496
+ /* @__PURE__ */ jsx6(FieldGroup, { labelWidth: section.labelWidth, children: section.fields.map((row) => /* @__PURE__ */ jsx6(FieldRow, { row, controller }, row.id)) })
3228
3497
  ]
3229
3498
  }
3230
3499
  );
3231
3500
  }
3232
- function AddMenu({ section, controller }) {
3233
- const options = (section.addMenu ?? []).map((i) => ({ value: i.id, label: i.label, disabled: i.disabled, group: i.group }));
3234
- return /* @__PURE__ */ jsx6(Select2, { size: "sm", placeholder: "\uFF0B", options, value: "", label: `Add to ${section.title}`, onChange: (id) => controller.addItem(section.id, id) });
3235
- }
3236
- function FieldRow({ field, controller }) {
3237
- const focused = controller.getFocused() === field.id;
3238
- const bound = !!field.token?.tokenId;
3239
- const anim = field.anim;
3240
- const showKeyframes = controller.areKeyframesShown() && !!anim;
3241
- const easingOptions = anim?.easingOptions ?? [];
3242
- const easing = anim?.easing ?? "";
3243
- const easingAll = easingOptions.some((o) => o.value === easing) ? easingOptions : [{ value: easing, label: easing || "\u2014" }, ...easingOptions];
3501
+ function FieldRow({ row, controller }) {
3502
+ const ui2 = (intent) => applyInspectorUi(controller, intent);
3244
3503
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
3245
- /* @__PURE__ */ jsx6(
3504
+ /* @__PURE__ */ jsxs5(
3246
3505
  Field,
3247
3506
  {
3248
- label: field.label,
3249
- title: field.description ?? field.label,
3250
- focused,
3251
- onLabelClick: () => controller.focusField(field.id),
3252
- onContextMenu: (x, y) => controller.requestContextMenu(field.id, x, y),
3253
- leading: showKeyframes ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3254
- /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(StopwatchIcon, {}), label: "Toggle animation", active: anim.animated, onClick: () => controller.keyframe(field.id, "toggleAnim") }),
3255
- anim.animated ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3256
- /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(PrevIcon, {}), label: "Previous keyframe", disabled: anim.canPrev === false, onClick: () => controller.keyframe(field.id, "prev") }),
3257
- /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(DiamondIcon, { filled: !!anim.onKeyframe }), label: anim.onKeyframe ? "Remove keyframe" : "Add keyframe", onClick: () => controller.keyframe(field.id, anim.onKeyframe ? "remove" : "add") }),
3258
- /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(NextIcon, {}), label: "Next keyframe", disabled: anim.canNext === false, onClick: () => controller.keyframe(field.id, "next") })
3259
- ] }) : null
3507
+ label: row.label,
3508
+ title: row.title,
3509
+ focused: row.focused,
3510
+ onLabelClick: () => ui2({ kind: "focusField", fieldId: row.id }),
3511
+ onContextMenu: (x, y) => ui2({ kind: "contextMenu", fieldId: row.id, x, y }),
3512
+ leading: row.showKeyframes ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3513
+ /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(StopwatchIcon, {}), label: "Toggle animation", active: row.animated, onClick: () => ui2({ kind: "keyframe", fieldId: row.id, op: "toggleAnim" }) }),
3514
+ row.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(PrevIcon, {}), label: "Previous keyframe", disabled: row.prevDisabled, onClick: () => ui2({ kind: "keyframe", fieldId: row.id, op: "prev" }) }) : null,
3515
+ row.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(DiamondIcon, { filled: row.onKeyframe }), label: row.keyframeLabel, onClick: () => ui2({ kind: "keyframe", fieldId: row.id, op: row.keyframeOp }) }) : null,
3516
+ row.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(NextIcon, {}), label: "Next keyframe", disabled: row.nextDisabled, onClick: () => ui2({ kind: "keyframe", fieldId: row.id, op: "next" }) }) : null
3260
3517
  ] }) : null,
3261
3518
  trailing: /* @__PURE__ */ jsxs5(Fragment3, { children: [
3262
- showKeyframes && anim.animated && anim.onKeyframe ? /* @__PURE__ */ jsx6(Select2, { size: "sm", options: easingAll, value: easing, label: "Easing", onChange: (v) => controller.keyframe(field.id, "setEasing", v) }) : null,
3263
- showKeyframes ? /* @__PURE__ */ jsx6(Badge2, { size: "sm", children: controller.keyframeChip(field.id) }) : null,
3264
- controller.areTokensShown() && field.token?.bindable !== false ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(LinkIcon, {}), label: bound ? "Unbind token" : "Bind token", active: bound, onClick: () => controller.bindToken(field.id, bound ? null : controller.getTokens()[0]?.id ?? null) }) : null,
3265
- field.defaultValue !== void 0 && isFieldEditable(field) ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ResetIcon, {}), label: "Reset to default", onClick: () => controller.resetField(field.id) }) : null
3519
+ row.showEasing ? /* @__PURE__ */ jsx6(Select2, { size: "sm", options: row.easingOptions, value: row.easing, label: "Easing", onChange: (v) => ui2({ kind: "setEasing", fieldId: row.id, easing: v }) }) : null,
3520
+ row.showKeyframes ? /* @__PURE__ */ jsx6(Badge2, { size: "sm", children: row.keyframeChip }) : null,
3521
+ row.showToken ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(LinkIcon, {}), label: row.tokenActionLabel, active: row.bound, onClick: () => ui2({ kind: "bindToken", fieldId: row.id }) }) : null,
3522
+ row.showReset ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ResetIcon, {}), label: "Reset to default", onClick: () => ui2({ kind: "resetField", fieldId: row.id }) }) : null
3266
3523
  ] }),
3267
- children: bound ? /* @__PURE__ */ jsx6(Badge2, { title: `Bound to ${field.token?.tokenName ?? field.token?.tokenId}`, children: field.token?.tokenName ?? field.token?.tokenId }) : /* @__PURE__ */ jsx6(FieldWidget, { field, controller })
3524
+ children: [
3525
+ row.bound ? /* @__PURE__ */ jsx6(Badge2, { title: row.tokenTitle, children: row.tokenLabel }) : null,
3526
+ row.unbound ? /* @__PURE__ */ jsx6(FieldWidget, { field: row.field, controller }) : null
3527
+ ]
3268
3528
  }
3269
3529
  ),
3270
- field.description && field.type === "info" ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: field.description }) }) : null
3530
+ row.showInfoNote ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: row.infoNote }) }) : null
3271
3531
  ] });
3272
3532
  }
3273
3533
  function FieldWidget({
@@ -3308,6 +3568,19 @@ function FieldWidget({
3308
3568
  return /* @__PURE__ */ jsx6(JsonWidget, { field, controller });
3309
3569
  }
3310
3570
  }
3571
+ function inspectorRuntimeComponents(controller) {
3572
+ return {
3573
+ "inspector/FieldWidget": ({ field }) => /* @__PURE__ */ jsx6(FieldWidget, { field, controller }),
3574
+ "inspector/Properties": ({ children }) => /* @__PURE__ */ jsx6(InspectorProperties, { controller, children }),
3575
+ "inspector/ActionGlyph": ActionGlyph,
3576
+ "inspector/StopwatchIcon": StopwatchIcon,
3577
+ "inspector/DiamondIcon": DiamondIcon,
3578
+ "inspector/PrevIcon": PrevIcon,
3579
+ "inspector/NextIcon": NextIcon,
3580
+ "inspector/LinkIcon": LinkIcon,
3581
+ "inspector/ResetIcon": ResetIcon
3582
+ };
3583
+ }
3311
3584
 
3312
3585
  // src/edit/inspector/panel.ts
3313
3586
  function createInspectorPanel(options = {}) {
@@ -8314,11 +8587,11 @@ var FindController = class {
8314
8587
  * @param text - The new query text.
8315
8588
  * @param immediate - Skip the debounce (a paste, or pressing Enter).
8316
8589
  */
8317
- setText(text, immediate = false) {
8318
- if (text === this.text) return;
8319
- this.text = text;
8590
+ setText(text2, immediate = false) {
8591
+ if (text2 === this.text) return;
8592
+ this.text = text2;
8320
8593
  this.index = -1;
8321
- if (text === "") {
8594
+ if (text2 === "") {
8322
8595
  this.clearResults("idle");
8323
8596
  this.emitQuery(true);
8324
8597
  return;
@@ -8346,8 +8619,8 @@ var FindController = class {
8346
8619
  this.notify();
8347
8620
  }
8348
8621
  /** The replacement text. Never emitted on its own — a replacement is not a search. */
8349
- setReplaceText(text) {
8350
- this.replaceText = text;
8622
+ setReplaceText(text2) {
8623
+ this.replaceText = text2;
8351
8624
  this.notify();
8352
8625
  }
8353
8626
  /** Show or hide the replace row. */
@@ -11983,12 +12256,12 @@ var TreeController = class {
11983
12256
  }
11984
12257
  if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
11985
12258
  const fresh = now - this.typeAhead.at > this.typeAheadMs;
11986
- const text = (fresh ? "" : this.typeAhead.text) + event.key.toLowerCase();
11987
- this.typeAhead = { text, at: now };
12259
+ const text2 = (fresh ? "" : this.typeAhead.text) + event.key.toLowerCase();
12260
+ this.typeAhead = { text: text2, at: now };
11988
12261
  const from = fresh ? current : current + 1;
11989
12262
  for (let i = 0; i < rows.length; i += 1) {
11990
12263
  const candidate = rows[(from + i) % rows.length];
11991
- if (candidate.node.label.toLowerCase().startsWith(text)) {
12264
+ if (candidate.node.label.toLowerCase().startsWith(text2)) {
11992
12265
  this.focusedId = candidate.id;
11993
12266
  this.select([candidate.id]);
11994
12267
  return true;
@@ -12014,9 +12287,9 @@ var TreeController = class {
12014
12287
  return true;
12015
12288
  }
12016
12289
  /** Update the rename draft. */
12017
- updateRenameDraft(text) {
12290
+ updateRenameDraft(text2) {
12018
12291
  if (!this.renaming) return;
12019
- this.renaming = { ...this.renaming, draft: text };
12292
+ this.renaming = { ...this.renaming, draft: text2 };
12020
12293
  this.notify();
12021
12294
  }
12022
12295
  /**
@@ -13208,6 +13481,8 @@ export {
13208
13481
  HistoryPanelController,
13209
13482
  HistoryPanelView,
13210
13483
  IMPLEMENTED_TREE_MODES,
13484
+ BEHAVIOURS as INSPECTOR_BEHAVIOURS,
13485
+ INSPECTOR_DECLARATION,
13211
13486
  InspectorPanelController,
13212
13487
  InspectorPanelView,
13213
13488
  LAYER_COLUMNS,
@@ -13233,6 +13508,7 @@ export {
13233
13508
  allIds,
13234
13509
  anchorStateOf,
13235
13510
  appliedCount,
13511
+ applyInspectorUi,
13236
13512
  applyPrecision,
13237
13513
  applyTangentDrag,
13238
13514
  ariaSliderProps,
@@ -13306,6 +13582,9 @@ export {
13306
13582
  hsvToRgb,
13307
13583
  inspectorManifest,
13308
13584
  inspectorPanel,
13585
+ inspectorRuntimeComponents,
13586
+ inspectorViewManifest,
13587
+ inspectorViewState,
13309
13588
  interpolateSegment,
13310
13589
  isAncestorOf,
13311
13590
  isAnimated,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenosystem/blocks",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "XENO Blocks — rung 3 of the front ladder: components composed into a contract-bearing unit (manifest: ports, intents, commands, agent surface), rendered by @xenosystem/workbench. ONE package, one subpath per family (/auth, /edit, /data, /time, /ops, /trust, /agent), one named export per block — XENO PACKAGE NAMING - STANDARD.md.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@duckdb/duckdb-wasm": ">=1.28.0",
62
- "@xenosystem/block-sdk": ">=0.3.2 <1",
62
+ "@xenosystem/block-sdk": ">=0.3.3 <1",
63
63
  "@xenosystem/components": ">=0.16.0 <1",
64
64
  "@xenosystem/data-core": "^0.1.0",
65
65
  "@xenosystem/elements-react": ">=0.0.1",
@@ -75,7 +75,7 @@
75
75
  "@types/node": "^22.0.0",
76
76
  "@types/react": "^19.0.0",
77
77
  "@types/react-dom": "^19.0.0",
78
- "@xenosystem/block-sdk": "^0.3.2",
78
+ "@xenosystem/block-sdk": "^0.3.3",
79
79
  "@xenosystem/components": "^0.16.0",
80
80
  "@xenosystem/data-core": "^0.1.0",
81
81
  "@xenosystem/elements-react": "0.0.1",