better-cmdk 0.0.2 → 0.0.4

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 (2) hide show
  1. package/README.md +181 -117
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -37,53 +37,178 @@ Add the required CSS variables to your global styles:
37
37
 
38
38
  ```css
39
39
  :root {
40
- --background: 0 0% 100%;
41
- --foreground: 240 10% 3.9%;
42
- --popover: 0 0% 100%;
43
- --popover-foreground: 240 10% 3.9%;
44
- --primary: 240 5.9% 10%;
45
- --primary-foreground: 0 0% 98%;
46
- --muted: 240 4.8% 95.9%;
47
- --muted-foreground: 240 3.8% 46.1%;
48
- --accent: 240 4.8% 95.9%;
49
- --accent-foreground: 240 5.9% 10%;
50
- --border: 240 5.9% 90%;
51
- --input: 240 5.9% 90%;
52
- --ring: 240 5.9% 10%;
53
- --radius: 0.5rem;
40
+ --radius: 0.625rem;
41
+ --background: oklch(1 0 0);
42
+ --foreground: oklch(0.145 0 0);
43
+ --popover: oklch(1 0 0);
44
+ --popover-foreground: oklch(0.145 0 0);
45
+ --primary: oklch(0.205 0 0);
46
+ --primary-foreground: oklch(0.985 0 0);
47
+ --muted: oklch(0.97 0 0);
48
+ --muted-foreground: oklch(0.556 0 0);
49
+ --border: oklch(0.922 0 0);
50
+ --input: oklch(0.922 0 0);
51
+ --ring: oklch(0.708 0 0);
54
52
  }
55
53
 
56
54
  @theme inline {
57
- --color-background: hsl(var(--background));
58
- --color-foreground: hsl(var(--foreground));
59
- --color-popover: hsl(var(--popover));
60
- --color-popover-foreground: hsl(var(--popover-foreground));
61
- --color-primary: hsl(var(--primary));
62
- --color-primary-foreground: hsl(var(--primary-foreground));
63
- --color-muted: hsl(var(--muted));
64
- --color-muted-foreground: hsl(var(--muted-foreground));
65
- --color-accent: hsl(var(--accent));
66
- --color-accent-foreground: hsl(var(--accent-foreground));
67
- --color-border: hsl(var(--border));
68
- --color-input: hsl(var(--input));
69
- --color-ring: hsl(var(--ring));
55
+ --color-background: var(--background);
56
+ --color-foreground: var(--foreground);
57
+ --color-popover: var(--popover);
58
+ --color-popover-foreground: var(--popover-foreground);
59
+ --color-primary: var(--primary);
60
+ --color-primary-foreground: var(--primary-foreground);
61
+ --color-muted: var(--muted);
62
+ --color-muted-foreground: var(--muted-foreground);
63
+ --color-border: var(--border);
64
+ --color-input: var(--input);
65
+ --color-ring: var(--ring);
70
66
  }
71
67
  ```
72
68
 
73
69
  ## Usage
74
70
 
71
+ The recommended way to use better-cmdk is with the declarative `commands` prop. Define your commands as data and let the component handle rendering, grouping, and search.
72
+
73
+ ```tsx
74
+ "use client";
75
+
76
+ import { useState, useEffect } from "react";
77
+ import { CalendarIcon, SearchIcon, UserIcon, SettingsIcon } from "lucide-react";
78
+ import { CommandMenu, type CommandDefinition } from "better-cmdk";
79
+
80
+ const commands: CommandDefinition[] = [
81
+ {
82
+ name: "calendar",
83
+ label: "Calendar",
84
+ icon: <CalendarIcon className="size-4" />,
85
+ group: "Suggestions",
86
+ onSelect: () => console.log("Calendar selected"),
87
+ },
88
+ {
89
+ name: "search",
90
+ label: "Search",
91
+ icon: <SearchIcon className="size-4" />,
92
+ group: "Suggestions",
93
+ onSelect: () => console.log("Search selected"),
94
+ },
95
+ {
96
+ name: "profile",
97
+ label: "Profile",
98
+ icon: <UserIcon className="size-4" />,
99
+ group: "Settings",
100
+ shortcut: "⌘P",
101
+ onSelect: () => console.log("Profile selected"),
102
+ },
103
+ {
104
+ name: "settings",
105
+ label: "Settings",
106
+ icon: <SettingsIcon className="size-4" />,
107
+ group: "Settings",
108
+ shortcut: "⌘S",
109
+ onSelect: () => console.log("Settings selected"),
110
+ },
111
+ ];
112
+
113
+ export function CommandPalette() {
114
+ const [open, setOpen] = useState(false);
115
+
116
+ useEffect(() => {
117
+ const down = (e: KeyboardEvent) => {
118
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
119
+ e.preventDefault();
120
+ setOpen((open) => !open);
121
+ }
122
+ };
123
+
124
+ document.addEventListener("keydown", down);
125
+ return () => document.removeEventListener("keydown", down);
126
+ }, []);
127
+
128
+ return (
129
+ <CommandMenu
130
+ open={open}
131
+ onOpenChange={setOpen}
132
+ commands={commands}
133
+ commandsPlaceholder="Search or ask AI..."
134
+ />
135
+ );
136
+ }
137
+ ```
138
+
139
+ ### CommandDefinition
140
+
141
+ Each command in the `commands` array supports:
142
+
143
+ | Property | Type | Description |
144
+ |----------|------|-------------|
145
+ | `name` | `string` | **Required.** Unique key used for search matching |
146
+ | `label` | `string` | Display text (falls back to `name`) |
147
+ | `group` | `string` | Group heading — commands with the same group appear together |
148
+ | `icon` | `ReactNode` | Icon rendered before the label |
149
+ | `shortcut` | `string` | Keyboard shortcut hint (right-aligned) |
150
+ | `keywords` | `string[]` | Extra search terms |
151
+ | `disabled` | `boolean` | Grayed out, not selectable |
152
+ | `onSelect` | `() => void` | Called when the command is selected |
153
+
154
+ ### CommandMenu Props
155
+
156
+ | Prop | Type | Default | Description |
157
+ |------|------|---------|-------------|
158
+ | `commands` | `CommandDefinition[]` | — | Declarative command definitions |
159
+ | `commandsPlaceholder` | `string` | `"Search or ask AI..."` | Input placeholder |
160
+ | `commandsAskAILabel` | `string` | `"Ask AI"` | Label for the AI trigger |
161
+ | `open` | `boolean` | — | Controlled open state |
162
+ | `onOpenChange` | `(open: boolean) => void` | — | Open state callback |
163
+ | `corners` | `"none" \| "sm" \| "md" \| "lg" \| "xl"` | `"xl"` | Border radius |
164
+ | `borderColor` | `string` | — | Custom ring color |
165
+ | `chatEndpoint` | `string` | — | API endpoint for built-in AI chat |
166
+ | `chat` | `ExternalChat` | — | External chat integration |
167
+ | `onModeChange` | `(mode: CommandMenuMode) => void` | — | Fires when switching between command/chat |
168
+ | `historyStorageKey` | `string` | — | localStorage key for chat history |
169
+ | `maxConversations` | `number` | — | Max saved chat conversations |
170
+
171
+ ### AI Chat
172
+
173
+ Enable the built-in AI chat by providing either a `chatEndpoint` or an external `chat` object:
174
+
175
+ ```tsx
176
+ // Built-in chat with an API endpoint
177
+ <CommandMenu
178
+ commands={commands}
179
+ chatEndpoint="/api/chat"
180
+ open={open}
181
+ onOpenChange={setOpen}
182
+ />
183
+
184
+ // External chat integration (e.g. Vercel AI SDK useChat)
185
+ <CommandMenu
186
+ commands={commands}
187
+ chat={externalChat}
188
+ open={open}
189
+ onOpenChange={setOpen}
190
+ />
191
+ ```
192
+
193
+ Users can switch to chat mode via `⌘ Enter` or by selecting the "Ask AI" item.
194
+
195
+ ## Advanced: Custom Children
196
+
197
+ For full control over the command list rendering, you can pass children instead of `commands`. This approach is compatible with shadcn/ui patterns if you're migrating from an existing setup.
198
+
199
+ > **Note:** When both `commands` and `children` are provided, `commands` takes precedence.
200
+
75
201
  ```tsx
76
202
  "use client";
77
203
 
78
204
  import { useState, useEffect } from "react";
79
205
  import {
80
- CommandDialog,
206
+ CommandMenu,
207
+ CommandInput,
208
+ CommandList,
81
209
  CommandEmpty,
82
210
  CommandGroup,
83
- CommandInput,
84
211
  CommandItem,
85
- CommandList,
86
- CommandSeparator,
87
212
  CommandShortcut,
88
213
  } from "better-cmdk";
89
214
 
@@ -103,10 +228,9 @@ export function CommandPalette() {
103
228
  }, []);
104
229
 
105
230
  return (
106
- <CommandDialog open={open} onOpenChange={setOpen}>
107
- <CommandInput placeholder="Type a command or search..." />
231
+ <CommandMenu open={open} onOpenChange={setOpen}>
232
+ <CommandInput placeholder="Type a command or search..." showSendButton />
108
233
  <CommandList>
109
- <CommandEmpty>No results found.</CommandEmpty>
110
234
  <CommandGroup heading="Suggestions">
111
235
  <CommandItem>
112
236
  <span>Calendar</span>
@@ -115,7 +239,6 @@ export function CommandPalette() {
115
239
  <span>Search</span>
116
240
  </CommandItem>
117
241
  </CommandGroup>
118
- <CommandSeparator />
119
242
  <CommandGroup heading="Settings">
120
243
  <CommandItem>
121
244
  <span>Profile</span>
@@ -126,107 +249,48 @@ export function CommandPalette() {
126
249
  <CommandShortcut>⌘S</CommandShortcut>
127
250
  </CommandItem>
128
251
  </CommandGroup>
252
+ <CommandEmpty />
129
253
  </CommandList>
130
- </CommandDialog>
254
+ </CommandMenu>
131
255
  );
132
256
  }
133
257
  ```
134
258
 
135
- ## Components
136
-
137
- ### CommandDialog
138
-
139
- The main dialog wrapper. Opens as a modal command palette.
140
-
141
- ```tsx
142
- <CommandDialog
143
- open={open}
144
- onOpenChange={setOpen}
145
- title="Command Palette" // optional, for accessibility
146
- description="Search commands" // optional, for accessibility
147
- >
148
- {children}
149
- </CommandDialog>
150
- ```
151
-
152
- ### CommandInput
153
-
154
- Search input with built-in search icon.
155
-
156
- ```tsx
157
- <CommandInput placeholder="Search..." />
158
- ```
159
-
160
- ### CommandList
259
+ ### Render Props
161
260
 
162
- Scrollable container for command items.
261
+ Children can also be a function to access internal state:
163
262
 
164
263
  ```tsx
165
- <CommandList>{children}</CommandList>
166
- ```
167
-
168
- ### CommandGroup
169
-
170
- Groups related commands with an optional heading.
171
-
172
- ```tsx
173
- <CommandGroup heading="Actions">{children}</CommandGroup>
174
- ```
175
-
176
- ### CommandItem
177
-
178
- Individual selectable command item.
179
-
180
- ```tsx
181
- <CommandItem onSelect={() => console.log("Selected!")}>
182
- <Icon className="mr-2 h-4 w-4" />
183
- <span>Label</span>
184
- </CommandItem>
264
+ <CommandMenu open={open} onOpenChange={setOpen}>
265
+ {({ mode, messages, status, isEnabled }) => (
266
+ <>
267
+ <CommandInput placeholder="Search..." showSendButton />
268
+ <CommandList>
269
+ {/* Custom rendering based on mode/status */}
270
+ </CommandList>
271
+ </>
272
+ )}
273
+ </CommandMenu>
185
274
  ```
186
275
 
187
- ### CommandShortcut
188
-
189
- Displays keyboard shortcut hints.
190
-
191
- ```tsx
192
- <CommandShortcut>⌘K</CommandShortcut>
193
- ```
276
+ ## Styling
194
277
 
195
- ### CommandSeparator
278
+ The component uses Tailwind CSS with the shadcn/ui design tokens. Customize by:
196
279
 
197
- Visual separator between groups.
280
+ 1. Overriding CSS variables
281
+ 2. Passing `className` props to components
282
+ 3. Using the `cn()` utility for conditional classes
198
283
 
199
- ```tsx
200
- <CommandSeparator />
201
- ```
284
+ ## Telemetry
202
285
 
203
- ### CommandEmpty
286
+ better-cmdk collects anonymous error and performance data via [Sentry](https://sentry.io) to help improve reliability. No personally identifiable information (PII) is collected — user data, cookies, headers, and breadcrumbs are stripped before transmission.
204
287
 
205
- Shown when no results match the search.
288
+ To opt out, set the environment variable:
206
289
 
207
- ```tsx
208
- <CommandEmpty>No results found.</CommandEmpty>
209
290
  ```
210
-
211
- ## Subpath Exports
212
-
213
- Import specific components directly:
214
-
215
- ```tsx
216
- import { Command, CommandDialog } from "better-cmdk/command";
217
- import { Dialog, DialogContent } from "better-cmdk/dialog";
218
- import { Button } from "better-cmdk/button";
219
- import { cn } from "better-cmdk/utils";
291
+ BETTER_CMDK_TELEMETRY_DISABLED=1
220
292
  ```
221
293
 
222
- ## Styling
223
-
224
- The component uses Tailwind CSS with the shadcn/ui design tokens. Customize by:
225
-
226
- 1. Overriding CSS variables
227
- 2. Passing `className` props to components
228
- 3. Using the `cn()` utility for conditional classes
229
-
230
294
  ## License
231
295
 
232
296
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-cmdk",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "types": "./index.ts",
6
6
  "exports": {