aural-ui 3.0.7 → 4.1.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.
Files changed (183) hide show
  1. package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1199
  2. package/dist/components/avatar/Avatar.stories.tsx +235 -237
  3. package/dist/components/badge/Badge.stories.tsx +379 -116
  4. package/dist/components/banner/Banner.stories.tsx +445 -391
  5. package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
  6. package/dist/components/button/Button.stories.tsx +585 -230
  7. package/dist/components/button/index.tsx +7 -7
  8. package/dist/components/card/Card.stories.tsx +619 -301
  9. package/dist/components/char-count/CharCount.stories.tsx +350 -248
  10. package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
  11. package/dist/components/chip/Chip.stories.tsx +362 -168
  12. package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -620
  13. package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
  14. package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
  15. package/dist/components/command/Command.stories.tsx +533 -856
  16. package/dist/components/dialog/Dialog.stories.tsx +505 -949
  17. package/dist/components/divider/Divider.stories.tsx +265 -502
  18. package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
  19. package/dist/components/drawer/Drawer.stories.tsx +659 -993
  20. package/dist/components/drawer/index.tsx +3 -3
  21. package/dist/components/dropdown/Dropdown.stories.tsx +643 -1018
  22. package/dist/components/form/Form.stories.tsx +560 -274
  23. package/dist/components/helper-text/HelperText.stories.tsx +199 -200
  24. package/dist/components/hover-card/HoverCard.stories.tsx +318 -1221
  25. package/dist/components/icon-button/IconButton.stories.tsx +837 -194
  26. package/dist/components/if-else/if-else.stories.tsx +370 -83
  27. package/dist/components/input/Input.stories.tsx +436 -368
  28. package/dist/components/label/Label.stories.tsx +156 -154
  29. package/dist/components/list/List.stories.tsx +485 -822
  30. package/dist/components/marquee/Marquee.stories.tsx +356 -694
  31. package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -410
  32. package/dist/components/overlay/Overlay.stories.tsx +452 -818
  33. package/dist/components/overlay/index.tsx +4 -4
  34. package/dist/components/pagination/Pagination.stories.tsx +721 -210
  35. package/dist/components/popover/Popover.stories.tsx +484 -873
  36. package/dist/components/radio/Radio.stories.tsx +432 -124
  37. package/dist/components/resizable/Resizable.stories.tsx +496 -752
  38. package/dist/components/scroll-area/ScrollArea.stories.tsx +384 -1006
  39. package/dist/components/search/Search.stories.tsx +314 -575
  40. package/dist/components/select/Select.stories.tsx +684 -787
  41. package/dist/components/sheet/Sheet.stories.tsx +671 -936
  42. package/dist/components/skelton/Skelton.stories.tsx +230 -764
  43. package/dist/components/slider/Slider.stories.tsx +384 -737
  44. package/dist/components/stepper/Stepper.stories.tsx +371 -514
  45. package/dist/components/switch/Switch.stories.tsx +461 -208
  46. package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
  47. package/dist/components/table/Table.stories.tsx +770 -914
  48. package/dist/components/tabs/Tabs.stories.tsx +459 -1400
  49. package/dist/components/tag/Tag.stories.tsx +714 -542
  50. package/dist/components/textarea/TextArea.stories.tsx +621 -562
  51. package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -148
  52. package/dist/components/toast/Toast.stories.tsx +452 -1333
  53. package/dist/components/toggle/Toggle.stories.tsx +488 -909
  54. package/dist/components/tooltip/Tooltip.stories.tsx +344 -1372
  55. package/dist/components/typography/Typography.stories.tsx +406 -89
  56. package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
  57. package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
  58. package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
  59. package/dist/icons/Icons.stories.tsx +0 -12
  60. package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +226 -1013
  61. package/dist/icons/alert-icon/AlertIcon.stories.tsx +109 -929
  62. package/dist/icons/all-icons.tsx +124 -87
  63. package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +140 -971
  64. package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +148 -888
  65. package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +135 -1019
  66. package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +137 -953
  67. package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +138 -997
  68. package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +136 -942
  69. package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +148 -1092
  70. package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +146 -1211
  71. package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +126 -615
  72. package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +144 -1164
  73. package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +167 -985
  74. package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +122 -1179
  75. package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +124 -1168
  76. package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +119 -850
  77. package/dist/icons/camera-icon/CameraIcon.stories.tsx +112 -1213
  78. package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +117 -934
  79. package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +160 -961
  80. package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +163 -961
  81. package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +144 -942
  82. package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +129 -966
  83. package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +147 -964
  84. package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +145 -975
  85. package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +150 -1142
  86. package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +114 -461
  87. package/dist/icons/coin-icon/CoinIcon.stories.tsx +124 -1322
  88. package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +117 -1318
  89. package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +114 -903
  90. package/dist/icons/command-icon/CommandIcon.stories.tsx +127 -1042
  91. package/dist/icons/copy-icon/CopyIcon.stories.tsx +123 -962
  92. package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +147 -999
  93. package/dist/icons/cross-icon/CrossIcon.stories.tsx +139 -960
  94. package/dist/icons/download-icon/DownloadIcon.stories.tsx +126 -820
  95. package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +124 -1031
  96. package/dist/icons/email-icon/EmailIcon.stories.tsx +115 -936
  97. package/dist/icons/expand-icon/ExpandIcon.stories.tsx +112 -1111
  98. package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +144 -1025
  99. package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +143 -1036
  100. package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +127 -1011
  101. package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +126 -1056
  102. package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +125 -614
  103. package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +119 -1050
  104. package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +169 -989
  105. package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +115 -1145
  106. package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +115 -1122
  107. package/dist/icons/globe-icon/GlobeIcon.stories.tsx +130 -313
  108. package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +145 -940
  109. package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +119 -1174
  110. package/dist/icons/head-icon/HeadIcon.stories.tsx +111 -916
  111. package/dist/icons/heart-icon/HeartIcon.stories.tsx +120 -1019
  112. package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +119 -683
  113. package/dist/icons/image-icon/ImageIcon.stories.tsx +105 -1121
  114. package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +111 -1192
  115. package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +136 -1256
  116. package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +159 -962
  117. package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +161 -1385
  118. package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +124 -972
  119. package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +119 -948
  120. package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +119 -942
  121. package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +108 -1215
  122. package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +154 -1517
  123. package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +110 -1188
  124. package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +119 -678
  125. package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +123 -1184
  126. package/dist/icons/message-icon/MessageIcon.stories.tsx +114 -538
  127. package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +116 -1158
  128. package/dist/icons/moon-icon/MoonIcon.stories.tsx +120 -536
  129. package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +109 -1184
  130. package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +115 -1134
  131. package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +119 -971
  132. package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +111 -1100
  133. package/dist/icons/notes-icon/NotesIcon.stories.tsx +119 -1101
  134. package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +109 -1111
  135. package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +122 -684
  136. package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +113 -954
  137. package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +112 -877
  138. package/dist/icons/pause-icon/PauseIcon.stories.tsx +113 -1000
  139. package/dist/icons/pencil-icon/PencilIcon.stories.tsx +115 -1070
  140. package/dist/icons/phone-icon/PhoneIcon.stories.tsx +115 -978
  141. package/dist/icons/plus-icon/PlusIcon.stories.tsx +106 -1093
  142. package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +107 -829
  143. package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +102 -469
  144. package/dist/icons/search-icon/SearchIcon.stories.tsx +111 -1124
  145. package/dist/icons/setting-icon/SettingIcon.stories.tsx +107 -970
  146. package/dist/icons/share-icon/ShareIcon.stories.tsx +120 -1025
  147. package/dist/icons/shield-icon/ShieldIcon.stories.tsx +117 -931
  148. package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +137 -1104
  149. package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +172 -982
  150. package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +164 -983
  151. package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +105 -958
  152. package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +158 -580
  153. package/dist/icons/spinner-gradient-icon/index.tsx +6 -1
  154. package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +158 -587
  155. package/dist/icons/spinner-solid-icon/index.tsx +6 -1
  156. package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +146 -682
  157. package/dist/icons/spinner-solid-neutral-icon/index.tsx +1 -1
  158. package/dist/icons/star-icon/StarIcon.stories.tsx +124 -904
  159. package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +112 -964
  160. package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +116 -852
  161. package/dist/icons/sun-icon/SunIcon.stories.tsx +120 -831
  162. package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +116 -950
  163. package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +123 -980
  164. package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +156 -1427
  165. package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +146 -1142
  166. package/dist/icons/tick-icon/TickIcon.stories.tsx +145 -1276
  167. package/dist/icons/trash-icon/TrashIcon.stories.tsx +108 -933
  168. package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +157 -1402
  169. package/dist/icons/upload-icon/UploadIcon.stories.tsx +115 -889
  170. package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +118 -984
  171. package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +125 -1049
  172. package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +123 -1356
  173. package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +110 -1171
  174. package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +112 -1093
  175. package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +115 -1087
  176. package/dist/icons/warning-icon/WarningIcon.stories.tsx +122 -1046
  177. package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +161 -936
  178. package/dist/index.cjs +84 -84
  179. package/dist/index.js +84 -84
  180. package/dist/styles/aural-all-theme.css +1222 -0
  181. package/dist/styles/{aural-theme.css → aural-dark-theme.css} +15 -3
  182. package/dist/styles/aural-light-theme.css +1047 -0
  183. package/package.json +1 -1
@@ -1,30 +1,30 @@
1
1
  import React from "react"
2
- import { Badge } from "@components/badge"
3
2
  import { Button } from "@components/button"
4
3
  import {
5
- ArrowRightIcon,
6
- BubbleCheckIcon,
7
- BubbleSparkleIcon,
4
+ AudioBarIcon,
8
5
  ChevronLeftIcon,
9
- ChevronUpIcon,
10
- CommandIcon,
11
- CrossCircleIcon,
6
+ ChevronRightIcon,
7
+ CircleTickIcon,
8
+ DownloadIcon,
12
9
  EditBigIcon,
13
- EyeOpenIcon,
14
- FeatureShineIcon,
15
- FileChartIcon,
10
+ FileTextIcon,
11
+ HeartIcon,
16
12
  ImageIcon,
17
- ImportFolderIcon,
18
- LightBulbSimpleIcon,
19
- MagicBookIcon,
20
13
  MaintenanceIcon,
14
+ MusicalNoteIcon,
21
15
  SearchIcon,
22
- TickIcon,
16
+ ShareIcon,
17
+ SkipForwardIcon,
18
+ SparklesSoftIcon,
19
+ StarIcon,
23
20
  TrashIcon,
24
21
  UploadIcon,
22
+ VerticalMenuIcon,
25
23
  } from "@icons/index"
26
24
  import type { Meta, StoryObj } from "@storybook/react-vite"
27
25
 
26
+ import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
27
+
28
28
  import {
29
29
  Command,
30
30
  CommandDialog,
@@ -43,81 +43,29 @@ const meta: Meta<typeof Command> = {
43
43
  component: Command,
44
44
  parameters: {
45
45
  layout: "fullscreen",
46
- backgrounds: {
47
- default: "dark",
48
- values: [
49
- { name: "dark", value: "#0a0a0a" },
50
- { name: "light", value: "#ffffff" },
51
- ],
52
- },
53
46
  docs: {
54
47
  description: {
55
- component: `
56
- # Command Component
57
-
58
- A fast, unstyled command menu component built on top of CMDK with integration to our design system's List components. Perfect for creating command palettes, search interfaces, and quick action menus.
59
-
60
- ## Features
61
-
62
- - **Fast Fuzzy Search**: Built-in fuzzy search with instant results
63
- - **Keyboard Navigation**: Full keyboard support with arrow keys and Enter
64
- - **Grouping**: Organize commands into logical groups with labels
65
- - **Shortcuts**: Display keyboard shortcuts for commands
66
- - **Icon Support**: Rich icon support for visual command identification
67
- - **Customizable**: Extensive theming and styling options
68
- - **Accessible**: ARIA compliant with screen reader support
69
- - **Dialog Mode**: Can be used as a modal command palette
70
- - **Empty States**: Customizable empty state when no results found
71
-
72
- ## Usage Examples
73
-
74
- ### Basic Command Menu
75
- \`\`\`tsx
76
- <Command>
77
- <CommandInput placeholder="Type a command..." />
78
- <CommandList>
79
- <CommandEmpty>No results found.</CommandEmpty>
80
- <CommandGroup>
81
- <CommandLabel>Suggestions</CommandLabel>
82
- <CommandItem>
83
- <FileChartIcon />
84
- New File
85
- <CommandShortcut>⌘N</CommandShortcut>
86
- </CommandItem>
87
- </CommandGroup>
88
- </CommandList>
89
- </Command>
90
- \`\`\`
91
-
92
- ### Command Dialog
93
- \`\`\`tsx
94
- <CommandDialog open={open} onOpenChange={setOpen}>
95
- <CommandInput placeholder="Search commands..." />
96
- <CommandList>
97
- <CommandEmpty>No commands found.</CommandEmpty>
98
- <CommandGroup>
99
- <CommandLabel>Actions</CommandLabel>
100
- <CommandItem>Save File</CommandItem>
101
- <CommandItem>Export</CommandItem>
102
- </CommandGroup>
103
- </CommandList>
104
- </CommandDialog>
105
- \`\`\`
106
-
107
- ### With Custom Styling
108
- \`\`\`tsx
109
- <Command
110
- listProps={{ variant: "elevated", size: "lg" }}
111
- classes={{ list: "custom-command-list" }}
112
- >
113
- <CommandInput />
114
- <CommandList>
115
- <CommandItem variant="destructive">Delete</CommandItem>
116
- </CommandList>
117
- </Command>
118
- \`\`\`
119
- `,
48
+ component:
49
+ "A fast command palette component built on CMDK, integrated with the design system's List components. Supports inline and modal (CommandDialog) modes, grouped items, live fuzzy search, keyboard navigation, icon support, keyboard shortcuts, and a destructive variant for dangerous actions. Use it for global search, quick actions, and context-sensitive command menus in any audio-focused or general-purpose application.",
120
50
  },
51
+ page: () => (
52
+ <AuralComponentDocsPage
53
+ features={[
54
+ {
55
+ title: "Inline & Modal Modes",
56
+ description: "Palette or dialog overlay",
57
+ },
58
+ {
59
+ title: "Fuzzy Search",
60
+ description: "Live filtering built in",
61
+ },
62
+ {
63
+ title: "Keyboard Navigation",
64
+ description: "Arrow keys and shortcuts",
65
+ },
66
+ ]}
67
+ />
68
+ ),
121
69
  },
122
70
  },
123
71
  tags: ["autodocs"],
@@ -126,172 +74,145 @@ A fast, unstyled command menu component built on top of CMDK with integration to
126
74
  export default meta
127
75
  type Story = StoryObj<typeof Command>
128
76
 
129
- // 1. Basic Command
130
- export const BasicCommand: Story = {
131
- render: () => (
132
- <div className="mx-auto max-w-lg p-8">
133
- <h3 className="mb-4 text-lg font-medium text-white">
134
- Basic Command Menu
135
- </h3>
136
- <Command className="rounded-lg border border-white/10">
137
- <CommandInput placeholder="Type a command or search..." />
138
- <CommandList>
139
- <CommandEmpty>No results found.</CommandEmpty>
140
- <CommandGroup>
141
- <CommandLabel>Suggestions</CommandLabel>
142
- <CommandItem>
143
- <SearchIcon />
144
- Search Files
145
- </CommandItem>
146
- <CommandItem>
147
- <ImageIcon />
148
- View Images
149
- </CommandItem>
150
- <CommandItem>
151
- <FileChartIcon />
152
- Open Reports
153
- </CommandItem>
154
- </CommandGroup>
155
- <CommandSeparator />
156
- <CommandGroup>
157
- <CommandLabel>Settings</CommandLabel>
158
- <CommandItem shortcut="⌘P">
159
- <EyeOpenIcon />
160
- View Profile
161
- </CommandItem>
162
- <CommandItem shortcut="⌘,">
163
- <MaintenanceIcon />
164
- Settings
165
- </CommandItem>
166
- </CommandGroup>
167
- </CommandList>
168
- </Command>
169
- </div>
170
- ),
171
- parameters: {
172
- docs: {
173
- description: {
174
- story:
175
- "A basic command menu with search input, grouped items, icons, and keyboard shortcuts.",
176
- },
177
- },
178
- },
179
- }
77
+ // ─── Configurations ──────────────────────────────────────────────────────────
180
78
 
181
- // 2. Command Dialog
182
- export const CommandDialogExample: Story = {
79
+ export const Configurations: Story = {
183
80
  render: () => {
184
- const [open, setOpen] = React.useState(false)
81
+ const [dialogOpen, setDialogOpen] = React.useState(false)
185
82
 
186
83
  React.useEffect(() => {
187
84
  const down = (e: KeyboardEvent) => {
188
85
  if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
189
86
  e.preventDefault()
190
- setOpen((open) => !open)
87
+ setDialogOpen((v) => !v)
191
88
  }
192
89
  }
193
-
194
90
  document.addEventListener("keydown", down)
195
91
  return () => document.removeEventListener("keydown", down)
196
92
  }, [])
197
93
 
198
94
  return (
199
95
  <div className="space-y-8 p-8">
200
- <div className="text-center">
201
- <h3 className="mb-2 text-lg font-medium text-white">
202
- Command Dialog
203
- </h3>
204
- <p className="text-sm text-white/60">
205
- Press{" "}
206
- <kbd className="bg-muted text-muted-foreground pointer-events-none inline-flex h-5 items-center gap-1 rounded border px-1.5 font-mono text-[10px] font-medium opacity-100 select-none">
207
- <span className="text-xs">⌘</span>K
208
- </kbd>{" "}
209
- to open the command dialog
210
- </p>
96
+ {/* Inline palette */}
97
+ <div className="space-y-3">
98
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
99
+ Inline Command Palette
100
+ </h4>
101
+ <div className="mx-auto max-w-md">
102
+ <Command className="border-fm-divider-secondary rounded-lg border">
103
+ <CommandInput placeholder="Search tracks, artists, playlists…" />
104
+ <CommandList>
105
+ <CommandEmpty>No results found.</CommandEmpty>
106
+ <CommandGroup>
107
+ <CommandLabel>Library</CommandLabel>
108
+ <CommandItem>
109
+ <MusicalNoteIcon />
110
+ Browse Tracks
111
+ <CommandShortcut>⌘T</CommandShortcut>
112
+ </CommandItem>
113
+ <CommandItem>
114
+ <StarIcon />
115
+ Favourites
116
+ <CommandShortcut>⌘F</CommandShortcut>
117
+ </CommandItem>
118
+ <CommandItem>
119
+ <DownloadIcon />
120
+ Downloaded
121
+ <CommandShortcut>⌘D</CommandShortcut>
122
+ </CommandItem>
123
+ </CommandGroup>
124
+ <CommandSeparator />
125
+ <CommandGroup>
126
+ <CommandLabel>Actions</CommandLabel>
127
+ <CommandItem>
128
+ <UploadIcon />
129
+ Upload Track
130
+ <CommandShortcut>⌘U</CommandShortcut>
131
+ </CommandItem>
132
+ <CommandItem variant="destructive">
133
+ <TrashIcon />
134
+ Delete Selected
135
+ <CommandShortcut>⌘⌫</CommandShortcut>
136
+ </CommandItem>
137
+ </CommandGroup>
138
+ </CommandList>
139
+ </Command>
140
+ </div>
211
141
  </div>
212
142
 
213
- <div className="flex justify-center">
214
- <Button
215
- variant="outline"
216
- onClick={() => setOpen(true)}
217
- className="relative w-80"
218
- innerClassName="flex items-center justify-between gap-2"
143
+ {/* Dialog / modal palette */}
144
+ <div className="space-y-3">
145
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
146
+ Modal Command Dialog (⌘K)
147
+ </h4>
148
+ <div className="flex justify-center">
149
+ <Button
150
+ variant="outline"
151
+ onClick={() => setDialogOpen(true)}
152
+ innerClassName="flex items-center gap-2"
153
+ >
154
+ <SearchIcon className="size-4" />
155
+ Search commands…
156
+ <span className="font-fm-brand text-fm-secondary ml-2 text-xs tracking-widest opacity-60">
157
+ ⌘K
158
+ </span>
159
+ </Button>
160
+ </div>
161
+
162
+ <CommandDialog
163
+ open={dialogOpen}
164
+ onOpenChange={setDialogOpen}
165
+ title="Command Palette"
166
+ description="Search tracks, artists, and app actions"
219
167
  >
220
- <span className="flex items-center gap-2">
221
- <SearchIcon className="mr-2 h-4 w-4" />
222
- Search commands...
223
- </span>
224
- <kbd className="font-fm-brand text-fm-xs pointer-events-none hidden h-5 items-center gap-1 rounded border px-1.5 font-medium opacity-100 select-none sm:flex">
225
- <span className="text-xs">⌘</span>K
226
- </kbd>
227
- </Button>
168
+ <CommandInput placeholder="Search tracks, artists, actions…" />
169
+ <CommandList>
170
+ <CommandEmpty>No results found.</CommandEmpty>
171
+ <CommandGroup>
172
+ <CommandLabel>Quick Actions</CommandLabel>
173
+ <CommandItem onSelect={() => setDialogOpen(false)}>
174
+ <MusicalNoteIcon />
175
+ Play Next
176
+ <CommandShortcut>⌘→</CommandShortcut>
177
+ </CommandItem>
178
+ <CommandItem onSelect={() => setDialogOpen(false)}>
179
+ <HeartIcon />
180
+ Like Current Track
181
+ <CommandShortcut>⌘L</CommandShortcut>
182
+ </CommandItem>
183
+ <CommandItem onSelect={() => setDialogOpen(false)}>
184
+ <ShareIcon />
185
+ Share Track
186
+ <CommandShortcut>⌘⇧S</CommandShortcut>
187
+ </CommandItem>
188
+ </CommandGroup>
189
+ <CommandSeparator />
190
+ <CommandGroup>
191
+ <CommandLabel>Navigation</CommandLabel>
192
+ <CommandItem onSelect={() => setDialogOpen(false)}>
193
+ <ChevronRightIcon />
194
+ Library
195
+ </CommandItem>
196
+ <CommandItem onSelect={() => setDialogOpen(false)}>
197
+ <ChevronLeftIcon />
198
+ Go Back
199
+ </CommandItem>
200
+ </CommandGroup>
201
+ <CommandSeparator />
202
+ <CommandGroup>
203
+ <CommandLabel>Danger Zone</CommandLabel>
204
+ <CommandItem
205
+ variant="destructive"
206
+ onSelect={() => setDialogOpen(false)}
207
+ >
208
+ <TrashIcon />
209
+ Remove from Library
210
+ <CommandShortcut>⌘⌫</CommandShortcut>
211
+ </CommandItem>
212
+ </CommandGroup>
213
+ </CommandList>
214
+ </CommandDialog>
228
215
  </div>
229
-
230
- <CommandDialog
231
- open={open}
232
- onOpenChange={setOpen}
233
- title="Command Palette"
234
- description="Search for commands and actions"
235
- >
236
- <CommandInput placeholder="Type a command or search..." />
237
- <CommandList>
238
- <CommandEmpty>No results found.</CommandEmpty>
239
- <CommandGroup>
240
- <CommandLabel>Quick Actions</CommandLabel>
241
- <CommandItem onSelect={() => setOpen(false)}>
242
- <FileChartIcon />
243
- New File
244
- <CommandShortcut>⌘N</CommandShortcut>
245
- </CommandItem>
246
- <CommandItem onSelect={() => setOpen(false)}>
247
- <ImportFolderIcon />
248
- Import Folder
249
- <CommandShortcut>⌘⇧N</CommandShortcut>
250
- </CommandItem>
251
- <CommandItem onSelect={() => setOpen(false)}>
252
- <UploadIcon />
253
- Upload
254
- <CommandShortcut>⌘U</CommandShortcut>
255
- </CommandItem>
256
- <CommandItem onSelect={() => setOpen(false)}>
257
- <ImageIcon />
258
- Add Image
259
- <CommandShortcut>⌘I</CommandShortcut>
260
- </CommandItem>
261
- </CommandGroup>
262
- <CommandSeparator />
263
- <CommandGroup>
264
- <CommandLabel>Navigation</CommandLabel>
265
- <CommandItem onSelect={() => setOpen(false)}>
266
- <ArrowRightIcon />
267
- Go Forward
268
- <CommandShortcut>⌘→</CommandShortcut>
269
- </CommandItem>
270
- <CommandItem onSelect={() => setOpen(false)}>
271
- <ChevronLeftIcon />
272
- Go Back
273
- <CommandShortcut>⌘←</CommandShortcut>
274
- </CommandItem>
275
- <CommandItem onSelect={() => setOpen(false)}>
276
- <ChevronUpIcon />
277
- Go Up
278
- <CommandShortcut>⌘↑</CommandShortcut>
279
- </CommandItem>
280
- </CommandGroup>
281
- <CommandSeparator />
282
- <CommandGroup>
283
- <CommandLabel>Dangerous Actions</CommandLabel>
284
- <CommandItem
285
- variant="destructive"
286
- onSelect={() => setOpen(false)}
287
- >
288
- <TrashIcon />
289
- Delete
290
- <CommandShortcut>⌘⌫</CommandShortcut>
291
- </CommandItem>
292
- </CommandGroup>
293
- </CommandList>
294
- </CommandDialog>
295
216
  </div>
296
217
  )
297
218
  },
@@ -299,254 +220,239 @@ export const CommandDialogExample: Story = {
299
220
  docs: {
300
221
  description: {
301
222
  story:
302
- "A command dialog that can be opened with ⌘K. Includes multiple command groups and keyboard shortcuts.",
223
+ "Two configuration modes side by side: an inline Command palette always visible on the page, and a CommandDialog that opens as a modal overlay triggered by a button or ⌘K. Both share the same internal anatomy — input, list, groups, separator, shortcuts, and the destructive variant.",
303
224
  },
304
225
  },
305
226
  },
306
227
  }
307
228
 
308
- // 3. File Management Commands
309
- export const FileManagementCommands: Story = {
310
- render: () => (
311
- <div className="mx-auto max-w-lg space-y-8 p-8">
312
- <h3 className="text-lg font-medium text-white">File Management</h3>
313
- <Command className="rounded-lg border border-white/10">
314
- <CommandInput placeholder="Search files and actions..." />
315
- <CommandList>
316
- <CommandEmpty>
317
- <div className="py-6 text-center">
318
- <SearchIcon className="mx-auto h-8 w-8 text-white/30" />
319
- <p className="mt-2 text-sm text-white/60">
320
- No files or actions found.
321
- </p>
322
- </div>
323
- </CommandEmpty>
324
-
325
- <CommandGroup>
326
- <CommandLabel>Create</CommandLabel>
327
- <CommandItem>
328
- <FileChartIcon />
329
- New Document
330
- <CommandShortcut>⌘N</CommandShortcut>
331
- </CommandItem>
332
- <CommandItem>
333
- <ImportFolderIcon />
334
- New Folder
335
- <CommandShortcut>⌘⇧N</CommandShortcut>
336
- </CommandItem>
337
- <CommandItem>
338
- <EditBigIcon />
339
- New Template
340
- <CommandShortcut>⌘T</CommandShortcut>
341
- </CommandItem>
342
- </CommandGroup>
343
-
344
- <CommandSeparator />
345
-
346
- <CommandGroup>
347
- <CommandLabel>Actions</CommandLabel>
348
- <CommandItem>
349
- <BubbleCheckIcon />
350
- Approve
351
- <CommandShortcut>⌘A</CommandShortcut>
352
- </CommandItem>
353
- <CommandItem>
354
- <EditBigIcon />
355
- Edit
356
- <CommandShortcut>⌘E</CommandShortcut>
357
- </CommandItem>
358
- <CommandItem>
359
- <BubbleSparkleIcon />
360
- Share
361
- <CommandShortcut>⌘⇧S</CommandShortcut>
362
- </CommandItem>
363
- <CommandItem>
364
- <TickIcon />
365
- Mark Complete
366
- <CommandShortcut>⌘⇧F</CommandShortcut>
367
- </CommandItem>
368
- </CommandGroup>
369
-
370
- <CommandSeparator />
371
-
372
- <CommandGroup>
373
- <CommandLabel>Import/Export</CommandLabel>
374
- <CommandItem>
375
- <UploadIcon />
376
- Import Files
377
- <CommandShortcut>⌘I</CommandShortcut>
378
- </CommandItem>
379
- <CommandItem>
380
- <ArrowRightIcon />
381
- Export Selection
382
- <CommandShortcut>⌘E</CommandShortcut>
383
- </CommandItem>
384
- <CommandItem>
385
- <MaintenanceIcon />
386
- Sync
387
- <CommandShortcut>⌘R</CommandShortcut>
388
- </CommandItem>
389
- </CommandGroup>
390
-
391
- <CommandSeparator />
392
-
393
- <CommandGroup>
394
- <CommandLabel>Danger Zone</CommandLabel>
395
- <CommandItem variant="destructive">
396
- <TrashIcon />
397
- Move to Trash
398
- <CommandShortcut>⌘⌫</CommandShortcut>
399
- </CommandItem>
400
- </CommandGroup>
401
- </CommandList>
402
- </Command>
403
- </div>
404
- ),
405
- parameters: {
406
- docs: {
407
- description: {
408
- story:
409
- "File management command palette with create, action, import/export, and destructive commands organized in groups.",
410
- },
411
- },
412
- },
413
- }
229
+ // ─── Interactive ──────────────────────────────────────────────────────────────
414
230
 
415
- // 4. Search and Filter Commands
416
- export const SearchAndFilterCommands: Story = {
231
+ export const Interactive: Story = {
417
232
  render: () => {
418
- const [searchTerm, setSearchTerm] = React.useState("")
233
+ const [search, setSearch] = React.useState("")
234
+ const [lastSelected, setLastSelected] = React.useState<string | null>(null)
419
235
 
420
- const allItems = [
421
- {
422
- id: "recent",
423
- label: "Recent Files",
424
- icon: <FileChartIcon />,
425
- group: "Quick Access",
426
- },
427
- {
428
- id: "images",
429
- label: "Images",
430
- icon: <ImageIcon />,
431
- group: "Quick Access",
432
- },
433
- {
434
- id: "uploads",
435
- label: "Uploads",
436
- icon: <UploadIcon />,
437
- group: "Quick Access",
438
- },
439
- {
440
- id: "magic",
441
- label: "Magic Book",
442
- icon: <MagicBookIcon />,
443
- group: "Quick Access",
444
- },
236
+ const tracks = [
445
237
  {
446
- id: "charts",
447
- label: "Charts",
448
- icon: <FileChartIcon />,
449
- group: "Apps",
238
+ id: "t1",
239
+ label: "Midnight Echoes",
240
+ artist: "Luna Vex",
241
+ group: "Tracks",
450
242
  },
451
243
  {
452
- id: "search",
453
- label: "Search",
454
- icon: <SearchIcon />,
455
- group: "Apps",
456
- },
457
- {
458
- id: "maintenance",
459
- label: "Maintenance",
460
- icon: <MaintenanceIcon />,
461
- group: "Apps",
462
- },
463
- {
464
- id: "profile",
465
- label: "Profile",
466
- icon: <EyeOpenIcon />,
467
- group: "Account",
244
+ id: "t2",
245
+ label: "Solar Drift",
246
+ artist: "The Velvet Faders",
247
+ group: "Tracks",
468
248
  },
249
+ { id: "t3", label: "Neon Requiem", artist: "Axiom", group: "Tracks" },
250
+ ]
251
+ const artists = [
252
+ { id: "a1", label: "Luna Vex", genre: "Electronic", group: "Artists" },
469
253
  {
470
- id: "features",
471
- label: "Features",
472
- icon: <FeatureShineIcon />,
473
- group: "Account",
254
+ id: "a2",
255
+ label: "The Velvet Faders",
256
+ genre: "Indie",
257
+ group: "Artists",
474
258
  },
475
259
  ]
260
+ const playlists = [
261
+ { id: "p1", label: "Late Night Drive", count: 18, group: "Playlists" },
262
+ { id: "p2", label: "Focus Mode", count: 34, group: "Playlists" },
263
+ { id: "p3", label: "Morning Energy", count: 22, group: "Playlists" },
264
+ ]
476
265
 
477
- const filteredItems = searchTerm
478
- ? allItems.filter((item) =>
479
- item.label.toLowerCase().includes(searchTerm.toLowerCase())
480
- )
481
- : allItems
266
+ const filterItems = <T extends { label: string }>(items: T[]) =>
267
+ search
268
+ ? items.filter((i) =>
269
+ i.label.toLowerCase().includes(search.toLowerCase())
270
+ )
271
+ : items
482
272
 
483
- const groupedItems = filteredItems.reduce(
484
- (acc, item) => {
485
- if (!acc[item.group]) {
486
- acc[item.group] = []
487
- }
488
- acc[item.group].push(item)
489
- return acc
490
- },
491
- {} as Record<string, typeof allItems>
492
- )
273
+ const filteredTracks = filterItems(tracks)
274
+ const filteredArtists = filterItems(artists)
275
+ const filteredPlaylists = filterItems(playlists)
276
+
277
+ const totalResults =
278
+ filteredTracks.length + filteredArtists.length + filteredPlaylists.length
493
279
 
494
280
  return (
495
- <div className="mx-auto max-w-lg space-y-8 p-8">
496
- <div className="space-y-2">
497
- <h3 className="text-lg font-medium text-white">Search and Filter</h3>
498
- <p className="text-sm text-white/60">
499
- Dynamic filtering based on search input
500
- </p>
501
- </div>
281
+ <div className="w-full p-8">
282
+ <div className="mx-auto max-w-3xl space-y-6">
283
+ <div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
284
+ {/* Controls panel */}
285
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-5 rounded-xl border p-5">
286
+ <p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
287
+ Search State
288
+ </p>
502
289
 
503
- <Command className="rounded-lg border border-white/10">
504
- <CommandInput
505
- placeholder="Search apps, files, and more..."
506
- value={searchTerm}
507
- onValueChange={setSearchTerm}
508
- />
509
- <CommandList>
510
- <CommandEmpty>
511
- <div className="py-6 text-center">
512
- <SearchIcon className="mx-auto h-8 w-8 text-white/30" />
513
- <p className="mt-2 text-sm text-white/60">
514
- No results for "{searchTerm}"
290
+ <div className="space-y-2">
291
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
292
+ Query
515
293
  </p>
516
- <p className="text-xs text-white/40">
517
- Try searching for something else
294
+ <p className="text-fm-primary font-fm-text text-fm-md leading-fm-md truncate font-medium">
295
+ {search || "—"}
518
296
  </p>
519
297
  </div>
520
- </CommandEmpty>
521
298
 
522
- {Object.entries(groupedItems).map(([group, items]) => (
523
- <React.Fragment key={group}>
524
- <CommandGroup>
525
- <CommandLabel>{group}</CommandLabel>
526
- {items.map((item) => (
527
- <CommandItem key={item.id}>
528
- {item.icon}
529
- {item.label}
530
- </CommandItem>
531
- ))}
532
- </CommandGroup>
533
- {Object.keys(groupedItems).indexOf(group) <
534
- Object.keys(groupedItems).length - 1 && <CommandSeparator />}
535
- </React.Fragment>
536
- ))}
537
- </CommandList>
538
- </Command>
539
-
540
- {searchTerm && (
541
- <div className="rounded-lg border border-white/10 bg-white/5 p-4">
542
- <h4 className="text-sm font-medium text-white">Search Stats</h4>
543
- <div className="mt-2 space-y-1 text-xs text-white/60">
544
- <p>Search term: "{searchTerm}"</p>
545
- <p>Results found: {filteredItems.length}</p>
546
- <p>Groups: {Object.keys(groupedItems).length}</p>
299
+ <div className="border-fm-divider-secondary border-t pt-4" />
300
+
301
+ <div className="space-y-2">
302
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
303
+ Results
304
+ </p>
305
+ <div className="space-y-1">
306
+ <div className="flex justify-between">
307
+ <span className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
308
+ Tracks
309
+ </span>
310
+ <span className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium">
311
+ {filteredTracks.length}
312
+ </span>
313
+ </div>
314
+ <div className="flex justify-between">
315
+ <span className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
316
+ Artists
317
+ </span>
318
+ <span className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium">
319
+ {filteredArtists.length}
320
+ </span>
321
+ </div>
322
+ <div className="flex justify-between">
323
+ <span className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
324
+ Playlists
325
+ </span>
326
+ <span className="text-fm-primary font-fm-text text-fm-sm leading-fm-sm font-medium">
327
+ {filteredPlaylists.length}
328
+ </span>
329
+ </div>
330
+ </div>
331
+ </div>
332
+
333
+ <div className="border-fm-divider-secondary border-t pt-4" />
334
+
335
+ <div className="space-y-2">
336
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
337
+ Last Selected
338
+ </p>
339
+ <p className="text-fm-primary font-fm-text text-fm-md leading-fm-md truncate font-medium">
340
+ {lastSelected ?? "—"}
341
+ </p>
342
+ </div>
343
+
344
+ {search && (
345
+ <>
346
+ <div className="border-fm-divider-secondary border-t pt-4" />
347
+ <Button
348
+ variant="outline"
349
+ size="sm"
350
+ onClick={() => setSearch("")}
351
+ className="w-full"
352
+ >
353
+ Clear search
354
+ </Button>
355
+ </>
356
+ )}
357
+ </div>
358
+
359
+ {/* Preview stage */}
360
+ <div className="flex flex-col gap-3 lg:col-span-2">
361
+ <Command className="border-fm-divider-secondary rounded-lg border">
362
+ <CommandInput
363
+ placeholder="Search tracks, artists, playlists…"
364
+ value={search}
365
+ onValueChange={setSearch}
366
+ />
367
+ <CommandList>
368
+ <CommandEmpty>
369
+ <div className="py-6 text-center">
370
+ <SearchIcon className="text-fm-tertiary mx-auto size-8" />
371
+ <p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mt-2">
372
+ No results for &quot;{search}&quot;
373
+ </p>
374
+ </div>
375
+ </CommandEmpty>
376
+
377
+ {filteredTracks.length > 0 && (
378
+ <CommandGroup>
379
+ <CommandLabel>Tracks</CommandLabel>
380
+ {filteredTracks.map((t) => (
381
+ <CommandItem
382
+ key={t.id}
383
+ onSelect={() => setLastSelected(t.label)}
384
+ >
385
+ <MusicalNoteIcon />
386
+ <span className="flex flex-col">
387
+ <span>{t.label}</span>
388
+ <span className="text-fm-tertiary text-xs">
389
+ {t.artist}
390
+ </span>
391
+ </span>
392
+ <CommandShortcut>↵</CommandShortcut>
393
+ </CommandItem>
394
+ ))}
395
+ </CommandGroup>
396
+ )}
397
+
398
+ {filteredTracks.length > 0 && filteredArtists.length > 0 && (
399
+ <CommandSeparator />
400
+ )}
401
+
402
+ {filteredArtists.length > 0 && (
403
+ <CommandGroup>
404
+ <CommandLabel>Artists</CommandLabel>
405
+ {filteredArtists.map((a) => (
406
+ <CommandItem
407
+ key={a.id}
408
+ onSelect={() => setLastSelected(a.label)}
409
+ >
410
+ <StarIcon />
411
+ <span className="flex flex-col">
412
+ <span>{a.label}</span>
413
+ <span className="text-fm-tertiary text-xs">
414
+ {a.genre}
415
+ </span>
416
+ </span>
417
+ </CommandItem>
418
+ ))}
419
+ </CommandGroup>
420
+ )}
421
+
422
+ {filteredArtists.length > 0 &&
423
+ filteredPlaylists.length > 0 && <CommandSeparator />}
424
+
425
+ {filteredPlaylists.length > 0 && (
426
+ <CommandGroup>
427
+ <CommandLabel>Playlists</CommandLabel>
428
+ {filteredPlaylists.map((p) => (
429
+ <CommandItem
430
+ key={p.id}
431
+ onSelect={() => setLastSelected(p.label)}
432
+ >
433
+ <AudioBarIcon />
434
+ <span className="flex flex-col">
435
+ <span>{p.label}</span>
436
+ <span className="text-fm-tertiary text-xs">
437
+ {p.count} tracks
438
+ </span>
439
+ </span>
440
+ </CommandItem>
441
+ ))}
442
+ </CommandGroup>
443
+ )}
444
+ </CommandList>
445
+ </Command>
446
+
447
+ <div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
448
+ <code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
449
+ {totalResults} result{totalResults !== 1 ? "s" : ""} —
450
+ navigate with ↑↓ and confirm with ↵
451
+ </code>
452
+ </div>
547
453
  </div>
548
454
  </div>
549
- )}
455
+ </div>
550
456
  </div>
551
457
  )
552
458
  },
@@ -554,432 +460,203 @@ export const SearchAndFilterCommands: Story = {
554
460
  docs: {
555
461
  description: {
556
462
  story:
557
- "Dynamic command menu that filters results based on search input with real-time statistics.",
463
+ "Live search filtering across tracks, artists, and playlists in an audio app context. The left panel shows real-time result counts and the last selected item. Use the keyboard arrow keys to navigate and Enter to select — demonstrating CMDK's built-in keyboard navigation.",
558
464
  },
559
465
  },
560
466
  },
561
467
  }
562
468
 
563
- // 5. Custom Styling Variants
564
- export const CustomStylingVariants: Story = {
565
- render: () => (
566
- <div className="space-y-8 p-8">
567
- <h3 className="text-center text-lg font-medium text-white">
568
- Custom Styling Variants
569
- </h3>
570
-
571
- <div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
572
- {/* Elevated Command */}
573
- <div className="space-y-4">
574
- <h4 className="text-sm font-medium text-white/70">Elevated Style</h4>
575
- <Command
576
- className="rounded-lg border border-white/10"
577
- listProps={{ variant: "elevated", size: "lg" }}
469
+ // ─── UseCases ─────────────────────────────────────────────────────────────────
470
+
471
+ export const UseCases: Story = {
472
+ render: () => {
473
+ const [globalOpen, setGlobalOpen] = React.useState(false)
474
+
475
+ React.useEffect(() => {
476
+ const down = (e: KeyboardEvent) => {
477
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
478
+ e.preventDefault()
479
+ setGlobalOpen((v) => !v)
480
+ }
481
+ }
482
+ document.addEventListener("keydown", down)
483
+ return () => document.removeEventListener("keydown", down)
484
+ }, [])
485
+
486
+ return (
487
+ <div className="mx-auto max-w-3xl space-y-8 p-8">
488
+ {/* 1 — Global app search */}
489
+ <div className="space-y-3">
490
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
491
+ Global App Search (⌘K)
492
+ </h4>
493
+ <div className="flex justify-center">
494
+ <Button
495
+ variant="outline"
496
+ onClick={() => setGlobalOpen(true)}
497
+ innerClassName="flex items-center gap-2"
498
+ >
499
+ <SearchIcon className="size-4" />
500
+ Search everything…
501
+ <span className="font-fm-brand text-fm-secondary ml-2 text-xs tracking-widest opacity-60">
502
+ ⌘K
503
+ </span>
504
+ </Button>
505
+ </div>
506
+ <CommandDialog
507
+ open={globalOpen}
508
+ onOpenChange={setGlobalOpen}
509
+ title="Global Search"
510
+ description="Search tracks, artists, playlists, and settings"
578
511
  >
579
- <CommandInput placeholder="Elevated command menu..." />
512
+ <CommandInput placeholder="Search everything…" />
580
513
  <CommandList>
581
514
  <CommandEmpty>No results found.</CommandEmpty>
582
515
  <CommandGroup>
583
- <CommandLabel>Premium Actions</CommandLabel>
584
- <CommandItem>
585
- <FeatureShineIcon />
586
- Premium Feature
587
- <Badge color="positive" className="ml-auto">
588
- Pro
589
- </Badge>
516
+ <CommandLabel>Tracks</CommandLabel>
517
+ <CommandItem onSelect={() => setGlobalOpen(false)}>
518
+ <MusicalNoteIcon />
519
+ Midnight Echoes — Luna Vex
590
520
  </CommandItem>
591
- <CommandItem>
592
- <BubbleSparkleIcon />
593
- Favorite
521
+ <CommandItem onSelect={() => setGlobalOpen(false)}>
522
+ <MusicalNoteIcon />
523
+ Solar Drift — The Velvet Faders
594
524
  </CommandItem>
595
525
  </CommandGroup>
596
- </CommandList>
597
- </Command>
598
- </div>
599
-
600
- {/* Compact Command */}
601
- <div className="space-y-4">
602
- <h4 className="text-sm font-medium text-white/70">Compact Style</h4>
603
- <Command
604
- className="rounded-lg border border-white/10"
605
- listProps={{ size: "sm" }}
606
- >
607
- <CommandInput
608
- placeholder="Compact menu..."
609
- classes={{ input: "text-xs" }}
610
- />
611
- <CommandList>
612
- <CommandEmpty>No results found.</CommandEmpty>
526
+ <CommandSeparator />
613
527
  <CommandGroup>
614
- <CommandLabel>Quick Actions</CommandLabel>
615
- <CommandItem>
616
- <FileChartIcon />
617
- New
618
- <CommandShortcut>⌘N</CommandShortcut>
619
- </CommandItem>
620
- <CommandItem>
621
- <SearchIcon />
622
- Find
623
- <CommandShortcut>⌘F</CommandShortcut>
528
+ <CommandLabel>Playlists</CommandLabel>
529
+ <CommandItem onSelect={() => setGlobalOpen(false)}>
530
+ <AudioBarIcon />
531
+ Late Night Drive
624
532
  </CommandItem>
625
- <CommandItem>
626
- <MaintenanceIcon />
627
- Refresh
628
- <CommandShortcut>⌘R</CommandShortcut>
533
+ <CommandItem onSelect={() => setGlobalOpen(false)}>
534
+ <AudioBarIcon />
535
+ Focus Mode
629
536
  </CommandItem>
630
537
  </CommandGroup>
631
- </CommandList>
632
- </Command>
633
- </div>
634
-
635
- {/* Flat Style */}
636
- <div className="space-y-4">
637
- <h4 className="text-sm font-medium text-white/70">Flat Style</h4>
638
- <Command
639
- className="rounded-lg border border-white/10"
640
- listProps={{ variant: "flat", rounded: "lg" }}
641
- >
642
- <CommandInput placeholder="Flat design menu..." />
643
- <CommandList>
644
- <CommandEmpty>No results found.</CommandEmpty>
538
+ <CommandSeparator />
645
539
  <CommandGroup>
646
- <CommandLabel>Interface</CommandLabel>
647
- <CommandItem>
540
+ <CommandLabel>Settings</CommandLabel>
541
+ <CommandItem onSelect={() => setGlobalOpen(false)}>
648
542
  <MaintenanceIcon />
649
- Settings
650
- </CommandItem>
651
- <CommandItem>
652
- <EyeOpenIcon />
653
- Account
543
+ Audio Quality
544
+ <CommandShortcut>⌘,</CommandShortcut>
654
545
  </CommandItem>
655
546
  </CommandGroup>
656
547
  </CommandList>
657
- </Command>
548
+ </CommandDialog>
658
549
  </div>
659
550
 
660
- {/* Custom Classes */}
661
- <div className="space-y-4">
662
- <h4 className="text-sm font-medium text-white/70">Custom Classes</h4>
663
- <Command
664
- className="rounded-lg border border-purple-500/30 bg-purple-900/10"
665
- classes={{
666
- list: "bg-purple-900/20",
667
- }}
668
- >
669
- <CommandInput
670
- placeholder="Purple themed menu..."
671
- classes={{
672
- wrapper: "border-purple-500/30",
673
- icon: "text-purple-400",
674
- }}
675
- />
676
- <CommandList>
677
- <CommandEmpty>No results found.</CommandEmpty>
678
- <CommandGroup>
679
- <CommandLabel>Custom Theme</CommandLabel>
680
- <CommandItem classes={{ root: "hover:bg-purple-500/20" }}>
681
- <FeatureShineIcon />
682
- Special Action
683
- </CommandItem>
684
- <CommandItem classes={{ root: "hover:bg-purple-500/20" }}>
685
- <BubbleSparkleIcon />
686
- Another Action
687
- </CommandItem>
688
- </CommandGroup>
689
- </CommandList>
690
- </Command>
691
- </div>
692
- </div>
693
- </div>
694
- ),
695
- parameters: {
696
- docs: {
697
- description: {
698
- story:
699
- "Various styling options including elevated, compact, flat styles and custom theming with purple color scheme.",
700
- },
701
- },
702
- },
703
- }
704
-
705
- // 6. Complex Command Structure
706
- export const ComplexCommandStructure: Story = {
707
- render: () => (
708
- <div className="mx-auto max-w-lg space-y-8 p-8">
709
- <h3 className="text-lg font-medium text-white">
710
- Complex Command Structure
711
- </h3>
712
-
713
- <Command className="rounded-lg border border-white/10">
714
- <CommandInput placeholder="Search all commands..." />
715
- <CommandList>
716
- <CommandEmpty>
717
- <div className="py-8 text-center">
718
- <SearchIcon className="mx-auto h-12 w-12 text-white/20" />
719
- <h4 className="mt-2 text-sm font-medium text-white">
720
- No commands found
721
- </h4>
722
- <p className="mt-1 text-xs text-white/60">
723
- Try adjusting your search to find what you're looking for.
724
- </p>
725
- </div>
726
- </CommandEmpty>
727
-
728
- <CommandGroup>
729
- <CommandLabel>
730
- <FileChartIcon className="mr-2" />
731
- File Operations
732
- </CommandLabel>
733
- <CommandItem>
734
- <FileChartIcon />
735
- New Document
736
- <Badge color="info" className="ml-auto">
737
- Ctrl+N
738
- </Badge>
739
- </CommandItem>
740
- <CommandItem>
741
- <ImportFolderIcon />
742
- Open Folder
743
- <CommandShortcut>⌘O</CommandShortcut>
744
- </CommandItem>
745
- <CommandItem>
746
- <EditBigIcon />
747
- Recent Files
748
- <Badge color="neutral" className="ml-auto">
749
- 5
750
- </Badge>
751
- </CommandItem>
752
- </CommandGroup>
753
-
754
- <CommandSeparator />
755
-
756
- <CommandGroup>
757
- <CommandLabel>
758
- <EyeOpenIcon className="mr-2" />
759
- User Management
760
- </CommandLabel>
761
- <CommandItem>
762
- <EyeOpenIcon />
763
- View Profile
764
- <CommandShortcut>⌘P</CommandShortcut>
765
- </CommandItem>
766
- <CommandItem>
767
- <MaintenanceIcon />
768
- User Settings
769
- <CommandShortcut>⌘,</CommandShortcut>
770
- </CommandItem>
771
- <CommandItem>
772
- <BubbleSparkleIcon />
773
- Share Profile
774
- <Badge color="warning" className="ml-auto">
775
- Beta
776
- </Badge>
777
- </CommandItem>
778
- </CommandGroup>
779
-
780
- <CommandSeparator />
781
-
782
- <CommandGroup>
783
- <CommandLabel>
784
- <UploadIcon className="mr-2" />
785
- Data Management
786
- </CommandLabel>
787
- <CommandItem>
788
- <ArrowRightIcon />
789
- Export Data
790
- <CommandShortcut>⌘E</CommandShortcut>
791
- </CommandItem>
792
- <CommandItem>
793
- <UploadIcon />
794
- Import Data
795
- <CommandShortcut>⌘I</CommandShortcut>
796
- </CommandItem>
797
- <CommandItem>
798
- <MaintenanceIcon />
799
- Sync Data
800
- <Badge color="positive" className="ml-auto">
801
- Auto
802
- </Badge>
803
- </CommandItem>
804
- </CommandGroup>
805
-
806
- <CommandSeparator />
807
-
808
- <CommandGroup>
809
- <CommandLabel>
810
- <FeatureShineIcon className="mr-2" />
811
- Features
812
- </CommandLabel>
813
- <CommandItem>
814
- <MagicBookIcon />
815
- Magic Features
816
- <Badge color="neutral" className="ml-auto">
817
- 12
818
- </Badge>
819
- </CommandItem>
820
- <CommandItem>
821
- <LightBulbSimpleIcon />
822
- Ideas
823
- <Badge color="positive" className="ml-auto">
824
- New
825
- </Badge>
826
- </CommandItem>
827
- <CommandItem>
828
- <FeatureShineIcon />
829
- Special Features
830
- <CommandShortcut>⌘⇧S</CommandShortcut>
831
- </CommandItem>
832
- </CommandGroup>
833
-
834
- <CommandSeparator />
835
-
836
- <CommandGroup>
837
- <CommandLabel>
838
- <TrashIcon className="mr-2" />
839
- Dangerous Actions
840
- </CommandLabel>
841
- <CommandItem variant="destructive">
842
- <TrashIcon />
843
- Delete Account
844
- <Badge color="negative" className="ml-auto">
845
- !
846
- </Badge>
847
- </CommandItem>
848
- <CommandItem variant="destructive">
849
- <CrossCircleIcon />
850
- Reset All Data
851
- <CommandShortcut>⌘⇧R</CommandShortcut>
852
- </CommandItem>
853
- </CommandGroup>
854
- </CommandList>
855
- </Command>
856
- </div>
857
- ),
858
- parameters: {
859
- docs: {
860
- description: {
861
- story:
862
- "Complex command structure with multiple groups, icons in labels, badges for additional context, and both regular and destructive actions.",
863
- },
864
- },
865
- },
866
- }
867
-
868
- // 7. Performance Demo
869
- export const PerformanceDemo: Story = {
870
- render: () => {
871
- const [itemCount, setItemCount] = React.useState(100)
872
-
873
- const generateItems = (count: number) => {
874
- const categories = ["Files", "Actions", "Settings", "Images", "Tools"]
875
- const icons = [
876
- FileChartIcon,
877
- EditBigIcon,
878
- MaintenanceIcon,
879
- ImageIcon,
880
- CommandIcon,
881
- ]
882
- const items = []
883
-
884
- for (let i = 0; i < count; i++) {
885
- const categoryIndex = i % categories.length
886
- items.push({
887
- id: i,
888
- label: `${categories[categoryIndex]} Item ${i + 1}`,
889
- icon: icons[categoryIndex],
890
- category: categories[categoryIndex],
891
- shortcut: i % 10 === 0 ? `⌘${i / 10}` : undefined,
892
- })
893
- }
894
-
895
- return items
896
- }
897
-
898
- const items = React.useMemo(() => generateItems(itemCount), [itemCount])
899
- const groupedItems = React.useMemo(() => {
900
- return items.reduce(
901
- (acc, item) => {
902
- if (!acc[item.category]) {
903
- acc[item.category] = []
904
- }
905
- acc[item.category].push(item)
906
- return acc
907
- },
908
- {} as Record<string, typeof items>
909
- )
910
- }, [items])
911
-
912
- return (
913
- <div className="space-y-8 p-8">
914
- <div className="space-y-4 text-center">
915
- <h3 className="text-lg font-medium text-white">Performance Demo</h3>
916
- <p className="text-sm text-white/60">
917
- Test command menu performance with large datasets
918
- </p>
919
-
920
- <div className="flex items-center justify-center gap-4">
921
- <label className="text-sm text-white/70">Items:</label>
922
- <select
923
- value={itemCount}
924
- onChange={(e) => setItemCount(Number(e.target.value))}
925
- className="rounded border border-white/20 bg-white/10 px-3 py-1 text-white"
926
- >
927
- <option value={50}>50</option>
928
- <option value={100}>100</option>
929
- <option value={500}>500</option>
930
- <option value={1000}>1000</option>
931
- </select>
932
- <Badge color="info">{itemCount} items</Badge>
551
+ {/* 2 File management commands */}
552
+ <div className="space-y-3">
553
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
554
+ File Management Commands
555
+ </h4>
556
+ <div className="mx-auto max-w-md">
557
+ <Command className="border-fm-divider-secondary rounded-lg border">
558
+ <CommandInput placeholder="Search file actions…" />
559
+ <CommandList>
560
+ <CommandEmpty>No file actions found.</CommandEmpty>
561
+ <CommandGroup>
562
+ <CommandLabel>Create</CommandLabel>
563
+ <CommandItem>
564
+ <FileTextIcon />
565
+ New Playlist
566
+ <CommandShortcut>⌘N</CommandShortcut>
567
+ </CommandItem>
568
+ <CommandItem>
569
+ <UploadIcon />
570
+ Upload Track
571
+ <CommandShortcut>⌘U</CommandShortcut>
572
+ </CommandItem>
573
+ </CommandGroup>
574
+ <CommandSeparator />
575
+ <CommandGroup>
576
+ <CommandLabel>Manage</CommandLabel>
577
+ <CommandItem>
578
+ <EditBigIcon />
579
+ Edit Metadata
580
+ <CommandShortcut>⌘E</CommandShortcut>
581
+ </CommandItem>
582
+ <CommandItem>
583
+ <DownloadIcon />
584
+ Download Offline
585
+ <CommandShortcut>⌘D</CommandShortcut>
586
+ </CommandItem>
587
+ <CommandItem>
588
+ <ShareIcon />
589
+ Share
590
+ <CommandShortcut>⌘⇧S</CommandShortcut>
591
+ </CommandItem>
592
+ </CommandGroup>
593
+ <CommandSeparator />
594
+ <CommandGroup>
595
+ <CommandLabel>Danger Zone</CommandLabel>
596
+ <CommandItem variant="destructive">
597
+ <TrashIcon />
598
+ Remove from Library
599
+ <CommandShortcut>⌘⌫</CommandShortcut>
600
+ </CommandItem>
601
+ </CommandGroup>
602
+ </CommandList>
603
+ </Command>
933
604
  </div>
934
605
  </div>
935
606
 
936
- <div className="mx-auto max-w-lg">
937
- <Command className="rounded-lg border border-white/10">
938
- <CommandInput placeholder={`Search ${itemCount} items...`} />
939
- <CommandList>
940
- <CommandEmpty>
941
- No results found in {itemCount} items.
942
- </CommandEmpty>
943
-
944
- {Object.entries(groupedItems).map(([category, categoryItems]) => (
945
- <React.Fragment key={category}>
946
- <CommandGroup>
947
- <CommandLabel>
948
- {category} ({categoryItems.length})
949
- </CommandLabel>
950
- {categoryItems.map((item) => {
951
- const IconComponent = item.icon
952
- return (
953
- <CommandItem key={item.id}>
954
- <IconComponent />
955
- {item.label}
956
- {item.shortcut && (
957
- <CommandShortcut>{item.shortcut}</CommandShortcut>
958
- )}
959
- </CommandItem>
960
- )
961
- })}
962
- </CommandGroup>
963
- {Object.keys(groupedItems).indexOf(category) <
964
- Object.keys(groupedItems).length - 1 && (
965
- <CommandSeparator />
966
- )}
967
- </React.Fragment>
968
- ))}
969
- </CommandList>
970
- </Command>
971
- </div>
972
-
973
- <div className="mx-auto max-w-lg rounded-lg border border-white/10 bg-white/5 p-4">
974
- <h4 className="text-sm font-medium text-white">Performance Info</h4>
975
- <div className="mt-2 grid grid-cols-2 gap-4 text-xs text-white/60">
976
- <div>Total Items: {itemCount}</div>
977
- <div>Categories: {Object.keys(groupedItems).length}</div>
978
- <div>
979
- Avg per Category:{" "}
980
- {Math.round(itemCount / Object.keys(groupedItems).length)}
981
- </div>
982
- <div>With Shortcuts: {items.filter((i) => i.shortcut).length}</div>
607
+ {/* 3 — Action shortcuts */}
608
+ <div className="space-y-3">
609
+ <h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
610
+ Playback Action Shortcuts
611
+ </h4>
612
+ <div className="mx-auto max-w-md">
613
+ <Command className="border-fm-divider-secondary rounded-lg border">
614
+ <CommandInput placeholder="Search playback actions…" />
615
+ <CommandList>
616
+ <CommandEmpty>No playback actions found.</CommandEmpty>
617
+ <CommandGroup>
618
+ <CommandLabel>Now Playing</CommandLabel>
619
+ <CommandItem>
620
+ <HeartIcon />
621
+ Like Track
622
+ <CommandShortcut>⌘L</CommandShortcut>
623
+ </CommandItem>
624
+ <CommandItem>
625
+ <SkipForwardIcon />
626
+ Skip to Next
627
+ <CommandShortcut>⌘→</CommandShortcut>
628
+ </CommandItem>
629
+ <CommandItem>
630
+ <AudioBarIcon />
631
+ Add to Queue
632
+ <CommandShortcut>⌘Q</CommandShortcut>
633
+ </CommandItem>
634
+ <CommandItem>
635
+ <SparklesSoftIcon />
636
+ Start Radio
637
+ <CommandShortcut>⌘R</CommandShortcut>
638
+ </CommandItem>
639
+ <CommandItem>
640
+ <CircleTickIcon />
641
+ Mark as Listened
642
+ </CommandItem>
643
+ </CommandGroup>
644
+ <CommandSeparator />
645
+ <CommandGroup>
646
+ <CommandLabel>View</CommandLabel>
647
+ <CommandItem>
648
+ <ImageIcon />
649
+ Show Album Art
650
+ <CommandShortcut>⌘⇧A</CommandShortcut>
651
+ </CommandItem>
652
+ <CommandItem>
653
+ <VerticalMenuIcon />
654
+ Track Info
655
+ <CommandShortcut>⌘I</CommandShortcut>
656
+ </CommandItem>
657
+ </CommandGroup>
658
+ </CommandList>
659
+ </Command>
983
660
  </div>
984
661
  </div>
985
662
  </div>
@@ -989,7 +666,7 @@ export const PerformanceDemo: Story = {
989
666
  docs: {
990
667
  description: {
991
668
  story:
992
- "Performance demonstration with configurable item counts to test search and rendering performance with large datasets.",
669
+ "Three real-world use cases in a single export: (1) a global app search dialog triggered by ⌘K covering tracks, playlists, and settings; (2) a file-management command palette for create/manage/delete operations; (3) a playback action shortcut palette for controlling the currently playing track without leaving the keyboard.",
993
670
  },
994
671
  },
995
672
  },