@adea-ai/ui 0.62.0 → 0.63.1

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.
Files changed (49) hide show
  1. package/README.md +50 -0
  2. package/dist/NOTICE +51 -0
  3. package/dist/components/composites/appearance-editor/appearance-choices.d.ts +5 -0
  4. package/dist/components/composites/appearance-editor/appearance-choices.d.ts.map +1 -0
  5. package/dist/components/composites/appearance-editor/appearance-choices.js +211 -0
  6. package/dist/components/composites/appearance-editor/appearance-choices.js.map +1 -0
  7. package/dist/components/composites/appearance-editor/appearance-editor.d.ts +15 -0
  8. package/dist/components/composites/appearance-editor/appearance-editor.d.ts.map +1 -0
  9. package/dist/components/composites/appearance-editor/appearance-editor.js +280 -0
  10. package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -0
  11. package/dist/components/composites/appearance-editor/appearance-rows.d.ts +18 -0
  12. package/dist/components/composites/appearance-editor/appearance-rows.d.ts.map +1 -0
  13. package/dist/components/composites/appearance-editor/appearance-rows.js +101 -0
  14. package/dist/components/composites/appearance-editor/appearance-rows.js.map +1 -0
  15. package/dist/components/composites/appearance-editor/appearance-types.d.ts +42 -0
  16. package/dist/components/composites/appearance-editor/appearance-types.d.ts.map +1 -0
  17. package/dist/components/composites/appearance-editor/index.d.ts +4 -0
  18. package/dist/components/composites/appearance-editor/index.d.ts.map +1 -0
  19. package/dist/components/composites/appearance-editor/index.js +3 -0
  20. package/dist/components/composites/appearance-editor/theme-preview.d.ts +17 -0
  21. package/dist/components/composites/appearance-editor/theme-preview.d.ts.map +1 -0
  22. package/dist/components/composites/appearance-editor/theme-preview.js +158 -0
  23. package/dist/components/composites/appearance-editor/theme-preview.js.map +1 -0
  24. package/dist/components/ui/select/select.d.ts +5 -1
  25. package/dist/components/ui/select/select.d.ts.map +1 -1
  26. package/dist/components/ui/select/select.js +24 -20
  27. package/dist/components/ui/select/select.js.map +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +3 -1
  31. package/dist/r/appearance-editor.json +51 -0
  32. package/dist/r/registry.json +51 -0
  33. package/dist/r/src/components/composites/appearance-editor/appearance-choices.tsx +173 -0
  34. package/dist/r/src/components/composites/appearance-editor/appearance-editor.tsx +239 -0
  35. package/dist/r/src/components/composites/appearance-editor/appearance-rows.tsx +122 -0
  36. package/dist/r/src/components/composites/appearance-editor/appearance-types.ts +40 -0
  37. package/dist/r/src/components/composites/appearance-editor/index.ts +12 -0
  38. package/dist/r/src/components/composites/appearance-editor/theme-preview.tsx +128 -0
  39. package/dist/r/src/components/ui/select/select.tsx +8 -3
  40. package/package.json +3 -2
  41. package/registry.json +51 -0
  42. package/src/components/composites/appearance-editor/appearance-choices.tsx +173 -0
  43. package/src/components/composites/appearance-editor/appearance-editor.tsx +239 -0
  44. package/src/components/composites/appearance-editor/appearance-rows.tsx +122 -0
  45. package/src/components/composites/appearance-editor/appearance-types.ts +40 -0
  46. package/src/components/composites/appearance-editor/index.ts +12 -0
  47. package/src/components/composites/appearance-editor/theme-preview.tsx +128 -0
  48. package/src/components/ui/select/select.tsx +8 -3
  49. package/src/index.ts +1 -0
package/dist/index.js CHANGED
@@ -82,8 +82,10 @@ import { AccountMenu } from "./components/composites/account-menu/account-menu.j
82
82
  import { ListGroup, ListRow } from "./components/composites/list-row/list-row.js";
83
83
  import { SettingsField, SettingsPage, SettingsRow, SettingsSection } from "./components/composites/settings/settings.js";
84
84
  import { PropertyList, PropertyRow, PropertyTerm, PropertyValue, Stat, StatGroup } from "./components/composites/stat/stat.js";
85
+ import { PalettePreview, ThemeMiniature, ThemeMiniatureSplit } from "./components/composites/appearance-editor/theme-preview.js";
86
+ import { AppearanceEditor, AppearancePopover } from "./components/composites/appearance-editor/appearance-editor.js";
85
87
  import { formatBytes, formatReleaseDate, plainTextFromMarkdown } from "./lib/version-notes.js";
86
88
  import { UpdateDialog } from "./components/composites/update-dialog/update-dialog.js";
87
89
  import { WorkspaceMark } from "./components/composites/workspace-mark/workspace-mark.js";
88
90
  import { keyedRows } from "./lib/keyed-rows.js";
89
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearancePanel, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposerAttachmentButton, ComposerHint, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ConversationAvatar, ConversationSurface, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EntityIcon, Field, FieldDescription, FieldError, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdGroup, Label, ListGroup, ListRow, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavSection, SidebarNavTitle, Skeleton, Slider, Spinner, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, UpdateDialog, WorkspaceMark, accentPresets, alertVariants, allTokens, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, cn, colorTokens, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, fontOptions, formatBytes, formatReleaseDate, initialsFrom, inlineScriptLiteral, itemVariants, keyedRows, monogram, motionTokens, parseDiff, plainTextFromMarkdown, radiusTokens, resolvedDensity, sheetVariants, sidebarNavItemClass, sidebarNavItemStateClass, statusDotVariants, surfaceInteractive, themeById, themeCssVariables, themeFamilies, themeScript, themesForAppearance, toast, toaster, toggleVariants, typographyTokens, undocumentedTokenAliases, useSideRail, useTheme, validateTheme, validateThemeRegistry, zIndexTokens };
91
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposerAttachmentButton, ComposerHint, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ConversationAvatar, ConversationSurface, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EntityIcon, Field, FieldDescription, FieldError, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdGroup, Label, ListGroup, ListRow, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, ScrollArea, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavSection, SidebarNavTitle, Skeleton, Slider, Spinner, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, UpdateDialog, WorkspaceMark, accentPresets, alertVariants, allTokens, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, cn, colorTokens, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, fontOptions, formatBytes, formatReleaseDate, initialsFrom, inlineScriptLiteral, itemVariants, keyedRows, monogram, motionTokens, parseDiff, plainTextFromMarkdown, radiusTokens, resolvedDensity, sheetVariants, sidebarNavItemClass, sidebarNavItemStateClass, statusDotVariants, surfaceInteractive, themeById, themeCssVariables, themeFamilies, themeScript, themesForAppearance, toast, toaster, toggleVariants, typographyTokens, undocumentedTokenAliases, useSideRail, useTheme, validateTheme, validateThemeRegistry, zIndexTokens };
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "appearance-editor",
3
+ "type": "registry:ui",
4
+ "title": "Appearance Editor",
5
+ "description": "",
6
+ "dependencies": [
7
+ "@adea-ai/themes@^0.5.0",
8
+ "@kobalte/core@^0.13.14",
9
+ "lucide-solid@^1.48.0"
10
+ ],
11
+ "registryDependencies": [
12
+ "@adea-ai/ui/button",
13
+ "@adea-ai/ui/input",
14
+ "@adea-ai/ui/popover",
15
+ "@adea-ai/ui/select",
16
+ "@adea-ai/ui/switch",
17
+ "lib"
18
+ ],
19
+ "files": [
20
+ {
21
+ "path": "src/components/composites/appearance-editor/appearance-choices.tsx",
22
+ "type": "registry:ui",
23
+ "target": "components/composites/appearance-editor/appearance-choices.tsx"
24
+ },
25
+ {
26
+ "path": "src/components/composites/appearance-editor/appearance-editor.tsx",
27
+ "type": "registry:ui",
28
+ "target": "components/composites/appearance-editor/appearance-editor.tsx"
29
+ },
30
+ {
31
+ "path": "src/components/composites/appearance-editor/appearance-rows.tsx",
32
+ "type": "registry:ui",
33
+ "target": "components/composites/appearance-editor/appearance-rows.tsx"
34
+ },
35
+ {
36
+ "path": "src/components/composites/appearance-editor/appearance-types.ts",
37
+ "type": "registry:ui",
38
+ "target": "components/composites/appearance-editor/appearance-types.ts"
39
+ },
40
+ {
41
+ "path": "src/components/composites/appearance-editor/index.ts",
42
+ "type": "registry:ui",
43
+ "target": "components/composites/appearance-editor/index.ts"
44
+ },
45
+ {
46
+ "path": "src/components/composites/appearance-editor/theme-preview.tsx",
47
+ "type": "registry:ui",
48
+ "target": "components/composites/appearance-editor/theme-preview.tsx"
49
+ }
50
+ ]
51
+ }
@@ -1599,6 +1599,57 @@
1599
1599
  }
1600
1600
  ]
1601
1601
  },
1602
+ {
1603
+ "name": "appearance-editor",
1604
+ "type": "registry:ui",
1605
+ "title": "Appearance Editor",
1606
+ "description": "",
1607
+ "dependencies": [
1608
+ "@adea-ai/themes@^0.5.0",
1609
+ "@kobalte/core@^0.13.14",
1610
+ "lucide-solid@^1.48.0"
1611
+ ],
1612
+ "registryDependencies": [
1613
+ "@adea-ai/ui/button",
1614
+ "@adea-ai/ui/input",
1615
+ "@adea-ai/ui/popover",
1616
+ "@adea-ai/ui/select",
1617
+ "@adea-ai/ui/switch",
1618
+ "lib"
1619
+ ],
1620
+ "files": [
1621
+ {
1622
+ "path": "src/components/composites/appearance-editor/appearance-choices.tsx",
1623
+ "type": "registry:ui",
1624
+ "target": "components/composites/appearance-editor/appearance-choices.tsx"
1625
+ },
1626
+ {
1627
+ "path": "src/components/composites/appearance-editor/appearance-editor.tsx",
1628
+ "type": "registry:ui",
1629
+ "target": "components/composites/appearance-editor/appearance-editor.tsx"
1630
+ },
1631
+ {
1632
+ "path": "src/components/composites/appearance-editor/appearance-rows.tsx",
1633
+ "type": "registry:ui",
1634
+ "target": "components/composites/appearance-editor/appearance-rows.tsx"
1635
+ },
1636
+ {
1637
+ "path": "src/components/composites/appearance-editor/appearance-types.ts",
1638
+ "type": "registry:ui",
1639
+ "target": "components/composites/appearance-editor/appearance-types.ts"
1640
+ },
1641
+ {
1642
+ "path": "src/components/composites/appearance-editor/index.ts",
1643
+ "type": "registry:ui",
1644
+ "target": "components/composites/appearance-editor/index.ts"
1645
+ },
1646
+ {
1647
+ "path": "src/components/composites/appearance-editor/theme-preview.tsx",
1648
+ "type": "registry:ui",
1649
+ "target": "components/composites/appearance-editor/theme-preview.tsx"
1650
+ }
1651
+ ]
1652
+ },
1602
1653
  {
1603
1654
  "name": "list-row",
1604
1655
  "type": "registry:ui",
@@ -0,0 +1,173 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2026 Wing
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ // Pinned mode-card, accent-swatch, and glass-chip composition. Kobalte replaces
25
+ // the app port's manual roving focus; it does not replace the donor hierarchy.
26
+ import { RadioGroup as Radio } from '@kobalte/core/radio-group'
27
+ import { For, Show } from 'solid-js'
28
+ import type { AppearanceEditorProps } from './appearance-types'
29
+ import { ThemeMiniature, ThemeMiniatureSplit } from './theme-preview'
30
+
31
+ const MODES = [
32
+ { value: 'system', label: 'System' },
33
+ { value: 'light', label: 'Light' },
34
+ { value: 'dark', label: 'Dark' },
35
+ ] as const
36
+ const SURFACES = [
37
+ { value: 'theme', label: 'Theme default' },
38
+ { value: 'frosted', label: 'Frosted' },
39
+ { value: 'opaque', label: 'Opaque' },
40
+ ] as const
41
+
42
+ export function ModeChoices(props: AppearanceEditorProps) {
43
+ return (
44
+ <Radio
45
+ value={props.draft.mode}
46
+ disabled={props.saving}
47
+ orientation="horizontal"
48
+ aria-label="Appearance mode"
49
+ onChange={(mode) => {
50
+ const option = MODES.find((candidate) => candidate.value === mode)
51
+ if (option) props.onChange({ mode: option.value })
52
+ }}
53
+ class="grid grid-cols-1 gap-3 px-4 py-4 sm:grid-cols-3"
54
+ >
55
+ <For each={MODES}>
56
+ {(option) => (
57
+ <Radio.Item
58
+ value={option.value}
59
+ data-mode={option.value}
60
+ class="min-w-0 rounded-lg focus-within:ring-3 focus-within:ring-ring/50"
61
+ >
62
+ <Radio.ItemInput />
63
+ <Radio.ItemLabel class="flex cursor-pointer flex-col gap-2 rounded-lg border p-2 text-center text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50">
64
+ <span class="block h-20 w-full">
65
+ <Show
66
+ when={option.value === 'system'}
67
+ fallback={
68
+ <ThemeMiniature
69
+ theme={option.value === 'light' ? props.lightTheme : props.darkTheme}
70
+ />
71
+ }
72
+ >
73
+ <ThemeMiniatureSplit light={props.lightTheme} dark={props.darkTheme} />
74
+ </Show>
75
+ </span>
76
+ <span>{option.label}</span>
77
+ </Radio.ItemLabel>
78
+ </Radio.Item>
79
+ )}
80
+ </For>
81
+ </Radio>
82
+ )
83
+ }
84
+
85
+ export function AccentChoices(props: AppearanceEditorProps) {
86
+ const custom = () =>
87
+ props.draft.accent !== 'theme' &&
88
+ !props.accentOptions.some((option) => option.id === props.draft.accent)
89
+ const accentSelection = () => (custom() ? 'custom' : props.draft.accent)
90
+ return (
91
+ <Radio
92
+ value={accentSelection()}
93
+ disabled={props.saving}
94
+ orientation="horizontal"
95
+ aria-label="Accent"
96
+ class="flex flex-wrap items-center gap-2"
97
+ onChange={(accent) =>
98
+ props.onChange({
99
+ accent: accent === 'custom' ? (props.customAccentValue ?? '') : accent,
100
+ })
101
+ }
102
+ >
103
+ <Radio.Item value="theme">
104
+ <Radio.ItemInput />
105
+ <Radio.ItemLabel class="flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary">
106
+ Theme default
107
+ </Radio.ItemLabel>
108
+ </Radio.Item>
109
+ <For each={props.accentOptions}>
110
+ {(option) => (
111
+ <Radio.Item
112
+ value={option.id}
113
+ class="rounded-full focus-within:ring-3 focus-within:ring-ring/50"
114
+ >
115
+ <Radio.ItemInput />
116
+ <Radio.ItemLabel class="block size-7 cursor-pointer rounded-full border p-0.5 data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary">
117
+ <span
118
+ class="block size-full rounded-full"
119
+ aria-hidden="true"
120
+ style={{
121
+ 'background-color':
122
+ (props.draft.mode === 'system'
123
+ ? props.resolvedAppearance
124
+ : props.draft.mode) === 'light'
125
+ ? option.light
126
+ : option.dark,
127
+ }}
128
+ />
129
+ <span class="sr-only">{option.label}</span>
130
+ </Radio.ItemLabel>
131
+ </Radio.Item>
132
+ )}
133
+ </For>
134
+ <Radio.Item value="custom">
135
+ <Radio.ItemInput />
136
+ <Radio.ItemLabel class="flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary">
137
+ Custom
138
+ </Radio.ItemLabel>
139
+ </Radio.Item>
140
+ </Radio>
141
+ )
142
+ }
143
+
144
+ export function GlassChoices(props: AppearanceEditorProps) {
145
+ return (
146
+ <Radio
147
+ value={props.draft.surface}
148
+ disabled={props.saving}
149
+ orientation="horizontal"
150
+ aria-label="Glass"
151
+ class="flex flex-wrap gap-1"
152
+ onChange={(surface) => {
153
+ const option = SURFACES.find((candidate) => candidate.value === surface)
154
+ if (option) props.onChange({ surface: option.value })
155
+ }}
156
+ >
157
+ <For each={SURFACES}>
158
+ {(option) => (
159
+ <Radio.Item
160
+ value={option.value}
161
+ disabled={option.value === 'frosted' && !props.surfaceCapability.frosted}
162
+ class="rounded-md focus-within:ring-3 focus-within:ring-ring/50"
163
+ >
164
+ <Radio.ItemInput />
165
+ <Radio.ItemLabel class="flex h-7 cursor-pointer items-center rounded-md border px-2 text-xs data-[checked]:border-primary data-[checked]:ring-1 data-[checked]:ring-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50">
166
+ {option.label}
167
+ </Radio.ItemLabel>
168
+ </Radio.Item>
169
+ )}
170
+ </For>
171
+ </Radio>
172
+ )
173
+ }
@@ -0,0 +1,239 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2026 Wing
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ /*
25
+ * Composition substantially translated from Zeron settings/appearance.rs and
26
+ * settings/widgets.rs, pinned at 30a9a9537c5ec96226c87f4bf349b6f77c5dfb59,
27
+ * preserving Adea's existing appearance-surface.tsx port: System/Light/Dark
28
+ * miniatures, compact independent theme rows, accent swatches and helper copy,
29
+ * glass chips, and theme library affordance. Kobalte replaces the port's manual
30
+ * radio keyboard model. The controlled draft and explicit action ports replace
31
+ * GPUI globals and app-specific storage/native authority. Save/Cancel/Reset and
32
+ * custom accent/reduced-transparency are accepted Adea divergences (#425).
33
+ */
34
+ import { EyeOff, Palette, PanelsTopLeft, SlidersHorizontal } from 'lucide-solid'
35
+ import { Show, createUniqueId } from 'solid-js'
36
+ import { cn } from '../../../lib/utils'
37
+ import { Button } from '../../ui/button/button'
38
+ import { Input } from '../../ui/input/input'
39
+
40
+ import { Switch } from '../../ui/switch/switch'
41
+ import {
42
+ Popover,
43
+ PopoverContent,
44
+ PopoverDescription,
45
+ PopoverTitle,
46
+ PopoverTrigger,
47
+ } from '../../ui/popover/popover'
48
+ import type { AppearanceEditorProps, AppearancePopoverProps } from './appearance-types'
49
+ import { ModeChoices, AccentChoices, GlassChoices } from './appearance-choices'
50
+ import { SettingsRow, ThemeRow } from './appearance-rows'
51
+
52
+ /**
53
+ * A live editor whose persistence and preview authority belong to its host.
54
+ * No ThemeProvider is required, and no catalogue, renderer engine or storage is
55
+ * imported. Hosts normalize unknown IDs and custom colors, report recovery,
56
+ * snapshot on open, preview on changes, commit on Save, and restore on dismissal.
57
+ * Both theme rows remain mounted when mode changes. Typeface/density axes are
58
+ * deliberately absent from Adea #425's accepted surface.
59
+ */
60
+ export function AppearanceEditor(props: AppearanceEditorProps) {
61
+ const errorId = createUniqueId()
62
+ const saveReasonId = createUniqueId()
63
+ const custom = () =>
64
+ props.draft.accent !== 'theme' &&
65
+ !props.accentOptions.some((option) => option.id === props.draft.accent)
66
+ const accentDescription = () => {
67
+ if (props.draft.accent === 'theme') return "Theme default · Uses the palette's intended color."
68
+ const name = props.accentOptions.find((option) => option.id === props.draft.accent)?.label
69
+ return `${name ?? 'Custom'} · Controls, glyphs, selections, code, and activity.`
70
+ }
71
+ const glassDescription = () =>
72
+ props.draft.surface === 'opaque'
73
+ ? 'Solid surfaces for every theme.'
74
+ : props.draft.surface === 'frosted'
75
+ ? 'Theme-colored glass where supported.'
76
+ : (props.surfaceCapability.themeDefaultDescription ?? "Uses this theme's default surface.")
77
+ return (
78
+ <div data-appearance-editor class={cn('min-w-0', props.class)}>
79
+ <Show when={props.recoveryNotice}>
80
+ <p role="status" class="px-4 py-2 text-sm text-muted-foreground">
81
+ {props.recoveryNotice}
82
+ </p>
83
+ </Show>
84
+ <ModeChoices {...props} />
85
+ <div class="divide-y divide-border rounded-lg border">
86
+ <ThemeRow
87
+ appearance="light"
88
+ selectedId={props.draft.lightThemeId}
89
+ preview={props.lightTheme}
90
+ themes={props.themes}
91
+ disabled={props.saving}
92
+ onSelect={(lightThemeId) => props.onChange({ lightThemeId })}
93
+ />
94
+ <ThemeRow
95
+ appearance="dark"
96
+ selectedId={props.draft.darkThemeId}
97
+ preview={props.darkTheme}
98
+ themes={props.themes}
99
+ disabled={props.saving}
100
+ onSelect={(darkThemeId) => props.onChange({ darkThemeId })}
101
+ />
102
+ <SettingsRow
103
+ stacked
104
+ title="Accent"
105
+ icon={<SlidersHorizontal />}
106
+ description={accentDescription()}
107
+ >
108
+ <AccentChoices {...props} />
109
+ </SettingsRow>
110
+ <Show when={custom()}>
111
+ <div class="px-4 py-3">
112
+ <label class="mb-2 block text-xs font-medium" for={`${errorId}-input`}>
113
+ Custom accent
114
+ </label>
115
+ <Input
116
+ id={`${errorId}-input`}
117
+ value={props.draft.accent}
118
+ disabled={props.saving}
119
+ aria-invalid={!!props.customAccentError}
120
+ aria-describedby={props.customAccentError ? errorId : undefined}
121
+ onInput={(event) => props.onChange({ accent: event.currentTarget.value })}
122
+ />
123
+ <Show when={props.customAccentError}>
124
+ <p id={errorId} role="alert" class="mt-1 text-xs text-destructive">
125
+ {props.customAccentError}
126
+ </p>
127
+ </Show>
128
+ </div>
129
+ </Show>
130
+ <SettingsRow title="Glass" icon={<PanelsTopLeft />} description={glassDescription()}>
131
+ <GlassChoices {...props} />
132
+ </SettingsRow>
133
+ <Show when={!props.surfaceCapability.frosted && props.surfaceCapability.reason}>
134
+ <p role="status" class="px-4 py-2 text-xs text-muted-foreground">
135
+ {props.surfaceCapability.reason}
136
+ </p>
137
+ </Show>
138
+ <SettingsRow
139
+ title="Reduce transparency"
140
+ icon={<EyeOff />}
141
+ description="Prefer solid surfaces, including during live preview."
142
+ >
143
+ <Switch
144
+ checked={props.draft.reduceTransparency}
145
+ disabled={props.saving}
146
+ onChange={(reduceTransparency: boolean) => props.onChange({ reduceTransparency })}
147
+ aria-label="Reduce transparency"
148
+ />
149
+ </SettingsRow>
150
+ <SettingsRow
151
+ title="Theme library"
152
+ icon={<Palette />}
153
+ description={
154
+ props.onManageThemes
155
+ ? 'Manage themes through the application.'
156
+ : 'Theme import is unavailable.'
157
+ }
158
+ >
159
+ <Button
160
+ type="button"
161
+ size="sm"
162
+ variant="outline"
163
+ disabled={!props.onManageThemes || props.saving}
164
+ onClick={() => props.onManageThemes?.()}
165
+ >
166
+ Manage themes
167
+ </Button>
168
+ </SettingsRow>
169
+ </div>
170
+ <Show when={props.saveDisabledReason}>
171
+ <p id={saveReasonId} role="status" class="px-4 pt-3 text-xs text-muted-foreground">
172
+ {props.saveDisabledReason}
173
+ </p>
174
+ </Show>
175
+ <div class="flex flex-wrap items-center gap-2 px-4 py-4">
176
+ <Button
177
+ type="button"
178
+ size="sm"
179
+ variant="ghost"
180
+ disabled={props.saving}
181
+ onClick={() => props.onReset()}
182
+ >
183
+ Reset
184
+ </Button>
185
+ <div class="ml-auto flex gap-2">
186
+ <Button
187
+ type="button"
188
+ size="sm"
189
+ variant="outline"
190
+ disabled={props.saving}
191
+ onClick={() => props.onCancel()}
192
+ >
193
+ Cancel
194
+ </Button>
195
+ <Button
196
+ type="button"
197
+ size="sm"
198
+ disabled={props.saving || !!props.customAccentError || !!props.saveDisabledReason}
199
+ aria-busy={props.saving}
200
+ aria-describedby={props.saveDisabledReason ? saveReasonId : undefined}
201
+ onClick={() => props.onSave()}
202
+ >
203
+ {props.saving ? 'Saving…' : 'Save'}
204
+ </Button>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ )
209
+ }
210
+
211
+ /** Modal anchoring/dismissal and nested-listbox behavior come from Kobalte.
212
+ * Its measured available height also bounds scrolling, keeping footer actions
213
+ * reachable when a narrow popup begins below its trigger. */
214
+ export function AppearancePopover(props: AppearancePopoverProps) {
215
+ return (
216
+ <Popover
217
+ open={props.open}
218
+ modal
219
+ onOpenChange={(open) => (open ? props.onOpen() : props.onDismiss())}
220
+ >
221
+ <PopoverTrigger
222
+ as={Button}
223
+ type="button"
224
+ variant="ghost"
225
+ size="icon-sm"
226
+ aria-label="Appearance settings"
227
+ >
228
+ <Palette />
229
+ </PopoverTrigger>
230
+ <PopoverContent class="w-lg max-w-[calc(100vw-2rem)] max-h-(--kb-popper-content-available-height) overflow-y-auto">
231
+ <PopoverTitle>Appearance</PopoverTitle>
232
+ <PopoverDescription>
233
+ Changes preview immediately. Save keeps them; Cancel restores the previous appearance.
234
+ </PopoverDescription>
235
+ <AppearanceEditor {...props} />
236
+ </PopoverContent>
237
+ </Popover>
238
+ )
239
+ }