diff --git a/packages/core/src/widgets/types.ts b/packages/core/src/widgets/types.ts index 7575e3cf..e86effd7 100644 --- a/packages/core/src/widgets/types.ts +++ b/packages/core/src/widgets/types.ts @@ -17,2402 +17,243 @@ * @see docs/widgets/index.md */ -import type { EasingInput } from "../animation/types.js"; -import type { ConstraintExpr } from "../constraints/types.js"; -import type { FocusConfig } from "../focus/styles.js"; -import type { SpacingValue } from "../layout/spacing-scale.js"; -import type { - DisplayConstraint, - LayoutConstraints, - SizeConstraint, - SizeConstraintAtom, -} from "../layout/types.js"; -import type { InstanceId } from "../runtime/instance.js"; -import type { ThemeOverrides } from "../theme/extend.js"; -import type { ThemeDefinition } from "../theme/tokens.js"; -import type { WidgetSize, WidgetTone, WidgetVariant } from "../ui/designTokens.js"; -import type { TextStyle } from "./style.js"; - -/** Cross-axis alignment for stack layouts. */ -export type Align = "start" | "center" | "end" | "stretch"; -export type Overflow = "visible" | "hidden" | "scroll"; - -/** - * Spacing props for padding and margin. - * Accepts either a number (cells) or a spacing key ("xs", "sm", "md", "lg", "xl", "2xl"). - */ -export type SpacingProps = Readonly<{ - /** All sides padding */ - p?: SpacingValue; - /** Horizontal padding */ - px?: SpacingValue; - /** Vertical padding */ - py?: SpacingValue; - /** Top padding */ - pt?: SpacingValue; - /** Bottom padding */ - pb?: SpacingValue; - /** Left padding */ - pl?: SpacingValue; - /** Right padding */ - pr?: SpacingValue; - /** All sides margin */ - m?: SpacingValue; - /** Horizontal margin */ - mx?: SpacingValue; - /** Vertical margin */ - my?: SpacingValue; - /** Top margin */ - mt?: SpacingValue; - /** Right margin */ - mr?: SpacingValue; - /** Bottom margin */ - mb?: SpacingValue; - /** Left margin */ - ml?: SpacingValue; -}>; - -export type ScopedThemeOverride = ThemeDefinition | ThemeOverrides; - -export type ThemedProps = Readonly<{ - key?: string; - /** Partial theme overrides applied to children. */ - theme: ThemeOverrides; -}>; - -export type FragmentProps = Readonly<{ - key?: string; -}>; - -type DisplayableProps = Readonly<{ - /** Conditional layout visibility. false (or expr <= 0) hides this node. */ - display?: DisplayConstraint; -}>; - -/** - * Text display variants with predefined styling. - */ -export type TextVariant = "body" | "heading" | "caption" | "code" | "label"; - -/** Properties that can be animated via widget transition props. */ -export type TransitionProperty = "position" | "size" | "opacity"; - -/** Declarative transition settings for render-time widget animation. */ -export type TransitionSpec = Readonly<{ - /** Transition duration in milliseconds. */ - duration?: number; - /** Easing curve for interpolation. */ - easing?: EasingInput; - /** - * Transitioned properties. Defaults to `"all"` when omitted. - * Phase 1 implementation supports `"position"` (x/y). - */ - properties?: "all" | readonly TransitionProperty[]; -}>; - -export type ExitAnimationState = Readonly<{ - instanceId: InstanceId; - startMs: number; - durationMs: number; - easing: (t: number) => number; - properties: "all" | readonly TransitionProperty[]; -}>; - -/** Props for text widget. key is for reconciliation; style for visual appearance. */ -export type TextProps = Readonly<{ - id?: string; - key?: string; - style?: TextStyle; - /** Text variant with predefined styling */ - variant?: TextVariant; - /** How to handle text that exceeds available width. Defaults to "clip". */ - textOverflow?: "clip" | "ellipsis" | "middle" | "start"; - /** Maximum width for overflow handling (in cells) */ - maxWidth?: SizeConstraint; - /** When true, text wraps to multiple lines instead of single-line truncation. */ - wrap?: boolean; - /** Internal callback used by ink-compat to transform rendered lines. */ - __inkTransform?: (line: string, index: number) => string; -}> & - DisplayableProps; - -/** - * Shadow configuration for box widgets. - */ -export type BoxShadow = - | boolean - | Readonly<{ - /** Horizontal offset (default: 1) */ - offsetX?: number; - /** Vertical offset (default: 1) */ - offsetY?: number; - /** Shadow density: "light", "medium", "dense" (default: "light") */ - density?: "light" | "medium" | "dense"; - }>; - -export type BoxPreset = "card" | "surface" | "well" | "elevated"; - -export type BoxBorderSideStyles = Readonly<{ - top?: TextStyle; - right?: TextStyle; - bottom?: TextStyle; - left?: TextStyle; -}>; - -/** Props for box container. border defaults to "single". */ -export type BoxProps = Readonly< - { - id?: string; - key?: string; - title?: string; - /** Alignment of title within the top border. */ - titleAlign?: "left" | "center" | "right"; - /** - * @deprecated Use `p`/`px`/`py`/`pt`/`pr`/`pb`/`pl` instead. Will be removed in v2.0. - */ - pad?: SpacingValue; - /** Style preset. Applied before explicit border/style props. */ - preset?: BoxPreset; - /** Border style. */ - border?: "none" | "single" | "double" | "rounded" | "heavy" | "dashed" | "heavy-dashed"; - /** - * Render individual border sides. Defaults to true when `border` is not "none". - * Mirrors Ink-style per-side border toggles. - */ - borderTop?: boolean; - borderRight?: boolean; - borderBottom?: boolean; - borderLeft?: boolean; - /** - * Enable shadow effect for depth. - * Pass `true` for default shadow, or an object for custom configuration. - */ - shadow?: BoxShadow; - /** - * Optional style applied to the box surface (for borders and background fill). - * When `bg` is provided, the renderer fills the box rect. - * When `borderStyle` is not set, this also styles the border. - */ - style?: TextStyle; - /** - * Optional style applied only to the box border and title. - * When set, decouples border appearance from child content: - * `style` controls children/fill, `borderStyle` controls the border. - * When not set, `style` applies to both (backward compatible). - */ - borderStyle?: TextStyle; - /** Optional per-edge border style overrides. */ - borderStyleSides?: BoxBorderSideStyles; - /** - * Style inherited by descendants when they do not override their own style. - * Unlike `style`, this does not force container background fill. - */ - inheritStyle?: TextStyle; - /** Child overflow behavior. Defaults to "visible". */ - overflow?: Overflow; - /** Horizontal scroll offset in cells. Clamped by layout metadata bounds. */ - scrollX?: number; - /** Vertical scroll offset in cells. Clamped by layout metadata bounds. */ - scrollY?: number; - /** Scrollbar glyph variant for overflow: "scroll" (default: "minimal"). */ - scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; - /** Optional style override for rendered scrollbars. */ - scrollbarStyle?: TextStyle; - /** Optional scoped theme override for this container subtree. */ - theme?: ScopedThemeOverride; - /** Surface opacity in [0..1]. Defaults to 1. */ - opacity?: number; - /** Gap between box children when laid out by the synthetic inner column. */ - gap?: SpacingValue; - /** Optional declarative transition settings for this container. */ - transition?: TransitionSpec; - /** Optional declarative exit transition before unmount removal. */ - exitTransition?: TransitionSpec; - } & SpacingProps & - LayoutConstraints ->; - -/** Props for row/column stack layouts. pad is internal, gap is between children. */ -export type JustifyContent = "start" | "end" | "center" | "between" | "around" | "evenly"; - -export type AlignItems = "start" | "end" | "center" | "stretch"; - -export type StackProps = Readonly< - { - id?: string; - key?: string; - /** - * @deprecated Use `p`/`px`/`py`/`pt`/`pr`/`pb`/`pl` instead. Will be removed in v2.0. - */ - pad?: SpacingValue; - /** Gap between children. Accepts number or spacing key. */ - gap?: SpacingValue; - /** Render children in reverse order while preserving original child arrays. */ - reverse?: boolean; - align?: Align; - justify?: JustifyContent; - items?: AlignItems; - /** - * Optional style applied to the stack surface. - * When `bg` is provided, the renderer fills the stack rect. - */ - style?: TextStyle; - /** - * Style inherited by descendants when they do not override their own style. - * Unlike `style`, this does not force stack background fill. - */ - inheritStyle?: TextStyle; - /** Child overflow behavior. Defaults to "visible". */ - overflow?: Overflow; - /** Horizontal scroll offset in cells. Clamped by layout metadata bounds. */ - scrollX?: number; - /** Vertical scroll offset in cells. Clamped by layout metadata bounds. */ - scrollY?: number; - /** Scrollbar glyph variant for overflow: "scroll" (default: "minimal"). */ - scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; - /** Optional style override for rendered scrollbars. */ - scrollbarStyle?: TextStyle; - /** Optional scoped theme override for this container subtree. */ - theme?: ScopedThemeOverride; - /** Optional declarative transition settings for this container. */ - transition?: TransitionSpec; - /** Optional declarative exit transition before unmount removal. */ - exitTransition?: TransitionSpec; - } & SpacingProps & - LayoutConstraints ->; - -/** Props for horizontal stacks. `wrap` controls line breaking (default: false). */ -export type RowProps = StackProps & - Readonly<{ - wrap?: boolean; - }>; - -/** Props for vertical stacks. `wrap` controls line breaking (default: false). */ -export type ColumnProps = StackProps & - Readonly<{ - wrap?: boolean; - }>; - -export type GridProps = Readonly< - { - id?: string; - key?: string; - columns: number | string; - rows?: number | string; - gap?: number; - rowGap?: number; - columnGap?: number; - theme?: ScopedThemeOverride; - transition?: TransitionSpec; - exitTransition?: TransitionSpec; - } & LayoutConstraints ->; - -/** Props for spacer element. size is in cells along stack axis. */ -export type SpacerProps = Readonly<{ - key?: string; - size?: number; - flex?: number; -}>; - -export type DividerProps = Readonly<{ - key?: string; - direction?: "horizontal" | "vertical"; - char?: string; - label?: string; - color?: string; -}>; - -/** - * Props for icon widget. - * Icons are single-character glyphs from the icon registry. - */ -export type IconProps = Readonly<{ - key?: string; - /** Icon path (e.g., "status.check", "arrow.right", "ui.search") */ - icon: string; - /** Optional style for the icon */ - style?: TextStyle; - /** Use ASCII fallback instead of Unicode/Nerd Font character */ - fallback?: boolean; -}>; - -/** - * Props for spinner widget. - * Animated loading indicator driven by tick events. - */ -export type SpinnerProps = Readonly<{ - key?: string; - /** Spinner animation variant */ - variant?: "dots" | "line" | "circle" | "bounce" | "pulse" | "arrows" | "dots2"; - /** Optional style for the spinner */ - style?: TextStyle; - /** Optional label text after spinner */ - label?: string; -}>; - -/** - * Progress bar variant styles. - */ -export type ProgressVariant = "bar" | "blocks" | "minimal"; - -/** - * Props for progress bar widget. - * Displays completion progress with customizable appearance. - */ -export type ProgressProps = Readonly<{ - key?: string; - /** Progress value from 0 to 1 */ - value: number; - /** Display width in cells (default: fills available space) */ - width?: number; - /** Visual variant */ - variant?: ProgressVariant; - /** Show percentage label */ - showPercent?: boolean; - /** Optional label before the bar */ - label?: string; - /** Style for filled portion */ - style?: TextStyle; - /** Style for track/unfilled portion */ - trackStyle?: TextStyle; - /** Design system: color tone. */ - dsTone?: WidgetTone; -}>; - -/** - * Skeleton variant styles. - */ -export type SkeletonVariant = "text" | "rect" | "circle"; - -/** - * Props for skeleton loading widget. - * Placeholder for content that is loading. - */ -export type SkeletonProps = Readonly<{ - key?: string; - /** Width in cells */ - width: number; - /** Height in rows (default: 1) */ - height?: number; - /** Visual variant */ - variant?: SkeletonVariant; - /** Optional style override */ - style?: TextStyle; -}>; - -/** - * A span of styled text within a richText widget. - */ -export type RichTextSpan = Readonly<{ - /** Text content */ - text: string; - /** Optional style for this span */ - style?: TextStyle; -}>; - -/** - * Props for rich text widget with multiple styled spans. - */ -export type RichTextProps = Readonly<{ - key?: string; - /** Array of styled text spans */ - spans: readonly RichTextSpan[]; -}>; - -/** - * Props for keyboard shortcut display widget. - */ -export type KbdProps = Readonly<{ - key?: string; - /** Key or keys to display (e.g., "Ctrl+S" or ["Ctrl", "S"]) */ - keys: string | readonly string[]; - /** Separator between keys (default: "+") */ - separator?: string; - /** Optional style override */ - style?: TextStyle; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** - * Badge visual variants. - */ -export type BadgeVariant = "default" | "success" | "warning" | "error" | "info"; - -/** - * Props for badge widget. - */ -export type BadgeProps = Readonly<{ - key?: string; - /** Badge text */ - text: string; - /** Visual variant */ - variant?: BadgeVariant; - /** Optional style override */ - style?: TextStyle; -}>; - -/** - * Status indicator types. - */ -export type StatusType = "online" | "offline" | "away" | "busy" | "unknown"; - -/** - * Props for status indicator widget. - * Shows a colored dot with optional label. - */ -export type StatusProps = Readonly<{ - key?: string; - /** Status type determines color */ - status: StatusType; - /** Optional label after the indicator */ - label?: string; - /** Show the label text (default: true if label provided) */ - showLabel?: boolean; - /** Optional style override */ - style?: TextStyle; -}>; - -/** - * Props for tag widget. - * Inline label with background color. - */ -export type TagProps = Readonly<{ - key?: string; - /** Tag text */ - text: string; - /** Tag color variant */ - variant?: BadgeVariant; - /** Make tag removable (shows x) */ - removable?: boolean; - /** Optional style override */ - style?: TextStyle; -}>; - -/** - * Props for gauge widget. - * Compact progress display with label. - */ -export type GaugeProps = Readonly<{ - key?: string; - /** Value from 0 to 1 */ - value: number; - /** Optional forced width in cells. */ - width?: SizeConstraint; - /** Optional forced height in rows. */ - height?: SizeConstraint; - /** Label before the gauge */ - label?: string; - /** Display variant */ - variant?: "linear" | "compact"; - /** Color thresholds for value ranges */ - thresholds?: readonly { value: number; variant: BadgeVariant }[]; - /** Optional style override */ - style?: TextStyle; -}> & - DisplayableProps; - -/** - * Props for empty state widget. - * Displays a centered placeholder when content is empty. - */ -export type EmptyProps = Readonly<{ - key?: string; - /** Icon path to display (e.g., "ui.search", "file.folder") */ - icon?: string; - /** Main title text */ - title: string; - /** Optional description text */ - description?: string; - /** Optional action button or other widget */ - action?: VNode; - /** Optional style override */ - style?: TextStyle; -}>; - -/** - * Props for error display widget. - * Shows error information with optional stack trace and retry action. - */ -export type ErrorDisplayProps = Readonly<{ - key?: string; - /** Error title (default: "Error") */ - title?: string; - /** Error message to display */ - message: string; - /** Optional stack trace */ - stack?: string; - /** Show stack trace (default: false) */ - showStack?: boolean; - /** Callback for retry action */ - onRetry?: () => void; - /** Optional style override */ - style?: TextStyle; -}>; - -/** - * Error payload passed to `errorBoundary` fallbacks. - */ -export type ErrorBoundaryError = Readonly<{ - /** Runtime error code for the trapped boundary failure. */ - code: "ZRUI_USER_CODE_THROW"; - /** Friendly error message (for display in fallbacks). */ - message: string; - /** Full detail string emitted by runtime error reporting. */ - detail: string; - /** Optional stack trace when available. */ - stack?: string; - /** Retry this boundary subtree on the next commit turn. */ - retry: () => void; -}>; - -/** - * Props for error boundary container widget. - * Isolates subtree render failures and renders a fallback instead of faulting the app. - */ -export type ErrorBoundaryProps = Readonly<{ - key?: string; - /** Risky subtree to protect. */ - children: VNode; - /** Fallback renderer invoked when the protected subtree throws. */ - fallback: (error: ErrorBoundaryError) => VNode; -}>; - -/** - * Props for callout/alert widget. - * Highlighted message box for important information. - */ -export type CalloutProps = Readonly<{ - key?: string; - /** Callout variant determines styling */ - variant?: "info" | "success" | "warning" | "error"; - /** Optional title */ - title?: string; - /** Message content */ - message: string; - /** Optional icon override */ - icon?: string; - /** Optional style override */ - style?: TextStyle; -}>; - -export type GraphicsBlitter = "auto" | "braille" | "sextant" | "quadrant" | "halfblock" | "ascii"; - -export type CanvasPoint = Readonly<{ - x: number; - y: number; -}>; - -export type CanvasContext = Readonly<{ - readonly width: number; - readonly height: number; - line: (x0: number, y0: number, x1: number, y1: number, color: string) => void; - polyline: (points: readonly CanvasPoint[], color: string) => void; - fillRect: (x: number, y: number, w: number, h: number, color: string) => void; - strokeRect: (x: number, y: number, w: number, h: number, color: string) => void; - roundedRect: (x: number, y: number, w: number, h: number, radius: number, color: string) => void; - circle: (cx: number, cy: number, radius: number, color: string) => void; - arc: ( - cx: number, - cy: number, - radius: number, - startAngle: number, - endAngle: number, - color: string, - ) => void; - fillCircle: (cx: number, cy: number, radius: number, color: string) => void; - fillTriangle: ( - x0: number, - y0: number, - x1: number, - y1: number, - x2: number, - y2: number, - color: string, - ) => void; - setPixel: (x: number, y: number, color: string) => void; - text: (x: number, y: number, str: string, color?: string) => void; - clear: (color?: string) => void; -}>; - -export type LinkProps = Readonly<{ - id?: string; - key?: string; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** URL to open in terminal hyperlink-capable renderers. */ - url: string; - /** Link label text. Defaults to url. */ - label?: string; - /** Optional text style for the link. */ - style?: TextStyle; - /** Optional local press handler. */ - onPress?: () => void; - /** Disabled links are rendered but not focusable/pressable. */ - disabled?: boolean; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; -}> & - DisplayableProps; - -export type CanvasProps = Readonly<{ - id?: string; - key?: string; - /** Width in terminal columns. */ - width?: SizeConstraint; - /** Height in terminal rows. */ - height?: SizeConstraint; - /** Drawing callback, called every frame with a fresh context. */ - draw: (ctx: CanvasContext) => void; - /** Preferred blitter. */ - blitter?: GraphicsBlitter; -}> & - DisplayableProps; - -export type ImageFit = "fill" | "contain" | "cover"; -export type ImageProtocol = "auto" | "kitty" | "sixel" | "iterm2" | "blitter"; - -export type ImageProps = Readonly<{ - id?: string; - key?: string; - /** Image bytes (PNG or RGBA payload). */ - src: Uint8Array; - /** Optional source width in pixels (recommended for raw RGBA inputs). */ - sourceWidth?: number; - /** Optional source height in pixels (recommended for raw RGBA inputs). */ - sourceHeight?: number; - /** Width in terminal columns. */ - width?: SizeConstraint; - /** Height in terminal rows. */ - height?: SizeConstraint; - /** Fit mode (default: contain). */ - fit?: ImageFit; - /** Alt text for unsupported terminals or decode failures. */ - alt?: string; - /** Preferred image protocol. */ - protocol?: ImageProtocol; - /** Z-layer for compositing. */ - zLayer?: -1 | 0 | 1; - /** Stable image id for protocol-level caching. */ - imageId?: number; -}> & - DisplayableProps; - -export type LineChartSeries = Readonly<{ - data: readonly number[]; - color: string; - label?: string; -}>; - -export type ChartAxis = Readonly<{ - label?: string; - min?: number; - max?: number; -}>; - -export type LineChartProps = Readonly<{ - id?: string; - key?: string; - width?: SizeConstraint; - height?: SizeConstraint; - series: readonly LineChartSeries[]; - axes?: Readonly<{ x?: ChartAxis; y?: ChartAxis }>; - blitter?: Exclude; - showLegend?: boolean; -}> & - DisplayableProps; - -export type ScatterPoint = Readonly<{ - x: number; - y: number; - color?: string; -}>; - -export type ScatterProps = Readonly<{ - id?: string; - key?: string; - width?: SizeConstraint; - height?: SizeConstraint; - points: readonly ScatterPoint[]; - axes?: Readonly<{ x?: ChartAxis; y?: ChartAxis }>; - color?: string; - blitter?: Exclude; -}> & - DisplayableProps; - -export type HeatmapColorScale = "viridis" | "plasma" | "inferno" | "magma" | "turbo" | "grayscale"; - -export type HeatmapProps = Readonly<{ - id?: string; - key?: string; - width?: SizeConstraint; - height?: SizeConstraint; - /** 2D value matrix [row][col]. */ - data: readonly (readonly number[])[]; - colorScale?: HeatmapColorScale; - min?: number; - max?: number; -}> & - DisplayableProps; - -/** - * Props for sparkline widget. - * Mini inline chart using block characters. - */ -export type SparklineProps = Readonly<{ - key?: string; - /** Data points (will be normalized to 0-1 range) */ - data: readonly number[]; - /** Display width in cells (default: data.length) */ - width?: SizeConstraint; - /** Display height in rows (default: 1) */ - height?: SizeConstraint; - /** Minimum value for scaling (default: auto) */ - min?: number; - /** Maximum value for scaling (default: auto) */ - max?: number; - /** Enables sub-cell rendering when supported. */ - highRes?: boolean; - /** Blitter for highRes mode. */ - blitter?: Exclude; - /** Optional style override */ - style?: TextStyle; -}> & - DisplayableProps; - -/** - * Single item in a bar chart. - */ -export type BarChartItem = Readonly<{ - /** Item label */ - label: string; - /** Item value */ - value: number; - /** Optional color variant */ - variant?: BadgeVariant; -}>; - -/** - * Props for bar chart widget. - * Horizontal or vertical bar chart. - */ -export type BarChartProps = Readonly<{ - key?: string; - /** Optional forced width in cells. */ - width?: SizeConstraint; - /** Optional forced height in rows. */ - height?: SizeConstraint; - /** Data items to display */ - data: readonly BarChartItem[]; - /** Chart orientation (default: "horizontal") */ - orientation?: "horizontal" | "vertical"; - /** Show value labels (default: true) */ - showValues?: boolean; - /** Show item labels (default: true) */ - showLabels?: boolean; - /** Maximum bar length in cells */ - maxBarLength?: number; - /** Enables sub-cell rendering when supported. */ - highRes?: boolean; - /** Blitter for highRes mode. */ - blitter?: Exclude; - /** Optional style override */ - style?: TextStyle; -}> & - DisplayableProps; +export type { + Align, + Overflow, + SpacingProps, + ScopedThemeOverride, + ThemedProps, + FragmentProps, + TextVariant, + TransitionProperty, + TransitionSpec, + ExitAnimationState, + TextProps, + BoxShadow, + BoxPreset, + BoxBorderSideStyles, + BoxProps, + JustifyContent, + AlignItems, + StackProps, + RowProps, + ColumnProps, + GridProps, + SpacerProps, + DividerProps, + IconProps, + SpinnerProps, + ProgressVariant, + ProgressProps, + SkeletonVariant, + SkeletonProps, + RichTextSpan, + RichTextProps, + KbdProps, + BadgeVariant, + BadgeProps, + StatusType, + StatusProps, + TagProps, + GaugeProps, + EmptyProps, + ErrorDisplayProps, + ErrorBoundaryError, + ErrorBoundaryProps, + CalloutProps, + GraphicsBlitter, + CanvasPoint, + CanvasContext, + LinkProps, + CanvasProps, + ImageFit, + ImageProtocol, + ImageProps, + LineChartSeries, + ChartAxis, + LineChartProps, + ScatterPoint, + ScatterProps, + HeatmapColorScale, + HeatmapProps, + SparklineProps, + BarChartItem, + BarChartProps, + MiniChartProps, + ButtonIntent, + ButtonProps, + InputProps, + TextareaProps, + FocusAnnouncerProps, + FocusZoneNavigation, + FocusZoneProps, + FocusTrapProps, + ItemHeightSpec, + VirtualListMeasureItemHeightCtx, + VirtualListProps, +} from "./types/base.js"; +export type { + BackdropStyle, + OverlayFrameStyle, + ModalBackdrop, + DropdownPosition, + LayersProps, + ModalProps, + DialogActionIntent, + DialogAction, + DialogProps, + AppShellSidebar, + AppShellOptions, + PageOptions, + CardOptions, + ToolbarOptions, + StatusBarOptions, + HeaderOptions, + SidebarItem, + SidebarOptions, + MasterDetailOptions, + DropdownItem, + DropdownProps, + LayerProps, +} from "./types/overlaysShell.js"; +export type { + TableColumnOverflow, + TableStripeStyle, + TableBorderVariant, + TableBorderStyle, + TableColumn, + TableProps, +} from "./types/table.js"; +export type { + SelectOption, + FieldProps, + SelectProps, + SliderProps, + CheckboxProps, + RadioGroupProps, +} from "./types/forms.js"; +export type { + TabsVariant, + TabsPosition, + TabsItem, + TabsProps, + AccordionItem, + AccordionProps, + BreadcrumbItem, + BreadcrumbProps, + PaginationProps, +} from "./types/navigation.js"; +export type { + CommandSource, + CommandItem, + CommandPaletteProps, + FileNode, + FileNodeState, + FilePickerProps, + FileTreeExplorerProps, + SplitDirection, + SplitPaneProps, + ResizablePanelProps, + PanelGroupProps, + CursorPosition, + EditorSelection, + SearchMatch, + CodeEditorDiagnosticSeverity, + CodeEditorDiagnostic, + CodeEditorSyntaxLanguage, + CodeEditorSyntaxTokenKind, + CodeEditorSyntaxToken, + CodeEditorTokenizeContext, + CodeEditorLineTokenizer, + CodeEditorProps, + DiffLine, + DiffHunk, + DiffData, + DiffViewerProps, + ToolFileChange, + ToolRequest, + ToolApprovalDialogProps, + LogLevel, + TokenCount, + LogEntry, + LogsConsoleProps, + ToastPosition, + ToastAction, + Toast, + ToastContainerProps, +} from "./types/advanced.js"; +export type { NodeState, TreeProps } from "./types/tree.js"; -/** - * Props for mini chart widget. - * Compact multi-value display. - */ -export type MiniChartProps = Readonly<{ - key?: string; - /** Optional forced width in cells. */ - width?: SizeConstraint; - /** Optional forced height in rows. */ - height?: SizeConstraint; - /** Chart values (2-4 values recommended) */ - values: readonly { label: string; value: number; max?: number }[]; - /** Display variant */ - variant?: "bars" | "pills"; - /** Optional style override */ - style?: TextStyle; -}> & - DisplayableProps; - -export type ButtonIntent = "primary" | "secondary" | "danger" | "success" | "warning" | "link"; - -/** Props for button widget. id is required for focus/routing; label is display text. */ -export type ButtonProps = Readonly<{ - id: string; - key?: string; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - label: string; - disabled?: boolean; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** - * Horizontal padding in cells. - * - Legacy/manual path default: 1 - * - Recipe path: derived from `dsSize` unless overridden by `px` - */ - px?: number; - /** Optional style applied to the button label (merged with focus/disabled state). */ - style?: TextStyle; - /** Optional style applied while the button is pressed. */ - pressedStyle?: TextStyle; - /** Optional callback invoked when the button is activated. */ - onPress?: () => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** - * Design system: visual variant. - * - "solid": Accent background, inverse text (primary CTA) - * - "soft": Subtle background, accent text (secondary) - * - "outline": Border, no fill (tertiary) - * - "ghost": No background or border (minimal) - * @default "soft" - */ - dsVariant?: WidgetVariant; - /** - * Design system: color tone. - * @default "default" - */ - dsTone?: WidgetTone; - /** - * Design system: size preset. - * @default "md" - */ - dsSize?: WidgetSize; - /** Shorthand for dsVariant + dsTone. Overridden by explicit dsVariant/dsTone. */ - intent?: ButtonIntent; -}>; - -/** Props for input widget. id is required; value is controlled by app state. */ -export type InputProps = Readonly<{ - id: string; - key?: string; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - value: string; - disabled?: boolean; - /** Keep the input focusable/selectable while preventing edits. */ - readOnly?: boolean; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional style applied to the input value (merged with focus/disabled state). */ - style?: TextStyle; - /** Optional callback invoked on input edits (docs/18). */ - onInput?: (value: string, cursor: number) => void; - /** Optional callback invoked when the input loses focus. */ - onBlur?: () => void; - /** Internal multiline mode used by ui.textarea(). */ - multiline?: boolean; - /** Visible line count when multiline mode is enabled (default: 3). */ - rows?: number; - /** Wrap long lines in multiline mode (default: true). */ - wordWrap?: boolean; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** - * Design system: size preset. - * @default "md" - */ - dsSize?: WidgetSize; - /** - * Design system: placeholder text displayed when value is empty. - */ - placeholder?: string; -}>; - -/** Props for ui.textarea(). Multi-line controlled text input. */ -export type TextareaProps = Readonly<{ - id: string; - key?: string; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - value: string; - disabled?: boolean; - /** Keep the textarea focusable/selectable while preventing edits. */ - readOnly?: boolean; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Visible line count (default: 3). */ - rows?: number; - /** Wrap long lines (default: true). */ - wordWrap?: boolean; - /** Placeholder text shown when value is empty. */ - placeholder?: string; - /** Optional style applied to the textarea value (merged with focus/disabled state). */ - style?: TextStyle; - /** Optional callback invoked on input edits. */ - onInput?: (value: string, cursor: number) => void; - /** Optional callback invoked when the textarea loses focus. */ - onBlur?: () => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; -}>; - -/** Props for focus announcer widget. Renders a live summary for the focused element. */ -export type FocusAnnouncerProps = Readonly<{ - key?: string; - /** Optional fallback text when no widget is focused. */ - emptyText?: string; - /** Optional style applied to the announcement line. */ - style?: TextStyle; -}>; - -/** Navigation mode for focus zones. */ -export type FocusZoneNavigation = "linear" | "grid" | "none"; - -/** Props for focus zone container. Groups focusable widgets for TAB navigation. */ -export type FocusZoneProps = Readonly<{ - id: string; - key?: string; - tabIndex?: number; // Zone order (default: 0) - navigation?: FocusZoneNavigation; // Default: "linear" - columns?: number; // For grid mode (default: 1) - wrapAround?: boolean; // Default: true - onEnter?: () => void; - onExit?: () => void; -}>; - -/** Props for focus trap container. Contains focus within its boundaries when active. */ -export type FocusTrapProps = Readonly<{ - id: string; - key?: string; - active: boolean; - returnFocusTo?: string; - initialFocus?: string; -}>; - -/** Height specification for virtual list items: fixed number or callback for variable heights. */ -export type ItemHeightSpec = number | ((item: T, index: number) => number); - -/** Context passed to custom virtual-list measurement callbacks. */ -export type VirtualListMeasureItemHeightCtx = Readonly<{ - /** Available content width in terminal cells for this item. */ - width: number; - /** Estimated height used for initial windowing before correction. */ - estimatedHeight: number; - /** Rendered VNode returned by `renderItem`. */ - vnode: VNode; -}>; - -/** Props for virtualList widget. Efficiently renders large datasets with windowed virtualization. */ -export type VirtualListProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - items: readonly T[]; - /** - * Exact item height specification (fixed or callback). - * Use this for fixed-height or precomputed variable-height rows. - */ - itemHeight?: ItemHeightSpec; - /** - * Estimated item height used for variable-height virtualization. - * When provided, visible items are measured and cached to correct offsets. - */ - estimateItemHeight?: ItemHeightSpec; - /** - * Optional custom measurement callback for estimate mode. - * Return the measured height in cells for the rendered VNode. - */ - measureItemHeight?: (item: T, index: number, ctx: VirtualListMeasureItemHeightCtx) => number; - /** Number of items to render outside the visible viewport (default: 3) */ - overscan?: number; - renderItem: (item: T, index: number, focused: boolean) => VNode; - onScroll?: (scrollTop: number, visibleRange: [number, number]) => void; - onSelect?: (item: T, index: number) => void; - /** Enable keyboard navigation with arrow keys, page up/down, home/end (default: true) */ - keyboardNavigation?: boolean; - /** Wrap selection from last item to first and vice versa (default: false) */ - wrapAround?: boolean; - /** - * Mouse wheel scroll direction (default: "traditional"). - * - "traditional": wheel down moves the viewport down - * - "natural": wheel down moves content down (trackpad-style) - */ - scrollDirection?: "natural" | "traditional"; - /** Optional style override for the selected/focused row highlight. */ - selectionStyle?: TextStyle; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; -}> & - LayoutConstraints; - -/* ========== Layer System (GitHub issue #117) ========== */ - -/** Backdrop style presets for modals and overlays. */ -export type BackdropStyle = "none" | "dim" | "opaque"; - -/** Shared frame/surface styling for overlay widgets. */ -export type OverlayFrameStyle = Readonly<{ - /** Surface background color. */ - background?: NonNullable; - /** Default text/icon color for overlay content. */ - foreground?: NonNullable; - /** Border color for framed overlays. */ - border?: NonNullable; -}>; - -/** Extended modal backdrop config (preset-compatible). */ -export type ModalBackdrop = - | BackdropStyle - | Readonly<{ - /** Backdrop variant (default: "dim"). */ - variant?: BackdropStyle; - /** Optional dim-pattern character (default: "░", dim variant only). */ - pattern?: string; - /** Optional backdrop foreground color. */ - foreground?: NonNullable; - /** Optional backdrop background color. */ - background?: NonNullable; - }>; - -/** Position for dropdown relative to anchor. */ -export type DropdownPosition = - | "below-start" - | "below-center" - | "below-end" - | "above-start" - | "above-center" - | "above-end"; - -/** Props for layers container. Stacks children with later items on top. */ -export type LayersProps = Readonly<{ - key?: string; -}>; - -/** Props for modal overlay. Centered with optional backdrop and focus trap. */ -export type ModalProps = Readonly<{ - id: string; - key?: string; - /** Optional title rendered in modal header. */ - title?: string; - /** Main content of the modal. */ - content: VNode; - /** Action buttons rendered in modal footer. */ - actions?: readonly VNode[]; - /** Modal width sizing. Supports fixed values, "auto", "full", `fluid(...)`, or `expr(...)`. */ - width?: SizeConstraintAtom; - /** Modal height sizing. Supports fixed values, "full", `fluid(...)`, or `expr(...)` (no "auto" height). */ - height?: Exclude; - /** Maximum width bound (cells, "full", `fluid(...)`, or `expr(...)`). */ - maxWidth?: Exclude; - /** Minimum width bound (cells, "full", `fluid(...)`, or `expr(...)`). */ - minWidth?: Exclude; - /** Minimum height bound (cells, "full", `fluid(...)`, or `expr(...)`). */ - minHeight?: Exclude; - /** Frame/surface colors for modal body and border. */ - frameStyle?: OverlayFrameStyle; - /** Backdrop style/config (default: "dim"). */ - backdrop?: ModalBackdrop; - /** Close when backdrop is clicked (default: true). */ - closeOnBackdrop?: boolean; - /** Close when ESC is pressed (default: true). */ - closeOnEscape?: boolean; - /** Callback when modal should close. */ - onClose?: () => void; - /** ID of element to focus when modal opens. */ - initialFocus?: string; - /** ID of element to return focus to when modal closes. */ - returnFocusTo?: string; -}>; - -/** Intent hint for declarative dialog actions. */ -export type DialogActionIntent = "primary" | "danger"; - -/** Action descriptor for declarative dialog buttons. */ -export type DialogAction = Readonly<{ - id?: string; - label: string; - intent?: DialogActionIntent; - onPress: () => void; - disabled?: boolean; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; -}>; - -/** Declarative dialog props sugar over `ui.modal(...)`. */ -export type DialogProps = Readonly< - Omit & { - message: string | VNode; - actions: readonly DialogAction[]; - } ->; - -export type AppShellSidebar = Readonly<{ - content: VNode; - /** Sidebar width. Supports fixed values, "full", `fluid(...)`, or `expr(...)`. */ - width?: SizeConstraint; -}>; - -export type AppShellOptions = Readonly< - { - id?: string; - key?: string; - /** Header content — typically title text, badges, action buttons */ - header?: VNode | null; - /** Sidebar content — typically navigation */ - sidebar?: AppShellSidebar | null; - /** Main body content */ - body: VNode; - /** Footer/status bar content */ - footer?: VNode | null; - /** Padding around the shell (default: 1) */ - p?: SpacingValue; - /** Gap between sections (default: 1) */ - gap?: number; - } & LayoutConstraints ->; - -export type PageOptions = Readonly< - { - id?: string; - key?: string; - header?: VNode | null; - body: VNode; - footer?: VNode | null; - gap?: SpacingValue; - p?: SpacingValue; - } & LayoutConstraints ->; - -export type CardOptions = Readonly<{ - id?: string; - key?: string; - title?: string; - subtitle?: string; - actions?: readonly VNode[]; - border?: BoxProps["border"]; - p?: SpacingValue; - gap?: number; - style?: TextStyle; -}>; - -export type ToolbarOptions = Readonly<{ - id?: string; - key?: string; - gap?: number; -}>; - -export type StatusBarOptions = Readonly<{ - id?: string; - key?: string; - left?: readonly VNode[]; - right?: readonly VNode[]; - style?: TextStyle; -}>; - -export type HeaderOptions = Readonly<{ - id?: string; - key?: string; - title: string; - subtitle?: string; - actions?: readonly VNode[]; -}>; - -export type SidebarItem = Readonly<{ - id: string; - label: string; - icon?: string; -}>; - -export type SidebarOptions = Readonly<{ - id?: string; - key?: string; - items: readonly SidebarItem[]; - selected?: string; - onSelect?: (id: string) => void; - width?: number; - title?: string; -}>; - -export type MasterDetailOptions = Readonly<{ - id?: string; - key?: string; - master: VNode; - detail: VNode; - masterWidth?: number; - gap?: number; -}>; - -/** Dropdown menu item. */ -export type DropdownItem = Readonly<{ - id: string; - label: string; - shortcut?: string; - disabled?: boolean; - /** Render as a divider instead of a selectable item. */ - divider?: boolean; -}>; - -/** Props for dropdown menu. Positioned relative to an anchor element. */ -export type DropdownProps = Readonly<{ - id: string; - key?: string; - /** ID of the anchor element to position relative to. */ - anchorId: string; - /** Position relative to anchor (default: "below-start"). */ - position?: DropdownPosition; - /** Frame/surface colors for dropdown background, text, and border. */ - frameStyle?: OverlayFrameStyle; - /** Menu items to render. */ - items: readonly DropdownItem[]; - /** Callback when an item is selected. */ - onSelect?: (item: DropdownItem) => void; - /** Callback when dropdown should close. */ - onClose?: () => void; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** Props for a layer in the layer stack. */ -export type LayerProps = Readonly<{ - id: string; - key?: string; - /** - * Z-index for layer ordering (higher = on top). Default is insertion order. - * Values are truncated to integers and clamped to `±9,007,199,253` for deterministic ordering. - */ - zIndex?: number; - /** Frame/surface colors for the layer container. */ - frameStyle?: OverlayFrameStyle; - /** Backdrop to render behind content. */ - backdrop?: BackdropStyle; - /** Whether layer blocks input to lower layers. */ - modal?: boolean; - /** Whether layer should close on ESC key (default: true). */ - closeOnEscape?: boolean; - /** Callback when layer should close. */ - onClose?: () => void; - /** Content to render in the layer. */ - content: VNode; -}>; - -/* ========== Table Widget (GitHub issue #118) ========== */ - -/** Overflow behavior for table header/cell text. */ -export type TableColumnOverflow = "clip" | "ellipsis" | "middle"; - -/** Row stripe styling for table body backgrounds. */ -export type TableStripeStyle = Readonly<{ - /** Background color for odd body rows (0-based index: 1, 3, 5, ...). */ - odd?: NonNullable; - /** Background color for even body rows (0-based index: 0, 2, 4, ...). */ - even?: NonNullable; -}>; - -/** Border glyph variants supported by table borders. */ -export type TableBorderVariant = - | "single" - | "double" - | "rounded" - | "heavy" - | "dashed" - | "heavy-dashed"; - -/** Border styling options for tables. */ -export type TableBorderStyle = Readonly<{ - /** Border glyph variant (default: "single"). */ - variant?: TableBorderVariant; - /** Border foreground color override. */ - color?: NonNullable; -}>; - -/** Column definition for table widget. */ -export type TableColumn = Readonly<{ - /** Unique column identifier. */ - key: string; - /** Column header text. */ - header: string; - /** Fixed width in cells. */ - width?: number; - /** Minimum width in cells. */ - minWidth?: number; - /** Maximum width in cells. */ - maxWidth?: number; - /** Flex factor for distributing remaining space. */ - flex?: number; - /** Custom render function for cell content. */ - render?: (value: unknown, row: T, index: number) => VNode; - /** Cell content alignment. */ - align?: "left" | "center" | "right"; - /** Overflow handling for this column (default: "ellipsis"). */ - overflow?: TableColumnOverflow; - /** Whether column is sortable. */ - sortable?: boolean; -}>; - -/** Props for table widget. */ -export type TableProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Column definitions. */ - columns: readonly TableColumn[]; - /** Row data array. */ - data: readonly T[]; - /** Function to get unique key for each row. */ - getRowKey: (row: T, index: number) => string; - /** Height of each row in cells (default: 1). */ - rowHeight?: number; - /** Height of header row in cells (default: 1). */ - headerHeight?: number; - /** Currently selected row keys. */ - selection?: readonly string[]; - /** Selection mode (default: "none"). */ - selectionMode?: "none" | "single" | "multi"; - /** Callback when selection changes. */ - onSelectionChange?: (keys: readonly string[]) => void; - /** Currently sorted column key. */ - sortColumn?: string; - /** Sort direction. */ - sortDirection?: "asc" | "desc"; - /** Callback when sort changes. */ - onSort?: (column: string, direction: "asc" | "desc") => void; - /** Callback when row is pressed (Enter key or click). */ - onRowPress?: (row: T, index: number) => void; - /** Callback when row is double-pressed (double-click). */ - onRowDoublePress?: (row: T, index: number) => void; - /** Enable virtualization for large datasets (default: true). */ - virtualized?: boolean; - /** Number of rows to render outside viewport (default: 3). */ - overscan?: number; - /** Legacy stripe toggle. */ - stripedRows?: boolean; - /** Stripe background styling for body rows. */ - stripeStyle?: TableStripeStyle; - /** Optional style override for selected rows. */ - selectionStyle?: TextStyle; - /** Show header row (default: true). */ - showHeader?: boolean; - /** Legacy border toggle. */ - border?: "none" | "single"; - /** Border styling for rendered table frame. */ - borderStyle?: TableBorderStyle; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Design system: size preset. */ - dsSize?: WidgetSize; - /** Design system: tone (reserved for future table recipe variants). */ - dsTone?: WidgetTone; -}> & - LayoutConstraints; - -/* ========== Form Widgets (GitHub issue #119) ========== */ - -/** Option for select and radio group widgets. */ -export type SelectOption = Readonly<{ - /** Option value used in form state. */ - value: string; - /** Display label for the option. */ - label: string; - /** Whether this option is disabled. */ - disabled?: boolean; -}>; - -/** Props for field wrapper widget. Wraps an input with label, error, and hint. */ -export type FieldProps = Readonly<{ - key?: string; - /** Field label displayed above the input. */ - label: string; - /** Error message to display below the input. */ - error?: string; - /** Whether the field is required (shows asterisk). */ - required?: boolean; - /** Help text displayed below the input. */ - hint?: string; - /** The wrapped input widget. */ - children: VNode; -}>; - -/** Props for select dropdown widget. */ -export type SelectProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Currently selected value. */ - value: string; - /** Available options. */ - options: readonly SelectOption[]; - /** Callback when selection changes. */ - onChange?: (value: string) => void; - /** Whether the select is disabled. */ - disabled?: boolean; - /** Placeholder text when no value is selected. */ - placeholder?: string; - /** Whether to show the select in an error state. */ - error?: boolean; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Design system: visual variant (reserved for future select recipes). */ - dsVariant?: WidgetVariant; - /** Design system: tone (reserved for future select recipes). */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** Props for slider widget. */ -export type SliderProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Current slider value. */ - value: number; - /** Minimum value (default: 0). */ - min?: number; - /** Maximum value (default: 100). */ - max?: number; - /** Step increment for keyboard changes (default: 1). */ - step?: number; - /** Optional fixed track width in cells (default: fills available width). */ - width?: number; - /** Optional label shown before the track. */ - label?: string; - /** Show numeric value text (default: true). */ - showValue?: boolean; - /** Callback when value changes. */ - onChange?: (value: number) => void; - /** Whether the slider is disabled. */ - disabled?: boolean; - /** Whether the slider is read-only (focusable but non-editable). */ - readOnly?: boolean; - /** Optional style applied to label/value text. */ - style?: TextStyle; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; -}>; - -/** Props for checkbox widget. */ -export type CheckboxProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Whether the checkbox is checked. */ - checked: boolean; - /** Label displayed next to the checkbox. */ - label?: string; - /** Callback when checked state changes. */ - onChange?: (checked: boolean) => void; - /** Whether the checkbox is disabled. */ - disabled?: boolean; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Design system: tone for checked/focus rendering. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** Props for radio group widget. */ -export type RadioGroupProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Currently selected value. */ - value: string; - /** Available options. */ - options: readonly SelectOption[]; - /** Callback when selection changes. */ - onChange?: (value: string) => void; - /** Layout direction. */ - direction?: "horizontal" | "vertical"; - /** Whether the radio group is disabled. */ - disabled?: boolean; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Design system: tone for selected/focus rendering. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/* ========== Navigation Widgets ========== */ - -/** Tabs visual style variant. */ -export type TabsVariant = "line" | "enclosed" | "pills"; - -/** Tabs bar position relative to content. */ -export type TabsPosition = "top" | "bottom"; - -/** Tab item descriptor. */ -export type TabsItem = Readonly<{ - key: string; - label: string; - content: VNode; -}>; - -/** Props for tabs widget. */ -export type TabsProps = Readonly<{ - id: string; - key?: string; - tabs: readonly TabsItem[]; - activeTab: string; - onChange: (key: string) => void; - variant?: TabsVariant; - position?: TabsPosition; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** Accordion item descriptor. */ -export type AccordionItem = Readonly<{ - key: string; - title: string; - content: VNode; -}>; - -/** Props for accordion widget. */ -export type AccordionProps = Readonly<{ - id: string; - key?: string; - items: readonly AccordionItem[]; - expanded: readonly string[]; - onChange: (expanded: readonly string[]) => void; - allowMultiple?: boolean; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** Breadcrumb item descriptor. */ -export type BreadcrumbItem = Readonly<{ - label: string; - onPress?: () => void; -}>; - -/** Props for breadcrumb widget. */ -export type BreadcrumbProps = Readonly<{ - id?: string; - key?: string; - items: readonly BreadcrumbItem[]; - separator?: string; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/** Props for pagination widget. */ -export type PaginationProps = Readonly<{ - id: string; - key?: string; - page: number; - totalPages: number; - onChange: (page: number) => void; - showFirstLast?: boolean; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}>; - -/* ========== Advanced Widgets (GitHub issue #136) ========== */ - -/* ---------- CommandPalette Widget ---------- */ - -/** Source of commands for CommandPalette. */ -export type CommandSource = Readonly<{ - /** Source identifier. */ - id: string; - /** Source display name. */ - name: string; - /** Prefix trigger (e.g., ">" for commands, "@" for symbols). */ - prefix?: string; - /** Sync or async item provider. */ - getItems: (query: string) => readonly CommandItem[] | Promise; - /** Priority for sorting (higher = first). */ - priority?: number; -}>; - -/** Item in CommandPalette. */ -export type CommandItem = Readonly<{ - /** Unique item identifier. */ - id: string; - /** Display label. */ - label: string; - /** Secondary description. */ - description?: string; - /** Keyboard shortcut hint. */ - shortcut?: string; - /** Icon character (single cell). */ - icon?: string; - /** Source ID this item came from. */ - sourceId: string; - /** Payload for onSelect. */ - data?: unknown; - /** Whether item is disabled. */ - disabled?: boolean; -}>; - -/** Props for CommandPalette widget. Quick-access command execution and navigation. */ -export type CommandPaletteProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Visible state. */ - open: boolean; - /** Current search query. */ - query: string; - /** Command sources. */ - sources: readonly CommandSource[]; - /** Selected item index. */ - selectedIndex: number; - /** Loading state for async sources. */ - loading?: boolean; - /** Placeholder text. */ - placeholder?: string; - /** Maximum visible items (default: 10). */ - maxVisible?: number; - /** Palette width in cells (default: 60). */ - width?: number; - /** Frame/surface colors for palette background, text, and border. */ - frameStyle?: OverlayFrameStyle; - /** Optional style override for selected result row highlighting. */ - selectionStyle?: TextStyle; - /** Callback when query changes. */ - onChange: (query: string) => void; - /** Callback when item is selected. */ - onSelect: (item: CommandItem) => void; - /** Callback when palette should close. */ - onClose: () => void; - /** Callback when selection index changes. */ - onSelectionChange?: (index: number) => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; -}>; - -/* ---------- FilePicker & FileTreeExplorer Widgets ---------- */ - -/** Node in file tree. */ -export type FileNode = Readonly<{ - /** File/directory name. */ - name: string; - /** Full path. */ - path: string; - /** Node type. */ - type: "file" | "directory"; - /** Child nodes (for directories). */ - children?: readonly FileNode[]; - /** Git status indicator. */ - status?: "modified" | "staged" | "untracked" | "deleted" | "renamed"; -}>; - -/** State information for a file tree node during rendering. */ -export type FileNodeState = Readonly<{ - /** Whether the node is expanded. */ - expanded: boolean; - /** Whether the node is selected. */ - selected: boolean; - /** Whether the node is focused. */ - focused: boolean; - /** Depth level in the tree (0 = root). */ - depth: number; - /** Whether this is the first sibling. */ - isFirst: boolean; - /** Whether this is the last sibling. */ - isLast: boolean; - /** Whether the node has children. */ - hasChildren: boolean; -}>; - -/** Props for FilePicker widget. Browse and select workspace files. */ -export type FilePickerProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Root path for file browsing. */ - rootPath: string; - /** File tree data to render (provided by app/runtime; core does not read the filesystem). */ - data: FileNode | readonly FileNode[]; - /** Currently selected file path. */ - selectedPath?: string; - /** Expanded directory paths. */ - expandedPaths: readonly string[]; - /** Files with modified state. */ - modifiedPaths?: readonly string[]; - /** Files with staged state. */ - stagedPaths?: readonly string[]; - /** Optional style override for selected rows. */ - selectionStyle?: TextStyle; - /** Filter pattern (glob). */ - filter?: string; - /** Show hidden files. */ - showHidden?: boolean; - /** Allow multiple selection. */ - multiSelect?: boolean; - /** Selected paths for multi-select. */ - selection?: readonly string[]; - /** Callback when file is selected. */ - onSelect: (path: string) => void; - /** Callback when directory expand state changes. */ - onChange: (path: string, expanded: boolean) => void; - /** Callback when file is opened (double-click / Enter). */ - onPress: (path: string) => void; - /** Callback when selection changes (multi-select). */ - onSelectionChange?: (paths: readonly string[]) => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; -}> & - LayoutConstraints; - -/** Props for FileTreeExplorer widget. Tree view of files with expand/collapse. */ -export type FileTreeExplorerProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** File tree data. */ - data: FileNode | readonly FileNode[]; - /** Expanded node paths. */ - expanded: readonly string[]; - /** Selected node path. */ - selected?: string; - /** Focused node path (for keyboard nav). */ - focused?: string; - /** Show file icons. */ - showIcons?: boolean; - /** Show git status indicators. */ - showStatus?: boolean; - /** Optional style override for selected rows. */ - selectionStyle?: TextStyle; - /** Indentation per level (default: 2). */ - indentSize?: number; - /** Callback when node expand state changes. */ - onChange: (node: FileNode, expanded: boolean) => void; - /** Callback when node is selected. */ - onSelect: (node: FileNode) => void; - /** Callback when node is activated (Enter / double-click). */ - onPress: (node: FileNode) => void; - /** Callback for context menu (right-click / Menu key). */ - onContextMenu?: (node: FileNode) => void; - /** Custom node renderer. */ - renderNode?: (node: FileNode, depth: number, state: FileNodeState) => VNode; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; -}> & - LayoutConstraints; - -/* ---------- SplitPane & ResizablePanels Widgets ---------- */ - -/** Direction for split pane layout. */ -export type SplitDirection = "horizontal" | "vertical"; - -/** Props for SplitPane widget. Draggable divider between panels. */ -export type SplitPaneProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Split direction. */ - direction: SplitDirection; - /** Panel sizes (percentages 0-100 or absolute cells). */ - sizes: readonly number[]; - /** Size mode. */ - sizeMode?: "percent" | "absolute"; - /** Minimum panel sizes. */ - minSizes?: readonly number[]; - /** Maximum panel sizes. */ - maxSizes?: readonly number[]; - /** Divider size in cells (default: 1). */ - dividerSize?: number; - /** Allow collapsing panels. */ - collapsible?: boolean; - /** Collapsed panel indices. */ - collapsed?: readonly number[]; - /** Callback when sizes change from dragging. */ - onChange: (sizes: readonly number[]) => void; - /** Callback when panel collapse state changes. */ - onCollapse?: (index: number, collapsed: boolean) => void; -}>; - -/** Props for ResizablePanel widget. Panel within SplitPane/PanelGroup. */ -export type ResizablePanelProps = Readonly<{ - key?: string; - /** Initial size (percent or cells based on parent sizeMode). */ - defaultSize?: number; - /** Minimum size. */ - minSize?: number; - /** Maximum size. */ - maxSize?: number; - /** Whether panel can be collapsed. */ - collapsible?: boolean; -}>; - -/** Props for PanelGroup widget. Container for resizable panels. */ -export type PanelGroupProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Layout direction. */ - direction: SplitDirection; -}>; - -/* ---------- CodeEditor Widget ---------- */ - -/** Cursor position in CodeEditor. */ -export type CursorPosition = Readonly<{ - /** Line number (0-indexed). */ - line: number; - /** Column number (0-indexed, in characters not cells). */ - column: number; -}>; - -/** Selection range in CodeEditor. */ -export type EditorSelection = Readonly<{ - /** Selection anchor (start). */ - anchor: CursorPosition; - /** Selection active end (cursor position). */ - active: CursorPosition; -}>; - -/** Search match in CodeEditor. */ -export type SearchMatch = Readonly<{ - /** Line number of match. */ - line: number; - /** Start column of match. */ - startColumn: number; - /** End column of match. */ - endColumn: number; -}>; - -/** Diagnostic severity for CodeEditor inline markers. */ -export type CodeEditorDiagnosticSeverity = "error" | "warning" | "info" | "hint"; - -/** Inline diagnostic range rendered in CodeEditor. */ -export type CodeEditorDiagnostic = Readonly<{ - /** 0-based line index. */ - line: number; - /** 0-based start column. */ - startColumn: number; - /** 0-based end column (exclusive). */ - endColumn: number; - /** Severity bucket controlling underline color. */ - severity: CodeEditorDiagnosticSeverity; - /** Optional diagnostic message. */ - message?: string; -}>; - -/** Built-in syntax language presets for CodeEditor tokenization. */ -export type CodeEditorSyntaxLanguage = - | "plain" - | "typescript" - | "javascript" - | "json" - | "go" - | "rust" - | "c" - | "cpp" - | "c++" - | "csharp" - | "c#" - | "java" - | "python" - | "bash"; - -/** Semantic token buckets produced by CodeEditor syntax tokenizers. */ -export type CodeEditorSyntaxTokenKind = - | "plain" - | "keyword" - | "type" - | "string" - | "number" - | "comment" - | "operator" - | "punctuation" - | "function" - | "variable"; - -/** Single syntax token emitted by a CodeEditor line tokenizer. */ -export type CodeEditorSyntaxToken = Readonly<{ - /** Token text (must map back to the source line). */ - text: string; - /** Semantic token kind used for style mapping. */ - kind: CodeEditorSyntaxTokenKind; -}>; - -/** Context passed to custom CodeEditor line tokenizers. */ -export type CodeEditorTokenizeContext = Readonly<{ - /** Active syntax language preset. */ - language: CodeEditorSyntaxLanguage; - /** 0-based document line index. */ - lineNumber: number; -}>; - -/** Optional custom per-line tokenizer for CodeEditor. */ -export type CodeEditorLineTokenizer = ( - line: string, - context: CodeEditorTokenizeContext, -) => readonly CodeEditorSyntaxToken[]; - -/** Props for CodeEditor widget. Multiline text editing with selections. */ -export type CodeEditorProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Document content (lines). */ - lines: readonly string[]; - /** Cursor position. */ - cursor: CursorPosition; - /** Selection (null if no selection). */ - selection: EditorSelection | null; - /** Scroll position (lines from top). */ - scrollTop: number; - /** Horizontal scroll position (columns from left). */ - scrollLeft: number; - /** Tab size in spaces (default: 2). */ - tabSize?: number; - /** Insert spaces instead of tabs (default: true). */ - insertSpaces?: boolean; - /** Show line numbers (default: true). */ - lineNumbers?: boolean; - /** Wrap long lines (default: false). */ - wordWrap?: boolean; - /** Read-only mode. */ - readOnly?: boolean; - /** Search query. */ - searchQuery?: string; - /** Search match positions. */ - searchMatches?: readonly SearchMatch[]; - /** Currently highlighted match index. */ - currentMatchIndex?: number; - /** Optional diagnostics rendered as styled underlines. */ - diagnostics?: readonly CodeEditorDiagnostic[]; - /** Built-in syntax language preset (default: "plain"). */ - syntaxLanguage?: CodeEditorSyntaxLanguage; - /** Optional custom tokenizer for per-line syntax highlighting. */ - tokenizeLine?: CodeEditorLineTokenizer; - /** Render a visible highlighted cursor cell (default: true). */ - highlightActiveCursorCell?: boolean; - /** Callback when content changes. */ - onChange: (lines: readonly string[], cursor: CursorPosition) => void; - /** Callback when selection changes. */ - onSelectionChange: (selection: EditorSelection | null) => void; - /** Callback when scroll position changes. */ - onScroll: (scrollTop: number, scrollLeft: number) => void; - /** Callback for undo action. */ - onUndo?: () => void; - /** Callback for redo action. */ - onRedo?: () => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Scrollbar glyph variant (default: "minimal"). */ - scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; - /** Optional style override for rendered scrollbar. */ - scrollbarStyle?: TextStyle; -}> & - LayoutConstraints; - -/* ---------- DiffViewer Widget ---------- */ - -/** Line in a diff hunk. */ -export type DiffLine = Readonly<{ - /** Line type. */ - type: "context" | "add" | "delete"; - /** Line content. */ - content: string; - /** Original line number (for context and delete). */ - oldLineNumber?: number; - /** New line number (for context and add). */ - newLineNumber?: number; - /** Intra-line change highlights as [start, end] pairs. */ - highlights?: readonly (readonly [number, number])[]; -}>; - -/** Diff hunk containing a group of changes. */ -export type DiffHunk = Readonly<{ - /** Original line range start. */ - oldStart: number; - /** Original line count. */ - oldCount: number; - /** New line range start. */ - newStart: number; - /** New line count. */ - newCount: number; - /** Header text (e.g., function name). */ - header?: string; - /** Diff lines in this hunk. */ - lines: readonly DiffLine[]; -}>; - -/** Complete diff data for a file. */ -export type DiffData = Readonly<{ - /** Original file path. */ - oldPath: string; - /** New file path. */ - newPath: string; - /** Diff hunks. */ - hunks: readonly DiffHunk[]; - /** Binary file flag. */ - isBinary?: boolean; - /** File change status. */ - status: "added" | "deleted" | "modified" | "renamed" | "copied"; -}>; - -/** Props for DiffViewer widget. Show unified or side-by-side diffs. */ -export type DiffViewerProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Diff data to display. */ - diff: DiffData; - /** View mode. */ - mode: "unified" | "sideBySide"; - /** Scroll position (lines from top). */ - scrollTop: number; - /** Expanded hunk indices (collapsed by default if > threshold). */ - expandedHunks?: readonly number[]; - /** Currently focused hunk index. */ - focusedHunk?: number; - /** Show line numbers. */ - lineNumbers?: boolean; - /** Context lines around changes (default: 3). */ - contextLines?: number; - /** Optional style override for focused hunk header. */ - focusedHunkStyle?: TextStyle; - /** Callback when scroll position changes. */ - onScroll: (scrollTop: number) => void; - /** Callback when hunk expand state changes. */ - onHunkToggle?: (hunkIndex: number, expanded: boolean) => void; - /** Callback to stage a hunk. */ - onStageHunk?: (hunkIndex: number) => void; - /** Callback to unstage a hunk. */ - onUnstageHunk?: (hunkIndex: number) => void; - /** Callback to apply a hunk. */ - onApplyHunk?: (hunkIndex: number) => void; - /** Callback to revert a hunk. */ - onRevertHunk?: (hunkIndex: number) => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Scrollbar glyph variant (default: "minimal"). */ - scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; - /** Optional style override for rendered scrollbar. */ - scrollbarStyle?: TextStyle; -}> & - LayoutConstraints; - -/* ---------- ToolApprovalDialog Widget ---------- */ - -/** File change in a tool request. */ -export type ToolFileChange = Readonly<{ - /** File path. */ - path: string; - /** Type of change. */ - changeType: "create" | "modify" | "delete" | "rename"; - /** Preview of changes (first N lines). */ - preview?: string; - /** Old path for renames. */ - oldPath?: string; -}>; - -/** Tool request being approved. */ -export type ToolRequest = Readonly<{ - /** Tool identifier. */ - toolId: string; - /** Tool display name. */ - toolName: string; - /** Tool description. */ - description?: string; - /** Command to execute (if CLI tool). */ - command?: string; - /** Files that will be modified. */ - fileChanges?: readonly ToolFileChange[]; - /** Risk level. */ - riskLevel: "low" | "medium" | "high"; - /** Additional context/arguments. */ - args?: Record; -}>; - -/** Props for ToolApprovalDialog widget. Modal for reviewing tool execution. */ -export type ToolApprovalDialogProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Tool request being approved. */ - request: ToolRequest; - /** Visible state. */ - open: boolean; - /** Dialog width in cells (default: 50). */ - width?: number; - /** Dialog height in cells (default: 15). */ - height?: number; - /** Focused action button. */ - focusedAction?: "allow" | "deny" | "allowSession"; - /** Callback when an allow/deny action is pressed. */ - onPress: (action: "allow" | "deny") => void; - /** Callback when allowed for session. */ - onAllowForSession?: () => void; - /** Callback when dialog should close. */ - onClose: () => void; -}>; - -/* ---------- LogsConsole Widget ---------- */ - -/** Log severity level. */ -export type LogLevel = "trace" | "debug" | "info" | "warn" | "error"; - -/** Token usage count. */ -export type TokenCount = Readonly<{ - /** Input tokens. */ - input: number; - /** Output tokens. */ - output: number; - /** Total tokens. */ - total: number; -}>; - -/** Log entry in LogsConsole. */ -export type LogEntry = Readonly<{ - /** Unique entry identifier. */ - id: string; - /** Timestamp (Unix ms). */ - timestamp: number; - /** Log level. */ - level: LogLevel; - /** Source/category. */ - source: string; - /** Log message. */ - message: string; - /** Expandable details. */ - details?: string; - /** Token count (for LLM responses). */ - tokens?: TokenCount; - /** Duration in milliseconds. */ - durationMs?: number; - /** Cost in cents. */ - costCents?: number; -}>; - -/** Props for LogsConsole widget. Streaming tool output and events. */ -export type LogsConsoleProps = Readonly<{ - /** REQUIRED - Interactive widget identifier. */ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Log entries. */ - entries: readonly LogEntry[]; - /** Auto-scroll to bottom (default: true). */ - autoScroll?: boolean; - /** Filter by log level. */ - levelFilter?: readonly LogLevel[]; - /** Filter by source. */ - sourceFilter?: readonly string[]; - /** Search query. */ - searchQuery?: string; - /** Scroll position (entries from top). */ - scrollTop: number; - /** Show timestamps (default: true). */ - showTimestamps?: boolean; - /** Show source labels (default: true). */ - showSource?: boolean; - /** Expanded entry IDs. */ - expandedEntries?: readonly string[]; - /** Optional style override for focused-console ring. */ - focusedStyle?: TextStyle; - /** Callback when scroll position changes. */ - onScroll: (scrollTop: number) => void; - /** Callback when entry expand state changes. */ - onChange?: (entryId: string, expanded: boolean) => void; - /** Callback to clear logs. */ - onPress?: () => void; - /** Optional focus appearance configuration. */ - focusConfig?: FocusConfig; - /** Scrollbar glyph variant (default: "minimal"). */ - scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; - /** Optional style override for rendered scrollbar. */ - scrollbarStyle?: TextStyle; -}> & - LayoutConstraints; - -/* ---------- Toast/Notifications Widget ---------- */ - -/** Position for toast container. */ -export type ToastPosition = - | "top-left" - | "top-center" - | "top-right" - | "bottom-left" - | "bottom-center" - | "bottom-right"; - -/** Action button in a toast. */ -export type ToastAction = Readonly<{ - /** Action button label. */ - label: string; - /** Callback when action is clicked. */ - onAction: () => void; -}>; - -/** Toast notification. */ -export type Toast = Readonly<{ - /** Unique toast identifier. */ - id: string; - /** Message text. */ - message: string; - /** Toast type. */ - type: "info" | "success" | "warning" | "error"; - /** Auto-dismiss duration in ms (0 = persistent, default: 3000). */ - duration?: number; - /** Action button. */ - action?: ToastAction; - /** Progress indicator (0-100). */ - progress?: number; -}>; - -/** Props for ToastContainer widget. Non-blocking feedback messages. */ -export type ToastContainerProps = Readonly<{ - key?: string; - /** Active toasts. */ - toasts: readonly Toast[]; - /** Position on screen (default: "bottom-right"). */ - position?: ToastPosition; - /** Maximum visible toasts (default: 5). */ - maxVisible?: number; - /** Toast container width in cells (default: 40). */ - width?: number; - /** Frame/surface colors for toast backgrounds, text, and borders. */ - frameStyle?: OverlayFrameStyle; - /** Callback when toast is dismissed. */ - onClose: (id: string) => void; -}>; - -/* ========== Tree Widget (GitHub issue #122) ========== */ - -/** State information for a tree node during rendering. */ -export type NodeState = Readonly<{ - /** Whether the node is expanded. */ - expanded: boolean; - /** Whether the node is selected. */ - selected: boolean; - /** Whether the node is focused. */ - focused: boolean; - /** Whether the node is loading children. */ - loading: boolean; - /** Depth level in the tree (0 = root). */ - depth: number; - /** Whether this is the first sibling. */ - isFirst: boolean; - /** Whether this is the last sibling. */ - isLast: boolean; - /** Whether the node has children (or could have). */ - hasChildren: boolean; -}>; - -/** Props for tree widget. */ -export type TreeProps = Readonly<{ - id: string; - key?: string; - /** Opt out of Tab focus order while keeping id-based routing available. */ - focusable?: boolean; - /** Optional semantic label used for accessibility/debug announcements. */ - accessibleLabel?: string; - /** Root node(s). Can be a single root or array of roots. */ - data: T | readonly T[]; - /** Function to get unique key for each node. */ - getKey: (node: T) => string; - /** Function to get children of a node (undefined = leaf node). */ - getChildren?: (node: T) => readonly T[] | undefined; - /** Function to check if node has children (for lazy loading). */ - hasChildren?: (node: T) => boolean; - /** Set of expanded node keys. */ - expanded: readonly string[]; - /** Currently selected node key. */ - selected?: string; - /** Callback when node expand/collapse state changes. */ - onChange: (node: T, expanded: boolean) => void; - /** Callback when node is selected. */ - onSelect?: (node: T) => void; - /** Callback when node is activated (Enter key or double-click). */ - onPress?: (node: T) => void; - /** Custom render function for node content. */ - renderNode: (node: T, depth: number, state: NodeState) => VNode; - /** Function to load children asynchronously. */ - loadChildren?: (node: T) => Promise; - /** Indentation per depth level in cells (default: 2). */ - indentSize?: number; - /** Show tree lines (├── └── │). */ - showLines?: boolean; - /** Design system: visual variant. */ - dsVariant?: WidgetVariant; - /** Design system: color tone. */ - dsTone?: WidgetTone; - /** Design system: size preset. */ - dsSize?: WidgetSize; -}> & - LayoutConstraints; +import type { + CodeEditorProps, + CommandPaletteProps, + DiffViewerProps, + FilePickerProps, + FileTreeExplorerProps, + LogsConsoleProps, + PanelGroupProps, + ResizablePanelProps, + SplitPaneProps, + ToastContainerProps, + ToolApprovalDialogProps, +} from "./types/advanced.js"; +import type { + BadgeProps, + BarChartProps, + BoxProps, + ButtonProps, + CalloutProps, + CanvasProps, + ColumnProps, + DividerProps, + EmptyProps, + ErrorBoundaryProps, + ErrorDisplayProps, + FocusAnnouncerProps, + FocusTrapProps, + FocusZoneProps, + FragmentProps, + GaugeProps, + GridProps, + HeatmapProps, + IconProps, + ImageProps, + InputProps, + KbdProps, + LineChartProps, + LinkProps, + MiniChartProps, + ProgressProps, + RichTextProps, + RowProps, + ScatterProps, + SkeletonProps, + SpacerProps, + SparklineProps, + SpinnerProps, + StatusProps, + TagProps, + TextProps, + TextareaProps, + ThemedProps, + VirtualListProps, +} from "./types/base.js"; +import type { + CheckboxProps, + FieldProps, + RadioGroupProps, + SelectProps, + SliderProps, +} from "./types/forms.js"; +import type { + AccordionProps, + BreadcrumbProps, + PaginationProps, + TabsProps, +} from "./types/navigation.js"; +import type { DropdownProps, LayerProps, LayersProps, ModalProps } from "./types/overlaysShell.js"; +import type { TableProps } from "./types/table.js"; +import type { TreeProps } from "./types/tree.js"; export type VNode = | Readonly<{ kind: "text"; text: string; props: TextProps }> diff --git a/packages/core/src/widgets/types/advanced.ts b/packages/core/src/widgets/types/advanced.ts new file mode 100644 index 00000000..dcf27797 --- /dev/null +++ b/packages/core/src/widgets/types/advanced.ts @@ -0,0 +1,703 @@ +import type { FocusConfig } from "../../focus/styles.js"; +import type { LayoutConstraints } from "../../layout/types.js"; +import type { TextStyle } from "../style.js"; +import type { VNode } from "../types.js"; +import type { OverlayFrameStyle } from "./overlaysShell.js"; + +/* ========== Advanced Widgets (GitHub issue #136) ========== */ + +/* ---------- CommandPalette Widget ---------- */ + +/** Source of commands for CommandPalette. */ +export type CommandSource = Readonly<{ + /** Source identifier. */ + id: string; + /** Source display name. */ + name: string; + /** Prefix trigger (e.g., ">" for commands, "@" for symbols). */ + prefix?: string; + /** Sync or async item provider. */ + getItems: (query: string) => readonly CommandItem[] | Promise; + /** Priority for sorting (higher = first). */ + priority?: number; +}>; + +/** Item in CommandPalette. */ +export type CommandItem = Readonly<{ + /** Unique item identifier. */ + id: string; + /** Display label. */ + label: string; + /** Secondary description. */ + description?: string; + /** Keyboard shortcut hint. */ + shortcut?: string; + /** Icon character (single cell). */ + icon?: string; + /** Source ID this item came from. */ + sourceId: string; + /** Payload for onSelect. */ + data?: unknown; + /** Whether item is disabled. */ + disabled?: boolean; +}>; + +/** Props for CommandPalette widget. Quick-access command execution and navigation. */ +export type CommandPaletteProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Visible state. */ + open: boolean; + /** Current search query. */ + query: string; + /** Command sources. */ + sources: readonly CommandSource[]; + /** Selected item index. */ + selectedIndex: number; + /** Loading state for async sources. */ + loading?: boolean; + /** Placeholder text. */ + placeholder?: string; + /** Maximum visible items (default: 10). */ + maxVisible?: number; + /** Palette width in cells (default: 60). */ + width?: number; + /** Frame/surface colors for palette background, text, and border. */ + frameStyle?: OverlayFrameStyle; + /** Optional style override for selected result row highlighting. */ + selectionStyle?: TextStyle; + /** Callback when query changes. */ + onChange: (query: string) => void; + /** Callback when item is selected. */ + onSelect: (item: CommandItem) => void; + /** Callback when palette should close. */ + onClose: () => void; + /** Callback when selection index changes. */ + onSelectionChange?: (index: number) => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; +}>; + +/* ---------- FilePicker & FileTreeExplorer Widgets ---------- */ + +/** Node in file tree. */ +export type FileNode = Readonly<{ + /** File/directory name. */ + name: string; + /** Full path. */ + path: string; + /** Node type. */ + type: "file" | "directory"; + /** Child nodes (for directories). */ + children?: readonly FileNode[]; + /** Git status indicator. */ + status?: "modified" | "staged" | "untracked" | "deleted" | "renamed"; +}>; + +/** State information for a file tree node during rendering. */ +export type FileNodeState = Readonly<{ + /** Whether the node is expanded. */ + expanded: boolean; + /** Whether the node is selected. */ + selected: boolean; + /** Whether the node is focused. */ + focused: boolean; + /** Depth level in the tree (0 = root). */ + depth: number; + /** Whether this is the first sibling. */ + isFirst: boolean; + /** Whether this is the last sibling. */ + isLast: boolean; + /** Whether the node has children. */ + hasChildren: boolean; +}>; + +/** Props for FilePicker widget. Browse and select workspace files. */ +export type FilePickerProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Root path for file browsing. */ + rootPath: string; + /** File tree data to render (provided by app/runtime; core does not read the filesystem). */ + data: FileNode | readonly FileNode[]; + /** Currently selected file path. */ + selectedPath?: string; + /** Expanded directory paths. */ + expandedPaths: readonly string[]; + /** Files with modified state. */ + modifiedPaths?: readonly string[]; + /** Files with staged state. */ + stagedPaths?: readonly string[]; + /** Optional style override for selected rows. */ + selectionStyle?: TextStyle; + /** Filter pattern (glob). */ + filter?: string; + /** Show hidden files. */ + showHidden?: boolean; + /** Allow multiple selection. */ + multiSelect?: boolean; + /** Selected paths for multi-select. */ + selection?: readonly string[]; + /** Callback when file is selected. */ + onSelect: (path: string) => void; + /** Callback when directory expand state changes. */ + onChange: (path: string, expanded: boolean) => void; + /** Callback when file is opened (double-click / Enter). */ + onPress: (path: string) => void; + /** Callback when selection changes (multi-select). */ + onSelectionChange?: (paths: readonly string[]) => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; +}> & + LayoutConstraints; + +/** Props for FileTreeExplorer widget. Tree view of files with expand/collapse. */ +export type FileTreeExplorerProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** File tree data. */ + data: FileNode | readonly FileNode[]; + /** Expanded node paths. */ + expanded: readonly string[]; + /** Selected node path. */ + selected?: string; + /** Focused node path (for keyboard nav). */ + focused?: string; + /** Show file icons. */ + showIcons?: boolean; + /** Show git status indicators. */ + showStatus?: boolean; + /** Optional style override for selected rows. */ + selectionStyle?: TextStyle; + /** Indentation per level (default: 2). */ + indentSize?: number; + /** Callback when node expand state changes. */ + onChange: (node: FileNode, expanded: boolean) => void; + /** Callback when node is selected. */ + onSelect: (node: FileNode) => void; + /** Callback when node is activated (Enter / double-click). */ + onPress: (node: FileNode) => void; + /** Callback for context menu (right-click / Menu key). */ + onContextMenu?: (node: FileNode) => void; + /** Custom node renderer. */ + renderNode?: (node: FileNode, depth: number, state: FileNodeState) => VNode; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; +}> & + LayoutConstraints; + +/* ---------- SplitPane & ResizablePanels Widgets ---------- */ + +/** Direction for split pane layout. */ +export type SplitDirection = "horizontal" | "vertical"; + +/** Props for SplitPane widget. Draggable divider between panels. */ +export type SplitPaneProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Split direction. */ + direction: SplitDirection; + /** Panel sizes (percentages 0-100 or absolute cells). */ + sizes: readonly number[]; + /** Size mode. */ + sizeMode?: "percent" | "absolute"; + /** Minimum panel sizes. */ + minSizes?: readonly number[]; + /** Maximum panel sizes. */ + maxSizes?: readonly number[]; + /** Divider size in cells (default: 1). */ + dividerSize?: number; + /** Allow collapsing panels. */ + collapsible?: boolean; + /** Collapsed panel indices. */ + collapsed?: readonly number[]; + /** Callback when sizes change from dragging. */ + onChange: (sizes: readonly number[]) => void; + /** Callback when panel collapse state changes. */ + onCollapse?: (index: number, collapsed: boolean) => void; +}>; + +/** Props for ResizablePanel widget. Panel within SplitPane/PanelGroup. */ +export type ResizablePanelProps = Readonly<{ + key?: string; + /** Initial size (percent or cells based on parent sizeMode). */ + defaultSize?: number; + /** Minimum size. */ + minSize?: number; + /** Maximum size. */ + maxSize?: number; + /** Whether panel can be collapsed. */ + collapsible?: boolean; +}>; + +/** Props for PanelGroup widget. Container for resizable panels. */ +export type PanelGroupProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Layout direction. */ + direction: SplitDirection; +}>; + +/* ---------- CodeEditor Widget ---------- */ + +/** Cursor position in CodeEditor. */ +export type CursorPosition = Readonly<{ + /** Line number (0-indexed). */ + line: number; + /** Column number (0-indexed, in characters not cells). */ + column: number; +}>; + +/** Selection range in CodeEditor. */ +export type EditorSelection = Readonly<{ + /** Selection anchor (start). */ + anchor: CursorPosition; + /** Selection active end (cursor position). */ + active: CursorPosition; +}>; + +/** Search match in CodeEditor. */ +export type SearchMatch = Readonly<{ + /** Line number of match. */ + line: number; + /** Start column of match. */ + startColumn: number; + /** End column of match. */ + endColumn: number; +}>; + +/** Diagnostic severity for CodeEditor inline markers. */ +export type CodeEditorDiagnosticSeverity = "error" | "warning" | "info" | "hint"; + +/** Inline diagnostic range rendered in CodeEditor. */ +export type CodeEditorDiagnostic = Readonly<{ + /** 0-based line index. */ + line: number; + /** 0-based start column. */ + startColumn: number; + /** 0-based end column (exclusive). */ + endColumn: number; + /** Severity bucket controlling underline color. */ + severity: CodeEditorDiagnosticSeverity; + /** Optional diagnostic message. */ + message?: string; +}>; + +/** Built-in syntax language presets for CodeEditor tokenization. */ +export type CodeEditorSyntaxLanguage = + | "plain" + | "typescript" + | "javascript" + | "json" + | "go" + | "rust" + | "c" + | "cpp" + | "c++" + | "csharp" + | "c#" + | "java" + | "python" + | "bash"; + +/** Semantic token buckets produced by CodeEditor syntax tokenizers. */ +export type CodeEditorSyntaxTokenKind = + | "plain" + | "keyword" + | "type" + | "string" + | "number" + | "comment" + | "operator" + | "punctuation" + | "function" + | "variable"; + +/** Single syntax token emitted by a CodeEditor line tokenizer. */ +export type CodeEditorSyntaxToken = Readonly<{ + /** Token text (must map back to the source line). */ + text: string; + /** Semantic token kind used for style mapping. */ + kind: CodeEditorSyntaxTokenKind; +}>; + +/** Context passed to custom CodeEditor line tokenizers. */ +export type CodeEditorTokenizeContext = Readonly<{ + /** Active syntax language preset. */ + language: CodeEditorSyntaxLanguage; + /** 0-based document line index. */ + lineNumber: number; +}>; + +/** Optional custom per-line tokenizer for CodeEditor. */ +export type CodeEditorLineTokenizer = ( + line: string, + context: CodeEditorTokenizeContext, +) => readonly CodeEditorSyntaxToken[]; + +/** Props for CodeEditor widget. Multiline text editing with selections. */ +export type CodeEditorProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Document content (lines). */ + lines: readonly string[]; + /** Cursor position. */ + cursor: CursorPosition; + /** Selection (null if no selection). */ + selection: EditorSelection | null; + /** Scroll position (lines from top). */ + scrollTop: number; + /** Horizontal scroll position (columns from left). */ + scrollLeft: number; + /** Tab size in spaces (default: 2). */ + tabSize?: number; + /** Insert spaces instead of tabs (default: true). */ + insertSpaces?: boolean; + /** Show line numbers (default: true). */ + lineNumbers?: boolean; + /** Wrap long lines (default: false). */ + wordWrap?: boolean; + /** Read-only mode. */ + readOnly?: boolean; + /** Search query. */ + searchQuery?: string; + /** Search match positions. */ + searchMatches?: readonly SearchMatch[]; + /** Currently highlighted match index. */ + currentMatchIndex?: number; + /** Optional diagnostics rendered as styled underlines. */ + diagnostics?: readonly CodeEditorDiagnostic[]; + /** Built-in syntax language preset (default: "plain"). */ + syntaxLanguage?: CodeEditorSyntaxLanguage; + /** Optional custom tokenizer for per-line syntax highlighting. */ + tokenizeLine?: CodeEditorLineTokenizer; + /** Render a visible highlighted cursor cell (default: true). */ + highlightActiveCursorCell?: boolean; + /** Callback when content changes. */ + onChange: (lines: readonly string[], cursor: CursorPosition) => void; + /** Callback when selection changes. */ + onSelectionChange: (selection: EditorSelection | null) => void; + /** Callback when scroll position changes. */ + onScroll: (scrollTop: number, scrollLeft: number) => void; + /** Callback for undo action. */ + onUndo?: () => void; + /** Callback for redo action. */ + onRedo?: () => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Scrollbar glyph variant (default: "minimal"). */ + scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; + /** Optional style override for rendered scrollbar. */ + scrollbarStyle?: TextStyle; +}> & + LayoutConstraints; + +/* ---------- DiffViewer Widget ---------- */ + +/** Line in a diff hunk. */ +export type DiffLine = Readonly<{ + /** Line type. */ + type: "context" | "add" | "delete"; + /** Line content. */ + content: string; + /** Original line number (for context and delete). */ + oldLineNumber?: number; + /** New line number (for context and add). */ + newLineNumber?: number; + /** Intra-line change highlights as [start, end] pairs. */ + highlights?: readonly (readonly [number, number])[]; +}>; + +/** Diff hunk containing a group of changes. */ +export type DiffHunk = Readonly<{ + /** Original line range start. */ + oldStart: number; + /** Original line count. */ + oldCount: number; + /** New line range start. */ + newStart: number; + /** New line count. */ + newCount: number; + /** Header text (e.g., function name). */ + header?: string; + /** Diff lines in this hunk. */ + lines: readonly DiffLine[]; +}>; + +/** Complete diff data for a file. */ +export type DiffData = Readonly<{ + /** Original file path. */ + oldPath: string; + /** New file path. */ + newPath: string; + /** Diff hunks. */ + hunks: readonly DiffHunk[]; + /** Binary file flag. */ + isBinary?: boolean; + /** File change status. */ + status: "added" | "deleted" | "modified" | "renamed" | "copied"; +}>; + +/** Props for DiffViewer widget. Show unified or side-by-side diffs. */ +export type DiffViewerProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Diff data to display. */ + diff: DiffData; + /** View mode. */ + mode: "unified" | "sideBySide"; + /** Scroll position (lines from top). */ + scrollTop: number; + /** Expanded hunk indices (collapsed by default if > threshold). */ + expandedHunks?: readonly number[]; + /** Currently focused hunk index. */ + focusedHunk?: number; + /** Show line numbers. */ + lineNumbers?: boolean; + /** Context lines around changes (default: 3). */ + contextLines?: number; + /** Optional style override for focused hunk header. */ + focusedHunkStyle?: TextStyle; + /** Callback when scroll position changes. */ + onScroll: (scrollTop: number) => void; + /** Callback when hunk expand state changes. */ + onHunkToggle?: (hunkIndex: number, expanded: boolean) => void; + /** Callback to stage a hunk. */ + onStageHunk?: (hunkIndex: number) => void; + /** Callback to unstage a hunk. */ + onUnstageHunk?: (hunkIndex: number) => void; + /** Callback to apply a hunk. */ + onApplyHunk?: (hunkIndex: number) => void; + /** Callback to revert a hunk. */ + onRevertHunk?: (hunkIndex: number) => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Scrollbar glyph variant (default: "minimal"). */ + scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; + /** Optional style override for rendered scrollbar. */ + scrollbarStyle?: TextStyle; +}> & + LayoutConstraints; + +/* ---------- ToolApprovalDialog Widget ---------- */ + +/** File change in a tool request. */ +export type ToolFileChange = Readonly<{ + /** File path. */ + path: string; + /** Type of change. */ + changeType: "create" | "modify" | "delete" | "rename"; + /** Preview of changes (first N lines). */ + preview?: string; + /** Old path for renames. */ + oldPath?: string; +}>; + +/** Tool request being approved. */ +export type ToolRequest = Readonly<{ + /** Tool identifier. */ + toolId: string; + /** Tool display name. */ + toolName: string; + /** Tool description. */ + description?: string; + /** Command to execute (if CLI tool). */ + command?: string; + /** Files that will be modified. */ + fileChanges?: readonly ToolFileChange[]; + /** Risk level. */ + riskLevel: "low" | "medium" | "high"; + /** Additional context/arguments. */ + args?: Record; +}>; + +/** Props for ToolApprovalDialog widget. Modal for reviewing tool execution. */ +export type ToolApprovalDialogProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Tool request being approved. */ + request: ToolRequest; + /** Visible state. */ + open: boolean; + /** Dialog width in cells (default: 50). */ + width?: number; + /** Dialog height in cells (default: 15). */ + height?: number; + /** Focused action button. */ + focusedAction?: "allow" | "deny" | "allowSession"; + /** Callback when an allow/deny action is pressed. */ + onPress: (action: "allow" | "deny") => void; + /** Callback when allowed for session. */ + onAllowForSession?: () => void; + /** Callback when dialog should close. */ + onClose: () => void; +}>; + +/* ---------- LogsConsole Widget ---------- */ + +/** Log severity level. */ +export type LogLevel = "trace" | "debug" | "info" | "warn" | "error"; + +/** Token usage count. */ +export type TokenCount = Readonly<{ + /** Input tokens. */ + input: number; + /** Output tokens. */ + output: number; + /** Total tokens. */ + total: number; +}>; + +/** Log entry in LogsConsole. */ +export type LogEntry = Readonly<{ + /** Unique entry identifier. */ + id: string; + /** Timestamp (Unix ms). */ + timestamp: number; + /** Log level. */ + level: LogLevel; + /** Source/category. */ + source: string; + /** Log message. */ + message: string; + /** Expandable details. */ + details?: string; + /** Token count (for LLM responses). */ + tokens?: TokenCount; + /** Duration in milliseconds. */ + durationMs?: number; + /** Cost in cents. */ + costCents?: number; +}>; + +/** Props for LogsConsole widget. Streaming tool output and events. */ +export type LogsConsoleProps = Readonly<{ + /** REQUIRED - Interactive widget identifier. */ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Log entries. */ + entries: readonly LogEntry[]; + /** Auto-scroll to bottom (default: true). */ + autoScroll?: boolean; + /** Filter by log level. */ + levelFilter?: readonly LogLevel[]; + /** Filter by source. */ + sourceFilter?: readonly string[]; + /** Search query. */ + searchQuery?: string; + /** Scroll position (entries from top). */ + scrollTop: number; + /** Show timestamps (default: true). */ + showTimestamps?: boolean; + /** Show source labels (default: true). */ + showSource?: boolean; + /** Expanded entry IDs. */ + expandedEntries?: readonly string[]; + /** Optional style override for focused-console ring. */ + focusedStyle?: TextStyle; + /** Callback when scroll position changes. */ + onScroll: (scrollTop: number) => void; + /** Callback when entry expand state changes. */ + onChange?: (entryId: string, expanded: boolean) => void; + /** Callback to clear logs. */ + onPress?: () => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Scrollbar glyph variant (default: "minimal"). */ + scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; + /** Optional style override for rendered scrollbar. */ + scrollbarStyle?: TextStyle; +}> & + LayoutConstraints; + +/* ---------- Toast/Notifications Widget ---------- */ + +/** Position for toast container. */ +export type ToastPosition = + | "top-left" + | "top-center" + | "top-right" + | "bottom-left" + | "bottom-center" + | "bottom-right"; + +/** Action button in a toast. */ +export type ToastAction = Readonly<{ + /** Action button label. */ + label: string; + /** Callback when action is clicked. */ + onAction: () => void; +}>; + +/** Toast notification. */ +export type Toast = Readonly<{ + /** Unique toast identifier. */ + id: string; + /** Message text. */ + message: string; + /** Toast type. */ + type: "info" | "success" | "warning" | "error"; + /** Auto-dismiss duration in ms (0 = persistent, default: 3000). */ + duration?: number; + /** Action button. */ + action?: ToastAction; + /** Progress indicator (0-100). */ + progress?: number; +}>; + +/** Props for ToastContainer widget. Non-blocking feedback messages. */ +export type ToastContainerProps = Readonly<{ + key?: string; + /** Active toasts. */ + toasts: readonly Toast[]; + /** Position on screen (default: "bottom-right"). */ + position?: ToastPosition; + /** Maximum visible toasts (default: 5). */ + maxVisible?: number; + /** Toast container width in cells (default: 40). */ + width?: number; + /** Frame/surface colors for toast backgrounds, text, and borders. */ + frameStyle?: OverlayFrameStyle; + /** Callback when toast is dismissed. */ + onClose: (id: string) => void; +}>; diff --git a/packages/core/src/widgets/types/base.ts b/packages/core/src/widgets/types/base.ts new file mode 100644 index 00000000..89fac171 --- /dev/null +++ b/packages/core/src/widgets/types/base.ts @@ -0,0 +1,1035 @@ +import type { EasingInput } from "../../animation/types.js"; +import type { ConstraintExpr } from "../../constraints/types.js"; +import type { FocusConfig } from "../../focus/styles.js"; +import type { SpacingValue } from "../../layout/spacing-scale.js"; +import type { + DisplayConstraint, + LayoutConstraints, + SizeConstraint, + SizeConstraintAtom, +} from "../../layout/types.js"; +import type { InstanceId } from "../../runtime/instance.js"; +import type { ThemeOverrides } from "../../theme/extend.js"; +import type { ThemeDefinition } from "../../theme/tokens.js"; +import type { WidgetSize, WidgetTone, WidgetVariant } from "../../ui/designTokens.js"; +import type { TextStyle } from "../style.js"; +import type { VNode } from "../types.js"; + +/** Cross-axis alignment for stack layouts. */ +export type Align = "start" | "center" | "end" | "stretch"; +export type Overflow = "visible" | "hidden" | "scroll"; + +/** + * Spacing props for padding and margin. + * Accepts either a number (cells) or a spacing key ("xs", "sm", "md", "lg", "xl", "2xl"). + */ +export type SpacingProps = Readonly<{ + /** All sides padding */ + p?: SpacingValue; + /** Horizontal padding */ + px?: SpacingValue; + /** Vertical padding */ + py?: SpacingValue; + /** Top padding */ + pt?: SpacingValue; + /** Bottom padding */ + pb?: SpacingValue; + /** Left padding */ + pl?: SpacingValue; + /** Right padding */ + pr?: SpacingValue; + /** All sides margin */ + m?: SpacingValue; + /** Horizontal margin */ + mx?: SpacingValue; + /** Vertical margin */ + my?: SpacingValue; + /** Top margin */ + mt?: SpacingValue; + /** Right margin */ + mr?: SpacingValue; + /** Bottom margin */ + mb?: SpacingValue; + /** Left margin */ + ml?: SpacingValue; +}>; + +export type ScopedThemeOverride = ThemeDefinition | ThemeOverrides; + +export type ThemedProps = Readonly<{ + key?: string; + /** Partial theme overrides applied to children. */ + theme: ThemeOverrides; +}>; + +export type FragmentProps = Readonly<{ + key?: string; +}>; + +type DisplayableProps = Readonly<{ + /** Conditional layout visibility. false (or expr <= 0) hides this node. */ + display?: DisplayConstraint; +}>; + +/** + * Text display variants with predefined styling. + */ +export type TextVariant = "body" | "heading" | "caption" | "code" | "label"; + +/** Properties that can be animated via widget transition props. */ +export type TransitionProperty = "position" | "size" | "opacity"; + +/** Declarative transition settings for render-time widget animation. */ +export type TransitionSpec = Readonly<{ + /** Transition duration in milliseconds. */ + duration?: number; + /** Easing curve for interpolation. */ + easing?: EasingInput; + /** + * Transitioned properties. Defaults to `"all"` when omitted. + * Phase 1 implementation supports `"position"` (x/y). + */ + properties?: "all" | readonly TransitionProperty[]; +}>; + +export type ExitAnimationState = Readonly<{ + instanceId: InstanceId; + startMs: number; + durationMs: number; + easing: (t: number) => number; + properties: "all" | readonly TransitionProperty[]; +}>; + +/** Props for text widget. key is for reconciliation; style for visual appearance. */ +export type TextProps = Readonly<{ + id?: string; + key?: string; + style?: TextStyle; + /** Text variant with predefined styling */ + variant?: TextVariant; + /** How to handle text that exceeds available width. Defaults to "clip". */ + textOverflow?: "clip" | "ellipsis" | "middle" | "start"; + /** Maximum width for overflow handling (in cells) */ + maxWidth?: SizeConstraint; + /** When true, text wraps to multiple lines instead of single-line truncation. */ + wrap?: boolean; + /** Internal callback used by ink-compat to transform rendered lines. */ + __inkTransform?: (line: string, index: number) => string; +}> & + DisplayableProps; + +/** + * Shadow configuration for box widgets. + */ +export type BoxShadow = + | boolean + | Readonly<{ + /** Horizontal offset (default: 1) */ + offsetX?: number; + /** Vertical offset (default: 1) */ + offsetY?: number; + /** Shadow density: "light", "medium", "dense" (default: "light") */ + density?: "light" | "medium" | "dense"; + }>; + +export type BoxPreset = "card" | "surface" | "well" | "elevated"; + +export type BoxBorderSideStyles = Readonly<{ + top?: TextStyle; + right?: TextStyle; + bottom?: TextStyle; + left?: TextStyle; +}>; + +/** Props for box container. border defaults to "single". */ +export type BoxProps = Readonly< + { + id?: string; + key?: string; + title?: string; + /** Alignment of title within the top border. */ + titleAlign?: "left" | "center" | "right"; + /** + * @deprecated Use `p`/`px`/`py`/`pt`/`pr`/`pb`/`pl` instead. Will be removed in v2.0. + */ + pad?: SpacingValue; + /** Style preset. Applied before explicit border/style props. */ + preset?: BoxPreset; + /** Border style. */ + border?: "none" | "single" | "double" | "rounded" | "heavy" | "dashed" | "heavy-dashed"; + /** + * Render individual border sides. Defaults to true when `border` is not "none". + * Mirrors Ink-style per-side border toggles. + */ + borderTop?: boolean; + borderRight?: boolean; + borderBottom?: boolean; + borderLeft?: boolean; + /** + * Enable shadow effect for depth. + * Pass `true` for default shadow, or an object for custom configuration. + */ + shadow?: BoxShadow; + /** + * Optional style applied to the box surface (for borders and background fill). + * When `bg` is provided, the renderer fills the box rect. + * When `borderStyle` is not set, this also styles the border. + */ + style?: TextStyle; + /** + * Optional style applied only to the box border and title. + * When set, decouples border appearance from child content: + * `style` controls children/fill, `borderStyle` controls the border. + * When not set, `style` applies to both (backward compatible). + */ + borderStyle?: TextStyle; + /** Optional per-edge border style overrides. */ + borderStyleSides?: BoxBorderSideStyles; + /** + * Style inherited by descendants when they do not override their own style. + * Unlike `style`, this does not force container background fill. + */ + inheritStyle?: TextStyle; + /** Child overflow behavior. Defaults to "visible". */ + overflow?: Overflow; + /** Horizontal scroll offset in cells. Clamped by layout metadata bounds. */ + scrollX?: number; + /** Vertical scroll offset in cells. Clamped by layout metadata bounds. */ + scrollY?: number; + /** Scrollbar glyph variant for overflow: "scroll" (default: "minimal"). */ + scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; + /** Optional style override for rendered scrollbars. */ + scrollbarStyle?: TextStyle; + /** Optional scoped theme override for this container subtree. */ + theme?: ScopedThemeOverride; + /** Surface opacity in [0..1]. Defaults to 1. */ + opacity?: number; + /** Gap between box children when laid out by the synthetic inner column. */ + gap?: SpacingValue; + /** Optional declarative transition settings for this container. */ + transition?: TransitionSpec; + /** Optional declarative exit transition before unmount removal. */ + exitTransition?: TransitionSpec; + } & SpacingProps & + LayoutConstraints +>; + +/** Props for row/column stack layouts. pad is internal, gap is between children. */ +export type JustifyContent = "start" | "end" | "center" | "between" | "around" | "evenly"; + +export type AlignItems = "start" | "end" | "center" | "stretch"; + +export type StackProps = Readonly< + { + id?: string; + key?: string; + /** + * @deprecated Use `p`/`px`/`py`/`pt`/`pr`/`pb`/`pl` instead. Will be removed in v2.0. + */ + pad?: SpacingValue; + /** Gap between children. Accepts number or spacing key. */ + gap?: SpacingValue; + /** Render children in reverse order while preserving original child arrays. */ + reverse?: boolean; + align?: Align; + justify?: JustifyContent; + items?: AlignItems; + /** + * Optional style applied to the stack surface. + * When `bg` is provided, the renderer fills the stack rect. + */ + style?: TextStyle; + /** + * Style inherited by descendants when they do not override their own style. + * Unlike `style`, this does not force stack background fill. + */ + inheritStyle?: TextStyle; + /** Child overflow behavior. Defaults to "visible". */ + overflow?: Overflow; + /** Horizontal scroll offset in cells. Clamped by layout metadata bounds. */ + scrollX?: number; + /** Vertical scroll offset in cells. Clamped by layout metadata bounds. */ + scrollY?: number; + /** Scrollbar glyph variant for overflow: "scroll" (default: "minimal"). */ + scrollbarVariant?: "minimal" | "classic" | "modern" | "dots" | "thin"; + /** Optional style override for rendered scrollbars. */ + scrollbarStyle?: TextStyle; + /** Optional scoped theme override for this container subtree. */ + theme?: ScopedThemeOverride; + /** Optional declarative transition settings for this container. */ + transition?: TransitionSpec; + /** Optional declarative exit transition before unmount removal. */ + exitTransition?: TransitionSpec; + } & SpacingProps & + LayoutConstraints +>; + +/** Props for horizontal stacks. `wrap` controls line breaking (default: false). */ +export type RowProps = StackProps & + Readonly<{ + wrap?: boolean; + }>; + +/** Props for vertical stacks. `wrap` controls line breaking (default: false). */ +export type ColumnProps = StackProps & + Readonly<{ + wrap?: boolean; + }>; + +export type GridProps = Readonly< + { + id?: string; + key?: string; + columns: number | string; + rows?: number | string; + gap?: number; + rowGap?: number; + columnGap?: number; + theme?: ScopedThemeOverride; + transition?: TransitionSpec; + exitTransition?: TransitionSpec; + } & LayoutConstraints +>; + +/** Props for spacer element. size is in cells along stack axis. */ +export type SpacerProps = Readonly<{ + key?: string; + size?: number; + flex?: number; +}>; + +export type DividerProps = Readonly<{ + key?: string; + direction?: "horizontal" | "vertical"; + char?: string; + label?: string; + color?: string; +}>; + +/** + * Props for icon widget. + * Icons are single-character glyphs from the icon registry. + */ +export type IconProps = Readonly<{ + key?: string; + /** Icon path (e.g., "status.check", "arrow.right", "ui.search") */ + icon: string; + /** Optional style for the icon */ + style?: TextStyle; + /** Use ASCII fallback instead of Unicode/Nerd Font character */ + fallback?: boolean; +}>; + +/** + * Props for spinner widget. + * Animated loading indicator driven by tick events. + */ +export type SpinnerProps = Readonly<{ + key?: string; + /** Spinner animation variant */ + variant?: "dots" | "line" | "circle" | "bounce" | "pulse" | "arrows" | "dots2"; + /** Optional style for the spinner */ + style?: TextStyle; + /** Optional label text after spinner */ + label?: string; +}>; + +/** + * Progress bar variant styles. + */ +export type ProgressVariant = "bar" | "blocks" | "minimal"; + +/** + * Props for progress bar widget. + * Displays completion progress with customizable appearance. + */ +export type ProgressProps = Readonly<{ + key?: string; + /** Progress value from 0 to 1 */ + value: number; + /** Display width in cells (default: fills available space) */ + width?: number; + /** Visual variant */ + variant?: ProgressVariant; + /** Show percentage label */ + showPercent?: boolean; + /** Optional label before the bar */ + label?: string; + /** Style for filled portion */ + style?: TextStyle; + /** Style for track/unfilled portion */ + trackStyle?: TextStyle; + /** Design system: color tone. */ + dsTone?: WidgetTone; +}>; + +/** + * Skeleton variant styles. + */ +export type SkeletonVariant = "text" | "rect" | "circle"; + +/** + * Props for skeleton loading widget. + * Placeholder for content that is loading. + */ +export type SkeletonProps = Readonly<{ + key?: string; + /** Width in cells */ + width: number; + /** Height in rows (default: 1) */ + height?: number; + /** Visual variant */ + variant?: SkeletonVariant; + /** Optional style override */ + style?: TextStyle; +}>; + +/** + * A span of styled text within a richText widget. + */ +export type RichTextSpan = Readonly<{ + /** Text content */ + text: string; + /** Optional style for this span */ + style?: TextStyle; +}>; + +/** + * Props for rich text widget with multiple styled spans. + */ +export type RichTextProps = Readonly<{ + key?: string; + /** Array of styled text spans */ + spans: readonly RichTextSpan[]; +}>; + +/** + * Props for keyboard shortcut display widget. + */ +export type KbdProps = Readonly<{ + key?: string; + /** Key or keys to display (e.g., "Ctrl+S" or ["Ctrl", "S"]) */ + keys: string | readonly string[]; + /** Separator between keys (default: "+") */ + separator?: string; + /** Optional style override */ + style?: TextStyle; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** + * Badge visual variants. + */ +export type BadgeVariant = "default" | "success" | "warning" | "error" | "info"; + +/** + * Props for badge widget. + */ +export type BadgeProps = Readonly<{ + key?: string; + /** Badge text */ + text: string; + /** Visual variant */ + variant?: BadgeVariant; + /** Optional style override */ + style?: TextStyle; +}>; + +/** + * Status indicator types. + */ +export type StatusType = "online" | "offline" | "away" | "busy" | "unknown"; + +/** + * Props for status indicator widget. + * Shows a colored dot with optional label. + */ +export type StatusProps = Readonly<{ + key?: string; + /** Status type determines color */ + status: StatusType; + /** Optional label after the indicator */ + label?: string; + /** Show the label text (default: true if label provided) */ + showLabel?: boolean; + /** Optional style override */ + style?: TextStyle; +}>; + +/** + * Props for tag widget. + * Inline label with background color. + */ +export type TagProps = Readonly<{ + key?: string; + /** Tag text */ + text: string; + /** Tag color variant */ + variant?: BadgeVariant; + /** Make tag removable (shows x) */ + removable?: boolean; + /** Optional style override */ + style?: TextStyle; +}>; + +/** + * Props for gauge widget. + * Compact progress display with label. + */ +export type GaugeProps = Readonly<{ + key?: string; + /** Value from 0 to 1 */ + value: number; + /** Optional forced width in cells. */ + width?: SizeConstraint; + /** Optional forced height in rows. */ + height?: SizeConstraint; + /** Label before the gauge */ + label?: string; + /** Display variant */ + variant?: "linear" | "compact"; + /** Color thresholds for value ranges */ + thresholds?: readonly { value: number; variant: BadgeVariant }[]; + /** Optional style override */ + style?: TextStyle; +}> & + DisplayableProps; + +/** + * Props for empty state widget. + * Displays a centered placeholder when content is empty. + */ +export type EmptyProps = Readonly<{ + key?: string; + /** Icon path to display (e.g., "ui.search", "file.folder") */ + icon?: string; + /** Main title text */ + title: string; + /** Optional description text */ + description?: string; + /** Optional action button or other widget */ + action?: VNode; + /** Optional style override */ + style?: TextStyle; +}>; + +/** + * Props for error display widget. + * Shows error information with optional stack trace and retry action. + */ +export type ErrorDisplayProps = Readonly<{ + key?: string; + /** Error title (default: "Error") */ + title?: string; + /** Error message to display */ + message: string; + /** Optional stack trace */ + stack?: string; + /** Show stack trace (default: false) */ + showStack?: boolean; + /** Callback for retry action */ + onRetry?: () => void; + /** Optional style override */ + style?: TextStyle; +}>; + +/** + * Error payload passed to `errorBoundary` fallbacks. + */ +export type ErrorBoundaryError = Readonly<{ + /** Runtime error code for the trapped boundary failure. */ + code: "ZRUI_USER_CODE_THROW"; + /** Friendly error message (for display in fallbacks). */ + message: string; + /** Full detail string emitted by runtime error reporting. */ + detail: string; + /** Optional stack trace when available. */ + stack?: string; + /** Retry this boundary subtree on the next commit turn. */ + retry: () => void; +}>; + +/** + * Props for error boundary container widget. + * Isolates subtree render failures and renders a fallback instead of faulting the app. + */ +export type ErrorBoundaryProps = Readonly<{ + key?: string; + /** Risky subtree to protect. */ + children: VNode; + /** Fallback renderer invoked when the protected subtree throws. */ + fallback: (error: ErrorBoundaryError) => VNode; +}>; + +/** + * Props for callout/alert widget. + * Highlighted message box for important information. + */ +export type CalloutProps = Readonly<{ + key?: string; + /** Callout variant determines styling */ + variant?: "info" | "success" | "warning" | "error"; + /** Optional title */ + title?: string; + /** Message content */ + message: string; + /** Optional icon override */ + icon?: string; + /** Optional style override */ + style?: TextStyle; +}>; + +export type GraphicsBlitter = "auto" | "braille" | "sextant" | "quadrant" | "halfblock" | "ascii"; + +export type CanvasPoint = Readonly<{ + x: number; + y: number; +}>; + +export type CanvasContext = Readonly<{ + readonly width: number; + readonly height: number; + line: (x0: number, y0: number, x1: number, y1: number, color: string) => void; + polyline: (points: readonly CanvasPoint[], color: string) => void; + fillRect: (x: number, y: number, w: number, h: number, color: string) => void; + strokeRect: (x: number, y: number, w: number, h: number, color: string) => void; + roundedRect: (x: number, y: number, w: number, h: number, radius: number, color: string) => void; + circle: (cx: number, cy: number, radius: number, color: string) => void; + arc: ( + cx: number, + cy: number, + radius: number, + startAngle: number, + endAngle: number, + color: string, + ) => void; + fillCircle: (cx: number, cy: number, radius: number, color: string) => void; + fillTriangle: ( + x0: number, + y0: number, + x1: number, + y1: number, + x2: number, + y2: number, + color: string, + ) => void; + setPixel: (x: number, y: number, color: string) => void; + text: (x: number, y: number, str: string, color?: string) => void; + clear: (color?: string) => void; +}>; + +export type LinkProps = Readonly<{ + id?: string; + key?: string; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** URL to open in terminal hyperlink-capable renderers. */ + url: string; + /** Link label text. Defaults to url. */ + label?: string; + /** Optional text style for the link. */ + style?: TextStyle; + /** Optional local press handler. */ + onPress?: () => void; + /** Disabled links are rendered but not focusable/pressable. */ + disabled?: boolean; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; +}> & + DisplayableProps; + +export type CanvasProps = Readonly<{ + id?: string; + key?: string; + /** Width in terminal columns. */ + width?: SizeConstraint; + /** Height in terminal rows. */ + height?: SizeConstraint; + /** Drawing callback, called every frame with a fresh context. */ + draw: (ctx: CanvasContext) => void; + /** Preferred blitter. */ + blitter?: GraphicsBlitter; +}> & + DisplayableProps; + +export type ImageFit = "fill" | "contain" | "cover"; +export type ImageProtocol = "auto" | "kitty" | "sixel" | "iterm2" | "blitter"; + +export type ImageProps = Readonly<{ + id?: string; + key?: string; + /** Image bytes (PNG or RGBA payload). */ + src: Uint8Array; + /** Optional source width in pixels (recommended for raw RGBA inputs). */ + sourceWidth?: number; + /** Optional source height in pixels (recommended for raw RGBA inputs). */ + sourceHeight?: number; + /** Width in terminal columns. */ + width?: SizeConstraint; + /** Height in terminal rows. */ + height?: SizeConstraint; + /** Fit mode (default: contain). */ + fit?: ImageFit; + /** Alt text for unsupported terminals or decode failures. */ + alt?: string; + /** Preferred image protocol. */ + protocol?: ImageProtocol; + /** Z-layer for compositing. */ + zLayer?: -1 | 0 | 1; + /** Stable image id for protocol-level caching. */ + imageId?: number; +}> & + DisplayableProps; + +export type LineChartSeries = Readonly<{ + data: readonly number[]; + color: string; + label?: string; +}>; + +export type ChartAxis = Readonly<{ + label?: string; + min?: number; + max?: number; +}>; + +export type LineChartProps = Readonly<{ + id?: string; + key?: string; + width?: SizeConstraint; + height?: SizeConstraint; + series: readonly LineChartSeries[]; + axes?: Readonly<{ x?: ChartAxis; y?: ChartAxis }>; + blitter?: Exclude; + showLegend?: boolean; +}> & + DisplayableProps; + +export type ScatterPoint = Readonly<{ + x: number; + y: number; + color?: string; +}>; + +export type ScatterProps = Readonly<{ + id?: string; + key?: string; + width?: SizeConstraint; + height?: SizeConstraint; + points: readonly ScatterPoint[]; + axes?: Readonly<{ x?: ChartAxis; y?: ChartAxis }>; + color?: string; + blitter?: Exclude; +}> & + DisplayableProps; + +export type HeatmapColorScale = "viridis" | "plasma" | "inferno" | "magma" | "turbo" | "grayscale"; + +export type HeatmapProps = Readonly<{ + id?: string; + key?: string; + width?: SizeConstraint; + height?: SizeConstraint; + /** 2D value matrix [row][col]. */ + data: readonly (readonly number[])[]; + colorScale?: HeatmapColorScale; + min?: number; + max?: number; +}> & + DisplayableProps; + +/** + * Props for sparkline widget. + * Mini inline chart using block characters. + */ +export type SparklineProps = Readonly<{ + key?: string; + /** Data points (will be normalized to 0-1 range) */ + data: readonly number[]; + /** Display width in cells (default: data.length) */ + width?: SizeConstraint; + /** Display height in rows (default: 1) */ + height?: SizeConstraint; + /** Minimum value for scaling (default: auto) */ + min?: number; + /** Maximum value for scaling (default: auto) */ + max?: number; + /** Enables sub-cell rendering when supported. */ + highRes?: boolean; + /** Blitter for highRes mode. */ + blitter?: Exclude; + /** Optional style override */ + style?: TextStyle; +}> & + DisplayableProps; + +/** + * Single item in a bar chart. + */ +export type BarChartItem = Readonly<{ + /** Item label */ + label: string; + /** Item value */ + value: number; + /** Optional color variant */ + variant?: BadgeVariant; +}>; + +/** + * Props for bar chart widget. + * Horizontal or vertical bar chart. + */ +export type BarChartProps = Readonly<{ + key?: string; + /** Optional forced width in cells. */ + width?: SizeConstraint; + /** Optional forced height in rows. */ + height?: SizeConstraint; + /** Data items to display */ + data: readonly BarChartItem[]; + /** Chart orientation (default: "horizontal") */ + orientation?: "horizontal" | "vertical"; + /** Show value labels (default: true) */ + showValues?: boolean; + /** Show item labels (default: true) */ + showLabels?: boolean; + /** Maximum bar length in cells */ + maxBarLength?: number; + /** Enables sub-cell rendering when supported. */ + highRes?: boolean; + /** Blitter for highRes mode. */ + blitter?: Exclude; + /** Optional style override */ + style?: TextStyle; +}> & + DisplayableProps; + +/** + * Props for mini chart widget. + * Compact multi-value display. + */ +export type MiniChartProps = Readonly<{ + key?: string; + /** Optional forced width in cells. */ + width?: SizeConstraint; + /** Optional forced height in rows. */ + height?: SizeConstraint; + /** Chart values (2-4 values recommended) */ + values: readonly { label: string; value: number; max?: number }[]; + /** Display variant */ + variant?: "bars" | "pills"; + /** Optional style override */ + style?: TextStyle; +}> & + DisplayableProps; + +export type ButtonIntent = "primary" | "secondary" | "danger" | "success" | "warning" | "link"; + +/** Props for button widget. id is required for focus/routing; label is display text. */ +export type ButtonProps = Readonly<{ + id: string; + key?: string; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + label: string; + disabled?: boolean; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** + * Horizontal padding in cells. + * - Legacy/manual path default: 1 + * - Recipe path: derived from `dsSize` unless overridden by `px` + */ + px?: number; + /** Optional style applied to the button label (merged with focus/disabled state). */ + style?: TextStyle; + /** Optional style applied while the button is pressed. */ + pressedStyle?: TextStyle; + /** Optional callback invoked when the button is activated. */ + onPress?: () => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** + * Design system: visual variant. + * - "solid": Accent background, inverse text (primary CTA) + * - "soft": Subtle background, accent text (secondary) + * - "outline": Border, no fill (tertiary) + * - "ghost": No background or border (minimal) + * @default "soft" + */ + dsVariant?: WidgetVariant; + /** + * Design system: color tone. + * @default "default" + */ + dsTone?: WidgetTone; + /** + * Design system: size preset. + * @default "md" + */ + dsSize?: WidgetSize; + /** Shorthand for dsVariant + dsTone. Overridden by explicit dsVariant/dsTone. */ + intent?: ButtonIntent; +}>; + +/** Props for input widget. id is required; value is controlled by app state. */ +export type InputProps = Readonly<{ + id: string; + key?: string; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + value: string; + disabled?: boolean; + /** Keep the input focusable/selectable while preventing edits. */ + readOnly?: boolean; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional style applied to the input value (merged with focus/disabled state). */ + style?: TextStyle; + /** Optional callback invoked on input edits (docs/18). */ + onInput?: (value: string, cursor: number) => void; + /** Optional callback invoked when the input loses focus. */ + onBlur?: () => void; + /** Internal multiline mode used by ui.textarea(). */ + multiline?: boolean; + /** Visible line count when multiline mode is enabled (default: 3). */ + rows?: number; + /** Wrap long lines in multiline mode (default: true). */ + wordWrap?: boolean; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** + * Design system: size preset. + * @default "md" + */ + dsSize?: WidgetSize; + /** + * Design system: placeholder text displayed when value is empty. + */ + placeholder?: string; +}>; + +/** Props for ui.textarea(). Multi-line controlled text input. */ +export type TextareaProps = Readonly<{ + id: string; + key?: string; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + value: string; + disabled?: boolean; + /** Keep the textarea focusable/selectable while preventing edits. */ + readOnly?: boolean; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Visible line count (default: 3). */ + rows?: number; + /** Wrap long lines (default: true). */ + wordWrap?: boolean; + /** Placeholder text shown when value is empty. */ + placeholder?: string; + /** Optional style applied to the textarea value (merged with focus/disabled state). */ + style?: TextStyle; + /** Optional callback invoked on input edits. */ + onInput?: (value: string, cursor: number) => void; + /** Optional callback invoked when the textarea loses focus. */ + onBlur?: () => void; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; +}>; + +/** Props for focus announcer widget. Renders a live summary for the focused element. */ +export type FocusAnnouncerProps = Readonly<{ + key?: string; + /** Optional fallback text when no widget is focused. */ + emptyText?: string; + /** Optional style applied to the announcement line. */ + style?: TextStyle; +}>; + +/** Navigation mode for focus zones. */ +export type FocusZoneNavigation = "linear" | "grid" | "none"; + +/** Props for focus zone container. Groups focusable widgets for TAB navigation. */ +export type FocusZoneProps = Readonly<{ + id: string; + key?: string; + tabIndex?: number; // Zone order (default: 0) + navigation?: FocusZoneNavigation; // Default: "linear" + columns?: number; // For grid mode (default: 1) + wrapAround?: boolean; // Default: true + onEnter?: () => void; + onExit?: () => void; +}>; + +/** Props for focus trap container. Contains focus within its boundaries when active. */ +export type FocusTrapProps = Readonly<{ + id: string; + key?: string; + active: boolean; + returnFocusTo?: string; + initialFocus?: string; +}>; + +/** Height specification for virtual list items: fixed number or callback for variable heights. */ +export type ItemHeightSpec = number | ((item: T, index: number) => number); + +/** Context passed to custom virtual-list measurement callbacks. */ +export type VirtualListMeasureItemHeightCtx = Readonly<{ + /** Available content width in terminal cells for this item. */ + width: number; + /** Estimated height used for initial windowing before correction. */ + estimatedHeight: number; + /** Rendered VNode returned by `renderItem`. */ + vnode: VNode; +}>; + +/** Props for virtualList widget. Efficiently renders large datasets with windowed virtualization. */ +export type VirtualListProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + items: readonly T[]; + /** + * Exact item height specification (fixed or callback). + * Use this for fixed-height or precomputed variable-height rows. + */ + itemHeight?: ItemHeightSpec; + /** + * Estimated item height used for variable-height virtualization. + * When provided, visible items are measured and cached to correct offsets. + */ + estimateItemHeight?: ItemHeightSpec; + /** + * Optional custom measurement callback for estimate mode. + * Return the measured height in cells for the rendered VNode. + */ + measureItemHeight?: (item: T, index: number, ctx: VirtualListMeasureItemHeightCtx) => number; + /** Number of items to render outside the visible viewport (default: 3) */ + overscan?: number; + renderItem: (item: T, index: number, focused: boolean) => VNode; + onScroll?: (scrollTop: number, visibleRange: [number, number]) => void; + onSelect?: (item: T, index: number) => void; + /** Enable keyboard navigation with arrow keys, page up/down, home/end (default: true) */ + keyboardNavigation?: boolean; + /** Wrap selection from last item to first and vice versa (default: false) */ + wrapAround?: boolean; + /** + * Mouse wheel scroll direction (default: "traditional"). + * - "traditional": wheel down moves the viewport down + * - "natural": wheel down moves content down (trackpad-style) + */ + scrollDirection?: "natural" | "traditional"; + /** Optional style override for the selected/focused row highlight. */ + selectionStyle?: TextStyle; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; +}> & + LayoutConstraints; diff --git a/packages/core/src/widgets/types/forms.ts b/packages/core/src/widgets/types/forms.ts new file mode 100644 index 00000000..06f4f8a8 --- /dev/null +++ b/packages/core/src/widgets/types/forms.ts @@ -0,0 +1,145 @@ +import type { FocusConfig } from "../../focus/styles.js"; +import type { WidgetSize, WidgetTone, WidgetVariant } from "../../ui/designTokens.js"; +import type { TextStyle } from "../style.js"; +import type { VNode } from "../types.js"; + +/* ========== Form Widgets (GitHub issue #119) ========== */ + +/** Option for select and radio group widgets. */ +export type SelectOption = Readonly<{ + /** Option value used in form state. */ + value: string; + /** Display label for the option. */ + label: string; + /** Whether this option is disabled. */ + disabled?: boolean; +}>; + +/** Props for field wrapper widget. Wraps an input with label, error, and hint. */ +export type FieldProps = Readonly<{ + key?: string; + /** Field label displayed above the input. */ + label: string; + /** Error message to display below the input. */ + error?: string; + /** Whether the field is required (shows asterisk). */ + required?: boolean; + /** Help text displayed below the input. */ + hint?: string; + /** The wrapped input widget. */ + children: VNode; +}>; + +/** Props for select dropdown widget. */ +export type SelectProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Currently selected value. */ + value: string; + /** Available options. */ + options: readonly SelectOption[]; + /** Callback when selection changes. */ + onChange?: (value: string) => void; + /** Whether the select is disabled. */ + disabled?: boolean; + /** Placeholder text when no value is selected. */ + placeholder?: string; + /** Whether to show the select in an error state. */ + error?: boolean; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Design system: visual variant (reserved for future select recipes). */ + dsVariant?: WidgetVariant; + /** Design system: tone (reserved for future select recipes). */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** Props for slider widget. */ +export type SliderProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Current slider value. */ + value: number; + /** Minimum value (default: 0). */ + min?: number; + /** Maximum value (default: 100). */ + max?: number; + /** Step increment for keyboard changes (default: 1). */ + step?: number; + /** Optional fixed track width in cells (default: fills available width). */ + width?: number; + /** Optional label shown before the track. */ + label?: string; + /** Show numeric value text (default: true). */ + showValue?: boolean; + /** Callback when value changes. */ + onChange?: (value: number) => void; + /** Whether the slider is disabled. */ + disabled?: boolean; + /** Whether the slider is read-only (focusable but non-editable). */ + readOnly?: boolean; + /** Optional style applied to label/value text. */ + style?: TextStyle; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; +}>; + +/** Props for checkbox widget. */ +export type CheckboxProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Whether the checkbox is checked. */ + checked: boolean; + /** Label displayed next to the checkbox. */ + label?: string; + /** Callback when checked state changes. */ + onChange?: (checked: boolean) => void; + /** Whether the checkbox is disabled. */ + disabled?: boolean; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Design system: tone for checked/focus rendering. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** Props for radio group widget. */ +export type RadioGroupProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Currently selected value. */ + value: string; + /** Available options. */ + options: readonly SelectOption[]; + /** Callback when selection changes. */ + onChange?: (value: string) => void; + /** Layout direction. */ + direction?: "horizontal" | "vertical"; + /** Whether the radio group is disabled. */ + disabled?: boolean; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Design system: tone for selected/focus rendering. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; diff --git a/packages/core/src/widgets/types/navigation.ts b/packages/core/src/widgets/types/navigation.ts new file mode 100644 index 00000000..f576c33d --- /dev/null +++ b/packages/core/src/widgets/types/navigation.ts @@ -0,0 +1,93 @@ +import type { WidgetSize, WidgetTone, WidgetVariant } from "../../ui/designTokens.js"; +import type { VNode } from "../types.js"; + +/* ========== Navigation Widgets ========== */ + +/** Tabs visual style variant. */ +export type TabsVariant = "line" | "enclosed" | "pills"; + +/** Tabs bar position relative to content. */ +export type TabsPosition = "top" | "bottom"; + +/** Tab item descriptor. */ +export type TabsItem = Readonly<{ + key: string; + label: string; + content: VNode; +}>; + +/** Props for tabs widget. */ +export type TabsProps = Readonly<{ + id: string; + key?: string; + tabs: readonly TabsItem[]; + activeTab: string; + onChange: (key: string) => void; + variant?: TabsVariant; + position?: TabsPosition; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** Accordion item descriptor. */ +export type AccordionItem = Readonly<{ + key: string; + title: string; + content: VNode; +}>; + +/** Props for accordion widget. */ +export type AccordionProps = Readonly<{ + id: string; + key?: string; + items: readonly AccordionItem[]; + expanded: readonly string[]; + onChange: (expanded: readonly string[]) => void; + allowMultiple?: boolean; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** Breadcrumb item descriptor. */ +export type BreadcrumbItem = Readonly<{ + label: string; + onPress?: () => void; +}>; + +/** Props for breadcrumb widget. */ +export type BreadcrumbProps = Readonly<{ + id?: string; + key?: string; + items: readonly BreadcrumbItem[]; + separator?: string; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** Props for pagination widget. */ +export type PaginationProps = Readonly<{ + id: string; + key?: string; + page: number; + totalPages: number; + onChange: (page: number) => void; + showFirstLast?: boolean; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; diff --git a/packages/core/src/widgets/types/overlaysShell.ts b/packages/core/src/widgets/types/overlaysShell.ts new file mode 100644 index 00000000..a7f616ee --- /dev/null +++ b/packages/core/src/widgets/types/overlaysShell.ts @@ -0,0 +1,260 @@ +import type { SpacingValue } from "../../layout/spacing-scale.js"; +import type { LayoutConstraints, SizeConstraint, SizeConstraintAtom } from "../../layout/types.js"; +import type { WidgetSize, WidgetTone, WidgetVariant } from "../../ui/designTokens.js"; +import type { TextStyle } from "../style.js"; +import type { VNode } from "../types.js"; +import type { BoxProps } from "./base.js"; + +/* ========== Layer System (GitHub issue #117) ========== */ + +/** Backdrop style presets for modals and overlays. */ +export type BackdropStyle = "none" | "dim" | "opaque"; + +/** Shared frame/surface styling for overlay widgets. */ +export type OverlayFrameStyle = Readonly<{ + /** Surface background color. */ + background?: NonNullable; + /** Default text/icon color for overlay content. */ + foreground?: NonNullable; + /** Border color for framed overlays. */ + border?: NonNullable; +}>; + +/** Extended modal backdrop config (preset-compatible). */ +export type ModalBackdrop = + | BackdropStyle + | Readonly<{ + /** Backdrop variant (default: "dim"). */ + variant?: BackdropStyle; + /** Optional dim-pattern character (default: "░", dim variant only). */ + pattern?: string; + /** Optional backdrop foreground color. */ + foreground?: NonNullable; + /** Optional backdrop background color. */ + background?: NonNullable; + }>; + +/** Position for dropdown relative to anchor. */ +export type DropdownPosition = + | "below-start" + | "below-center" + | "below-end" + | "above-start" + | "above-center" + | "above-end"; + +/** Props for layers container. Stacks children with later items on top. */ +export type LayersProps = Readonly<{ + key?: string; +}>; + +/** Props for modal overlay. Centered with optional backdrop and focus trap. */ +export type ModalProps = Readonly<{ + id: string; + key?: string; + /** Optional title rendered in modal header. */ + title?: string; + /** Main content of the modal. */ + content: VNode; + /** Action buttons rendered in modal footer. */ + actions?: readonly VNode[]; + /** Modal width sizing. Supports fixed values, "auto", "full", `fluid(...)`, or `expr(...)`. */ + width?: SizeConstraintAtom; + /** Modal height sizing. Supports fixed values, "full", `fluid(...)`, or `expr(...)` (no "auto" height). */ + height?: Exclude; + /** Maximum width bound (cells, "full", `fluid(...)`, or `expr(...)`). */ + maxWidth?: Exclude; + /** Minimum width bound (cells, "full", `fluid(...)`, or `expr(...)`). */ + minWidth?: Exclude; + /** Minimum height bound (cells, "full", `fluid(...)`, or `expr(...)`). */ + minHeight?: Exclude; + /** Frame/surface colors for modal body and border. */ + frameStyle?: OverlayFrameStyle; + /** Backdrop style/config (default: "dim"). */ + backdrop?: ModalBackdrop; + /** Close when backdrop is clicked (default: true). */ + closeOnBackdrop?: boolean; + /** Close when ESC is pressed (default: true). */ + closeOnEscape?: boolean; + /** Callback when modal should close. */ + onClose?: () => void; + /** ID of element to focus when modal opens. */ + initialFocus?: string; + /** ID of element to return focus to when modal closes. */ + returnFocusTo?: string; +}>; + +/** Intent hint for declarative dialog actions. */ +export type DialogActionIntent = "primary" | "danger"; + +/** Action descriptor for declarative dialog buttons. */ +export type DialogAction = Readonly<{ + id?: string; + label: string; + intent?: DialogActionIntent; + onPress: () => void; + disabled?: boolean; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; +}>; + +/** Declarative dialog props sugar over `ui.modal(...)`. */ +export type DialogProps = Readonly< + Omit & { + message: string | VNode; + actions: readonly DialogAction[]; + } +>; + +export type AppShellSidebar = Readonly<{ + content: VNode; + /** Sidebar width. Supports fixed values, "full", `fluid(...)`, or `expr(...)`. */ + width?: SizeConstraint; +}>; + +export type AppShellOptions = Readonly< + { + id?: string; + key?: string; + /** Header content — typically title text, badges, action buttons */ + header?: VNode | null; + /** Sidebar content — typically navigation */ + sidebar?: AppShellSidebar | null; + /** Main body content */ + body: VNode; + /** Footer/status bar content */ + footer?: VNode | null; + /** Padding around the shell (default: 1) */ + p?: SpacingValue; + /** Gap between sections (default: 1) */ + gap?: number; + } & LayoutConstraints +>; + +export type PageOptions = Readonly< + { + id?: string; + key?: string; + header?: VNode | null; + body: VNode; + footer?: VNode | null; + gap?: SpacingValue; + p?: SpacingValue; + } & LayoutConstraints +>; + +export type CardOptions = Readonly<{ + id?: string; + key?: string; + title?: string; + subtitle?: string; + actions?: readonly VNode[]; + border?: BoxProps["border"]; + p?: SpacingValue; + gap?: number; + style?: TextStyle; +}>; + +export type ToolbarOptions = Readonly<{ + id?: string; + key?: string; + gap?: number; +}>; + +export type StatusBarOptions = Readonly<{ + id?: string; + key?: string; + left?: readonly VNode[]; + right?: readonly VNode[]; + style?: TextStyle; +}>; + +export type HeaderOptions = Readonly<{ + id?: string; + key?: string; + title: string; + subtitle?: string; + actions?: readonly VNode[]; +}>; + +export type SidebarItem = Readonly<{ + id: string; + label: string; + icon?: string; +}>; + +export type SidebarOptions = Readonly<{ + id?: string; + key?: string; + items: readonly SidebarItem[]; + selected?: string; + onSelect?: (id: string) => void; + width?: number; + title?: string; +}>; + +export type MasterDetailOptions = Readonly<{ + id?: string; + key?: string; + master: VNode; + detail: VNode; + masterWidth?: number; + gap?: number; +}>; + +/** Dropdown menu item. */ +export type DropdownItem = Readonly<{ + id: string; + label: string; + shortcut?: string; + disabled?: boolean; + /** Render as a divider instead of a selectable item. */ + divider?: boolean; +}>; + +/** Props for dropdown menu. Positioned relative to an anchor element. */ +export type DropdownProps = Readonly<{ + id: string; + key?: string; + /** ID of the anchor element to position relative to. */ + anchorId: string; + /** Position relative to anchor (default: "below-start"). */ + position?: DropdownPosition; + /** Frame/surface colors for dropdown background, text, and border. */ + frameStyle?: OverlayFrameStyle; + /** Menu items to render. */ + items: readonly DropdownItem[]; + /** Callback when an item is selected. */ + onSelect?: (item: DropdownItem) => void; + /** Callback when dropdown should close. */ + onClose?: () => void; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}>; + +/** Props for a layer in the layer stack. */ +export type LayerProps = Readonly<{ + id: string; + key?: string; + /** + * Z-index for layer ordering (higher = on top). Default is insertion order. + * Values are truncated to integers and clamped to `±9,007,199,253` for deterministic ordering. + */ + zIndex?: number; + /** Frame/surface colors for the layer container. */ + frameStyle?: OverlayFrameStyle; + /** Backdrop to render behind content. */ + backdrop?: BackdropStyle; + /** Whether layer blocks input to lower layers. */ + modal?: boolean; + /** Whether layer should close on ESC key (default: true). */ + closeOnEscape?: boolean; + /** Callback when layer should close. */ + onClose?: () => void; + /** Content to render in the layer. */ + content: VNode; +}>; diff --git a/packages/core/src/widgets/types/table.ts b/packages/core/src/widgets/types/table.ts new file mode 100644 index 00000000..291e8b8e --- /dev/null +++ b/packages/core/src/widgets/types/table.ts @@ -0,0 +1,118 @@ +import type { FocusConfig } from "../../focus/styles.js"; +import type { LayoutConstraints } from "../../layout/types.js"; +import type { WidgetSize, WidgetTone } from "../../ui/designTokens.js"; +import type { TextStyle } from "../style.js"; +import type { VNode } from "../types.js"; + +/* ========== Table Widget (GitHub issue #118) ========== */ + +/** Overflow behavior for table header/cell text. */ +export type TableColumnOverflow = "clip" | "ellipsis" | "middle"; + +/** Row stripe styling for table body backgrounds. */ +export type TableStripeStyle = Readonly<{ + /** Background color for odd body rows (0-based index: 1, 3, 5, ...). */ + odd?: NonNullable; + /** Background color for even body rows (0-based index: 0, 2, 4, ...). */ + even?: NonNullable; +}>; + +/** Border glyph variants supported by table borders. */ +export type TableBorderVariant = + | "single" + | "double" + | "rounded" + | "heavy" + | "dashed" + | "heavy-dashed"; + +/** Border styling options for tables. */ +export type TableBorderStyle = Readonly<{ + /** Border glyph variant (default: "single"). */ + variant?: TableBorderVariant; + /** Border foreground color override. */ + color?: NonNullable; +}>; + +/** Column definition for table widget. */ +export type TableColumn = Readonly<{ + /** Unique column identifier. */ + key: string; + /** Column header text. */ + header: string; + /** Fixed width in cells. */ + width?: number; + /** Minimum width in cells. */ + minWidth?: number; + /** Maximum width in cells. */ + maxWidth?: number; + /** Flex factor for distributing remaining space. */ + flex?: number; + /** Custom render function for cell content. */ + render?: (value: unknown, row: T, index: number) => VNode; + /** Cell content alignment. */ + align?: "left" | "center" | "right"; + /** Overflow handling for this column (default: "ellipsis"). */ + overflow?: TableColumnOverflow; + /** Whether column is sortable. */ + sortable?: boolean; +}>; + +/** Props for table widget. */ +export type TableProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Column definitions. */ + columns: readonly TableColumn[]; + /** Row data array. */ + data: readonly T[]; + /** Function to get unique key for each row. */ + getRowKey: (row: T, index: number) => string; + /** Height of each row in cells (default: 1). */ + rowHeight?: number; + /** Height of header row in cells (default: 1). */ + headerHeight?: number; + /** Currently selected row keys. */ + selection?: readonly string[]; + /** Selection mode (default: "none"). */ + selectionMode?: "none" | "single" | "multi"; + /** Callback when selection changes. */ + onSelectionChange?: (keys: readonly string[]) => void; + /** Currently sorted column key. */ + sortColumn?: string; + /** Sort direction. */ + sortDirection?: "asc" | "desc"; + /** Callback when sort changes. */ + onSort?: (column: string, direction: "asc" | "desc") => void; + /** Callback when row is pressed (Enter key or click). */ + onRowPress?: (row: T, index: number) => void; + /** Callback when row is double-pressed (double-click). */ + onRowDoublePress?: (row: T, index: number) => void; + /** Enable virtualization for large datasets (default: true). */ + virtualized?: boolean; + /** Number of rows to render outside viewport (default: 3). */ + overscan?: number; + /** Legacy stripe toggle. */ + stripedRows?: boolean; + /** Stripe background styling for body rows. */ + stripeStyle?: TableStripeStyle; + /** Optional style override for selected rows. */ + selectionStyle?: TextStyle; + /** Show header row (default: true). */ + showHeader?: boolean; + /** Legacy border toggle. */ + border?: "none" | "single"; + /** Border styling for rendered table frame. */ + borderStyle?: TableBorderStyle; + /** Optional focus appearance configuration. */ + focusConfig?: FocusConfig; + /** Design system: size preset. */ + dsSize?: WidgetSize; + /** Design system: tone (reserved for future table recipe variants). */ + dsTone?: WidgetTone; +}> & + LayoutConstraints; diff --git a/packages/core/src/widgets/types/tree.ts b/packages/core/src/widgets/types/tree.ts new file mode 100644 index 00000000..eb40eab8 --- /dev/null +++ b/packages/core/src/widgets/types/tree.ts @@ -0,0 +1,68 @@ +import type { LayoutConstraints } from "../../layout/types.js"; +import type { WidgetSize, WidgetTone, WidgetVariant } from "../../ui/designTokens.js"; +import type { VNode } from "../types.js"; + +/* ========== Tree Widget (GitHub issue #122) ========== */ + +/** State information for a tree node during rendering. */ +export type NodeState = Readonly<{ + /** Whether the node is expanded. */ + expanded: boolean; + /** Whether the node is selected. */ + selected: boolean; + /** Whether the node is focused. */ + focused: boolean; + /** Whether the node is loading children. */ + loading: boolean; + /** Depth level in the tree (0 = root). */ + depth: number; + /** Whether this is the first sibling. */ + isFirst: boolean; + /** Whether this is the last sibling. */ + isLast: boolean; + /** Whether the node has children (or could have). */ + hasChildren: boolean; +}>; + +/** Props for tree widget. */ +export type TreeProps = Readonly<{ + id: string; + key?: string; + /** Opt out of Tab focus order while keeping id-based routing available. */ + focusable?: boolean; + /** Optional semantic label used for accessibility/debug announcements. */ + accessibleLabel?: string; + /** Root node(s). Can be a single root or array of roots. */ + data: T | readonly T[]; + /** Function to get unique key for each node. */ + getKey: (node: T) => string; + /** Function to get children of a node (undefined = leaf node). */ + getChildren?: (node: T) => readonly T[] | undefined; + /** Function to check if node has children (for lazy loading). */ + hasChildren?: (node: T) => boolean; + /** Set of expanded node keys. */ + expanded: readonly string[]; + /** Currently selected node key. */ + selected?: string; + /** Callback when node expand/collapse state changes. */ + onChange: (node: T, expanded: boolean) => void; + /** Callback when node is selected. */ + onSelect?: (node: T) => void; + /** Callback when node is activated (Enter key or double-click). */ + onPress?: (node: T) => void; + /** Custom render function for node content. */ + renderNode: (node: T, depth: number, state: NodeState) => VNode; + /** Function to load children asynchronously. */ + loadChildren?: (node: T) => Promise; + /** Indentation per depth level in cells (default: 2). */ + indentSize?: number; + /** Show tree lines (├── └── │). */ + showLines?: boolean; + /** Design system: visual variant. */ + dsVariant?: WidgetVariant; + /** Design system: color tone. */ + dsTone?: WidgetTone; + /** Design system: size preset. */ + dsSize?: WidgetSize; +}> & + LayoutConstraints;