vue-command-kit 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -2,101 +2,26 @@
2
2
  <img src="https://img.shields.io/npm/v/vue-command-kit?color=blue&label=version" alt="npm">
3
3
  <img src="https://img.shields.io/badge/vue-3.4%2B-brightgreen" alt="vue">
4
4
  <img src="https://img.shields.io/badge/license-MIT-blue" alt="license">
5
- <img src="https://img.shields.io/badge/bundle-3.4kB_gzip-green" alt="size">
5
+ <img src="https://img.shields.io/badge/bundle-5.0kB_gzip-green" alt="size">
6
+ <img src="https://img.shields.io/badge/tests-117_passed-brightgreen" alt="tests">
6
7
  </p>
7
8
 
8
9
  <h1 align="center">⌘K — vue-cmdk</h1>
9
10
  <p align="center">
10
- <b>A fast, composable, unstyled command palette for Vue 3.</b><br>
11
- Press <kbd>⌘K</kbd> and take control.
11
+ A fast, composable, unstyled command palette for Vue 3.
12
12
  </p>
13
13
 
14
- <p align="center">
15
- <img src="./demo.gif" alt="vue-cmdk demo" width="720">
16
- </p>
17
-
18
- ## 🧠 Inspiration
19
-
20
- This project is heavily inspired by two great projects:
21
-
22
- | Project | Author | Description |
23
- | --- | --- | --- |
24
- | [`vue-command-palette`](https://github.com/xiaoluoboding/vue-command-palette) | [@xiaoluoboding](https://github.com/xiaoluoboding) | The first composable command palette for Vue, with 590 ★ |
25
- | [`cmdk`](https://github.com/pacocoursey/cmdk) | [@pacocoursey](https://github.com/pacocoursey) | Fast, unstyled command menu React component (10k+ ★) |
26
-
27
- ### Why another one?
14
+ ## Features
28
15
 
29
- [`vue-command-palette`](https://github.com/xiaoluoboding/vue-command-palette) pioneered the ⌘K experience for Vue. Big thanks to [@xiaoluoboding](https://github.com/xiaoluoboding) for the original idea and work 🙌
30
-
31
- However, the project has been **inactive since September 2023** — 9 issues remain unanswered, no dependencies have been updated in years, and the bundle size is 28 kB. The Vue ecosystem deserves a **well-maintained, lightweight alternative** that keeps up with modern standards.
32
-
33
- **vue-cmdk** is built to fill that gap: same compound component API, zero dependencies, half the size, with full TypeScript support and a commitment to ongoing maintenance.
34
-
35
- ### vs vue-command-palette (legacy reference)
36
-
37
- | Feature | `vue-command-palette` | `vue-cmdk` |
38
- | ---------------- | :----------------------: | :-----------: |
39
- | 📦 Bundle (min) | 28.2 kB | **11.8 kB** |
40
- | 📦 Bundle (gzip) | 9.6 kB | **3.4 kB** |
41
- | 🔍 Search | fuse.js (extra dep) | **Built-in** |
42
- | 📋 TypeScript | Partial | **Full** |
43
- | 🔄 Async items | ❌ | ✅ |
44
- | 🧩 Custom filter | ❌ | ✅ |
45
- | 🔒 Focus trap | ❌ | ✅ |
46
- | 🧹 Dependencies | 3 (fuse.js, nanoid, ...) | **0** |
47
- | 🔧 Maintenance | ❌ Inactive since 2023 | ✅ **Active** |
48
-
49
- ---
16
+ - **Compound component API** Declarative composition with `<Command.Dialog>`, `<Command.Input>`, `<Command.Item>`, and more
17
+ - **Built-in search** — Case-insensitive filtering with keyword matching and result highlighting
18
+ - **Force render** — `forceMount` prop keeps items in the DOM when filtered out (for animations)
19
+ - **Keyboard-first** — Arrow key navigation, Enter to select, Escape to close, global shortcuts
20
+ - **Unstyled** Zero CSS opinions; bring your own styles
21
+ - **TypeScript** — Full type inference, exported types, and declaration files
22
+ - **Zero runtime dependencies** — Peer dependency only on `vue ^3.4.0`
50
23
 
51
- ## ✨ Features
52
-
53
- - **🧩 Compound component API** — `<Command.Dialog>`, `<Command.List>`, `<Command.Item>`, etc.
54
- - **💄 Unstyled** — Bring your own CSS, zero opinions, full design control
55
- - **🔍 Built-in search** — Fast case-insensitive filtering with keyword matching
56
- - **⌨️ Keyboard-first** — Arrow keys, Enter, Escape — all built-in, no config needed
57
- - **📦 Tiny** — 3.4 kB gzipped, **zero runtime dependencies** (peer: `vue` only)
58
- - **🎯 TypeScript** — Full type inference and declaration files
59
- - **🔄 Dynamic items** — Pass items as a reactive array, swap anytime
60
- - **🛠 Custom filter** — Provide your own filter function
61
- - **♿ Accessible** — ARIA attributes, focus trap, `aria-live` region
62
-
63
- ## 📊 Comparison with React cmdk
64
-
65
- `vue-cmdk` is a Vue 3 port inspired by the excellent [`cmdk`](https://github.com/pacocoursey/cmdk) (React). Below is the current feature parity status:
66
-
67
- | # | Feature | React `cmdk` | `vue-cmdk` | Status |
68
- | --- | -------------------------------------------- | :-------------: | :--------------: | :--------: |
69
- | 1 | `Command` root `value` / `onValueChange` | ✅ | ❌ | 📋 Planned |
70
- | 2 | `Command` root `shouldFilter` | ✅ | ❌ | 📋 Planned |
71
- | 3 | `Command` root `loop` | ✅ | ❌ | 📋 Planned |
72
- | 4 | `Command` root `label` (aria-label) | ✅ | ❌ | 📋 Planned |
73
- | 5 | `Command.Dialog` `open` / `onOpenChange` | ✅ `open` | ✅ `visible` | ✅ |
74
- | 6 | `Command.Dialog` `container` (portal target) | ✅ | ❌ | 💡 Future |
75
- | 7 | `Command.Input` `value` / `onValueChange` | ✅ | ✅ `searchQuery` | ✅ |
76
- | 8 | `Command.Item` `forceMount` | ✅ | ❌ | 📋 Planned |
77
- | 9 | `Command.Item` `keywords` | ✅ | ✅ | ✅ |
78
- | 10 | `Command.Item` `onSelect` | ✅ | ✅ | ✅ |
79
- | 11 | `Command.Item` auto value from textContent | ✅ | ❌ | 📋 Planned |
80
- | 12 | `Command.Group` `forceMount` | ✅ | ❌ | 📋 Planned |
81
- | 13 | `Command.Group` `heading` | ✅ | ✅ | ✅ |
82
- | 14 | `Command.Separator` `alwaysRender` | ✅ | ❌ | 💡 Future |
83
- | 15 | `Command.Empty` | ✅ | ✅ | ✅ |
84
- | 16 | `Command.Loading` | ✅ | ✅ | ✅ |
85
- | 17 | `useCommandState()` state selector | ✅ | ❌ | 📋 Planned |
86
- | 18 | **Nested items / Pages** | ✅ (pattern) | ❌ | 📋 Planned |
87
- | 19 | **Built-in search / filtering** | ✅ | ✅ | ✅ |
88
- | 20 | **Custom filter function** | ✅ (rank-based) | ✅ (item-based) | ✅ |
89
- | 21 | **Global shortcut listener** | ❌ (manual) | ✅ (built-in) | ✅ Bonus |
90
- | 22 | **Keyboard navigation** | ✅ | ✅ | ✅ |
91
- | 23 | **Focus trap** | ✅ (Radix) | ✅ (custom) | ✅ |
92
- | 24 | **Zero dependencies** | ❌ (Radix UI) | ✅ (0 deps) | ✅ Better |
93
- | 25 | **TypeScript** | ✅ | ✅ | ✅ |
94
- | 26 | **Unstyled** | ✅ | ✅ | ✅ |
95
- | 27 | **Bundle size (gzip)** | ~7 kB | **3.4 kB** | ✅ Smaller |
96
-
97
- > **Legend** — ✅ Done · 📋 High/Medium priority · 💡 Low priority / Nice to have
98
-
99
- ## 🚀 Install
24
+ ## Installation
100
25
 
101
26
  ```bash
102
27
  npm install vue-command-kit
@@ -104,24 +29,24 @@ npm install vue-command-kit
104
29
 
105
30
  ## Quick Start
106
31
 
107
- ### Simple items prop
32
+ ### With items prop
108
33
 
109
34
  ```vue
110
35
  <script setup lang="ts">
111
- import { ref } from 'vue'
112
- import { Command } from 'vue-command-kit'
113
- import type { CommandItemData } from 'vue-command-kit'
36
+ import { ref } from 'vue'
37
+ import { Command } from 'vue-command-kit'
38
+ import type { CommandItemData } from 'vue-command-kit'
114
39
 
115
- const visible = ref(false)
40
+ const visible = ref(false)
116
41
 
117
- const items: CommandItemData[] = [
118
- { value: 'settings', label: 'Open settings', shortcut: '⌘,' },
119
- { value: 'home', label: 'Go to home', shortcut: '⌘H' },
120
- ]
42
+ const items: CommandItemData[] = [
43
+ { value: 'settings', label: 'Open settings', shortcut: '⌘,' },
44
+ { value: 'home', label: 'Go to home', shortcut: '⌘H' },
45
+ ]
121
46
 
122
- function onSelect(item: CommandItemData) {
123
- console.log('selected:', item.value)
124
- }
47
+ function onSelect(item: CommandItemData) {
48
+ console.log('selected:', item.value)
49
+ }
125
50
  </script>
126
51
 
127
52
  <template>
@@ -136,7 +61,7 @@ npm install vue-command-kit
136
61
  </template>
137
62
  ```
138
63
 
139
- ### Advanced custom slot content
64
+ ### With custom slot content
140
65
 
141
66
  ```vue
142
67
  <Command.Dialog :visible="visible" @update:visible="visible = $event">
@@ -153,104 +78,39 @@ npm install vue-command-kit
153
78
  </Command.Dialog>
154
79
  ```
155
80
 
156
- ### With custom filter
157
-
158
- ```vue
159
- <script setup lang="ts">
160
- import { Command } from 'vue-command-kit'
161
-
162
- function myFilter(items: CommandItemData[], query: string) {
163
- // Return filtered items, or null to use default filter
164
- return items.filter((item) => item.label?.includes(query))
165
- }
166
- </script>
167
-
168
- <template>
169
- <Command.Dialog
170
- :filter="myFilter"
171
- ...
172
- />
173
- </template>
174
- ```
175
-
176
- ### With v-model:searchQuery
177
-
178
- ```vue
179
- <script setup lang="ts">
180
- import { ref } from 'vue'
181
- import { Command } from 'vue-command-kit'
182
- import type { CommandItemData } from 'vue-command-kit'
183
-
184
- const visible = ref(false)
185
- const query = ref('')
186
-
187
- const items: CommandItemData[] = [
188
- { value: 'home', label: 'Home', keywords: ['dashboard'] },
189
- { value: 'settings', label: 'Settings' },
190
- ]
191
- </script>
192
-
193
- <template>
194
- <Command.Dialog
195
- :visible="visible"
196
- :items="items"
197
- v-model:searchQuery="query"
198
- @update:visible="visible = $event"
199
- />
200
- </template>
201
- ```
202
-
203
- ### Async data + loading
204
-
205
- ```vue
206
- <script setup lang="ts">
207
- import { ref, watch } from 'vue'
208
- import { Command } from 'vue-command-kit'
209
- import type { CommandItemData } from 'vue-command-kit'
210
-
211
- const visible = ref(false)
212
- const loading = ref(false)
213
- const items = ref<CommandItemData[]>([])
214
-
215
- watch(visible, async (v) => {
216
- if (v) {
217
- loading.value = true
218
- const data = await fetch('/api/commands').then((r) => r.json())
219
- items.value = data.map((d: any) => ({
220
- value: d.id,
221
- label: d.name,
222
- group: d.category,
223
- }))
224
- loading.value = false
225
- }
226
- })
227
- </script>
81
+ ## API
228
82
 
229
- <template>
230
- <Command.Dialog
231
- :visible="visible"
232
- :items="items"
233
- :loading="loading"
234
- @update:visible="visible = $event"
235
- @select="console.log('selected', $event)"
236
- />
237
- </template>
238
- ```
83
+ ### Components
239
84
 
240
- ## 📖 API
85
+ | Component | Description |
86
+ | --------------------- | ---------------------------------------------- |
87
+ | `<Command.Dialog>` | Modal dialog with mask, transition, focus trap |
88
+ | `<Command.Menu>` | Inline command menu (non-modal) with slots |
89
+ | `<Command.Input>` | Search input with keyboard navigation |
90
+ | `<Command.List>` | Scrollable list rendering grouped items |
91
+ | `<Command.Group>` | Group of items with heading |
92
+ | `<Command.Item>` | Single selectable command item |
93
+ | `<Command.Separator>` | Visual separator with optional `alwaysRender` |
94
+ | `<Command.Empty>` | Shown when no results match |
95
+ | `<Command.Loading>` | Loading indicator |
241
96
 
242
97
  ### `<Command.Dialog>` Props
243
98
 
244
- | Prop | Type | Default | Description |
245
- | --------------- | ------------------- | --------------------- | ------------------------------------ |
246
- | `visible` | `boolean` | `false` | Controlled open state |
247
- | `items` | `CommandItemData[]` | `[]` | Items to display |
248
- | `searchQuery` | `string` | `''` | Search query (`v-model:searchQuery`) |
249
- | `placeholder` | `string` | `'Type a command...'` | Input placeholder |
250
- | `filter` | `FilterFn` | — | Custom filter function |
251
- | `loading` | `boolean` | `false` | Show loading state |
252
- | `autoFocus` | `boolean` | `true` | Auto-focus input on open |
253
- | `closeOnSelect` | `boolean` | `true` | Close dialog after selection |
99
+ | Prop | Type | Default | Description |
100
+ | --------------- | ----------------------- | ------------------------------- | ---------------------------------------------- |
101
+ | `visible` | `boolean` | `false` | Controlled open state |
102
+ | `items` | `CommandItemData[]` | `[]` | Items to display |
103
+ | `searchQuery` | `string` | | Search query (`v-model:searchQuery`) |
104
+ | `value` | `string` | — | Selected item value (`v-model:value`) |
105
+ | `placeholder` | `string` | `'Type a command or search...'` | Input placeholder |
106
+ | `filter` | `FilterFn` | | Custom filter function |
107
+ | `loading` | `boolean` | `false` | Show loading state |
108
+ | `autoFocus` | `boolean` | `true` | Auto-focus input on open |
109
+ | `closeOnSelect` | `boolean` | `true` | Close dialog after selection |
110
+ | `shouldFilter` | `boolean` | `true` | When `false`, skip built-in filtering |
111
+ | `loop` | `boolean` | `true` | When `false`, keyboard nav stops at boundaries |
112
+ | `label` | `string` | `'Command menu'` | `aria-label` for the dialog |
113
+ | `container` | `string \| HTMLElement` | `'body'` | Teleport target for the dialog |
254
114
 
255
115
  ### `<Command.Dialog>` Events
256
116
 
@@ -258,22 +118,9 @@ npm install vue-command-kit
258
118
  | -------------------- | ----------------- | --------------------------------- |
259
119
  | `update:visible` | `boolean` | Emitted when visibility changes |
260
120
  | `update:searchQuery` | `string` | Emitted when search query changes |
121
+ | `update:value` | `string` | Emitted when an item is selected |
261
122
  | `select` | `CommandItemData` | Emitted when an item is selected |
262
123
 
263
- ### Components
264
-
265
- | Component | Description |
266
- | --------------------- | ---------------------------------------------- |
267
- | `<Command.Dialog>` | Modal dialog with mask, transition, focus trap |
268
- | `<Command.Menu>` | Inline command menu (non-modal) |
269
- | `<Command.Input>` | Search input with keyboard navigation |
270
- | `<Command.List>` | Scrollable list rendering `groupedItems` |
271
- | `<Command.Group>` | Group of items with heading |
272
- | `<Command.Item>` | Single selectable command item |
273
- | `<Command.Empty>` | Shown when no results match |
274
- | `<Command.Separator>` | Visual separator |
275
- | `<Command.Loading>` | Loading indicator |
276
-
277
124
  ### `CommandItemData`
278
125
 
279
126
  ```ts
@@ -284,107 +131,55 @@ interface CommandItemData {
284
131
  shortcut?: string
285
132
  group?: string
286
133
  disabled?: boolean
287
- icon?: Component
134
+ forceMount?: boolean
135
+ icon?: Component | VNode | (() => VNode)
288
136
  onSelect?: (item: CommandItemData) => void
289
137
  }
290
138
  ```
291
139
 
292
- ### `useCommandMenu()` Composable
140
+ ### `useCommandMenu()`
141
+
142
+ The composable provides programmatic control outside of `Command.Dialog` / `Command.Menu`.
293
143
 
294
144
  ```ts
295
145
  import { useCommandMenu } from 'vue-command-kit'
296
- import type { UseCommandMenuReturn, FilterFn } from 'vue-command-kit'
297
146
 
298
- const menu: UseCommandMenuReturn = useCommandMenu(customFilter?)
147
+ const menu = useCommandMenu()
299
148
  menu.items.value = [...]
300
149
  menu.open()
301
150
  menu.close()
302
151
  menu.toggle()
303
152
  ```
304
153
 
305
- ### Keyboard
306
-
307
- | Key | Action |
308
- | -------- | -------------------------------------- |
309
- | `↑` `↓` | Navigate items (wraps around) |
310
- | `Enter` | Select current item |
311
- | `Escape` | Close dialog |
312
- | `Tab` | Moves focus within dialog (focus trap) |
313
-
314
- ## 🤝 Contributing
315
-
316
- ### Prerequisites
317
-
318
- - **Node.js** >= 22.13
319
- - **pnpm** >= 11.x
320
-
321
- ### Setup
322
-
323
- ```bash
324
- # Clone the repo
325
- git clone https://github.com/yvng-jie/vue-cmdk.git
326
- cd vue-cmdk
327
-
328
- # Install dependencies
329
- pnpm install
330
-
331
- # Start demo dev server
332
- pnpm dev
333
- ```
334
-
335
- ### Scripts
336
-
337
- | Command | Description |
338
- | ----------------- | ----------------------------------------- |
339
- | `pnpm dev` | Start demo dev server at `localhost:5173` |
340
- | `pnpm build` | Build the library + type declarations |
341
- | `pnpm typecheck` | Run TypeScript type checking |
342
- | `pnpm preview` | Preview production build |
343
- | `pnpm build:demo` | Build demo site to `dist-demo/` |
344
-
345
- ### Project Structure
346
-
347
- ```
348
- src/
349
- ├── useCommandMenu.ts # core composable (state, filter, shortcuts)
350
- ├── useCommandRoot.ts # shared composable (provide/inject wiring)
351
- ├── types.ts # TypeScript type definitions
352
- ├── injectionKeys.ts # provide/inject keys
353
- ├── utils/
354
- │ └── injectStrict.ts # type-safe inject helper
355
- ├── CommandMenu.vue # inline command menu
356
- ├── CommandDialog.vue # modal dialog command palette
357
- ├── CommandInput.vue # search input
358
- ├── CommandList.vue # scrollable filtered list
359
- ├── CommandGroup.vue # group with heading
360
- ├── CommandItem.vue # single selectable item
361
- ├── CommandEmpty.vue # empty state
362
- ├── CommandSeparator.vue # visual separator
363
- ├── CommandLoading.vue # loading indicator
364
- ├── index.ts # barrel exports
365
- └── env.d.ts # type shims
366
- demo/
367
- ├── App.vue # demo application
368
- ├── main.ts # demo entry
369
- └── style.css # demo styles
370
- ```
371
-
372
- ### Pull Request Process
373
-
374
- 1. Fork the repo and create a feature branch from `main`
375
- 2. Make your changes and run `pnpm typecheck && pnpm build`
376
- 3. Test your changes with `pnpm dev` (demo app)
377
- 4. Update `CHANGELOG.md` with a description of your changes
378
- 5. Submit a PR with a clear description of what and why
379
-
380
- PRs and issues are welcome!
381
-
382
- ## 📄 License
383
-
384
- MIT © [yvng-jie](https://github.com/yvng-jie)
154
+ | Return | Type | Description |
155
+ | ----------------- | ------------------------ | --------------------------------------- |
156
+ | `visible` | `Ref<boolean>` | Open state |
157
+ | `searchQuery` | `Ref<string>` | Current search query |
158
+ | `activeIndex` | `Ref<number>` | Currently highlighted item index |
159
+ | `items` | `Ref<CommandItemData[]>` | Raw item list |
160
+ | `filteredItems` | `ComputedRef<...>` | Items after filtering |
161
+ | `groupedItems` | `ComputedRef<...>` | Filtered items grouped by `group` field |
162
+ | `open()` | `() => void` | Open the menu |
163
+ | `close()` | `() => void` | Close and reset search |
164
+ | `toggle()` | `() => void` | Toggle open state |
165
+ | `selectNext()` | `() => void` | Move active index down |
166
+ | `selectPrev()` | `() => void` | Move active index up |
167
+ | `selectCurrent()` | `() => void` | Select currently active item |
168
+
169
+ ## Bundle Size
170
+
171
+ | Format | Size |
172
+ | ------- | ------- |
173
+ | ESM | 17.0 kB |
174
+ | UMD | 13.5 kB |
175
+ | Gzipped | 5.0 kB |
176
+
177
+ ## License
178
+
179
+ MIT &copy; [yvng-jie](https://github.com/yvng-jie)
385
180
 
386
181
  ---
387
182
 
388
183
  <p align="center">
389
- <sub>Made with ❤️ for the Vue community. Thanks to <a href="https://github.com/xiaoluoboding">@xiaoluoboding</a> for the original <a href="https://github.com/xiaoluoboding/vue-command-palette">vue-command-palette</a>.</sub>
184
+ <a href="./CONTRIBUTING.md">Contributing Guide</a>
390
185
  </p>
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/types.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface CommandItemData {
15
15
  disabled?: boolean;
16
16
  /** Custom render icon or prefix */
17
17
  icon?: Component | VNode | (() => VNode);
18
+ /** Whether to force this item to always appear in filtered results */
19
+ forceMount?: boolean;
18
20
  /** Callback when item is selected */
19
21
  onSelect?: (item: CommandItemData) => void;
20
22
  }
@@ -34,17 +36,28 @@ export interface CommandRootProps {
34
36
  closeOnSelect?: boolean;
35
37
  /** Whether the menu is loading */
36
38
  loading?: boolean;
39
+ /** aria-label for the dialog/menu */
40
+ label?: string;
41
+ /** When false, skip built-in filtering and show all items (custom filter still applies) */
42
+ shouldFilter?: boolean;
43
+ /** When false, keyboard navigation stops at boundaries instead of wrapping around */
44
+ loop?: boolean;
45
+ /** Controlled selected item value (v-model) */
46
+ value?: string;
37
47
  }
38
48
  /** Emitted events */
39
49
  export interface CommandRootEmits {
40
50
  (e: 'update:visible', value: boolean): void;
41
51
  (e: 'update:searchQuery', value: string): void;
52
+ (e: 'update:value', value: string): void;
42
53
  (e: 'select', item: CommandItemData): void;
43
54
  }
44
55
  /** Props for CommandDialog — extends shared root props with items array */
45
56
  export interface CommandDialogProps extends CommandRootProps {
46
57
  /** Command items to display */
47
58
  items?: CommandItemData[];
59
+ /** Teleport target for the dialog (defaults to 'body') */
60
+ container?: string | HTMLElement;
48
61
  }
49
62
  /** Group definition */
50
63
  export interface CommandGroupData {
@@ -1,10 +1,19 @@
1
1
  import { Ref, ComputedRef } from 'vue';
2
2
  import { CommandItemData, CommandGroupData } from './types';
3
3
  export type FilterFn = (items: CommandItemData[], query: string) => CommandItemData[] | null;
4
+ export interface UseCommandMenuOptions {
5
+ /** Custom filter function */
6
+ filter?: FilterFn;
7
+ /** When false, skip built-in filtering */
8
+ shouldFilter?: boolean;
9
+ /** When false, keyboard navigation stops at boundaries */
10
+ loop?: boolean;
11
+ }
4
12
  export interface UseCommandMenuReturn {
5
13
  visible: Ref<boolean>;
6
14
  searchQuery: Ref<string>;
7
15
  activeIndex: Ref<number>;
16
+ selectedValue: Ref<string | undefined>;
8
17
  items: Ref<CommandItemData[]>;
9
18
  toggle: () => void;
10
19
  open: () => void;
@@ -17,4 +26,4 @@ export interface UseCommandMenuReturn {
17
26
  selectPrev: () => void;
18
27
  selectCurrent: () => void;
19
28
  }
20
- export declare function useCommandMenu(customFilter?: FilterFn, onItemSelect?: (item: CommandItemData) => void): UseCommandMenuReturn;
29
+ export declare function useCommandMenu(optionsOrFilter?: UseCommandMenuOptions | FilterFn, onItemSelect?: (item: CommandItemData) => void): UseCommandMenuReturn;
@@ -4,6 +4,9 @@ export interface UseCommandRootOptions {
4
4
  filter?: FilterFn;
5
5
  loading?: boolean;
6
6
  closeOnSelect?: boolean;
7
+ shouldFilter?: boolean;
8
+ loop?: boolean;
9
+ value?: string;
7
10
  }
8
11
  export interface UseCommandRootReturn {
9
12
  state: ReturnType<typeof useCommandMenu>;
@@ -0,0 +1,9 @@
1
+ export interface HighlightSegment {
2
+ text: string;
3
+ highlighted: boolean;
4
+ }
5
+ /**
6
+ * Split text into segments, marking parts that match the query.
7
+ * Matching is case-insensitive.
8
+ */
9
+ export declare function highlightText(text: string, query: string): HighlightSegment[];
@@ -0,0 +1,12 @@
1
+ /** Parsed shortcut descriptor */
2
+ export interface ShortcutDescriptor {
3
+ key: string;
4
+ metaKey?: boolean;
5
+ ctrlKey?: boolean;
6
+ altKey?: boolean;
7
+ shiftKey?: boolean;
8
+ }
9
+ /** Parse a shortcut string like "⌘S" or "⌘⇧F" into a key+modifiers descriptor */
10
+ export declare function parseShortcut(shortcut: string): ShortcutDescriptor | null;
11
+ /** Check if a KeyboardEvent matches a parsed shortcut descriptor */
12
+ export declare function eventMatchesShortcut(e: KeyboardEvent, desc: ShortcutDescriptor | null): boolean;