better-cmdk 0.0.2 → 0.0.3

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 +158 -90
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -72,18 +72,147 @@ Add the required CSS variables to your global styles:
72
72
 
73
73
  ## Usage
74
74
 
75
+ 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.
76
+
77
+ ```tsx
78
+ "use client";
79
+
80
+ import { useState, useEffect } from "react";
81
+ import { CalendarIcon, SearchIcon, UserIcon, SettingsIcon } from "lucide-react";
82
+ import { CommandMenu, type CommandDefinition } from "better-cmdk";
83
+
84
+ const commands: CommandDefinition[] = [
85
+ {
86
+ name: "calendar",
87
+ label: "Calendar",
88
+ icon: <CalendarIcon className="size-4" />,
89
+ group: "Suggestions",
90
+ onSelect: () => console.log("Calendar selected"),
91
+ },
92
+ {
93
+ name: "search",
94
+ label: "Search",
95
+ icon: <SearchIcon className="size-4" />,
96
+ group: "Suggestions",
97
+ onSelect: () => console.log("Search selected"),
98
+ },
99
+ {
100
+ name: "profile",
101
+ label: "Profile",
102
+ icon: <UserIcon className="size-4" />,
103
+ group: "Settings",
104
+ shortcut: "⌘P",
105
+ onSelect: () => console.log("Profile selected"),
106
+ },
107
+ {
108
+ name: "settings",
109
+ label: "Settings",
110
+ icon: <SettingsIcon className="size-4" />,
111
+ group: "Settings",
112
+ shortcut: "⌘S",
113
+ onSelect: () => console.log("Settings selected"),
114
+ },
115
+ ];
116
+
117
+ export function CommandPalette() {
118
+ const [open, setOpen] = useState(false);
119
+
120
+ useEffect(() => {
121
+ const down = (e: KeyboardEvent) => {
122
+ if (e.key === "k" && (e.metaKey || e.ctrlKey)) {
123
+ e.preventDefault();
124
+ setOpen((open) => !open);
125
+ }
126
+ };
127
+
128
+ document.addEventListener("keydown", down);
129
+ return () => document.removeEventListener("keydown", down);
130
+ }, []);
131
+
132
+ return (
133
+ <CommandMenu
134
+ open={open}
135
+ onOpenChange={setOpen}
136
+ commands={commands}
137
+ commandsPlaceholder="Search or ask AI..."
138
+ />
139
+ );
140
+ }
141
+ ```
142
+
143
+ ### CommandDefinition
144
+
145
+ Each command in the `commands` array supports:
146
+
147
+ | Property | Type | Description |
148
+ |----------|------|-------------|
149
+ | `name` | `string` | **Required.** Unique key used for search matching |
150
+ | `label` | `string` | Display text (falls back to `name`) |
151
+ | `group` | `string` | Group heading — commands with the same group appear together |
152
+ | `icon` | `ReactNode` | Icon rendered before the label |
153
+ | `shortcut` | `string` | Keyboard shortcut hint (right-aligned) |
154
+ | `keywords` | `string[]` | Extra search terms |
155
+ | `disabled` | `boolean` | Grayed out, not selectable |
156
+ | `onSelect` | `() => void` | Called when the command is selected |
157
+
158
+ ### CommandMenu Props
159
+
160
+ | Prop | Type | Default | Description |
161
+ |------|------|---------|-------------|
162
+ | `commands` | `CommandDefinition[]` | — | Declarative command definitions |
163
+ | `commandsPlaceholder` | `string` | `"Search or ask AI..."` | Input placeholder |
164
+ | `commandsAskAILabel` | `string` | `"Ask AI"` | Label for the AI trigger |
165
+ | `open` | `boolean` | — | Controlled open state |
166
+ | `onOpenChange` | `(open: boolean) => void` | — | Open state callback |
167
+ | `corners` | `"none" \| "sm" \| "md" \| "lg" \| "xl"` | `"xl"` | Border radius |
168
+ | `borderColor` | `string` | — | Custom ring color |
169
+ | `chatEndpoint` | `string` | — | API endpoint for built-in AI chat |
170
+ | `chat` | `ExternalChat` | — | External chat integration |
171
+ | `onModeChange` | `(mode: CommandMenuMode) => void` | — | Fires when switching between command/chat |
172
+ | `historyStorageKey` | `string` | — | localStorage key for chat history |
173
+ | `maxConversations` | `number` | — | Max saved chat conversations |
174
+
175
+ ### AI Chat
176
+
177
+ Enable the built-in AI chat by providing either a `chatEndpoint` or an external `chat` object:
178
+
179
+ ```tsx
180
+ // Built-in chat with an API endpoint
181
+ <CommandMenu
182
+ commands={commands}
183
+ chatEndpoint="/api/chat"
184
+ open={open}
185
+ onOpenChange={setOpen}
186
+ />
187
+
188
+ // External chat integration (e.g. Vercel AI SDK useChat)
189
+ <CommandMenu
190
+ commands={commands}
191
+ chat={externalChat}
192
+ open={open}
193
+ onOpenChange={setOpen}
194
+ />
195
+ ```
196
+
197
+ Users can switch to chat mode via `⌘ Enter` or by selecting the "Ask AI" item.
198
+
199
+ ## Advanced: Custom Children
200
+
201
+ 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.
202
+
203
+ > **Note:** When both `commands` and `children` are provided, `commands` takes precedence.
204
+
75
205
  ```tsx
76
206
  "use client";
77
207
 
78
208
  import { useState, useEffect } from "react";
79
209
  import {
80
- CommandDialog,
210
+ CommandMenu,
211
+ CommandInput,
212
+ CommandList,
81
213
  CommandEmpty,
82
214
  CommandGroup,
83
- CommandInput,
84
215
  CommandItem,
85
- CommandList,
86
- CommandSeparator,
87
216
  CommandShortcut,
88
217
  } from "better-cmdk";
89
218
 
@@ -103,10 +232,9 @@ export function CommandPalette() {
103
232
  }, []);
104
233
 
105
234
  return (
106
- <CommandDialog open={open} onOpenChange={setOpen}>
107
- <CommandInput placeholder="Type a command or search..." />
235
+ <CommandMenu open={open} onOpenChange={setOpen}>
236
+ <CommandInput placeholder="Type a command or search..." showSendButton />
108
237
  <CommandList>
109
- <CommandEmpty>No results found.</CommandEmpty>
110
238
  <CommandGroup heading="Suggestions">
111
239
  <CommandItem>
112
240
  <span>Calendar</span>
@@ -115,7 +243,6 @@ export function CommandPalette() {
115
243
  <span>Search</span>
116
244
  </CommandItem>
117
245
  </CommandGroup>
118
- <CommandSeparator />
119
246
  <CommandGroup heading="Settings">
120
247
  <CommandItem>
121
248
  <span>Profile</span>
@@ -126,107 +253,48 @@ export function CommandPalette() {
126
253
  <CommandShortcut>⌘S</CommandShortcut>
127
254
  </CommandItem>
128
255
  </CommandGroup>
256
+ <CommandEmpty />
129
257
  </CommandList>
130
- </CommandDialog>
258
+ </CommandMenu>
131
259
  );
132
260
  }
133
261
  ```
134
262
 
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
263
+ ### Render Props
161
264
 
162
- Scrollable container for command items.
265
+ Children can also be a function to access internal state:
163
266
 
164
267
  ```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>
268
+ <CommandMenu open={open} onOpenChange={setOpen}>
269
+ {({ mode, messages, status, isEnabled }) => (
270
+ <>
271
+ <CommandInput placeholder="Search..." showSendButton />
272
+ <CommandList>
273
+ {/* Custom rendering based on mode/status */}
274
+ </CommandList>
275
+ </>
276
+ )}
277
+ </CommandMenu>
185
278
  ```
186
279
 
187
- ### CommandShortcut
188
-
189
- Displays keyboard shortcut hints.
190
-
191
- ```tsx
192
- <CommandShortcut>⌘K</CommandShortcut>
193
- ```
280
+ ## Styling
194
281
 
195
- ### CommandSeparator
282
+ The component uses Tailwind CSS with the shadcn/ui design tokens. Customize by:
196
283
 
197
- Visual separator between groups.
284
+ 1. Overriding CSS variables
285
+ 2. Passing `className` props to components
286
+ 3. Using the `cn()` utility for conditional classes
198
287
 
199
- ```tsx
200
- <CommandSeparator />
201
- ```
288
+ ## Telemetry
202
289
 
203
- ### CommandEmpty
290
+ 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
291
 
205
- Shown when no results match the search.
292
+ To opt out, set the environment variable:
206
293
 
207
- ```tsx
208
- <CommandEmpty>No results found.</CommandEmpty>
209
294
  ```
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";
295
+ BETTER_CMDK_TELEMETRY_DISABLED=1
220
296
  ```
221
297
 
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
298
  ## License
231
299
 
232
300
  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.3",
4
4
  "type": "module",
5
5
  "types": "./index.ts",
6
6
  "exports": {