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.
- package/README.md +181 -117
- 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
|
-
--
|
|
41
|
-
--
|
|
42
|
-
--
|
|
43
|
-
--popover
|
|
44
|
-
--
|
|
45
|
-
--primary
|
|
46
|
-
--
|
|
47
|
-
--muted
|
|
48
|
-
--
|
|
49
|
-
--
|
|
50
|
-
--
|
|
51
|
-
--
|
|
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:
|
|
58
|
-
--color-foreground:
|
|
59
|
-
--color-popover:
|
|
60
|
-
--color-popover-foreground:
|
|
61
|
-
--color-primary:
|
|
62
|
-
--color-primary-foreground:
|
|
63
|
-
--color-muted:
|
|
64
|
-
--color-muted-foreground:
|
|
65
|
-
--color-
|
|
66
|
-
--color-
|
|
67
|
-
--color-
|
|
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
|
-
|
|
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
|
-
<
|
|
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
|
-
</
|
|
254
|
+
</CommandMenu>
|
|
131
255
|
);
|
|
132
256
|
}
|
|
133
257
|
```
|
|
134
258
|
|
|
135
|
-
|
|
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
|
-
|
|
261
|
+
Children can also be a function to access internal state:
|
|
163
262
|
|
|
164
263
|
```tsx
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
Displays keyboard shortcut hints.
|
|
190
|
-
|
|
191
|
-
```tsx
|
|
192
|
-
<CommandShortcut>⌘K</CommandShortcut>
|
|
193
|
-
```
|
|
276
|
+
## Styling
|
|
194
277
|
|
|
195
|
-
|
|
278
|
+
The component uses Tailwind CSS with the shadcn/ui design tokens. Customize by:
|
|
196
279
|
|
|
197
|
-
|
|
280
|
+
1. Overriding CSS variables
|
|
281
|
+
2. Passing `className` props to components
|
|
282
|
+
3. Using the `cn()` utility for conditional classes
|
|
198
283
|
|
|
199
|
-
|
|
200
|
-
<CommandSeparator />
|
|
201
|
-
```
|
|
284
|
+
## Telemetry
|
|
202
285
|
|
|
203
|
-
|
|
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
|
-
|
|
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
|