@tangle-network/ui 11.37.0 → 11.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @tangle-network/ui
2
2
 
3
+ ## 11.38.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f115dda: Wizard: the shared multi-step flow primitive. Compound composition (`<Wizard.Step>`), uncontrolled by default with a controlled escape hatch, per-step primary actions with async gating, mobile-first progress, themed by the existing semantic tokens. The end of bespoke stepper markup across products.
8
+
3
9
  ## 11.37.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -96,6 +96,7 @@ import { Pagination, PaginationProps } from "./primitives/pagination.js";
96
96
  import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
97
97
  import { Tag, TagProps } from "./primitives/tag.js";
98
98
  import { Chip, ChipProps } from "./primitives/chip.js";
99
+ import { Wizard, WizardProps, WizardStep, WizardStepProps } from "./primitives/wizard.js";
99
100
  import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
100
101
  import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
101
102
  import { Logo, LogoProps, TangleKnot } from "./primitives.js";
@@ -134,4 +135,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
134
135
  import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
135
136
  import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
136
137
  import "./redaction.js";
137
- export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ApiSchema, type ApiSchemaProps, AppendUserMessageOptions, ApplySdkEventOptions, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, ArtifactPane, type ArtifactPaneProps, AuthHeader, type AuthHeaderProps, AuthUser, AutomationStreamEvent, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatAuthor, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ConnectionState, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, type CopyButtonProps, CustomToolRenderer, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, type DiffPreviewProps, DisplayVariant, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, FileArtifactPane, type FileArtifactPaneProps, FileCard, type FileCardProps, type FileDetails, type FileFormat, FileList, type FileListItem, type FileListProps, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, type FileUploader, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, Input, type InputProps, Label, LiveDuration, LoginLayout, type LoginLayoutProps, Logo, type LogoProps, Markdown, type MarkdownLinkChip, type MarkdownLinkChipResolver, type MarkdownProps, MessageAuthor, type MessageAuthorProps, MessageList, type MessageListProps, type MessageRole, MessageRun, MessageUser, Metric, type MetricProps, MetricStrip, type MetricStripProps, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, type OpenUIAction, type OpenUIActionsNode, OpenUIArtifactRenderer, type OpenUIArtifactRendererProps, type OpenUIBadgeNode, type OpenUICardNode, type OpenUICodeNode, type OpenUIComponentNode, type OpenUIGridNode, type OpenUIHeadingNode, type OpenUIKeyValueNode, type OpenUIMarkdownNode, type OpenUIPrimitive, type OpenUISchemaIssue, type OpenUISchemaValidation, type OpenUISeparatorNode, type OpenUIStackNode, type OpenUIStatNode, type OpenUITableNode, type OpenUITextNode, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, PdfViewer, type PdfViewerConfig, type PdfViewerProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RadioGroup, RadioGroupItem, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, ReasoningPart, type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, RegisterActiveSessionOptions, type RevealResult, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, Run, RunGroup, type RunGroupProps, type RunPhase, RunPhaseList, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, RunStats, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, UseAuthOptions, UseAuthResult, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, UserMenu, type UserMenuProps, UserMessage, type UserMessageProps, WebSearchPreview, type WebSearchPreviewProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
138
+ export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ApiSchema, type ApiSchemaProps, AppendUserMessageOptions, ApplySdkEventOptions, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, ArtifactPane, type ArtifactPaneProps, AuthHeader, type AuthHeaderProps, AuthUser, AutomationStreamEvent, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatAuthor, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ConnectionState, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, type CopyButtonProps, CustomToolRenderer, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, type DiffPreviewProps, DisplayVariant, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, FileArtifactPane, type FileArtifactPaneProps, FileCard, type FileCardProps, type FileDetails, type FileFormat, FileList, type FileListItem, type FileListProps, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, type FileUploader, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, Input, type InputProps, Label, LiveDuration, LoginLayout, type LoginLayoutProps, Logo, type LogoProps, Markdown, type MarkdownLinkChip, type MarkdownLinkChipResolver, type MarkdownProps, MessageAuthor, type MessageAuthorProps, MessageList, type MessageListProps, type MessageRole, MessageRun, MessageUser, Metric, type MetricProps, MetricStrip, type MetricStripProps, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, type OpenUIAction, type OpenUIActionsNode, OpenUIArtifactRenderer, type OpenUIArtifactRendererProps, type OpenUIBadgeNode, type OpenUICardNode, type OpenUICodeNode, type OpenUIComponentNode, type OpenUIGridNode, type OpenUIHeadingNode, type OpenUIKeyValueNode, type OpenUIMarkdownNode, type OpenUIPrimitive, type OpenUISchemaIssue, type OpenUISchemaValidation, type OpenUISeparatorNode, type OpenUIStackNode, type OpenUIStatNode, type OpenUITableNode, type OpenUITextNode, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, PdfViewer, type PdfViewerConfig, type PdfViewerProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RadioGroup, RadioGroupItem, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, ReasoningPart, type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, RegisterActiveSessionOptions, type RevealResult, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, Run, RunGroup, type RunGroupProps, type RunPhase, RunPhaseList, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, RunStats, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, UseAuthOptions, UseAuthResult, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, UserMenu, type UserMenuProps, UserMessage, type UserMessageProps, WebSearchPreview, type WebSearchPreviewProps, Wizard, type WizardProps, WizardStep, type WizardStepProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
package/dist/index.js CHANGED
@@ -53,6 +53,7 @@ import { StatusPill } from "./primitives/status-pill.js";
53
53
  import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
54
54
  import { Tag } from "./primitives/tag.js";
55
55
  import { Chip } from "./primitives/chip.js";
56
+ import { Wizard, WizardStep } from "./primitives/wizard.js";
56
57
  import { IconTile, initialsOf } from "./primitives/icon-tile.js";
57
58
  import { Metric, MetricStrip } from "./primitives/metric-strip.js";
58
59
  import { FilterField, Toolbar } from "./primitives/toolbar.js";
@@ -118,4 +119,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
118
119
  import { copyText } from "./utils/copy-text.js";
119
120
  import { timeAgo } from "./utils/time-ago.js";
120
121
  import { RedactedDocument } from "./redaction/redacted-document.js";
121
- export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApiSchema, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ExpandedToolDetail, FileArtifactPane, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TaskList, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, TimeSelect, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
122
+ export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApiSchema, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ExpandedToolDetail, FileArtifactPane, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TaskList, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, TimeSelect, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, Wizard, WizardStep, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ //#region src/primitives/wizard.d.ts
3
+ export interface WizardProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ children: React.ReactNode;
5
+ /** Drives the active step; omit for uncontrolled with `defaultStep`. */
6
+ step?: number;
7
+ defaultStep?: number;
8
+ onStepChange?: (step: number) => void;
9
+ /** Fires when the last step's primary action resolves. */
10
+ onFinished?: () => void;
11
+ }
12
+ export declare function Wizard({ children, step, defaultStep, onStepChange, onFinished, className, ...rest }: WizardProps): React.JSX.Element;
13
+ export interface WizardStepProps {
14
+ /** Shown in the rail (and the mobile position line). Steps read at a glance; keep it one or two words. */
15
+ title: string;
16
+ children: React.ReactNode;
17
+ /** The footer's primary action label. Omit when the step renders its own actions. */
18
+ nextLabel?: string;
19
+ /** Runs before advancing. Resolve (or return a non-false value) to advance; return false or throw to stay — the step's own content shows why. */
20
+ onNext?: () => Promise<unknown> | unknown;
21
+ /** Disables the primary action (a required choice is still missing). */
22
+ nextDisabled?: boolean;
23
+ /** Hides the Back button for steps where going back makes no sense (a completed handshake). */
24
+ hideBack?: boolean;
25
+ }
26
+ export declare function WizardStep({ title, children, nextLabel, onNext, nextDisabled, hideBack }: WizardStepProps): React.JSX.Element;
27
+ export interface Wizard extends React.FC<WizardProps> {
28
+ Step: typeof WizardStep;
29
+ }
30
+ //#endregion
@@ -0,0 +1,158 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import { Button } from "./button.js";
3
+ import * as React from "react";
4
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
+ import { Check, ChevronLeft } from "lucide-react";
6
+ //#region src/primitives/wizard.tsx
7
+ const WizardContext = React.createContext(null);
8
+ function useWizardContext(component) {
9
+ const context = React.useContext(WizardContext);
10
+ if (!context) throw new Error(`<${component}> must be used inside <Wizard>`);
11
+ return context;
12
+ }
13
+ function Wizard({ children, step, defaultStep = 0, onStepChange, onFinished, className, ...rest }) {
14
+ const steps = React.Children.toArray(children).filter(React.isValidElement);
15
+ const [internal, setInternal] = React.useState(defaultStep);
16
+ const current = step !== void 0 ? step : internal;
17
+ const total = steps.length;
18
+ const goTo = React.useCallback((target) => {
19
+ const bounded = Math.max(0, Math.min(target, total - 1));
20
+ if (bounded === current) return;
21
+ onStepChange?.(bounded);
22
+ if (step === void 0) setInternal(bounded);
23
+ }, [
24
+ current,
25
+ onStepChange,
26
+ step,
27
+ total
28
+ ]);
29
+ const context = React.useMemo(() => ({
30
+ current,
31
+ total,
32
+ goTo,
33
+ next: () => goTo(current + 1),
34
+ back: () => goTo(current - 1),
35
+ finish: () => onFinished?.()
36
+ }), [
37
+ current,
38
+ goTo,
39
+ onFinished,
40
+ total
41
+ ]);
42
+ const titles = steps.map((child) => child.props.title);
43
+ const activeTitle = titles[current] ?? "";
44
+ return /* @__PURE__ */ jsx(WizardContext.Provider, {
45
+ value: context,
46
+ children: /* @__PURE__ */ jsxs("div", {
47
+ className: cn("w-full", className),
48
+ ...rest,
49
+ children: [
50
+ /* @__PURE__ */ jsx("ol", {
51
+ "aria-label": "Steps",
52
+ className: "hidden items-center gap-2 sm:flex",
53
+ children: titles.map((title, index) => {
54
+ const state = index < current ? "done" : index === current ? "active" : "future";
55
+ const marker = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
56
+ className: cn("flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-medium", state === "active" && "bg-primary text-primary-foreground", state === "done" && "bg-primary/15 text-primary", state === "future" && "bg-muted text-muted-foreground"),
57
+ children: state === "done" ? /* @__PURE__ */ jsx(Check, {
58
+ className: "size-3.5",
59
+ "aria-hidden": true
60
+ }) : index + 1
61
+ }), /* @__PURE__ */ jsx("span", {
62
+ className: cn("text-sm", state === "active" && "font-medium text-foreground", state === "done" && "text-foreground", state === "future" && "text-muted-foreground"),
63
+ children: title
64
+ })] });
65
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [index > 0 && /* @__PURE__ */ jsx("li", {
66
+ "aria-hidden": true,
67
+ className: "h-px flex-1 bg-border"
68
+ }), /* @__PURE__ */ jsx("li", { children: state === "future" ? /* @__PURE__ */ jsx("span", {
69
+ "aria-disabled": "true",
70
+ className: "flex items-center gap-2 p-1 opacity-70",
71
+ children: marker
72
+ }) : /* @__PURE__ */ jsx("button", {
73
+ type: "button",
74
+ onClick: () => goTo(index),
75
+ className: "-m-1 flex items-center gap-2 rounded-md p-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
76
+ children: marker
77
+ }) })] }, `${index}-${title}`);
78
+ })
79
+ }),
80
+ /* @__PURE__ */ jsxs("div", {
81
+ className: "sm:hidden",
82
+ children: [/* @__PURE__ */ jsxs("p", {
83
+ className: "text-xs text-muted-foreground",
84
+ children: [
85
+ "Step ",
86
+ current + 1,
87
+ " of ",
88
+ total,
89
+ " · ",
90
+ /* @__PURE__ */ jsx("span", {
91
+ className: "font-medium text-foreground",
92
+ children: activeTitle
93
+ })
94
+ ]
95
+ }), /* @__PURE__ */ jsx("div", {
96
+ className: "mt-2 h-1 overflow-hidden rounded-full bg-muted",
97
+ role: "progressbar",
98
+ "aria-valuemin": 1,
99
+ "aria-valuemax": total,
100
+ "aria-valuenow": current + 1,
101
+ "aria-label": `Step ${current + 1} of ${total}`,
102
+ children: /* @__PURE__ */ jsx("div", {
103
+ className: "h-full rounded-full bg-primary transition-all",
104
+ style: { width: `${(current + 1) / total * 100}%` }
105
+ })
106
+ })]
107
+ }),
108
+ steps.map((child, index) => index === current ? /* @__PURE__ */ jsx(React.Fragment, { children: child }, index) : null)
109
+ ]
110
+ })
111
+ });
112
+ }
113
+ function WizardStep({ title, children, nextLabel, onNext, nextDisabled, hideBack }) {
114
+ const { current, total, goTo, back, finish } = useWizardContext("Wizard.Step");
115
+ const [busy, setBusy] = React.useState(false);
116
+ const isLast = current === total - 1;
117
+ async function runNext() {
118
+ if (!onNext) {
119
+ if (isLast) finish();
120
+ else goTo(current + 1);
121
+ return;
122
+ }
123
+ setBusy(true);
124
+ try {
125
+ if (await onNext() === false) return;
126
+ if (isLast) finish();
127
+ else goTo(current + 1);
128
+ } catch {} finally {
129
+ setBusy(false);
130
+ }
131
+ }
132
+ return /* @__PURE__ */ jsxs("section", {
133
+ "aria-label": title,
134
+ className: "pt-6",
135
+ children: [children, (nextLabel || !hideBack) && /* @__PURE__ */ jsxs("div", {
136
+ className: "mt-6 flex items-center justify-between gap-2 border-t border-border pt-4",
137
+ children: [!hideBack && current > 0 ? /* @__PURE__ */ jsxs(Button, {
138
+ type: "button",
139
+ variant: "ghost",
140
+ onClick: back,
141
+ children: [/* @__PURE__ */ jsx(ChevronLeft, {
142
+ className: "size-4",
143
+ "aria-hidden": true
144
+ }), " Back"]
145
+ }) : /* @__PURE__ */ jsx("span", {}), nextLabel && /* @__PURE__ */ jsx(Button, {
146
+ type: "button",
147
+ className: "w-full sm:w-auto",
148
+ onClick: () => void runNext(),
149
+ disabled: nextDisabled,
150
+ loading: busy,
151
+ children: nextLabel
152
+ })]
153
+ })]
154
+ });
155
+ }
156
+ Wizard.Step = WizardStep;
157
+ //#endregion
158
+ export { Wizard, WizardStep };
@@ -50,7 +50,8 @@ import { Pagination, PaginationProps } from "./primitives/pagination.js";
50
50
  import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
51
51
  import { Tag, TagProps } from "./primitives/tag.js";
52
52
  import { Chip, ChipProps } from "./primitives/chip.js";
53
+ import { Wizard, WizardProps, WizardStep, WizardStepProps } from "./primitives/wizard.js";
53
54
  import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
54
55
  import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
55
56
  import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
56
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ArtifactPane, type ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
57
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ArtifactPane, type ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, Wizard, type WizardProps, WizardStep, type WizardStepProps, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
@@ -49,8 +49,9 @@ import { StatusPill } from "./primitives/status-pill.js";
49
49
  import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
50
50
  import { Tag } from "./primitives/tag.js";
51
51
  import { Chip } from "./primitives/chip.js";
52
+ import { Wizard, WizardStep } from "./primitives/wizard.js";
52
53
  import { IconTile, initialsOf } from "./primitives/icon-tile.js";
53
54
  import { Metric, MetricStrip } from "./primitives/metric-strip.js";
54
55
  import { FilterField, Toolbar } from "./primitives/toolbar.js";
55
56
  import { Logo, TangleKnot } from "@tangle-network/brand";
56
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, FilterField, Heading, HelpText, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, TimeSelect, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
57
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, FilterField, Heading, HelpText, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, TimeSelect, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, Wizard, WizardStep, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.37.0",
3
+ "version": "11.38.0",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -217,6 +217,9 @@ export type { TagProps } from "./tag";
217
217
  export { Chip } from "./chip";
218
218
  export type { ChipProps } from "./chip";
219
219
 
220
+ export { Wizard, WizardStep } from "./wizard";
221
+ export type { WizardProps, WizardStepProps } from "./wizard";
222
+
220
223
  export { IconTile, initialsOf } from "./icon-tile";
221
224
  export type { IconTileProps } from "./icon-tile";
222
225
 
@@ -0,0 +1,51 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import * as React from "react";
3
+ import { Button } from "./button";
4
+ import { Input } from "./input";
5
+ import { Wizard } from "./wizard";
6
+
7
+ const meta: Meta<typeof Wizard> = {
8
+ title: "Primitives/Wizard",
9
+ component: Wizard,
10
+ };
11
+ export default meta;
12
+
13
+ /** The intended shape: linear steps, one primary action each, async gate in the middle. */
14
+ export const ConnectNumber: StoryObj<typeof Wizard> = {
15
+ render: () => (
16
+ <div className="mx-auto max-w-lg rounded-xl border border-border bg-card p-6">
17
+ <Wizard onFinished={() => alert("connected")}>
18
+ <Wizard.Step title="Number" nextLabel="Continue">
19
+ <label className="grid gap-2 text-sm">
20
+ Your business number
21
+ <Input placeholder="+1 618 297 8214" />
22
+ </label>
23
+ </Wizard.Step>
24
+ <Wizard.Step title="Verify" nextLabel="I replied" onNext={() => new Promise(resolve => setTimeout(resolve, 800))}>
25
+ <p className="text-sm text-muted-foreground">
26
+ We sent a code to your phone. Reply to the message, then continue.
27
+ </p>
28
+ </Wizard.Step>
29
+ <Wizard.Step title="Done" nextLabel="Finish">
30
+ <p className="text-sm">Your number is live. Guests can text it now.</p>
31
+ </Wizard.Step>
32
+ </Wizard>
33
+ </div>
34
+ ),
35
+ };
36
+
37
+ /** A step whose content owns the actions entirely: no footer, no default chrome. */
38
+ export const CustomActions: StoryObj<typeof Wizard> = {
39
+ render: () => (
40
+ <div className="mx-auto max-w-lg rounded-xl border border-border bg-card p-6">
41
+ <Wizard>
42
+ <Wizard.Step title="Choice">
43
+ <div className="grid gap-3">
44
+ <Button onClick={() => undefined}>Use my own number</Button>
45
+ <Button variant="outline" onClick={() => undefined}>Give me a Tangle number</Button>
46
+ </div>
47
+ </Wizard.Step>
48
+ </Wizard>
49
+ </div>
50
+ ),
51
+ };
@@ -0,0 +1,106 @@
1
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import * as React from "react";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import { Wizard } from "./wizard";
6
+
7
+ function Flow(props: Partial<React.ComponentProps<typeof Wizard>> = {}) {
8
+ return (
9
+ <Wizard {...props}>
10
+ <Wizard.Step title="Number" nextLabel="Continue">pick a number</Wizard.Step>
11
+ <Wizard.Step title="Verify" nextLabel="I replied">verify panel</Wizard.Step>
12
+ <Wizard.Step title="Done" nextLabel="Finish">done panel</Wizard.Step>
13
+ </Wizard>
14
+ );
15
+ }
16
+
17
+ describe("Wizard", () => {
18
+ it("renders the first step and the rail, marks the active step, hides future panels", () => {
19
+ render(<Flow />);
20
+ expect(screen.getByText("pick a number")).toBeVisible();
21
+ expect(screen.queryByText("verify panel")).toBeNull();
22
+ expect(screen.getByRole("list", { name: "Steps" })).toBeVisible();
23
+ expect(screen.getAllByText("Number").length).toBeGreaterThan(0);
24
+ });
25
+
26
+ it("advances on the primary action and mirrors onStepChange", async () => {
27
+ const onStepChange = vi.fn();
28
+ render(<Flow onStepChange={onStepChange} />);
29
+ await userEvent.click(screen.getByRole("button", { name: "Continue" }));
30
+ expect(screen.getByText("verify panel")).toBeVisible();
31
+ expect(onStepChange).toHaveBeenCalledWith(1);
32
+ });
33
+
34
+ it("back returns to the previous step; the first step has no back", async () => {
35
+ render(<Flow />);
36
+ await userEvent.click(screen.getByRole("button", { name: "Continue" }));
37
+ await userEvent.click(screen.getByRole("button", { name: "Back" }));
38
+ expect(screen.getByText("pick a number")).toBeVisible();
39
+ expect(screen.queryByRole("button", { name: "Back" })).toBeNull();
40
+ });
41
+
42
+ it("future steps are not clickable; past steps are", async () => {
43
+ render(<Flow />);
44
+ fireEvent.click(screen.getByText("Verify")); // future: a span, not a button
45
+ expect(screen.getByText("pick a number")).toBeVisible();
46
+ await userEvent.click(screen.getByRole("button", { name: "Continue" }));
47
+ await userEvent.click(screen.getAllByRole("button", { name: /Number/ })[0]!); // past: clickable
48
+ expect(screen.getByText("pick a number")).toBeVisible();
49
+ });
50
+
51
+ it("an async onNext advances only on resolve; returning false keeps the step", async () => {
52
+ let resolveCheck: (value: unknown) => void = () => {};
53
+ const onNext = vi.fn(() => new Promise(resolve => { resolveCheck = resolve; }));
54
+ render(
55
+ <Wizard>
56
+ <Wizard.Step title="Check" nextLabel="Check" onNext={onNext}>checking</Wizard.Step>
57
+ <Wizard.Step title="Next">next panel</Wizard.Step>
58
+ </Wizard>,
59
+ );
60
+ await userEvent.click(screen.getByRole("button", { name: "Check" }));
61
+ expect(screen.getByText("checking")).toBeVisible(); // still here while pending
62
+ resolveCheck(true);
63
+ await waitFor(() => expect(screen.getByText("next panel")).toBeVisible());
64
+ expect(onNext).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ it("a rejected onNext stays on the step without crashing", async () => {
68
+ const onNext = vi.fn(() => Promise.reject(new Error("no")));
69
+ render(
70
+ <Wizard>
71
+ <Wizard.Step title="Check" nextLabel="Check" onNext={onNext}>checking</Wizard.Step>
72
+ <Wizard.Step title="Next">next panel</Wizard.Step>
73
+ </Wizard>,
74
+ );
75
+ await userEvent.click(screen.getByRole("button", { name: "Check" }));
76
+ await waitFor(() => expect(screen.getByRole("button", { name: "Check" })).toBeEnabled());
77
+ expect(screen.getByText("checking")).toBeVisible();
78
+ });
79
+
80
+ it("the last step fires onFinished instead of advancing", async () => {
81
+ const onFinished = vi.fn();
82
+ render(<Flow onFinished={onFinished} defaultStep={2} />);
83
+ await userEvent.click(screen.getByRole("button", { name: "Finish" }));
84
+ expect(onFinished).toHaveBeenCalledTimes(1);
85
+ expect(screen.getByText("done panel")).toBeVisible();
86
+ });
87
+
88
+ it("step can drive externally (controlled) and omit the default footer action", () => {
89
+ render(
90
+ <Wizard step={1}>
91
+ <Wizard.Step title="One">one</Wizard.Step>
92
+ <Wizard.Step title="Two"><button type="button">custom</button></Wizard.Step>
93
+ </Wizard>,
94
+ );
95
+ expect(screen.getAllByText("Two").length).toBeGreaterThan(0);
96
+ expect(screen.getByText("custom")).toBeVisible();
97
+ expect(screen.queryByRole("button", { name: "Continue" })).toBeNull();
98
+ });
99
+
100
+ it("throws when a step is used outside a wizard", () => {
101
+ // Silence the expected console.error from React's error boundary path.
102
+ const spy = vi.spyOn(console, "error").mockImplementation(() => {});
103
+ expect(() => render(<Wizard.Step title="Alone">x</Wizard.Step>)).toThrow(/must be used inside/);
104
+ spy.mockRestore();
105
+ });
106
+ });
@@ -0,0 +1,212 @@
1
+ import { Check, ChevronLeft } from "lucide-react";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/utils";
4
+ import { Button } from "./button";
5
+
6
+ /**
7
+ * A multi-step flow — onboarding, connection setup, anything where a person
8
+ * should see one decision at a time.
9
+ *
10
+ * The library owns the chrome (step rail, progress, footer); each step owns
11
+ * its content and its primary action. That separation is the point: products
12
+ * stop rebuilding stepper markup with drifting styles, and a person gets the
13
+ * same rhythm — one screen, one obvious action — in every Tangle app.
14
+ *
15
+ * Composition (DOM order is the step order):
16
+ *
17
+ * ```tsx
18
+ * <Wizard onFinished={() => navigate("/done")}>
19
+ * <Wizard.Step title="Number" nextLabel="Continue">…</Wizard.Step>
20
+ * <Wizard.Step title="Verify" nextLabel="I replied" onNext={() => check()}>…</Wizard.Step>
21
+ * <Wizard.Step title="Done" nextLabel="Finish">…</Wizard.Step>
22
+ * </Wizard>
23
+ * ```
24
+ *
25
+ * - **Uncontrolled by default**; pass `step` to drive it (a background poll
26
+ * can jump straight to Done). `onStepChange` mirrors every move.
27
+ * - **The step names its action.** `nextLabel` renders the footer's primary
28
+ * button; an async `onNext` shows `loading` and advances only on resolve,
29
+ * so failures keep the person on the step with the app's inline error.
30
+ * Omit `nextLabel` for steps whose content renders its own actions.
31
+ * - **Forward is earned, backward is free.** Future steps are not clickable;
32
+ * past steps are (people re-check earlier choices mid-flow).
33
+ * - **Mobile is a first-class shape**, not a squeezed desktop: below `sm` the
34
+ * rail collapses to "Step 2 of 3 · Verify" and a thin progress bar.
35
+ *
36
+ * Wizard renders no card — compose it inside a `Card` when the surface needs
37
+ * one, so containers-within-containers stay a choice, not a default.
38
+ */
39
+
40
+ interface WizardContextValue {
41
+ current: number;
42
+ total: number;
43
+ goTo: (step: number) => void;
44
+ next: () => void;
45
+ back: () => void;
46
+ /** Set by Wizard: fires onFinished when the final step's action resolves. */
47
+ finish: () => void;
48
+ }
49
+
50
+ const WizardContext = React.createContext<WizardContextValue | null>(null);
51
+
52
+ function useWizardContext(component: string): WizardContextValue {
53
+ const context = React.useContext(WizardContext);
54
+ if (!context) throw new Error(`<${component}> must be used inside <Wizard>`);
55
+ return context;
56
+ }
57
+
58
+ export interface WizardProps extends React.HTMLAttributes<HTMLDivElement> {
59
+ children: React.ReactNode;
60
+ /** Drives the active step; omit for uncontrolled with `defaultStep`. */
61
+ step?: number;
62
+ defaultStep?: number;
63
+ onStepChange?: (step: number) => void;
64
+ /** Fires when the last step's primary action resolves. */
65
+ onFinished?: () => void;
66
+ }
67
+
68
+ export function Wizard({ children, step, defaultStep = 0, onStepChange, onFinished, className, ...rest }: WizardProps) {
69
+ const steps = React.Children.toArray(children).filter(React.isValidElement) as React.ReactElement<WizardStepProps>[];
70
+ const [internal, setInternal] = React.useState(defaultStep);
71
+ const current = step !== undefined ? step : internal;
72
+ const total = steps.length;
73
+
74
+ const goTo = React.useCallback((target: number) => {
75
+ const bounded = Math.max(0, Math.min(target, total - 1));
76
+ if (bounded === current) return;
77
+ onStepChange?.(bounded);
78
+ if (step === undefined) setInternal(bounded);
79
+ }, [current, onStepChange, step, total]);
80
+
81
+ const context = React.useMemo<WizardContextValue>(() => ({
82
+ current,
83
+ total,
84
+ goTo,
85
+ next: () => goTo(current + 1),
86
+ back: () => goTo(current - 1),
87
+ finish: () => onFinished?.(),
88
+ }), [current, goTo, onFinished, total]);
89
+
90
+ const titles = steps.map(child => child.props.title);
91
+ const activeTitle = titles[current] ?? "";
92
+
93
+ return (
94
+ <WizardContext.Provider value={context}>
95
+ <div className={cn("w-full", className)} {...rest}>
96
+ {/* Desktop rail. */}
97
+ <ol aria-label="Steps" className="hidden items-center gap-2 sm:flex">
98
+ {titles.map((title, index) => {
99
+ const state = index < current ? "done" : index === current ? "active" : "future";
100
+ const marker = (
101
+ <>
102
+ <span className={cn(
103
+ "flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-medium",
104
+ state === "active" && "bg-primary text-primary-foreground",
105
+ state === "done" && "bg-primary/15 text-primary",
106
+ state === "future" && "bg-muted text-muted-foreground",
107
+ )}>
108
+ {state === "done" ? <Check className="size-3.5" aria-hidden /> : index + 1}
109
+ </span>
110
+ <span className={cn(
111
+ "text-sm",
112
+ state === "active" && "font-medium text-foreground",
113
+ state === "done" && "text-foreground",
114
+ state === "future" && "text-muted-foreground",
115
+ )}>{title}</span>
116
+ </>
117
+ );
118
+ return (
119
+ <React.Fragment key={`${index}-${title}`}>
120
+ {index > 0 && <li aria-hidden className="h-px flex-1 bg-border" />}
121
+ <li>
122
+ {state === "future"
123
+ ? <span aria-disabled="true" className="flex items-center gap-2 p-1 opacity-70">{marker}</span>
124
+ : <button type="button" onClick={() => goTo(index)}
125
+ className="-m-1 flex items-center gap-2 rounded-md p-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
126
+ {marker}
127
+ </button>}
128
+ </li>
129
+ </React.Fragment>
130
+ );
131
+ })}
132
+ </ol>
133
+ {/* Mobile: position + progress, not a squeezed rail. */}
134
+ <div className="sm:hidden">
135
+ <p className="text-xs text-muted-foreground">
136
+ Step {current + 1} of {total} · <span className="font-medium text-foreground">{activeTitle}</span>
137
+ </p>
138
+ <div className="mt-2 h-1 overflow-hidden rounded-full bg-muted" role="progressbar"
139
+ aria-valuemin={1} aria-valuemax={total} aria-valuenow={current + 1} aria-label={`Step ${current + 1} of ${total}`}>
140
+ <div className="h-full rounded-full bg-primary transition-all" style={{ width: `${((current + 1) / total) * 100}%` }} />
141
+ </div>
142
+ </div>
143
+ {steps.map((child, index) => index === current
144
+ ? <React.Fragment key={index}>{child}</React.Fragment>
145
+ : null)}
146
+ </div>
147
+ </WizardContext.Provider>
148
+ );
149
+ }
150
+
151
+ export interface WizardStepProps {
152
+ /** Shown in the rail (and the mobile position line). Steps read at a glance; keep it one or two words. */
153
+ title: string;
154
+ children: React.ReactNode;
155
+ /** The footer's primary action label. Omit when the step renders its own actions. */
156
+ nextLabel?: string;
157
+ /** Runs before advancing. Resolve (or return a non-false value) to advance; return false or throw to stay — the step's own content shows why. */
158
+ onNext?: () => Promise<unknown> | unknown;
159
+ /** Disables the primary action (a required choice is still missing). */
160
+ nextDisabled?: boolean;
161
+ /** Hides the Back button for steps where going back makes no sense (a completed handshake). */
162
+ hideBack?: boolean;
163
+ }
164
+
165
+ export function WizardStep({ title, children, nextLabel, onNext, nextDisabled, hideBack }: WizardStepProps) {
166
+ const { current, total, goTo, back, finish } = useWizardContext("Wizard.Step");
167
+ const [busy, setBusy] = React.useState(false);
168
+ const isLast = current === total - 1;
169
+
170
+ async function runNext() {
171
+ if (!onNext) {
172
+ if (isLast) finish(); else goTo(current + 1);
173
+ return;
174
+ }
175
+ setBusy(true);
176
+ try {
177
+ const result = await onNext();
178
+ if (result === false) return;
179
+ if (isLast) finish(); else goTo(current + 1);
180
+ } catch {
181
+ // Stay on the step; its content owns the error presentation. A rejected
182
+ // promise here must not surface as an unhandled flow crash.
183
+ } finally {
184
+ setBusy(false);
185
+ }
186
+ }
187
+
188
+ return (
189
+ <section aria-label={title} className="pt-6">
190
+ {children}
191
+ {(nextLabel || !hideBack) && (
192
+ <div className="mt-6 flex items-center justify-between gap-2 border-t border-border pt-4">
193
+ {!hideBack && current > 0
194
+ ? <Button type="button" variant="ghost" onClick={back}><ChevronLeft className="size-4" aria-hidden /> Back</Button>
195
+ : <span />}
196
+ {nextLabel && (
197
+ <Button type="button" className="w-full sm:w-auto" onClick={() => void runNext()} disabled={nextDisabled} loading={busy}>
198
+ {nextLabel}
199
+ </Button>
200
+ )}
201
+ </div>
202
+ )}
203
+ </section>
204
+ );
205
+ }
206
+
207
+ // Dot-notation composition (`<Wizard.Step>`), attached after both definitions
208
+ // so each keeps a plain, tree-shakeable named export.
209
+ export interface Wizard extends React.FC<WizardProps> {
210
+ Step: typeof WizardStep;
211
+ }
212
+ (Wizard as Wizard).Step = WizardStep;