@tangle-network/ui 11.51.19 → 11.51.21

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,3 +1,16 @@
1
+ ## 11.51.21
2
+
3
+ ### Patch Changes
4
+
5
+ - b1f7c14: Reconcile preferences changed by another tab while theme consumers are unmounted, retaining in-memory preferences when browser storage is unavailable.
6
+ - 66a2c78: Add configurable theme persistence and product attributes through the existing theme engine, and controlled accessible sorting on TableHead. Existing useTheme callers retain their default behavior.
7
+
8
+ ## 11.51.20
9
+
10
+ ### Patch Changes
11
+
12
+ - 9d50cbe: `FileTabs`: `onClose` is optional. Without it the tabs render no close buttons and Delete does nothing, for tab strips that only switch between files.
13
+
1
14
  ## 11.51.19
2
15
 
3
16
  ### Patch Changes
package/README.md CHANGED
@@ -171,3 +171,9 @@ Retained report readers can import existing `chat/agent-timeline`, `chat/speaker
171
171
  `@tangle-network/ui/utils/format` is a pure module for duration, clock, compact counts, USD, bytes and UTC labels. It loads without React. Charts re-exports these helpers for static figures and record views. `formatDuration` and `formatUptime` remain aliases of `formatSpanDuration`; chat timestamps use explicit UTC, and file/upload sizes share `formatBytes`.
172
172
 
173
173
  `HoverCard` shares Popover positioning, portal and keyboard behavior. Its optional `anchor` accepts an existing DOM element for virtualized rows or canvas nodes. Hover delays default to the trigger's `--duration-fast` token. `HoverCardContent` and `PopoverContent` accept the local theme container, so previews preserve inherited tokens without copying computed styles.
174
+
175
+ ### Product theme and table sorting
176
+
177
+ Create a `createThemeController({ storageKey, defaultTheme, storageValues, attribute })` once outside React components when a product has established persisted names or a mode attribute. Its `useTheme` shares state across components and tabs; `setTheme` persists the preference, while `applyTheme` applies a temporary page mode without replacing it. The parameterless `useTheme` keeps the default `theme` key and named Brand themes.
178
+
179
+ `TableHead` accepts `sort={{ direction, onSort, label, buttonClassName }}`. It owns the native button, keyboard activation, indicator and column `aria-sort`; the consumer owns row ordering, comparator, URL state and sort cycle. `direction` is `asc`, `desc` or omitted. Supply `label` when the header is not plain text.
@@ -15,7 +15,7 @@ export interface FileTabsProps {
15
15
  tabs: FileTabData[];
16
16
  activeId?: string;
17
17
  onSelect: (id: string) => void;
18
- onClose: (id: string) => void;
18
+ onClose?: (id: string) => void;
19
19
  /** Accessible name of the tab list. Defaults to "Open files". */
20
20
  label?: string;
21
21
  className?: string;
@@ -64,14 +64,14 @@ function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open files", cla
64
64
  Home: 0,
65
65
  End: last
66
66
  }[event.key];
67
- if (at < 0 || next === void 0 && event.key !== "Delete") return;
67
+ if (at < 0 || next === void 0 && (event.key !== "Delete" || !onClose)) return;
68
68
  event.preventDefault();
69
69
  if (next !== void 0) {
70
70
  tabButtons()[next]?.focus();
71
71
  onSelect(tabs[next].id);
72
72
  return;
73
73
  }
74
- onClose(tabs[at].id);
74
+ onClose?.(tabs[at].id);
75
75
  requestAnimationFrame(() => (tabButtons().find((tab) => tab.getAttribute("aria-selected") === "true") ?? tabButtons().at(-1))?.focus());
76
76
  };
77
77
  return /* @__PURE__ */ jsx("div", {
@@ -96,7 +96,7 @@ function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open files", cla
96
96
  "aria-label": `${tab.name}${hint ? ` in ${hint}` : ""}${tab.dirty ? ", unsaved changes" : ""}`,
97
97
  tabIndex: isActive || !activeId && tab === tabs[0] ? 0 : -1,
98
98
  onClick: () => onSelect(tab.id),
99
- className: cn("flex h-full min-w-0 items-center gap-1.5 rounded-t-lg pl-3 pr-1", focusRingInset),
99
+ className: cn("flex h-full min-w-0 items-center gap-1.5 rounded-t-lg pl-3", onClose ? "pr-1" : "pr-3", focusRingInset),
100
100
  children: [
101
101
  /* @__PURE__ */ jsx(Icon, {
102
102
  "aria-hidden": true,
@@ -121,12 +121,12 @@ function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open files", cla
121
121
  className: "size-1.5 shrink-0 rounded-full bg-[var(--accent-text)]"
122
122
  })
123
123
  ]
124
- }), /* @__PURE__ */ jsx("button", {
124
+ }), onClose && /* @__PURE__ */ jsx("button", {
125
125
  type: "button",
126
126
  tabIndex: -1,
127
127
  "aria-label": `Close ${tab.name}`,
128
128
  onClick: () => onClose(tab.id),
129
- className: cn("mr-1.5 grid size-5 shrink-0 place-items-center rounded-md text-muted-foreground hover:bg-[var(--ix-hover-bg)] hover:text-foreground", controlMotion, !isActive && "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"),
129
+ className: cn("mr-1.5 grid size-5 shrink-0 place-items-center rounded-md text-muted-foreground hover:bg-[var(--ix-hover-bg)] hover:text-foreground", "[transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none", !isActive && "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100"),
130
130
  children: /* @__PURE__ */ jsx(X, {
131
131
  "aria-hidden": true,
132
132
  className: "size-3"
package/dist/index.d.ts CHANGED
@@ -69,7 +69,7 @@ import { Badge, BadgeProps, badgeVariants } from "./primitives/badge.js";
69
69
  import { Avatar, AvatarFallback, AvatarImage, AvatarProps } from "./primitives/avatar.js";
70
70
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./primitives/dropdown-menu.js";
71
71
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./primitives/select.js";
72
- import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableProps, TableRow } from "./primitives/table.js";
72
+ import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeadProps, TableHeader, TableProps, TableRow } from "./primitives/table.js";
73
73
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/tabs.js";
74
74
  import { SegmentedControl, SegmentedControlOption, SegmentedControlProps } from "./primitives/segmented-control.js";
75
75
  import { Progress } from "./primitives/progress.js";
@@ -91,7 +91,7 @@ import { UploadFile, UploadProgress, UploadProgressProps } from "./primitives/up
91
91
  import { SidebarDropZone, SidebarDropZoneProps } from "./primitives/sidebar-drop-zone.js";
92
92
  import { CodeBlock, CodeBlockProps, CopyButton, CopyButtonProps } from "./markdown/code-block.js";
93
93
  import { InlineCode, InlineCodeProps } from "./primitives/code-block.js";
94
- import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
94
+ import { ThemeControllerOptions, ThemeToggle, createThemeController, useTheme } from "./primitives/theme-toggle.js";
95
95
  import { Heading, HeadingProps, HeadingVariant } from "./primitives/heading.js";
96
96
  import { PageShell, PageShellProps } from "./primitives/page-shell.js";
97
97
  import { Field, FieldProps, fieldMessageId } from "./primitives/field.js";
@@ -149,4 +149,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
149
149
  import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
150
150
  import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
151
151
  import "./redaction.js";
152
- export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, AgentTimelineColumn, type AgentTimelineCustomItem, type AgentTimelineFoldItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, AgentTimelineStep, type AgentTimelineStepKind, type AgentTimelineStepProps, 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, type AvatarProps, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, type ChartReferenceLine, ChatAuthor, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ComplianceBadge, type ComplianceBadgeProps, type ComplianceProduct, type ComplianceStatus, 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, type EventStreamHandlers, type EventStreamOpener, type EventStreamSource, ExpandablePanel, type ExpandablePanelProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, Field, type FieldProps, 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, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, 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, MetricChart, type MetricChartProps, 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, PHONE_MEDIA, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, PdfViewer, type PdfViewerConfig, type PdfViewerProps, type PeriodDelta, 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, type RunStatus, RunStatusIcon, type RunStatusIconProps, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeriesExtent, type SeriesPoint, type SeriesPointInput, type SeriesScale, SeriesSparkline, type SeriesSparklineProps, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, SettingsList, type SettingsListAction, type SettingsListColumns, SettingsListItem, type SettingsListItemProps, type SettingsListMenuItem, type SettingsListProps, SettingsNote, type SettingsNoteProps, SettingsRow, type SettingsRowLayout, type SettingsRowProps, SettingsSection, type SettingsSectionProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, type Speaker, SpeakerHeader, type SpeakerHeaderProps, type SpeakerPart, type SpeakerSection, type SpeakerState, StatCard, type StatCardProps, type StatusMeta, 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, agentTimelineStepKind, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fieldMessageId, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, foldSummary, formatAxisDuration, formatBytes, formatCalendarDate, formatClock, formatCompact, formatCost, formatCount, formatSpanDuration as formatDuration, formatSpanDuration, formatSpanDuration as formatUptime, formatUtcTime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isOpen, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, normalizeSeries, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, partitionBySpeaker, periodDelta, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, runStatus, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, statusMeta, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useEventStream, useFollowTail, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, usePhone, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useRunNow, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
152
+ export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, AgentTimelineColumn, type AgentTimelineCustomItem, type AgentTimelineFoldItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, AgentTimelineStep, type AgentTimelineStepKind, type AgentTimelineStepProps, 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, type AvatarProps, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, type ChartReferenceLine, ChatAuthor, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ComplianceBadge, type ComplianceBadgeProps, type ComplianceProduct, type ComplianceStatus, 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, type EventStreamHandlers, type EventStreamOpener, type EventStreamSource, ExpandablePanel, type ExpandablePanelProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, Field, type FieldProps, 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, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, 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, MetricChart, type MetricChartProps, 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, PHONE_MEDIA, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, PdfViewer, type PdfViewerConfig, type PdfViewerProps, type PeriodDelta, 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, type RunStatus, RunStatusIcon, type RunStatusIconProps, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeriesExtent, type SeriesPoint, type SeriesPointInput, type SeriesScale, SeriesSparkline, type SeriesSparklineProps, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, SettingsList, type SettingsListAction, type SettingsListColumns, SettingsListItem, type SettingsListItemProps, type SettingsListMenuItem, type SettingsListProps, SettingsNote, type SettingsNoteProps, SettingsRow, type SettingsRowLayout, type SettingsRowProps, SettingsSection, type SettingsSectionProps, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, type Speaker, SpeakerHeader, type SpeakerHeaderProps, type SpeakerPart, type SpeakerSection, type SpeakerState, StatCard, type StatCardProps, type StatusMeta, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, TOOL_CATEGORY_ICONS, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, type TableHeadProps, 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, type ThemeControllerOptions, 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, agentTimelineStepKind, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, createThemeController, detectFileFormat, fenceInfo, fieldMessageId, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, foldSummary, formatAxisDuration, formatBytes, formatCalendarDate, formatClock, formatCompact, formatCost, formatCount, formatSpanDuration as formatDuration, formatSpanDuration, formatSpanDuration as formatUptime, formatUtcTime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isOpen, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, normalizeSeries, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, partitionBySpeaker, periodDelta, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, runStatus, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, statusMeta, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useEventStream, useFollowTail, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, usePhone, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useRunNow, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ import { ArtifactPane } from "./primitives/artifact-pane.js";
45
45
  import { codeBlockGrammar } from "./lib/code-presentation.js";
46
46
  import { CodeBlock, CopyButton } from "./markdown/code-block.js";
47
47
  import { InlineCode } from "./primitives/code-block.js";
48
- import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
48
+ import { ThemeToggle, createThemeController, useTheme } from "./primitives/theme-toggle.js";
49
49
  import { PageShell } from "./primitives/page-shell.js";
50
50
  import { Field, fieldMessageId } from "./primitives/field.js";
51
51
  import { StatusPill } from "./primitives/status-pill.js";
@@ -133,4 +133,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
133
133
  import { copyText } from "./utils/copy-text.js";
134
134
  import { timeAgo } from "./utils/time-ago.js";
135
135
  import { RedactedDocument } from "./redaction/redacted-document.js";
136
- export { AgentTimeline, AgentTimelineColumn, AgentTimelineStep, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApiSchema, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, ComplianceBadge, 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, ExpandablePanel, ExpandedToolDetail, Field, FileArtifactPane, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricChart, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PHONE_MEDIA, PageHeader, PageShell, Pagination, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, RunStatusIcon, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeriesSparkline, SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, SpeakerHeader, 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, agentTimelineStepKind, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fieldMessageId, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, foldSummary, formatAxisDuration, formatBytes, formatCalendarDate, formatClock, formatCompact, formatCost, formatCount, formatSpanDuration as formatDuration, formatSpanDuration, formatSpanDuration as formatUptime, formatUtcTime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isOpen, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, normalizeSeries, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, partitionBySpeaker, periodDelta, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, runStatus, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, statusMeta, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useEventStream, useFollowTail, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, usePhone, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useRunNow, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
136
+ export { AgentTimeline, AgentTimelineColumn, AgentTimelineStep, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApiSchema, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, ComplianceBadge, 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, ExpandablePanel, ExpandedToolDetail, Field, FileArtifactPane, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricChart, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PHONE_MEDIA, PageHeader, PageShell, Pagination, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, RunStatusIcon, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeriesSparkline, SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, SpeakerHeader, 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, agentTimelineStepKind, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, createThemeController, detectFileFormat, fenceInfo, fieldMessageId, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, foldSummary, formatAxisDuration, formatBytes, formatCalendarDate, formatClock, formatCompact, formatCost, formatCount, formatSpanDuration as formatDuration, formatSpanDuration, formatSpanDuration as formatUptime, formatUtcTime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isOpen, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, normalizeSeries, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, partitionBySpeaker, periodDelta, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, runStatus, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, statusMeta, tableCsv, taskProgress, taskStatus, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useEventStream, useFollowTail, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, usePhone, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useRunNow, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
@@ -14,7 +14,16 @@ declare const TableHeader: React.ForwardRefExoticComponent<React.HTMLAttributes<
14
14
  declare const TableBody: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
15
15
  declare const TableFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
16
16
  declare const TableRow: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableRowElement> & React.RefAttributes<HTMLTableRowElement>>;
17
- declare const TableHead: React.ForwardRefExoticComponent<React.ThHTMLAttributes<HTMLTableCellElement> & React.RefAttributes<HTMLTableCellElement>>;
17
+ export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
18
+ /** Controlled sort affordance. The caller retains row order and sort policy. */
19
+ sort?: {
20
+ direction?: "asc" | "desc";
21
+ onSort: () => void;
22
+ label?: string;
23
+ buttonClassName?: string;
24
+ };
25
+ }
26
+ declare const TableHead: React.ForwardRefExoticComponent<TableHeadProps & React.RefAttributes<HTMLTableCellElement>>;
18
27
  declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<HTMLTableCellElement> & React.RefAttributes<HTMLTableCellElement>>;
19
28
  declare const TableCaption: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableCaptionElement> & React.RefAttributes<HTMLTableCaptionElement>>;
20
29
  export { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow };
@@ -1,7 +1,7 @@
1
1
  import { cn } from "../lib/utils.js";
2
2
  import { tablePresentation } from "../presentation.js";
3
3
  import * as React from "react";
4
- import { jsx } from "react/jsx-runtime";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
5
  // src/primitives/table.tsx
6
6
  const Table = React.forwardRef(({ className, wrapper = true, wrapperProps, ...props }, ref) => {
7
7
  const table = /* @__PURE__ */ jsx("table", {
@@ -42,10 +42,22 @@ const TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__
42
42
  ...props
43
43
  }));
44
44
  TableRow.displayName = "TableRow";
45
- const TableHead = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("th", {
45
+ const TableHead = React.forwardRef(({ className, children, sort, ...props }, ref) => /* @__PURE__ */ jsx("th", {
46
46
  ref,
47
47
  className: cn(tablePresentation.columnHeader, className),
48
- ...props
48
+ ...props,
49
+ "aria-sort": sort ? sort.direction === "asc" ? "ascending" : sort.direction === "desc" ? "descending" : "none" : props["aria-sort"],
50
+ children: sort ? /* @__PURE__ */ jsxs("button", {
51
+ type: "button",
52
+ onClick: sort.onSort,
53
+ "aria-label": sort.label ?? (typeof children === "string" ? `Sort by ${children}` : void 0),
54
+ className: cn("inline-flex cursor-pointer items-center gap-1 rounded-sm font-inherit text-inherit hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2", sort.buttonClassName),
55
+ children: [children, /* @__PURE__ */ jsx("span", {
56
+ "aria-hidden": "true",
57
+ className: "text-xs",
58
+ children: sort.direction === "asc" ? "↑" : sort.direction === "desc" ? "↓" : "↕"
59
+ })]
60
+ }) : children
49
61
  }));
50
62
  TableHead.displayName = "TableHead";
51
63
  const TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("td", {
@@ -1,9 +1,31 @@
1
1
  type Theme = "light" | "dark" | "system";
2
2
  type Mode = Exclude<Theme, "system">;
3
- declare function setTheme(next: Theme): void;
4
- export declare function useTheme(): {
3
+ export interface ThemeControllerOptions {
4
+ storageKey?: string;
5
+ defaultTheme?: Theme;
6
+ /** Persisted spellings, including null to remove a system preference. */
7
+ storageValues?: Partial<Record<Theme, string | null>>;
8
+ /** Product mode attribute; absent mode values remove the attribute. */
9
+ attribute?: {
10
+ name: string;
11
+ light?: string;
12
+ dark?: string;
13
+ };
14
+ }
15
+ /** Create once at module scope. Each product owns its persisted names, not a second theme engine. */
16
+ export declare function createThemeController({ storageKey, defaultTheme, storageValues, attribute }?: ThemeControllerOptions): {
17
+ useTheme: () => {
18
+ theme: Theme;
19
+ setTheme: (next: Theme) => void;
20
+ resolvedTheme: Mode;
21
+ };
22
+ getTheme: () => Theme;
23
+ setTheme: (next: Theme) => void;
24
+ applyTheme: (theme?: Theme) => void;
25
+ };
26
+ export declare const useTheme: () => {
5
27
  theme: Theme;
6
- setTheme: typeof setTheme;
28
+ setTheme: (next: Theme) => void;
7
29
  resolvedTheme: Mode;
8
30
  };
9
31
  export declare function ThemeToggle(): import("react").JSX.Element;
@@ -25,7 +25,6 @@ const namedModes = [
25
25
  light: "agents-light"
26
26
  }
27
27
  ];
28
- let preference;
29
28
  function isTheme(value) {
30
29
  return value === "light" || value === "dark" || value === "system";
31
30
  }
@@ -33,88 +32,129 @@ function getSystemTheme() {
33
32
  if (typeof window === "undefined" || !window.matchMedia) return "dark";
34
33
  return window.matchMedia(systemQuery).matches ? "dark" : "light";
35
34
  }
36
- function getServerTheme() {
37
- return "system";
38
- }
39
- function getServerMode() {
40
- return "dark";
41
- }
42
- function readInitialTheme() {
43
- try {
44
- const stored = window.localStorage.getItem("theme");
45
- if (isTheme(stored)) return stored;
46
- } catch {}
47
- const root = document.documentElement;
48
- const named = root.getAttribute("data-theme");
49
- if (named === "light" || named === "dark") return named;
50
- for (const pair of namedModes) {
51
- if (named === pair.light) return "light";
52
- if (named === pair.dark) return "dark";
53
- }
54
- if (root.classList.contains("light")) return "light";
55
- if (root.classList.contains("dark")) return "dark";
56
- const legacy = root.getAttribute("data-sandbox-theme");
57
- if (legacy === "vault" || legacy === "dawn") return "light";
58
- return "system";
59
- }
60
- function getTheme() {
61
- if (typeof window === "undefined") return "system";
62
- return preference ??= readInitialTheme();
63
- }
64
- function applyMode(mode) {
65
- const root = document.documentElement;
66
- root.classList.toggle("dark", mode === "dark");
67
- root.classList.toggle("light", mode === "light");
68
- const named = root.getAttribute("data-theme");
69
- if (named === "dark" || named === "light") root.setAttribute("data-theme", mode);
70
- else {
71
- const pair = namedModes.find((pair) => named === pair.dark || named === pair.light);
72
- if (pair) root.setAttribute("data-theme", pair[mode]);
73
- }
74
- }
75
- function setTheme(next) {
76
- if (!isTheme(next) || typeof window === "undefined") return;
77
- preference = next;
78
- try {
79
- if (next === "system") window.localStorage.removeItem("theme");
80
- else window.localStorage.setItem("theme", next);
81
- } catch {}
82
- applyMode(next === "system" ? getSystemTheme() : next);
83
- window.dispatchEvent(new Event(preferenceEvent));
84
- }
85
- function subscribeTheme(notify) {
86
- const onStorage = (event) => {
87
- if (event.key !== "theme" && event.key !== null) return;
88
- preference = isTheme(event.newValue) ? event.newValue : "system";
89
- notify();
90
- };
91
- window.addEventListener(preferenceEvent, notify);
92
- window.addEventListener("storage", onStorage);
93
- return () => {
94
- window.removeEventListener(preferenceEvent, notify);
95
- window.removeEventListener("storage", onStorage);
96
- };
97
- }
98
35
  function subscribeSystem(notify) {
99
36
  if (!window.matchMedia) return () => {};
100
37
  const media = window.matchMedia(systemQuery);
101
38
  media.addEventListener("change", notify);
102
39
  return () => media.removeEventListener("change", notify);
103
40
  }
104
- function useTheme() {
105
- const theme = useSyncExternalStore(subscribeTheme, getTheme, getServerTheme);
106
- const system = useSyncExternalStore(subscribeSystem, getSystemTheme, getServerMode);
107
- const resolvedTheme = theme === "system" ? system : theme;
108
- useEffect(() => {
109
- const selected = getTheme();
110
- applyMode(selected === "system" ? getSystemTheme() : selected);
111
- }, [resolvedTheme]);
41
+ /** Create once at module scope. Each product owns its persisted names, not a second theme engine. */
42
+ function createThemeController({ storageKey = "theme", defaultTheme = "system", storageValues, attribute } = {}) {
43
+ const values = {
44
+ light: "light",
45
+ dark: "dark",
46
+ system: null,
47
+ ...storageValues
48
+ };
49
+ const eventName = `${preferenceEvent}:${storageKey}`;
50
+ let preference;
51
+ let persisted;
52
+ const decode = (value) => Object.keys(values).find((theme) => value !== null && values[theme] === value) ?? (value === "system" && values.system === null ? "system" : defaultTheme);
53
+ const serverTheme = () => defaultTheme;
54
+ const serverMode = () => defaultTheme === "system" ? "dark" : defaultTheme;
55
+ function readInitialTheme() {
56
+ try {
57
+ const stored = window.localStorage.getItem(storageKey);
58
+ persisted = stored;
59
+ if (stored !== null && (Object.values(values).includes(stored) || stored === "system" && values.system === null)) return decode(stored);
60
+ } catch {}
61
+ if (attribute || defaultTheme !== "system") return defaultTheme;
62
+ const root = document.documentElement;
63
+ const named = root.getAttribute("data-theme");
64
+ if (named === "light" || named === "dark") return named;
65
+ for (const pair of namedModes) {
66
+ if (named === pair.light) return "light";
67
+ if (named === pair.dark) return "dark";
68
+ }
69
+ if (root.classList.contains("light")) return "light";
70
+ if (root.classList.contains("dark")) return "dark";
71
+ const legacy = root.getAttribute("data-sandbox-theme");
72
+ return legacy === "vault" || legacy === "dawn" ? "light" : defaultTheme;
73
+ }
74
+ function getTheme() {
75
+ if (typeof window === "undefined") return defaultTheme;
76
+ if (preference === void 0) return preference = readInitialTheme();
77
+ try {
78
+ const stored = window.localStorage.getItem(storageKey);
79
+ if (stored !== persisted) {
80
+ persisted = stored;
81
+ preference = decode(stored);
82
+ }
83
+ } catch {}
84
+ return preference;
85
+ }
86
+ /** Apply a temporary page mode without overwriting the user's preference. */
87
+ function applyTheme(theme = getTheme()) {
88
+ if (typeof document === "undefined") return;
89
+ const mode = theme === "system" ? getSystemTheme() : theme;
90
+ const root = document.documentElement;
91
+ root.classList.toggle("dark", mode === "dark");
92
+ root.classList.toggle("light", !attribute && mode === "light");
93
+ if (attribute) {
94
+ const value = attribute[mode];
95
+ if (value === void 0) root.removeAttribute(attribute.name);
96
+ else root.setAttribute(attribute.name, value);
97
+ } else {
98
+ const named = root.getAttribute("data-theme");
99
+ if (named === "dark" || named === "light") root.setAttribute("data-theme", mode);
100
+ else {
101
+ const pair = namedModes.find((pair) => named === pair.dark || named === pair.light);
102
+ if (pair) root.setAttribute("data-theme", pair[mode]);
103
+ }
104
+ }
105
+ }
106
+ function setTheme(next) {
107
+ if (!isTheme(next) || typeof window === "undefined") return;
108
+ preference = next;
109
+ try {
110
+ const value = values[next];
111
+ if (value === null) window.localStorage.removeItem(storageKey);
112
+ else window.localStorage.setItem(storageKey, value);
113
+ persisted = value;
114
+ } catch {}
115
+ applyTheme(next);
116
+ window.dispatchEvent(new CustomEvent(eventName, { detail: next }));
117
+ }
118
+ function subscribeTheme(notify) {
119
+ const onStorage = (event) => {
120
+ if (event.key !== storageKey && event.key !== null) return;
121
+ persisted = event.newValue;
122
+ preference = decode(event.newValue);
123
+ notify();
124
+ };
125
+ const onPreference = (event) => {
126
+ const next = event.detail;
127
+ if (isTheme(next)) preference = next;
128
+ notify();
129
+ };
130
+ window.addEventListener(eventName, onPreference);
131
+ window.addEventListener("storage", onStorage);
132
+ return () => {
133
+ window.removeEventListener(eventName, onPreference);
134
+ window.removeEventListener("storage", onStorage);
135
+ };
136
+ }
137
+ function useTheme() {
138
+ const theme = useSyncExternalStore(subscribeTheme, getTheme, serverTheme);
139
+ const system = useSyncExternalStore(subscribeSystem, getSystemTheme, serverMode);
140
+ const resolvedTheme = theme === "system" ? system : theme;
141
+ useEffect(() => {
142
+ applyTheme(getTheme());
143
+ }, [resolvedTheme]);
144
+ return {
145
+ theme,
146
+ setTheme,
147
+ resolvedTheme
148
+ };
149
+ }
112
150
  return {
113
- theme,
151
+ useTheme,
152
+ getTheme,
114
153
  setTheme,
115
- resolvedTheme
154
+ applyTheme
116
155
  };
117
156
  }
157
+ const { useTheme } = createThemeController();
118
158
  const iconClass = "h-4 w-4";
119
159
  function SunIcon() {
120
160
  return /* @__PURE__ */ jsxs("svg", {
@@ -158,4 +198,4 @@ function ThemeToggle() {
158
198
  children: resolvedTheme === "dark" ? /* @__PURE__ */ jsx(SunIcon, {}) : /* @__PURE__ */ jsx(MoonIcon, {})
159
199
  });
160
200
  }
161
- export { ThemeToggle, useTheme };
201
+ export { ThemeToggle, createThemeController, useTheme };
@@ -19,7 +19,7 @@ import { Badge, BadgeProps, badgeVariants } from "./primitives/badge.js";
19
19
  import { Avatar, AvatarFallback, AvatarImage, AvatarProps } from "./primitives/avatar.js";
20
20
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./primitives/dropdown-menu.js";
21
21
  import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./primitives/select.js";
22
- import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableProps, TableRow } from "./primitives/table.js";
22
+ import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeadProps, TableHeader, TableProps, TableRow } from "./primitives/table.js";
23
23
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/tabs.js";
24
24
  import { SegmentedControl, SegmentedControlOption, SegmentedControlProps } from "./primitives/segmented-control.js";
25
25
  import { Progress } from "./primitives/progress.js";
@@ -41,7 +41,7 @@ import { UploadFile, UploadProgress, UploadProgressProps } from "./primitives/up
41
41
  import { SidebarDropZone, SidebarDropZoneProps } from "./primitives/sidebar-drop-zone.js";
42
42
  import { CodeBlock, CodeBlockProps, CopyButton } from "./markdown/code-block.js";
43
43
  import { InlineCode, InlineCodeProps } from "./primitives/code-block.js";
44
- import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
44
+ import { ThemeControllerOptions, ThemeToggle, createThemeController, useTheme } from "./primitives/theme-toggle.js";
45
45
  import { Heading, HeadingProps, HeadingVariant } from "./primitives/heading.js";
46
46
  import { PageShell, PageShellProps } from "./primitives/page-shell.js";
47
47
  import { Field, FieldProps, fieldMessageId } from "./primitives/field.js";
@@ -63,4 +63,4 @@ import { ChartReferenceLine, MetricChart, MetricChartProps } from "./primitives/
63
63
  import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
64
64
  import { ExpandablePanel, ExpandablePanelProps } from "./primitives/expandable-panel.js";
65
65
  import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
66
- 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, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, type ChartReferenceLine, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, type ComplianceBadgeProps, type ComplianceProduct, type ComplianceStatus, 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, ExpandablePanel, type ExpandablePanelProps, Field, type FieldProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, MetricChart, type MetricChartProps, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, type PeriodDelta, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeriesExtent, type SeriesPoint, type SeriesPointInput, type SeriesScale, SeriesSparkline, type SeriesSparklineProps, SettingsList, type SettingsListAction, type SettingsListColumns, SettingsListItem, type SettingsListItemProps, type SettingsListMenuItem, type SettingsListProps, SettingsNote, type SettingsNoteProps, SettingsRow, type SettingsRowLayout, type SettingsRowProps, SettingsSection, type SettingsSectionProps, 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, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
66
+ 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, type AvatarProps, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, type ChartReferenceLine, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, type ComplianceBadgeProps, type ComplianceProduct, type ComplianceStatus, 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, ExpandablePanel, type ExpandablePanelProps, Field, type FieldProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, MetricChart, type MetricChartProps, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, type PeriodDelta, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, type SeriesExtent, type SeriesPoint, type SeriesPointInput, type SeriesScale, SeriesSparkline, type SeriesSparklineProps, SettingsList, type SettingsListAction, type SettingsListColumns, SettingsListItem, type SettingsListItemProps, type SettingsListMenuItem, type SettingsListProps, SettingsNote, type SettingsNoteProps, SettingsRow, type SettingsRowLayout, type SettingsRowProps, SettingsSection, type SettingsSectionProps, 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, type TableHeadProps, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, type ThemeControllerOptions, 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, createThemeController, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
@@ -40,7 +40,7 @@ import { SidebarDropZone } from "./primitives/sidebar-drop-zone.js";
40
40
  import { ArtifactPane } from "./primitives/artifact-pane.js";
41
41
  import { CodeBlock, CopyButton } from "./markdown/code-block.js";
42
42
  import { InlineCode } from "./primitives/code-block.js";
43
- import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
43
+ import { ThemeToggle, createThemeController, useTheme } from "./primitives/theme-toggle.js";
44
44
  import { PageShell } from "./primitives/page-shell.js";
45
45
  import { Field, fieldMessageId } from "./primitives/field.js";
46
46
  import { StatusPill } from "./primitives/status-pill.js";
@@ -63,4 +63,4 @@ import { MetricChart } from "./primitives/metric-chart.js";
63
63
  import { FilterField, Toolbar } from "./primitives/toolbar.js";
64
64
  import { ExpandablePanel } from "./primitives/expandable-panel.js";
65
65
  import { Logo, TangleKnot } from "@tangle-network/brand";
66
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, 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, ExpandablePanel, Field, FilterField, Heading, HelpText, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, InlineCode, Input, Label, Logo, Metric, MetricChart, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeriesSparkline, SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection, 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, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
66
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ComplianceBadge, 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, ExpandablePanel, Field, FilterField, Heading, HelpText, HoverCard, HoverCardContent, HoverCardDescription, HoverCardHeader, HoverCardTitle, HoverCardTrigger, IconTile, InlineCode, Input, Label, Logo, Metric, MetricChart, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeriesSparkline, SettingsList, SettingsListItem, SettingsNote, SettingsRow, SettingsSection, 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, createThemeController, fieldMessageId, formatCalendarDate, initialsOf, localToday, normalizeSeries, parseCalendarDate, periodDelta, seriesAreaPath, seriesExtent, seriesLinePath, seriesMean, seriesScale, toneFor, useTheme, useToast };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.51.19",
3
+ "version": "11.51.21",
4
4
  "description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -216,7 +216,7 @@
216
216
  "peerDependencies": {
217
217
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
218
218
  "@nanostores/react": ">=0.8.4 <3.0.0",
219
- "@tangle-network/brand": "^1.31.4",
219
+ "@tangle-network/brand": "^1.31.8",
220
220
  "@tiptap/core": ">=3.20.4 <4.0.0",
221
221
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
222
222
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
@@ -23,7 +23,7 @@ export interface FileTabsProps {
23
23
  tabs: FileTabData[];
24
24
  activeId?: string;
25
25
  onSelect: (id: string) => void;
26
- onClose: (id: string) => void;
26
+ onClose?: (id: string) => void; // without it the tabs have no close buttons and Delete does nothing
27
27
  /** Accessible name of the tab list. Defaults to "Open files". */
28
28
  label?: string;
29
29
  className?: string;
@@ -84,14 +84,14 @@ export function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open file
84
84
  const at = tabButtons().indexOf(event.target as HTMLButtonElement);
85
85
  const last = tabs.length - 1;
86
86
  const next = ({ ArrowRight: at === last ? 0 : at + 1, ArrowLeft: at === 0 ? last : at - 1, Home: 0, End: last } as Record<string, number>)[event.key];
87
- if (at < 0 || (next === undefined && event.key !== "Delete")) return;
87
+ if (at < 0 || (next === undefined && (event.key !== "Delete" || !onClose))) return;
88
88
  event.preventDefault();
89
89
  if (next !== undefined) {
90
90
  tabButtons()[next]?.focus();
91
91
  onSelect(tabs[next]!.id);
92
92
  return;
93
93
  }
94
- onClose(tabs[at]!.id);
94
+ onClose?.(tabs[at]!.id);
95
95
  // Once the host has re-rendered, keep focus on the tab it opened.
96
96
  requestAnimationFrame(() => (tabButtons().find((tab) => tab.getAttribute("aria-selected") === "true") ?? tabButtons().at(-1))?.focus());
97
97
  };
@@ -136,7 +136,7 @@ export function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open file
136
136
  aria-label={`${tab.name}${hint ? ` in ${hint}` : ""}${tab.dirty ? ", unsaved changes" : ""}`}
137
137
  tabIndex={isActive || (!activeId && tab === tabs[0]) ? 0 : -1}
138
138
  onClick={() => onSelect(tab.id)}
139
- className={cn("flex h-full min-w-0 items-center gap-1.5 rounded-t-lg pl-3 pr-1", focusRingInset)}
139
+ className={cn("flex h-full min-w-0 items-center gap-1.5 rounded-t-lg pl-3", onClose ? "pr-1" : "pr-3", focusRingInset)}
140
140
  >
141
141
  <Icon aria-hidden className="size-3.5 shrink-0 text-muted-foreground" />
142
142
  <span className="flex min-w-0">
@@ -146,7 +146,7 @@ export function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open file
146
146
  {hint && <span className="min-w-0 truncate text-muted-foreground">· {hint}</span>}
147
147
  {tab.dirty && <span aria-hidden className="size-1.5 shrink-0 rounded-full bg-[var(--accent-text)]" />}
148
148
  </button>
149
- <button
149
+ {onClose && <button
150
150
  type="button"
151
151
  tabIndex={-1}
152
152
  aria-label={`Close ${tab.name}`}
@@ -158,7 +158,7 @@ export function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open file
158
158
  )}
159
159
  >
160
160
  <X aria-hidden className="size-3" />
161
- </button>
161
+ </button>}
162
162
  </div>
163
163
  );
164
164
  })}
@@ -192,7 +192,7 @@ export type { ArtifactPaneProps } from "./artifact-pane";
192
192
  export { CodeBlock, CopyButton, InlineCode } from "./code-block";
193
193
  export type { CodeBlockProps, InlineCodeProps } from "./code-block";
194
194
 
195
- export { ThemeToggle, useTheme } from "./theme-toggle";
195
+ export { ThemeToggle, useTheme, createThemeController } from "./theme-toggle";
196
196
 
197
197
  export { Heading } from "./heading";
198
198
  export type { HeadingProps, HeadingVariant } from "./heading";
@@ -282,3 +282,6 @@ export type { ToolbarProps, FilterFieldProps } from "./toolbar";
282
282
 
283
283
  export { ExpandablePanel } from "./expandable-panel";
284
284
  export type { ExpandablePanelProps } from "./expandable-panel";
285
+
286
+ export type { ThemeControllerOptions } from "./theme-toggle";
287
+ export type { TableHeadProps } from "./table";
@@ -87,19 +87,38 @@ const TableRow = React.forwardRef<
87
87
  ));
88
88
  TableRow.displayName = "TableRow";
89
89
 
90
- const TableHead = React.forwardRef<
91
- HTMLTableCellElement,
92
- React.ThHTMLAttributes<HTMLTableCellElement>
93
- >(({ className, ...props }, ref) => (
94
- <th
95
- ref={ref}
96
- className={cn(
97
- tablePresentation.columnHeader,
98
- className,
99
- )}
100
- {...props}
101
- />
102
- ));
90
+ export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
91
+ /** Controlled sort affordance. The caller retains row order and sort policy. */
92
+ sort?: {
93
+ direction?: "asc" | "desc";
94
+ onSort: () => void;
95
+ label?: string;
96
+ buttonClassName?: string;
97
+ };
98
+ }
99
+
100
+ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
101
+ ({ className, children, sort, ...props }, ref) => (
102
+ <th
103
+ ref={ref}
104
+ className={cn(tablePresentation.columnHeader, className)}
105
+ {...props}
106
+ aria-sort={sort ? (sort.direction === "asc" ? "ascending" : sort.direction === "desc" ? "descending" : "none") : props["aria-sort"]}
107
+ >
108
+ {sort ? (
109
+ <button
110
+ type="button"
111
+ onClick={sort.onSort}
112
+ aria-label={sort.label ?? (typeof children === "string" ? `Sort by ${children}` : undefined)}
113
+ className={cn("inline-flex cursor-pointer items-center gap-1 rounded-sm font-inherit text-inherit hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-2", sort.buttonClassName)}
114
+ >
115
+ {children}
116
+ <span aria-hidden="true" className="text-xs">{sort.direction === "asc" ? "↑" : sort.direction === "desc" ? "↓" : "↕"}</span>
117
+ </button>
118
+ ) : children}
119
+ </th>
120
+ ),
121
+ );
103
122
  TableHead.displayName = "TableHead";
104
123
 
105
124
  const TableCell = React.forwardRef<
@@ -15,10 +15,14 @@ const namedModes = [
15
15
  { dark: "agents", light: "agents-light" },
16
16
  ] as const;
17
17
 
18
- // Client-only preference cache also keeps the control usable when storage is
19
- // denied, and lets multiple useTheme consumers share the same preference.
20
- // Server renders ONLY read getServerTheme; no request state is stored here.
21
- let preference: Theme | undefined;
18
+ export interface ThemeControllerOptions {
19
+ storageKey?: string;
20
+ defaultTheme?: Theme;
21
+ /** Persisted spellings, including null to remove a system preference. */
22
+ storageValues?: Partial<Record<Theme, string | null>>;
23
+ /** Product mode attribute; absent mode values remove the attribute. */
24
+ attribute?: { name: string; light?: string; dark?: string };
25
+ }
22
26
 
23
27
  function isTheme(value: unknown): value is Theme {
24
28
  return value === "light" || value === "dark" || value === "system";
@@ -29,72 +33,6 @@ function getSystemTheme(): Mode {
29
33
  return window.matchMedia(systemQuery).matches ? "dark" : "light";
30
34
  }
31
35
 
32
- function getServerTheme(): Theme { return "system"; }
33
- function getServerMode(): Mode { return "dark"; }
34
-
35
- function readInitialTheme(): Theme {
36
- try {
37
- const stored = window.localStorage.getItem("theme");
38
- if (isTheme(stored)) return stored;
39
- } catch { /* Storage is optional, not a prerequisite for changing mode. */ }
40
-
41
- const root = document.documentElement;
42
- const named = root.getAttribute("data-theme");
43
- if (named === "light" || named === "dark") return named;
44
- for (const pair of namedModes) {
45
- if (named === pair.light) return "light";
46
- if (named === pair.dark) return "dark";
47
- }
48
- if (root.classList.contains("light")) return "light";
49
- if (root.classList.contains("dark")) return "dark";
50
- const legacy = root.getAttribute("data-sandbox-theme");
51
- if (legacy === "vault" || legacy === "dawn") return "light";
52
- return "system";
53
- }
54
-
55
- function getTheme(): Theme {
56
- if (typeof window === "undefined") return "system";
57
- return preference ??= readInitialTheme();
58
- }
59
-
60
- function applyMode(mode: Mode) {
61
- const root = document.documentElement;
62
- root.classList.toggle("dark", mode === "dark");
63
- root.classList.toggle("light", mode === "light");
64
- const named = root.getAttribute("data-theme");
65
- if (named === "dark" || named === "light") {
66
- root.setAttribute("data-theme", mode);
67
- } else {
68
- const pair = namedModes.find((pair) => named === pair.dark || named === pair.light);
69
- if (pair) root.setAttribute("data-theme", pair[mode]);
70
- }
71
- }
72
-
73
- function setTheme(next: Theme) {
74
- if (!isTheme(next) || typeof window === "undefined") return;
75
- preference = next;
76
- try {
77
- if (next === "system") window.localStorage.removeItem("theme");
78
- else window.localStorage.setItem("theme", next);
79
- } catch { /* Keep the in-memory preference when storage is unavailable. */ }
80
- applyMode(next === "system" ? getSystemTheme() : next);
81
- window.dispatchEvent(new Event(preferenceEvent));
82
- }
83
-
84
- function subscribeTheme(notify: () => void) {
85
- const onStorage = (event: StorageEvent) => {
86
- if (event.key !== "theme" && event.key !== null) return;
87
- preference = isTheme(event.newValue) ? event.newValue : "system";
88
- notify();
89
- };
90
- window.addEventListener(preferenceEvent, notify);
91
- window.addEventListener("storage", onStorage);
92
- return () => {
93
- window.removeEventListener(preferenceEvent, notify);
94
- window.removeEventListener("storage", onStorage);
95
- };
96
- }
97
-
98
36
  function subscribeSystem(notify: () => void) {
99
37
  if (!window.matchMedia) return () => {};
100
38
  const media = window.matchMedia(systemQuery);
@@ -102,20 +40,109 @@ function subscribeSystem(notify: () => void) {
102
40
  return () => media.removeEventListener("change", notify);
103
41
  }
104
42
 
105
- export function useTheme() {
106
- const theme = useSyncExternalStore(subscribeTheme, getTheme, getServerTheme);
107
- const system = useSyncExternalStore(subscribeSystem, getSystemTheme, getServerMode);
108
- const resolvedTheme = theme === "system" ? system : theme;
109
- useEffect(() => {
110
- // Hydration first renders the stable server snapshot. Apply the real client
111
- // preference, not that placeholder, so we never repaint a persisted light
112
- // page dark while React is catching up.
113
- const selected = getTheme();
114
- applyMode(selected === "system" ? getSystemTheme() : selected);
115
- }, [resolvedTheme]);
116
- return { theme, setTheme, resolvedTheme };
43
+ /** Create once at module scope. Each product owns its persisted names, not a second theme engine. */
44
+ export function createThemeController({ storageKey = "theme", defaultTheme = "system", storageValues, attribute }: ThemeControllerOptions = {}) {
45
+ const values = { light: "light", dark: "dark", system: null, ...storageValues };
46
+ const eventName = `${preferenceEvent}:${storageKey}`;
47
+ // Client-only cache keeps storage-denied controls usable. SSR never writes it.
48
+ let preference: Theme | undefined;
49
+ let persisted: string | null | undefined;
50
+ const decode = (value: string | null): Theme =>
51
+ (Object.keys(values) as Theme[]).find(theme => value !== null && values[theme] === value) ??
52
+ (value === "system" && values.system === null ? "system" : defaultTheme);
53
+ const serverTheme = () => defaultTheme;
54
+ const serverMode = (): Mode => defaultTheme === "system" ? "dark" : defaultTheme;
55
+ function readInitialTheme(): Theme {
56
+ try {
57
+ const stored = window.localStorage.getItem(storageKey);
58
+ persisted = stored;
59
+ if (stored !== null && (Object.values(values).includes(stored) || (stored === "system" && values.system === null))) return decode(stored);
60
+ } catch { /* Storage is optional. */ }
61
+ if (attribute || defaultTheme !== "system") return defaultTheme;
62
+ const root = document.documentElement;
63
+ const named = root.getAttribute("data-theme");
64
+ if (named === "light" || named === "dark") return named;
65
+ for (const pair of namedModes) {
66
+ if (named === pair.light) return "light";
67
+ if (named === pair.dark) return "dark";
68
+ }
69
+ if (root.classList.contains("light")) return "light";
70
+ if (root.classList.contains("dark")) return "dark";
71
+ const legacy = root.getAttribute("data-sandbox-theme");
72
+ return legacy === "vault" || legacy === "dawn" ? "light" : defaultTheme;
73
+ }
74
+ function getTheme(): Theme {
75
+ if (typeof window === "undefined") return defaultTheme;
76
+ if (preference === undefined) return preference = readInitialTheme();
77
+ try {
78
+ const stored = window.localStorage.getItem(storageKey);
79
+ if (stored !== persisted) { persisted = stored; preference = decode(stored); }
80
+ } catch { /* Keep the last choice when storage becomes unavailable. */ }
81
+ return preference;
82
+ }
83
+ /** Apply a temporary page mode without overwriting the user's preference. */
84
+ function applyTheme(theme: Theme = getTheme()) {
85
+ if (typeof document === "undefined") return;
86
+ const mode = theme === "system" ? getSystemTheme() : theme;
87
+ const root = document.documentElement;
88
+ root.classList.toggle("dark", mode === "dark");
89
+ root.classList.toggle("light", !attribute && mode === "light");
90
+ if (attribute) {
91
+ const value = attribute[mode];
92
+ if (value === undefined) root.removeAttribute(attribute.name);
93
+ else root.setAttribute(attribute.name, value);
94
+ } else {
95
+ const named = root.getAttribute("data-theme");
96
+ if (named === "dark" || named === "light") root.setAttribute("data-theme", mode);
97
+ else {
98
+ const pair = namedModes.find(pair => named === pair.dark || named === pair.light);
99
+ if (pair) root.setAttribute("data-theme", pair[mode]);
100
+ }
101
+ }
102
+ }
103
+ function setTheme(next: Theme) {
104
+ if (!isTheme(next) || typeof window === "undefined") return;
105
+ preference = next;
106
+ try {
107
+ const value = values[next];
108
+ if (value === null) window.localStorage.removeItem(storageKey);
109
+ else window.localStorage.setItem(storageKey, value);
110
+ persisted = value;
111
+ } catch { /* Keep the in-memory preference when storage is unavailable. */ }
112
+ applyTheme(next);
113
+ window.dispatchEvent(new CustomEvent(eventName, { detail: next }));
114
+ }
115
+ function subscribeTheme(notify: () => void) {
116
+ const onStorage = (event: StorageEvent) => {
117
+ if (event.key !== storageKey && event.key !== null) return;
118
+ persisted = event.newValue;
119
+ preference = decode(event.newValue);
120
+ notify();
121
+ };
122
+ const onPreference = (event: Event) => {
123
+ const next = (event as CustomEvent<Theme>).detail;
124
+ if (isTheme(next)) preference = next;
125
+ notify();
126
+ };
127
+ window.addEventListener(eventName, onPreference);
128
+ window.addEventListener("storage", onStorage);
129
+ return () => {
130
+ window.removeEventListener(eventName, onPreference);
131
+ window.removeEventListener("storage", onStorage);
132
+ };
133
+ }
134
+ function useTheme() {
135
+ const theme = useSyncExternalStore(subscribeTheme, getTheme, serverTheme);
136
+ const system = useSyncExternalStore(subscribeSystem, getSystemTheme, serverMode);
137
+ const resolvedTheme = theme === "system" ? system : theme;
138
+ useEffect(() => { applyTheme(getTheme()); }, [resolvedTheme]);
139
+ return { theme, setTheme, resolvedTheme };
140
+ }
141
+ return { useTheme, getTheme, setTheme, applyTheme };
117
142
  }
118
143
 
144
+ export const { useTheme } = createThemeController();
145
+
119
146
  const iconClass = "h-4 w-4";
120
147
 
121
148
  function SunIcon() {