@tangle-network/ui 11.27.0 → 11.28.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.28.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5f50a1b: Put `Button`, `Input`, `Textarea` and `SelectTrigger` on one control scale: `sm` 32px/12px text, `md` (default) 36px/14px, `lg` 44px/16px, read from Brand's control tokens with pixel fallbacks. Visible changes: a default `Input` is now 36px (was 44px) with 12px side padding (was 16px), matching the default `Button` and `SelectTrigger`; `Input` `sm` is 32px (was 36px) and `lg` 44px (was 48px); `SelectTrigger` uses the `rounded-lg` corner at md and gains `sm|md|lg`; `Button` `lg` text is 16px (was 14px); fields render 16px text on a coarse pointer so iOS does not zoom on focus. `Button` adds `md`, `icon-sm` (32px) and `icon-lg` (44px). New `HelpText` (`tone="hint" | "error"`, 12px) renders `Input` and `Textarea` hints and errors (were 14px); `Label` reads `--font-size-label`. `compact`, `touch` and `xl` are unchanged.
8
+
3
9
  ## 11.27.0
4
10
 
5
11
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -53,6 +53,7 @@ import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGr
53
53
  import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from "./primitives/popover.js";
54
54
  import { Slider } from "./primitives/slider.js";
55
55
  import { Input, InputProps, Textarea, TextareaProps } from "./primitives/input.js";
56
+ import { HelpText, HelpTextProps } from "./primitives/help-text.js";
56
57
  import { Badge, BadgeProps, badgeVariants } from "./primitives/badge.js";
57
58
  import { Avatar, AvatarFallback, AvatarImage } from "./primitives/avatar.js";
58
59
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./primitives/dropdown-menu.js";
@@ -119,4 +120,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
119
120
  import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
120
121
  import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
121
122
  import "./redaction.js";
122
- 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, AppendUserMessageOptions, ApplySdkEventOptions, 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, 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, CustomToolRenderer, 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, 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 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, 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, 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, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, 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, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, 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 };
123
+ 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, AppendUserMessageOptions, ApplySdkEventOptions, 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, 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, CustomToolRenderer, 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 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, 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, 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, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, 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, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, 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
@@ -9,6 +9,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./primitive
9
9
  import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./primitives/context-menu.js";
10
10
  import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from "./primitives/popover.js";
11
11
  import { Slider } from "./primitives/slider.js";
12
+ import { HelpText } from "./primitives/help-text.js";
12
13
  import { Label } from "./primitives/label.js";
13
14
  import { Input, Textarea } from "./primitives/input.js";
14
15
  import { Badge, badgeVariants } from "./primitives/badge.js";
@@ -103,4 +104,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
103
104
  import { copyText } from "./utils/copy-text.js";
104
105
  import { timeAgo } from "./utils/time-ago.js";
105
106
  import { RedactedDocument } from "./redaction/redacted-document.js";
106
- export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, 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, 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, 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, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, 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, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, 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 };
107
+ export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, 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, 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, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, 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, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, 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 };
@@ -2,10 +2,42 @@ import { focusField } from "./focus.js";
2
2
  import { cn } from "./utils.js";
3
3
  //#region src/lib/control-presentation.ts
4
4
  const controlMotion = "duration-[var(--duration-fast)] ease-[var(--ease-standard)] motion-reduce:transition-none motion-reduce:duration-0";
5
+ const controlHeight = {
6
+ sm: "h-[var(--control-height-sm,2rem)]",
7
+ md: "h-[var(--control-height,2.25rem)]",
8
+ lg: "h-[var(--control-height-lg,2.75rem)]"
9
+ };
10
+ const controlText = {
11
+ sm: "text-[length:var(--control-text-sm,0.75rem)]",
12
+ md: "text-[length:var(--control-text-md,0.875rem)]",
13
+ lg: "text-[length:var(--control-text-lg,1rem)]"
14
+ };
15
+ const controlRadius = {
16
+ sm: "rounded-md",
17
+ md: "rounded-lg",
18
+ lg: "rounded-lg"
19
+ };
20
+ /** Square icon-only controls on the same heights. */
21
+ const controlSquare = {
22
+ sm: "size-[var(--control-height-sm,2rem)]",
23
+ md: "size-[var(--control-height,2.25rem)]",
24
+ lg: "size-[var(--control-height-lg,2.75rem)]"
25
+ };
26
+ /**
27
+ * Field text never drops below 16px on a coarse pointer: iOS Safari zooms the
28
+ * page on focus for anything smaller.
29
+ */
30
+ const fieldTouchText = "pointer-coarse:text-base";
31
+ /** Field sizes: the control scale with field padding. */
32
+ const fieldSizes = {
33
+ sm: cn(controlHeight.sm, controlText.sm, controlRadius.sm, "px-2.5"),
34
+ md: cn(controlHeight.md, controlText.md, controlRadius.md, "px-3"),
35
+ lg: cn(controlHeight.lg, controlText.lg, controlRadius.lg, "px-4")
36
+ };
5
37
  const controlSizes = {
6
38
  compact: "h-[var(--control-height)] px-3 py-1 text-sm",
7
39
  touch: "h-11 min-h-11 min-w-11 px-4 py-2 text-base"
8
40
  };
9
41
  const fieldPresentation = cn(focusField, controlMotion, "min-w-0 bg-[var(--bg-input)] text-foreground", "aria-invalid:border-[var(--surface-danger-border)] aria-invalid:hover:border-[var(--surface-danger-border)] aria-invalid:focus:border-[var(--focus-border-danger)] aria-invalid:focus:ring-[var(--focus-halo-danger)]", "autofill:shadow-[inset_0_0_0_1000px_var(--bg-input)] autofill:[-webkit-text-fill-color:var(--text-primary)] autofill:caret-[var(--text-primary)]");
10
42
  //#endregion
11
- export { controlMotion, controlSizes, fieldPresentation };
43
+ export { controlHeight, controlMotion, controlRadius, controlSizes, controlSquare, controlText, fieldPresentation, fieldSizes, fieldTouchText };
@@ -2,11 +2,12 @@ import * as React from "react";
2
2
  //#region src/primitives/button.d.ts
3
3
  declare const buttonVariants: (props?: ({
4
4
  variant?: "default" | "destructive" | "ghost" | "link" | "outline" | "sandbox" | "secondary" | null | undefined;
5
- size?: "compact" | "default" | "icon" | "lg" | "sm" | "touch" | "xl" | null | undefined;
5
+ size?: "compact" | "default" | "icon" | "icon-lg" | "icon-sm" | "lg" | "md" | "sm" | "touch" | "xl" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
8
8
  variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "sandbox" | null;
9
- size?: "default" | "sm" | "lg" | "xl" | "icon" | "compact" | "touch" | null;
9
+ /** sm 32px, default/md 36px (`--control-height`), lg 44px; icon sizes are squares on the same heights. */
10
+ size?: "default" | "md" | "sm" | "lg" | "xl" | "icon" | "icon-sm" | "icon-lg" | "compact" | "touch" | null;
10
11
  asChild?: boolean;
11
12
  loading?: boolean;
12
13
  children?: React.ReactNode;
@@ -1,12 +1,12 @@
1
1
  import { focusRing } from "../lib/focus.js";
2
2
  import { cn } from "../lib/utils.js";
3
- import { controlMotion, controlSizes } from "../lib/control-presentation.js";
3
+ import { controlHeight, controlMotion, controlRadius, controlSizes, controlSquare, controlText } from "../lib/control-presentation.js";
4
4
  import { Slot } from "@radix-ui/react-slot";
5
5
  import { cva } from "class-variance-authority";
6
6
  import * as React from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/primitives/button.tsx
9
- const buttonVariants = cva(`inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium text-sm transition-[color,background-color,border-color,box-shadow,transform,scale] ${controlMotion} ${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
9
+ const buttonVariants = cva(`inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium transition-[color,background-color,border-color,box-shadow,transform,scale] ${controlMotion} ${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`, {
10
10
  variants: {
11
11
  variant: {
12
12
  default: "bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97]",
@@ -18,11 +18,14 @@ const buttonVariants = cva(`inline-flex items-center justify-center gap-2 whites
18
18
  sandbox: "bg-[var(--btn-primary-bg)] text-[var(--btn-primary-text)] border border-[var(--border-accent)] hover:bg-[var(--btn-primary-hover)] active:scale-[0.97]"
19
19
  },
20
20
  size: {
21
- default: "h-[var(--control-height)] px-4 py-2",
22
- sm: "h-8 rounded-md px-3 text-xs",
23
- lg: "h-11 rounded-lg px-7 text-sm",
21
+ default: `${controlHeight.md} ${controlText.md} ${controlRadius.md} px-4 py-2`,
22
+ md: `${controlHeight.md} ${controlText.md} ${controlRadius.md} px-4 py-2`,
23
+ sm: `${controlHeight.sm} ${controlText.sm} ${controlRadius.sm} px-3`,
24
+ lg: `${controlHeight.lg} ${controlText.lg} ${controlRadius.lg} px-7`,
24
25
  xl: "h-13 rounded-xl px-9 text-base",
25
- icon: "h-[var(--control-height)] w-[var(--control-height)]",
26
+ icon: `${controlSquare.md} ${controlText.md} ${controlRadius.md}`,
27
+ "icon-sm": `${controlSquare.sm} ${controlText.sm} ${controlRadius.sm}`,
28
+ "icon-lg": `${controlSquare.lg} ${controlText.lg} ${controlRadius.lg}`,
26
29
  ...controlSizes
27
30
  }
28
31
  },
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ //#region src/primitives/help-text.d.ts
3
+ export interface HelpTextProps extends React.HTMLAttributes<HTMLParagraphElement> {
4
+ /** `hint` describes a field; `error` reports what is wrong with its value. */
5
+ tone?: "hint" | "error";
6
+ }
7
+ /**
8
+ * Text under a field. One size (`--font-size-help`, 12px) for every field, so
9
+ * forms across products read the same. Point the field's `aria-describedby` at
10
+ * its `id`; Input and Textarea do this for their `hint` and `error` props.
11
+ */
12
+ declare const HelpText: React.ForwardRefExoticComponent<HelpTextProps & React.RefAttributes<HTMLParagraphElement>>;
13
+ //#endregion
14
+ export { HelpText };
@@ -0,0 +1,18 @@
1
+ import { cn } from "../lib/utils.js";
2
+ import * as React from "react";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/primitives/help-text.tsx
5
+ /**
6
+ * Text under a field. One size (`--font-size-help`, 12px) for every field, so
7
+ * forms across products read the same. Point the field's `aria-describedby` at
8
+ * its `id`; Input and Textarea do this for their `hint` and `error` props.
9
+ */
10
+ const HelpText = React.forwardRef(({ className, tone = "hint", ...props }, ref) => /* @__PURE__ */ jsx("p", {
11
+ ref,
12
+ "data-tone": tone,
13
+ className: cn("text-[length:var(--font-size-help,0.75rem)] leading-4", tone === "error" ? "font-medium text-[var(--surface-danger-text)]" : "text-[var(--text-dim)]", className),
14
+ ...props
15
+ }));
16
+ HelpText.displayName = "HelpText";
17
+ //#endregion
18
+ export { HelpText };
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  //#region src/primitives/input.d.ts
4
4
  declare const inputVariants: (props?: ({
5
5
  variant?: "default" | "error" | "sandbox" | null | undefined;
6
- size?: "compact" | "default" | "lg" | "sm" | "touch" | null | undefined;
6
+ size?: "compact" | "default" | "lg" | "md" | "sm" | "touch" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">, VariantProps<typeof inputVariants> {
9
9
  label?: string;
@@ -14,8 +14,8 @@ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttri
14
14
  export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
15
15
  /** Both values render the same field. */
16
16
  variant?: "default" | "sandbox";
17
- /** Opt in to density/typing presentation without changing the default 120px well. */
18
- size?: "default" | "compact" | "touch";
17
+ /** Text, padding and corner from the control scale; the 120px well is unchanged. compact and touch remain. */
18
+ size?: "default" | "sm" | "md" | "lg" | "compact" | "touch";
19
19
  label?: string;
20
20
  error?: string;
21
21
  hint?: string;
@@ -1,13 +1,14 @@
1
1
  "use client";
2
2
  import { focusFieldInvalid } from "../lib/focus.js";
3
3
  import { cn } from "../lib/utils.js";
4
- import { controlSizes, fieldPresentation } from "../lib/control-presentation.js";
4
+ import { controlRadius, controlSizes, controlText, fieldPresentation, fieldSizes, fieldTouchText } from "../lib/control-presentation.js";
5
+ import { HelpText } from "./help-text.js";
5
6
  import { Label } from "./label.js";
6
7
  import { cva } from "class-variance-authority";
7
8
  import * as React from "react";
8
9
  import { jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/primitives/input.tsx
10
- const inputVariants = cva(cn("flex w-full rounded-lg border px-4 py-2 text-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium file:text-sm", fieldPresentation), {
11
+ const inputVariants = cva(cn("flex w-full border py-1 placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium file:text-sm", fieldPresentation, fieldTouchText), {
11
12
  variants: {
12
13
  variant: {
13
14
  default: "",
@@ -15,10 +16,12 @@ const inputVariants = cva(cn("flex w-full rounded-lg border px-4 py-2 text-sm pl
15
16
  error: focusFieldInvalid
16
17
  },
17
18
  size: {
18
- default: "h-11",
19
- sm: "h-9 px-3",
20
- lg: "h-12 px-5",
21
- ...controlSizes
19
+ default: fieldSizes.md,
20
+ md: fieldSizes.md,
21
+ sm: fieldSizes.sm,
22
+ lg: fieldSizes.lg,
23
+ compact: cn(fieldSizes.md, controlSizes.compact),
24
+ touch: cn(fieldSizes.md, controlSizes.touch)
22
25
  }
23
26
  },
24
27
  defaultVariants: {
@@ -57,14 +60,13 @@ const Input = React.forwardRef(({ className, type, variant, size, label, error,
57
60
  children: label
58
61
  }),
59
62
  input,
60
- error && /* @__PURE__ */ jsx("p", {
63
+ error && /* @__PURE__ */ jsx(HelpText, {
61
64
  id: errorId,
62
- className: "text-[var(--surface-danger-text)] text-sm font-medium",
65
+ tone: "error",
63
66
  children: error
64
67
  }),
65
- hint && /* @__PURE__ */ jsx("p", {
68
+ hint && /* @__PURE__ */ jsx(HelpText, {
66
69
  id: hintId,
67
- className: "text-[var(--text-dim)] text-sm",
68
70
  children: hint
69
71
  })
70
72
  ]
@@ -78,7 +80,7 @@ const Textarea = React.forwardRef(({ className, variant: _variant, size = "defau
78
80
  const errorId = error ? `${textareaId}-error` : void 0;
79
81
  const textarea = /* @__PURE__ */ jsx("textarea", {
80
82
  id: textareaId,
81
- className: cn("flex min-h-[120px] w-full resize-y rounded-lg border px-4 py-3 text-sm", "placeholder:text-[var(--text-dim)]", "disabled:cursor-not-allowed disabled:opacity-50", fieldPresentation, error && "border-[var(--surface-danger-border)] hover:border-[var(--surface-danger-border)] focus:border-[var(--focus-border-danger)] focus:ring-[var(--focus-halo-danger)]", size === "compact" && "min-h-24 px-3 py-2", size === "touch" && "text-base", className),
83
+ className: cn("flex min-h-[120px] w-full resize-y border px-3 py-2", "placeholder:text-[var(--text-dim)]", "disabled:cursor-not-allowed disabled:opacity-50", fieldPresentation, fieldTouchText, size === "sm" ? cn(controlText.sm, controlRadius.sm, "px-2.5") : size === "lg" ? cn(controlText.lg, controlRadius.lg, "px-4 py-3") : cn(controlText.md, controlRadius.md), error && "border-[var(--surface-danger-border)] hover:border-[var(--surface-danger-border)] focus:border-[var(--focus-border-danger)] focus:ring-[var(--focus-halo-danger)]", size === "compact" && "min-h-24 px-3 py-2", size === "touch" && "text-base", className),
82
84
  ref,
83
85
  ...props,
84
86
  "aria-describedby": descriptionIds(describedBy, hintId, errorId),
@@ -94,14 +96,13 @@ const Textarea = React.forwardRef(({ className, variant: _variant, size = "defau
94
96
  children: label
95
97
  }),
96
98
  textarea,
97
- error && /* @__PURE__ */ jsx("p", {
99
+ error && /* @__PURE__ */ jsx(HelpText, {
98
100
  id: errorId,
99
- className: "text-[var(--surface-danger-text)] text-sm",
101
+ tone: "error",
100
102
  children: error
101
103
  }),
102
- hint && /* @__PURE__ */ jsx("p", {
104
+ hint && /* @__PURE__ */ jsx(HelpText, {
103
105
  id: hintId,
104
- className: "text-[var(--text-dim)] text-sm",
105
106
  children: hint
106
107
  })
107
108
  ]
@@ -5,7 +5,7 @@ import * as React from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import * as LabelPrimitive from "@radix-ui/react-label";
7
7
  //#region src/primitives/label.tsx
8
- const labelVariants = cva("font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
8
+ const labelVariants = cva("font-medium text-[length:var(--font-size-label,0.875rem)] leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70");
9
9
  const Label = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(LabelPrimitive.Root, {
10
10
  ref,
11
11
  className: cn(labelVariants(), className),
@@ -5,8 +5,8 @@ declare const Select: React.FC<SelectPrimitive.SelectProps>;
5
5
  declare const SelectGroup: React.ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & React.RefAttributes<HTMLDivElement>>;
6
6
  declare const SelectValue: React.ForwardRefExoticComponent<SelectPrimitive.SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
7
7
  declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & {
8
- /** Default remains 36px; compact follows density, touch is at least 44px. */
9
- size?: "default" | "compact" | "touch";
8
+ /** The shared control scale: sm 32px, default/md 36px, lg 44px. compact follows density, touch is at least 44px. */
9
+ size?: "default" | "sm" | "md" | "lg" | "compact" | "touch";
10
10
  } & React.RefAttributes<HTMLButtonElement>>;
11
11
  declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
12
12
  declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
- import { controlSizes, fieldPresentation } from "../lib/control-presentation.js";
3
+ import { controlSizes, fieldPresentation, fieldSizes, fieldTouchText } from "../lib/control-presentation.js";
4
4
  import * as React from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  import { Check, ChevronDown, ChevronUp } from "lucide-react";
@@ -11,7 +11,7 @@ const SelectGroup = SelectPrimitive.Group;
11
11
  const SelectValue = SelectPrimitive.Value;
12
12
  const SelectTrigger = React.forwardRef(({ className, children, size = "default", ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Trigger, {
13
13
  ref,
14
- className: cn("flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border px-3 py-2 text-sm shadow-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", fieldPresentation, "data-[placeholder]:text-[var(--text-dim)] [&>span]:min-w-0 [&>span]:text-left", "data-[state=open]:border-primary data-[state=open]:bg-[var(--accent-surface-strong)] [&[data-state=open]>svg]:rotate-180 [&>svg]:transition-transform", size !== "default" && controlSizes[size], className),
14
+ className: cn("flex w-full items-center justify-between whitespace-nowrap border py-1 shadow-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", fieldPresentation, fieldTouchText, "data-[placeholder]:text-[var(--text-dim)] [&>span]:min-w-0 [&>span]:text-left", "data-[state=open]:border-primary data-[state=open]:bg-[var(--accent-surface-strong)] [&[data-state=open]>svg]:rotate-180 [&>svg]:transition-transform", size === "compact" || size === "touch" ? cn(fieldSizes.md, controlSizes[size]) : fieldSizes[size === "default" ? "md" : size], className),
15
15
  ...props,
16
16
  children: [children, /* @__PURE__ */ jsx(SelectPrimitive.Icon, {
17
17
  asChild: true,
@@ -10,6 +10,7 @@ import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGr
10
10
  import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from "./primitives/popover.js";
11
11
  import { Slider } from "./primitives/slider.js";
12
12
  import { Input, InputProps, Textarea, TextareaProps } from "./primitives/input.js";
13
+ import { HelpText, HelpTextProps } from "./primitives/help-text.js";
13
14
  import { Badge, BadgeProps, badgeVariants } from "./primitives/badge.js";
14
15
  import { Avatar, AvatarFallback, AvatarImage } from "./primitives/avatar.js";
15
16
  import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./primitives/dropdown-menu.js";
@@ -47,4 +48,4 @@ import { Chip, ChipProps } from "./primitives/chip.js";
47
48
  import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
48
49
  import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
49
50
  import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
50
- 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, 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, 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, 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, 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, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
51
+ 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, 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, 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, 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, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
@@ -7,6 +7,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./primitive
7
7
  import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./primitives/context-menu.js";
8
8
  import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger } from "./primitives/popover.js";
9
9
  import { Slider } from "./primitives/slider.js";
10
+ import { HelpText } from "./primitives/help-text.js";
10
11
  import { Label } from "./primitives/label.js";
11
12
  import { Input, Textarea } from "./primitives/input.js";
12
13
  import { Badge, badgeVariants } from "./primitives/badge.js";
@@ -47,4 +48,4 @@ import { IconTile, initialsOf } from "./primitives/icon-tile.js";
47
48
  import { Metric, MetricStrip } from "./primitives/metric-strip.js";
48
49
  import { FilterField, Toolbar } from "./primitives/toolbar.js";
49
50
  import { Logo, TangleKnot } from "@tangle-network/brand";
50
- export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, 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, 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, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, 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, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
51
+ export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, 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, 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, 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, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/ui",
3
- "version": "11.27.0",
3
+ "version": "11.28.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",
@@ -149,7 +149,7 @@
149
149
  "peerDependencies": {
150
150
  "@hocuspocus/provider": ">=2.15.0 <5.0.0",
151
151
  "@nanostores/react": ">=0.8.4 <3.0.0",
152
- "@tangle-network/brand": "^1.17.1",
152
+ "@tangle-network/brand": "^1.18.0",
153
153
  "@tiptap/core": ">=3.20.4 <4.0.0",
154
154
  "@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
155
155
  "@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
@@ -6,8 +6,55 @@ import { cn } from "./utils";
6
6
  export const controlMotion =
7
7
  "duration-[var(--duration-fast)] ease-[var(--ease-standard)] motion-reduce:transition-none motion-reduce:duration-0";
8
8
 
9
- // Explicit opt-in sizes. Legacy defaults and sm/lg/icon sizes stay unchanged.
10
- // Compact follows the consumer's density token; touch does not shrink with it.
9
+ /**
10
+ * The control scale. Button, Input, Textarea and SelectTrigger take the same
11
+ * `sm | md | lg` sizes and read the same Brand tokens, so any row of controls
12
+ * at one size shares a height, a text size and a corner. md follows the
13
+ * `--control-height` density knob. The fallbacks keep an app on an older
14
+ * Brand stylesheet at the same pixels.
15
+ */
16
+ export type ControlSize = "sm" | "md" | "lg";
17
+
18
+ export const controlHeight = {
19
+ sm: "h-[var(--control-height-sm,2rem)]",
20
+ md: "h-[var(--control-height,2.25rem)]",
21
+ lg: "h-[var(--control-height-lg,2.75rem)]",
22
+ } as const satisfies Record<ControlSize, string>;
23
+
24
+ export const controlText = {
25
+ sm: "text-[length:var(--control-text-sm,0.75rem)]",
26
+ md: "text-[length:var(--control-text-md,0.875rem)]",
27
+ lg: "text-[length:var(--control-text-lg,1rem)]",
28
+ } as const satisfies Record<ControlSize, string>;
29
+
30
+ export const controlRadius = {
31
+ sm: "rounded-md",
32
+ md: "rounded-lg",
33
+ lg: "rounded-lg",
34
+ } as const satisfies Record<ControlSize, string>;
35
+
36
+ /** Square icon-only controls on the same heights. */
37
+ export const controlSquare = {
38
+ sm: "size-[var(--control-height-sm,2rem)]",
39
+ md: "size-[var(--control-height,2.25rem)]",
40
+ lg: "size-[var(--control-height-lg,2.75rem)]",
41
+ } as const satisfies Record<ControlSize, string>;
42
+
43
+ /**
44
+ * Field text never drops below 16px on a coarse pointer: iOS Safari zooms the
45
+ * page on focus for anything smaller.
46
+ */
47
+ export const fieldTouchText = "pointer-coarse:text-base";
48
+
49
+ /** Field sizes: the control scale with field padding. */
50
+ export const fieldSizes = {
51
+ sm: cn(controlHeight.sm, controlText.sm, controlRadius.sm, "px-2.5"),
52
+ md: cn(controlHeight.md, controlText.md, controlRadius.md, "px-3"),
53
+ lg: cn(controlHeight.lg, controlText.lg, controlRadius.lg, "px-4"),
54
+ } as const satisfies Record<ControlSize, string>;
55
+
56
+ // Explicit opt-in sizes kept for existing callers. Compact follows the
57
+ // consumer's density token (md); touch is lg with a guaranteed 44px minimum.
11
58
  export const controlSizes = {
12
59
  compact: "h-[var(--control-height)] px-3 py-1 text-sm",
13
60
  touch: "h-11 min-h-11 min-w-11 px-4 py-2 text-base",
@@ -1,12 +1,15 @@
1
1
  import { Slot } from "@radix-ui/react-slot";
2
2
  import { cva } from "class-variance-authority";
3
3
  import * as React from "react";
4
- import { controlMotion, controlSizes } from "../lib/control-presentation";
4
+ import { controlHeight, controlMotion, controlRadius, controlSizes, controlSquare, controlText } from "../lib/control-presentation";
5
5
  import { focusRing } from "../lib/focus";
6
6
  import { cn } from "../lib/utils";
7
7
 
8
+ // Sizes follow the shared control scale (control-presentation.ts): sm, md and
9
+ // lg match Input, Textarea and SelectTrigger at the same size. xl is a display
10
+ // call to action outside that scale.
8
11
  const buttonVariants = cva(
9
- `inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium text-sm transition-[color,background-color,border-color,box-shadow,transform,scale] ${controlMotion} ${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,
12
+ `inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium transition-[color,background-color,border-color,box-shadow,transform,scale] ${controlMotion} ${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0`,
10
13
  {
11
14
  variants: {
12
15
  variant: {
@@ -25,11 +28,14 @@ const buttonVariants = cva(
25
28
  "bg-[var(--btn-primary-bg)] text-[var(--btn-primary-text)] border border-[var(--border-accent)] hover:bg-[var(--btn-primary-hover)] active:scale-[0.97]",
26
29
  },
27
30
  size: {
28
- default: "h-[var(--control-height)] px-4 py-2",
29
- sm: "h-8 rounded-md px-3 text-xs",
30
- lg: "h-11 rounded-lg px-7 text-sm",
31
+ default: `${controlHeight.md} ${controlText.md} ${controlRadius.md} px-4 py-2`,
32
+ md: `${controlHeight.md} ${controlText.md} ${controlRadius.md} px-4 py-2`,
33
+ sm: `${controlHeight.sm} ${controlText.sm} ${controlRadius.sm} px-3`,
34
+ lg: `${controlHeight.lg} ${controlText.lg} ${controlRadius.lg} px-7`,
31
35
  xl: "h-13 rounded-xl px-9 text-base",
32
- icon: "h-[var(--control-height)] w-[var(--control-height)]",
36
+ icon: `${controlSquare.md} ${controlText.md} ${controlRadius.md}`,
37
+ "icon-sm": `${controlSquare.sm} ${controlText.sm} ${controlRadius.sm}`,
38
+ "icon-lg": `${controlSquare.lg} ${controlText.lg} ${controlRadius.lg}`,
33
39
  ...controlSizes,
34
40
  },
35
41
  },
@@ -43,7 +49,8 @@ const buttonVariants = cva(
43
49
  export interface ButtonProps
44
50
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
45
51
  variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "sandbox" | null;
46
- size?: "default" | "sm" | "lg" | "xl" | "icon" | "compact" | "touch" | null;
52
+ /** sm 32px, default/md 36px (`--control-height`), lg 44px; icon sizes are squares on the same heights. */
53
+ size?: "default" | "md" | "sm" | "lg" | "xl" | "icon" | "icon-sm" | "icon-lg" | "compact" | "touch" | null;
47
54
  asChild?: boolean;
48
55
  loading?: boolean;
49
56
  children?: React.ReactNode;
@@ -1,7 +1,9 @@
1
1
  import * as React from "react";
2
2
  import type { Meta, StoryObj } from "@storybook/react";
3
3
  import { Button } from "./button";
4
+ import { HelpText } from "./help-text";
4
5
  import { Input, Textarea } from "./input";
6
+ import { Label } from "./label";
5
7
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
6
8
  import { Metric, MetricStrip } from "./metric-strip";
7
9
 
@@ -11,7 +13,7 @@ function Contract() {
11
13
  return (
12
14
  <main className="mx-auto max-w-5xl space-y-6 bg-background p-4 text-foreground" data-presentation-contract>
13
15
  <h1 className="text-xl font-semibold">Field and metric presentation</h1>
14
- {(["default", "compact", "touch"] as const).map((size) => (
16
+ {(["sm", "md", "lg", "compact", "touch"] as const).map((size) => (
15
17
  <section key={size} data-size-row={size} className="space-y-2">
16
18
  <h2 className="font-semibold">{size}</h2>
17
19
  <div className="flex flex-wrap items-end gap-3">
@@ -24,10 +26,17 @@ function Contract() {
24
26
  </SelectContent>
25
27
  </Select>
26
28
  <Button size={size} onClick={() => setActions((value) => value + 1)}>Save</Button>
29
+ <Button size={size} variant="outline">Cancel</Button>
27
30
  </div>
28
31
  <Textarea size={size} aria-label={`${size} notes`} placeholder="Details" />
29
32
  </section>
30
33
  ))}
34
+ <section className="max-w-sm space-y-1.5" data-form-text>
35
+ <Label htmlFor="workspace-name">Workspace name</Label>
36
+ <Input id="workspace-name" aria-describedby="workspace-name-hint" placeholder="Acme" />
37
+ <HelpText id="workspace-name-hint">Shown to everyone you invite.</HelpText>
38
+ <HelpText tone="error">This name is already taken.</HelpText>
39
+ </section>
31
40
  <section className="space-y-2" data-state-controls>
32
41
  <h2 className="font-semibold">States and actions</h2>
33
42
  <Input label="Email" autoComplete="email" name="email" defaultValue="reader@example.test" />
@@ -1,7 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { cleanup, render, screen } from "@testing-library/react";
3
3
  import { afterEach, describe, expect, it } from "vitest";
4
- import { controlMotion, fieldPresentation } from "../lib/control-presentation";
4
+ import { controlHeight, controlText, controlMotion, fieldPresentation } from "../lib/control-presentation";
5
+ import { HelpText } from "./help-text";
6
+ import { Label } from "./label";
5
7
  import { Button, buttonVariants } from "./button";
6
8
  import { Input, Textarea } from "./input";
7
9
  import { Select, SelectTrigger, SelectValue } from "./select";
@@ -21,16 +23,39 @@ describe("shared control presentation", () => {
21
23
  expect(controlMotion).toContain("motion-reduce:duration-0");
22
24
  });
23
25
 
24
- it("keeps original defaults and aligns only explicit compact/touch sizes", () => {
25
- const { rerender } = render(<><Input aria-label="Input" /><Textarea aria-label="Textarea" /><Button>Action</Button><Select><SelectTrigger aria-label="Select"><SelectValue /></SelectTrigger></Select></>);
26
- expect(screen.getByLabelText("Input").className.split(" ")).toContain("h-11");
26
+ it("puts Button, Input, Textarea and SelectTrigger on one height and text scale", () => {
27
+ const classes = (name: string) => screen.getByLabelText(name).className.split(" ");
28
+ const { rerender } = render(<><Input aria-label="Input" /><Textarea aria-label="Textarea" /><Button aria-label="Action">Action</Button><Select><SelectTrigger aria-label="Select"><SelectValue /></SelectTrigger></Select></>);
29
+ // The default is md for every control: one 36px height, one 14px text size, one corner.
30
+ for (const name of ["Input", "Action", "Select"]) {
31
+ expect(classes(name)).toContain(controlHeight.md);
32
+ expect(classes(name)).toContain(controlText.md);
33
+ expect(classes(name)).toContain("rounded-lg");
34
+ }
35
+ expect(classes("Textarea")).toContain(controlText.md);
27
36
  expect(screen.getByLabelText("Textarea").className).toContain("min-h-[120px]");
28
- expect(screen.getByLabelText("Select").className.split(" ")).toContain("h-9");
29
- expect(screen.getByText("Action").className).toContain("h-[var(--control-height)]");
37
+ for (const size of ["sm", "md", "lg"] as const) {
38
+ rerender(<><Input size={size} aria-label="Input" /><Textarea size={size} aria-label="Textarea" /><Button size={size} aria-label="Action">Action</Button><Select><SelectTrigger size={size} aria-label="Select"><SelectValue /></SelectTrigger></Select></>);
39
+ for (const name of ["Input", "Action", "Select"]) {
40
+ expect(classes(name), `${name} ${size}`).toContain(controlHeight[size]);
41
+ expect(classes(name), `${name} ${size}`).toContain(controlText[size]);
42
+ expect(classes(name), `${name} ${size}`).toContain(size === "sm" ? "rounded-md" : "rounded-lg");
43
+ expect(screen.getByLabelText(name)).not.toHaveAttribute("size");
44
+ }
45
+ expect(classes("Textarea")).toContain(controlText[size]);
46
+ }
47
+ // Fields never drop below 16px on a coarse pointer (iOS zooms on focus otherwise).
48
+ for (const name of ["Input", "Textarea", "Select"]) expect(classes(name)).toContain("pointer-coarse:text-base");
49
+ expect(classes("Action")).not.toContain("pointer-coarse:text-base");
50
+ });
51
+
52
+ it("keeps the explicit compact and touch sizes", () => {
53
+ const { rerender } = render(<><Input size="compact" aria-label="Input" /><Button size="compact">Action</Button><Select><SelectTrigger size="compact" aria-label="Select"><SelectValue /></SelectTrigger></Select></>);
30
54
  for (const size of ["compact", "touch"] as const) {
31
55
  rerender(<><Input size={size} aria-label="Input" /><Textarea size={size} aria-label="Textarea" /><Button size={size}>Action</Button><Select><SelectTrigger size={size} aria-label="Select"><SelectValue /></SelectTrigger></Select></>);
32
56
  for (const name of ["Input", "Select"]) {
33
57
  expect(screen.getByLabelText(name).className).toContain(size === "compact" ? "h-[var(--control-height)]" : "min-h-11");
58
+ expect(screen.getByLabelText(name).className.split(" ")).toContain("rounded-lg");
34
59
  expect(screen.getByLabelText(name)).not.toHaveAttribute("size");
35
60
  }
36
61
  expect(screen.getByText("Action").className).toContain(size === "compact" ? "h-[var(--control-height)]" : "min-h-11");
@@ -38,6 +63,26 @@ describe("shared control presentation", () => {
38
63
  }
39
64
  });
40
65
 
66
+ it("gives icon buttons squares on the same heights", () => {
67
+ render(<><Button size="icon-sm" aria-label="Small" /><Button size="icon" aria-label="Medium" /><Button size="icon-lg" aria-label="Large" /></>);
68
+ expect(screen.getByLabelText("Small").className).toContain("size-[var(--control-height-sm,2rem)]");
69
+ expect(screen.getByLabelText("Medium").className).toContain("size-[var(--control-height,2.25rem)]");
70
+ expect(screen.getByLabelText("Large").className).toContain("size-[var(--control-height-lg,2.75rem)]");
71
+ });
72
+
73
+ it("renders labels and help text at one size each, and wires hint and error to the field", () => {
74
+ render(<><Label htmlFor="x">Name</Label><HelpText>Plain</HelpText><Input label="Email" hint="We never share it" error="Enter an address" /></>);
75
+ expect(screen.getByText("Name").className).toContain("text-[length:var(--font-size-label,0.875rem)]");
76
+ for (const text of ["Plain", "We never share it", "Enter an address"]) {
77
+ expect(screen.getByText(text).className).toContain("text-[length:var(--font-size-help,0.75rem)]");
78
+ }
79
+ expect(screen.getByText("Enter an address")).toHaveAttribute("data-tone", "error");
80
+ const field = screen.getByLabelText("Email");
81
+ const described = field.getAttribute("aria-describedby")!.split(" ");
82
+ expect(described).toContain(screen.getByText("We never share it").id);
83
+ expect(described).toContain(screen.getByText("Enter an address").id);
84
+ });
85
+
41
86
  it("preserves field and trigger refs, caller overrides, invalid/disabled and autofill attributes", () => {
42
87
  const input = React.createRef<HTMLInputElement>();
43
88
  const textarea = React.createRef<HTMLTextAreaElement>();
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import { cn } from "../lib/utils";
3
+
4
+ export interface HelpTextProps extends React.HTMLAttributes<HTMLParagraphElement> {
5
+ /** `hint` describes a field; `error` reports what is wrong with its value. */
6
+ tone?: "hint" | "error";
7
+ }
8
+
9
+ /**
10
+ * Text under a field. One size (`--font-size-help`, 12px) for every field, so
11
+ * forms across products read the same. Point the field's `aria-describedby` at
12
+ * its `id`; Input and Textarea do this for their `hint` and `error` props.
13
+ */
14
+ const HelpText = React.forwardRef<HTMLParagraphElement, HelpTextProps>(
15
+ ({ className, tone = "hint", ...props }, ref) => (
16
+ <p
17
+ ref={ref}
18
+ data-tone={tone}
19
+ className={cn(
20
+ "text-[length:var(--font-size-help,0.75rem)] leading-4",
21
+ tone === "error" ? "font-medium text-[var(--surface-danger-text)]" : "text-[var(--text-dim)]",
22
+ className,
23
+ )}
24
+ {...props}
25
+ />
26
+ ),
27
+ );
28
+ HelpText.displayName = "HelpText";
29
+
30
+ export { HelpText };
@@ -65,6 +65,9 @@ export { Slider } from "./slider";
65
65
  export { Input, Textarea } from "./input";
66
66
  export type { InputProps, TextareaProps } from "./input";
67
67
 
68
+ export { HelpText } from "./help-text";
69
+ export type { HelpTextProps } from "./help-text";
70
+
68
71
  export { Badge, badgeVariants } from "./badge";
69
72
  export type { BadgeProps } from "./badge";
70
73
 
@@ -1,17 +1,21 @@
1
1
  "use client";
2
2
 
3
3
  import * as React from "react";
4
- import { controlSizes, fieldPresentation } from "../lib/control-presentation";
4
+ import { controlRadius, controlSizes, controlText, fieldPresentation, fieldSizes, fieldTouchText } from "../lib/control-presentation";
5
5
  import { focusFieldInvalid } from "../lib/focus";
6
6
  import { cn } from "../lib/utils";
7
+ import { HelpText } from "./help-text";
7
8
  import { Label } from "./label";
8
9
 
9
10
  import { cva, type VariantProps } from "class-variance-authority";
10
11
 
12
+ // Sizes follow the shared control scale (control-presentation.ts), so an Input
13
+ // and a Button at the same size share a height, text size and corner.
11
14
  const inputVariants = cva(
12
15
  cn(
13
- "flex w-full rounded-lg border px-4 py-2 text-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium file:text-sm",
16
+ "flex w-full border py-1 placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium file:text-sm",
14
17
  fieldPresentation,
18
+ fieldTouchText,
15
19
  ),
16
20
  {
17
21
  variants: {
@@ -22,10 +26,12 @@ const inputVariants = cva(
22
26
  error: focusFieldInvalid,
23
27
  },
24
28
  size: {
25
- default: "h-11",
26
- sm: "h-9 px-3",
27
- lg: "h-12 px-5",
28
- ...controlSizes,
29
+ default: fieldSizes.md,
30
+ md: fieldSizes.md,
31
+ sm: fieldSizes.sm,
32
+ lg: fieldSizes.lg,
33
+ compact: cn(fieldSizes.md, controlSizes.compact),
34
+ touch: cn(fieldSizes.md, controlSizes.touch),
29
35
  }
30
36
  },
31
37
  defaultVariants: {
@@ -79,8 +85,8 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
79
85
  <div className="w-full space-y-1.5">
80
86
  {label && <Label htmlFor={inputId} className="block">{label}</Label>}
81
87
  {input}
82
- {error && <p id={errorId} className="text-[var(--surface-danger-text)] text-sm font-medium">{error}</p>}
83
- {hint && <p id={hintId} className="text-[var(--text-dim)] text-sm">{hint}</p>}
88
+ {error && <HelpText id={errorId} tone="error">{error}</HelpText>}
89
+ {hint && <HelpText id={hintId}>{hint}</HelpText>}
84
90
  </div>
85
91
  );
86
92
  },
@@ -91,8 +97,8 @@ export interface TextareaProps
91
97
  extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
92
98
  /** Both values render the same field. */
93
99
  variant?: "default" | "sandbox";
94
- /** Opt in to density/typing presentation without changing the default 120px well. */
95
- size?: "default" | "compact" | "touch";
100
+ /** Text, padding and corner from the control scale; the 120px well is unchanged. compact and touch remain. */
101
+ size?: "default" | "sm" | "md" | "lg" | "compact" | "touch";
96
102
  label?: string;
97
103
  error?: string;
98
104
  hint?: string;
@@ -116,10 +122,12 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
116
122
  <textarea
117
123
  id={textareaId}
118
124
  className={cn(
119
- "flex min-h-[120px] w-full resize-y rounded-lg border px-4 py-3 text-sm",
125
+ "flex min-h-[120px] w-full resize-y border px-3 py-2",
120
126
  "placeholder:text-[var(--text-dim)]",
121
127
  "disabled:cursor-not-allowed disabled:opacity-50",
122
128
  fieldPresentation,
129
+ fieldTouchText,
130
+ size === "sm" ? cn(controlText.sm, controlRadius.sm, "px-2.5") : size === "lg" ? cn(controlText.lg, controlRadius.lg, "px-4 py-3") : cn(controlText.md, controlRadius.md),
123
131
  error && focusFieldInvalid,
124
132
  size === "compact" && "min-h-24 px-3 py-2",
125
133
  size === "touch" && "text-base",
@@ -138,8 +146,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
138
146
  <div className="w-full space-y-1.5">
139
147
  {label && <Label htmlFor={textareaId} className="block">{label}</Label>}
140
148
  {textarea}
141
- {error && <p id={errorId} className="text-[var(--surface-danger-text)] text-sm">{error}</p>}
142
- {hint && <p id={hintId} className="text-[var(--text-dim)] text-sm">{hint}</p>}
149
+ {error && <HelpText id={errorId} tone="error">{error}</HelpText>}
150
+ {hint && <HelpText id={hintId}>{hint}</HelpText>}
143
151
  </div>
144
152
  );
145
153
  },
@@ -5,8 +5,9 @@ import { cva, type VariantProps } from "class-variance-authority";
5
5
  import * as React from "react";
6
6
  import { cn } from "../lib/utils";
7
7
 
8
+ // One label size (`--font-size-label`, 14px) for every field size.
8
9
  const labelVariants = cva(
9
- "font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
10
+ "font-medium text-[length:var(--font-size-label,0.875rem)] leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
10
11
  );
11
12
 
12
13
  const Label = React.forwardRef<
@@ -3,7 +3,7 @@
3
3
  import * as SelectPrimitive from "@radix-ui/react-select";
4
4
  import { Check, ChevronDown, ChevronUp } from "lucide-react";
5
5
  import * as React from "react";
6
- import { controlSizes, fieldPresentation } from "../lib/control-presentation";
6
+ import { controlSizes, fieldPresentation, fieldSizes, fieldTouchText } from "../lib/control-presentation";
7
7
  import { cn } from "../lib/utils";
8
8
 
9
9
  const Select = SelectPrimitive.Root;
@@ -15,19 +15,20 @@ const SelectValue = SelectPrimitive.Value;
15
15
  const SelectTrigger = React.forwardRef<
16
16
  React.ElementRef<typeof SelectPrimitive.Trigger>,
17
17
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
18
- /** Default remains 36px; compact follows density, touch is at least 44px. */
19
- size?: "default" | "compact" | "touch";
18
+ /** The shared control scale: sm 32px, default/md 36px, lg 44px. compact follows density, touch is at least 44px. */
19
+ size?: "default" | "sm" | "md" | "lg" | "compact" | "touch";
20
20
  }
21
21
  >(({ className, children, size = "default", ...props }, ref) => (
22
22
  <SelectPrimitive.Trigger
23
23
  ref={ref}
24
24
  className={cn(
25
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border px-3 py-2 text-sm shadow-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
25
+ "flex w-full items-center justify-between whitespace-nowrap border py-1 shadow-sm placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
26
26
  fieldPresentation,
27
+ fieldTouchText,
27
28
  "data-[placeholder]:text-[var(--text-dim)] [&>span]:min-w-0 [&>span]:text-left",
28
29
  // The designed open state: accent border and surface while the menu is up.
29
30
  "data-[state=open]:border-primary data-[state=open]:bg-[var(--accent-surface-strong)] [&[data-state=open]>svg]:rotate-180 [&>svg]:transition-transform",
30
- size !== "default" && controlSizes[size],
31
+ size === "compact" || size === "touch" ? cn(fieldSizes.md, controlSizes[size]) : fieldSizes[size === "default" ? "md" : size],
31
32
  className,
32
33
  )}
33
34
  {...props}