@womp/kookie-ui 0.33.0 → 0.34.1

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 (51) hide show
  1. package/components.css +202 -0
  2. package/dist/cjs/components/command.d.ts +133 -0
  3. package/dist/cjs/components/command.d.ts.map +1 -0
  4. package/dist/cjs/components/command.js +3 -0
  5. package/dist/cjs/components/command.js.map +7 -0
  6. package/dist/cjs/components/index.d.ts +1 -0
  7. package/dist/cjs/components/index.d.ts.map +1 -1
  8. package/dist/cjs/components/index.js +2 -2
  9. package/dist/cjs/components/index.js.map +4 -4
  10. package/dist/cjs/components/text-field.d.ts.map +1 -1
  11. package/dist/cjs/components/text-field.js +2 -2
  12. package/dist/cjs/components/text-field.js.map +3 -3
  13. package/dist/cjs/index.js +5 -5
  14. package/dist/cjs/index.js.map +4 -4
  15. package/dist/esm/{chunk-K35JUIUT.js → chunk-API4DAPY.js} +2 -2
  16. package/dist/esm/{chunk-W35ZKQMQ.js → chunk-CAM66IXC.js} +2 -2
  17. package/dist/esm/chunk-GZPB6MRQ.js +3 -0
  18. package/dist/esm/chunk-GZPB6MRQ.js.map +7 -0
  19. package/dist/esm/{chunk-6WHKGVWY.js → chunk-NZJRXZYX.js} +2 -2
  20. package/dist/esm/chunk-SHKPQ3RI.js +2 -0
  21. package/dist/esm/chunk-YLGIP3A5.js +6 -0
  22. package/dist/esm/chunk-YLGIP3A5.js.map +7 -0
  23. package/dist/esm/components/alert-dialog.js +1 -1
  24. package/dist/esm/components/command.d.ts +133 -0
  25. package/dist/esm/components/command.d.ts.map +1 -0
  26. package/dist/esm/components/command.js +3 -0
  27. package/dist/esm/components/command.js.map +7 -0
  28. package/dist/esm/components/dialog.js +1 -1
  29. package/dist/esm/components/index.d.ts +1 -0
  30. package/dist/esm/components/index.d.ts.map +1 -1
  31. package/dist/esm/components/index.js +1 -1
  32. package/dist/esm/components/navigator.js +1 -1
  33. package/dist/esm/components/sheet.js +1 -1
  34. package/dist/esm/components/text-field.d.ts.map +1 -1
  35. package/dist/esm/components/text-field.js +1 -1
  36. package/dist/esm/hooks/index.js +1 -1
  37. package/dist/esm/index.js +1 -1
  38. package/package.json +1 -1
  39. package/src/components/command.css +197 -0
  40. package/src/components/command.tsx +432 -0
  41. package/src/components/index.css +1 -0
  42. package/src/components/index.tsx +24 -0
  43. package/src/components/text-field.tsx +11 -4
  44. package/styles.css +202 -0
  45. package/dist/esm/chunk-RSG4XWHU.js +0 -2
  46. package/dist/esm/chunk-Y44DYJA5.js +0 -6
  47. package/dist/esm/chunk-Y44DYJA5.js.map +0 -7
  48. /package/dist/esm/{chunk-K35JUIUT.js.map → chunk-API4DAPY.js.map} +0 -0
  49. /package/dist/esm/{chunk-W35ZKQMQ.js.map → chunk-CAM66IXC.js.map} +0 -0
  50. /package/dist/esm/{chunk-6WHKGVWY.js.map → chunk-NZJRXZYX.js.map} +0 -0
  51. /package/dist/esm/{chunk-RSG4XWHU.js.map → chunk-SHKPQ3RI.js.map} +0 -0
package/dist/esm/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  "use client";
2
- import"./chunk-AYKOOEZR.js";import{a as Eo,b as Fo,c as Go,d as Ho}from"./chunk-3LIF53YY.js";import{a as Do}from"./chunk-RIHTVZVJ.js";import{a as Co}from"./chunk-WTP2KIK3.js";import"./chunk-RSG4XWHU.js";import{a as mo}from"./chunk-ZLGOZKB3.js";import{a as wo}from"./chunk-7QBNXNEN.js";import{a as $}from"./chunk-VBF3XALY.js";import{h as oo}from"./chunk-XF6Z5NKE.js";import{e as ro}from"./chunk-QBONJMSE.js";import{a as eo}from"./chunk-D42VCLKH.js";import{d as fo}from"./chunk-Y44DYJA5.js";import{g as Ao}from"./chunk-K35JUIUT.js";import{h as zo}from"./chunk-NO2JCJWI.js";import"./chunk-INGT6DB6.js";import"./chunk-B447PNRD.js";import{l as yo}from"./chunk-J6B7UCZN.js";import{a as Z}from"./chunk-COLJWVN7.js";import{a as _}from"./chunk-VYF6KKYM.js";import{c as X}from"./chunk-5VYQDA5S.js";import{h as Y}from"./chunk-TMINGBSQ.js";import{a as S}from"./chunk-AXODGYRY.js";import{a as T}from"./chunk-LFAUMDU5.js";import{c as U}from"./chunk-26O4RBOJ.js";import{a as V}from"./chunk-OPYTQ34H.js";import{a as W}from"./chunk-L66IUPDW.js";import{a as P}from"./chunk-AKSAKP35.js";import{a as Q}from"./chunk-3H33S3UA.js";import{i as Bo}from"./chunk-YOTTLPJ3.js";import{e as R}from"./chunk-EERKYRYI.js";import{a as ko,b as lo,c as no,d as qo,e as so,f as uo,g as vo}from"./chunk-XKYPJTRN.js";import{a as po,c as to,d as xo,e as ao,f as bo,g as co,h as go,i as ho,j as io}from"./chunk-BR6Z2CAE.js";import{a as jo}from"./chunk-65URNXF2.js";import{a as I}from"./chunk-NYIYRILR.js";import{l as K}from"./chunk-BQOFHLDP.js";import{g as J}from"./chunk-TUZ665BA.js";import{n as L}from"./chunk-TRJAVWCK.js";import{g as M}from"./chunk-W35ZKQMQ.js";import{o as N}from"./chunk-KBSH6CBD.js";import"./chunk-2ACVWLFA.js";import{a as O}from"./chunk-Q5SANVIW.js";import{a as u}from"./chunk-XD3C3GVF.js";import{a as A}from"./chunk-X7DGYRAY.js";import{m as E}from"./chunk-LG5DPQ46.js";import{b as B,c as C}from"./chunk-7YXHLU4C.js";import{a as D}from"./chunk-WUCBFB25.js";import{a as z}from"./chunk-JQMWNI62.js";import{a as y}from"./chunk-Y2DBXOLT.js";import{c as F}from"./chunk-3M5ZM67R.js";import"./chunk-2VSRZ4IT.js";import{a as G}from"./chunk-HVMRB3AH.js";import"./chunk-4KIZRO52.js";import{a as H}from"./chunk-JFJV4HL3.js";import{a as v,b as w}from"./chunk-NJ7RQYLX.js";import"./chunk-KDJM4NQQ.js";import{a as h}from"./chunk-B6YGNB3V.js";import{f as i}from"./chunk-OMPLFDS6.js";import{h as n}from"./chunk-6WHKGVWY.js";import"./chunk-FATCGZZE.js";import{a as l}from"./chunk-7VJF6EXN.js";import"./chunk-TGRHRMQI.js";import{a as j}from"./chunk-OBSZOD5M.js";import{a as k}from"./chunk-FICBVI4M.js";import"./chunk-BQC2P3SN.js";import{a as q}from"./chunk-L7PRLW6A.js";import{a as s}from"./chunk-IFHBMTFX.js";import"./chunk-UJ7JERJ5.js";import"./chunk-HGF4CKRY.js";import{a as c,b as d,c as g}from"./chunk-FVRVBB6O.js";import"./chunk-GHUAUFG7.js";import"./chunk-UMQOTGJC.js";import"./chunk-XD676L6S.js";import"./chunk-XUCR7L4G.js";import"./chunk-3QWGSRYC.js";import"./chunk-KGCGT4MJ.js";import"./chunk-QKCNGEX4.js";import"./chunk-WEBNIUEX.js";import"./chunk-SGKLJDA3.js";import{a as f}from"./chunk-BUXK5VGR.js";import{a as e}from"./chunk-KVVRFWGQ.js";import"./chunk-YCLPURTD.js";import"./chunk-CR65RYGM.js";import"./chunk-7N2Q5WOM.js";import"./chunk-OAJ5DARR.js";import{b as o,c as r}from"./chunk-5KMEUZWN.js";import{a as m,b as p,c as t,d as x}from"./chunk-GKDGNPBD.js";import"./chunk-5ON3XLRD.js";import"./chunk-HBWBPBSA.js";import"./chunk-5JYSYTHA.js";import"./chunk-ISCZ57VL.js";import"./chunk-EB4AG3OL.js";import{a}from"./chunk-QPRUTOLI.js";import"./chunk-G66WDIIV.js";import"./chunk-4KBKS3ZE.js";import"./chunk-VWRPVM5K.js";import"./chunk-ODHCDJ3Y.js";import{a as b}from"./chunk-DFQIIYLD.js";import"./chunk-KOAWATCI.js";import"./chunk-5KJIPH2N.js";import"./chunk-YOSLXJI5.js";import"./chunk-QEDKL23P.js";import"./chunk-5BXCPANF.js";import"./chunk-LPLW7OWA.js";import"./chunk-L54FWKJE.js";import"./chunk-XU74PNM2.js";import"./chunk-MAA6LEE2.js";import"./chunk-OKQAVYW4.js";import"./chunk-CLJ6SEGP.js";import"./chunk-ANQLWGFN.js";import"./chunk-VQPANPMI.js";export{Eo as APPEARANCE_STORAGE_KEY,h as AccessibleIcon,i as Accordion,n as AlertDialog,q as Avatar,s as Blockquote,u as Box,z as Button,A as Card,E as Chatbar,G as Checkbox,F as CheckboxGroup,t as ChevronDownIcon,H as Chip,I as Code,K as Combobox,C as Confirmation,L as ContextMenu,M as Dialog,N as DropdownMenu,e as Flex,O as Grid,j as Heading,P as Image,a as Kbd,Q as Link,Bo as MessageScroller,R as Navbar,ko as Navigator,qo as NavigatorBack,vo as NavigatorLink,lo as NavigatorOutlet,uo as NavigatorScreen,so as NavigatorTitle,B as Notice,jo as Page,J as Popover,S as Portal,T as Progress,V as Radio,U as RadioGroup,W as Reset,b as ScrollArea,X as SegmentedControl,Y as Select,Z as Separator,Ao as Sheet,zo as Shell,yo as Sidebar,_ as Slider,o as Slot,r as Slottable,f as Spinner,$ as Switch,oo as Table,ro as Tabs,k as Text,eo as TextArea,fo as TextField,g as Theme,c as ThemeContext,p as ThickCheckIcon,x as ThickChevronRightIcon,m as ThickDividerHorizontalIcon,mo as Toggle,io as Toolbar,xo as ToolbarButton,co as ToolbarFill,to as ToolbarGroup,ao as ToolbarOverflow,go as ToolbarSeparator,bo as ToolbarSlot,ho as ToolbarTitle,w as Tooltip,v as TooltipProvider,wo as VirtualMenu,D as VisuallyHidden,Fo as appearanceScript,Go as setAppearance,Ho as useAppearance,l as useBodyPointerEventsCleanup,Co as useLiveAnnouncer,no as useNavigator,d as useThemeContext,Do as useToggleState,po as useToolbarOverflow,y as useTooltipWrapper};
2
+ import"./chunk-AYKOOEZR.js";import{a as No}from"./chunk-RIHTVZVJ.js";import{a as Oo,b as Po,c as Qo,d as Ro}from"./chunk-3LIF53YY.js";import{a as Mo}from"./chunk-WTP2KIK3.js";import"./chunk-SHKPQ3RI.js";import{a as jo}from"./chunk-ZLGOZKB3.js";import{a as Ho}from"./chunk-7QBNXNEN.js";import{a as ao}from"./chunk-VYF6KKYM.js";import{a as bo}from"./chunk-VBF3XALY.js";import{h as co}from"./chunk-XF6Z5NKE.js";import{e as go}from"./chunk-QBONJMSE.js";import{a as ho}from"./chunk-D42VCLKH.js";import{d as io}from"./chunk-YLGIP3A5.js";import{h as to}from"./chunk-TMINGBSQ.js";import{g as Ko}from"./chunk-API4DAPY.js";import{h as Jo}from"./chunk-NO2JCJWI.js";import"./chunk-INGT6DB6.js";import"./chunk-B447PNRD.js";import{l as Io}from"./chunk-J6B7UCZN.js";import{a as xo}from"./chunk-COLJWVN7.js";import{a as mo}from"./chunk-L66IUPDW.js";import{c as po}from"./chunk-5VYQDA5S.js";import{a as Ao,b as Bo,c as Co,d as Do,e as Eo,f as Fo,g as Go}from"./chunk-XKYPJTRN.js";import{a as ko,c as lo,d as no,e as qo,f as so,g as uo,h as vo,i as wo,j as yo}from"./chunk-BR6Z2CAE.js";import{a as zo}from"./chunk-65URNXF2.js";import{a as oo}from"./chunk-AXODGYRY.js";import{a as ro}from"./chunk-LFAUMDU5.js";import{c as eo}from"./chunk-26O4RBOJ.js";import{a as fo}from"./chunk-OPYTQ34H.js";import{a as Z}from"./chunk-AKSAKP35.js";import{a as _}from"./chunk-3H33S3UA.js";import{i as Lo}from"./chunk-YOTTLPJ3.js";import{e as $}from"./chunk-EERKYRYI.js";import{a as I}from"./chunk-NYIYRILR.js";import{l as K}from"./chunk-BQOFHLDP.js";import{g as J}from"./chunk-TUZ665BA.js";import{b as L,c as M,d as N,e as O,f as P,g as Q,h as R,i as S,j as T,k as U}from"./chunk-GZPB6MRQ.js";import{n as V}from"./chunk-TRJAVWCK.js";import{g as W}from"./chunk-CAM66IXC.js";import{o as X}from"./chunk-KBSH6CBD.js";import"./chunk-2ACVWLFA.js";import{a as Y}from"./chunk-Q5SANVIW.js";import{a as u}from"./chunk-XD3C3GVF.js";import{a as A}from"./chunk-X7DGYRAY.js";import{m as E}from"./chunk-LG5DPQ46.js";import{b as B,c as C}from"./chunk-7YXHLU4C.js";import{a as D}from"./chunk-WUCBFB25.js";import{a as z}from"./chunk-JQMWNI62.js";import{a as y}from"./chunk-Y2DBXOLT.js";import{c as F}from"./chunk-3M5ZM67R.js";import"./chunk-2VSRZ4IT.js";import{a as G}from"./chunk-HVMRB3AH.js";import"./chunk-4KIZRO52.js";import{a as H}from"./chunk-JFJV4HL3.js";import{a as v,b as w}from"./chunk-NJ7RQYLX.js";import"./chunk-KDJM4NQQ.js";import{a as h}from"./chunk-B6YGNB3V.js";import{f as i}from"./chunk-OMPLFDS6.js";import{h as n}from"./chunk-NZJRXZYX.js";import"./chunk-FATCGZZE.js";import{a as j}from"./chunk-OBSZOD5M.js";import{a as l}from"./chunk-7VJF6EXN.js";import"./chunk-TGRHRMQI.js";import{a as k}from"./chunk-FICBVI4M.js";import"./chunk-BQC2P3SN.js";import{a as q}from"./chunk-L7PRLW6A.js";import{a as s}from"./chunk-IFHBMTFX.js";import"./chunk-UJ7JERJ5.js";import"./chunk-HGF4CKRY.js";import{a as c,b as d,c as g}from"./chunk-FVRVBB6O.js";import"./chunk-GHUAUFG7.js";import"./chunk-UMQOTGJC.js";import"./chunk-XD676L6S.js";import"./chunk-XUCR7L4G.js";import"./chunk-3QWGSRYC.js";import"./chunk-KGCGT4MJ.js";import"./chunk-QKCNGEX4.js";import"./chunk-WEBNIUEX.js";import"./chunk-SGKLJDA3.js";import{a as f}from"./chunk-BUXK5VGR.js";import{a as e}from"./chunk-KVVRFWGQ.js";import"./chunk-YCLPURTD.js";import"./chunk-CR65RYGM.js";import"./chunk-7N2Q5WOM.js";import"./chunk-OAJ5DARR.js";import{b as o,c as r}from"./chunk-5KMEUZWN.js";import{a as m,b as p,c as t,d as x}from"./chunk-GKDGNPBD.js";import"./chunk-5ON3XLRD.js";import"./chunk-HBWBPBSA.js";import"./chunk-5JYSYTHA.js";import"./chunk-ISCZ57VL.js";import"./chunk-EB4AG3OL.js";import{a}from"./chunk-QPRUTOLI.js";import"./chunk-G66WDIIV.js";import"./chunk-4KBKS3ZE.js";import"./chunk-VWRPVM5K.js";import"./chunk-ODHCDJ3Y.js";import{a as b}from"./chunk-DFQIIYLD.js";import"./chunk-KOAWATCI.js";import"./chunk-5KJIPH2N.js";import"./chunk-YOSLXJI5.js";import"./chunk-QEDKL23P.js";import"./chunk-5BXCPANF.js";import"./chunk-LPLW7OWA.js";import"./chunk-L54FWKJE.js";import"./chunk-XU74PNM2.js";import"./chunk-MAA6LEE2.js";import"./chunk-OKQAVYW4.js";import"./chunk-CLJ6SEGP.js";import"./chunk-ANQLWGFN.js";import"./chunk-VQPANPMI.js";export{Oo as APPEARANCE_STORAGE_KEY,h as AccessibleIcon,i as Accordion,n as AlertDialog,q as Avatar,s as Blockquote,u as Box,z as Button,A as Card,E as Chatbar,G as Checkbox,F as CheckboxGroup,t as ChevronDownIcon,H as Chip,I as Code,K as Combobox,L as Command,S as CommandCollection,N as CommandContent,U as CommandEmpty,Q as CommandGroup,R as CommandGroupLabel,O as CommandInput,T as CommandItem,P as CommandList,M as CommandTrigger,C as Confirmation,V as ContextMenu,W as Dialog,X as DropdownMenu,e as Flex,Y as Grid,j as Heading,Z as Image,a as Kbd,_ as Link,Lo as MessageScroller,$ as Navbar,Ao as Navigator,Do as NavigatorBack,Go as NavigatorLink,Bo as NavigatorOutlet,Fo as NavigatorScreen,Eo as NavigatorTitle,B as Notice,zo as Page,J as Popover,oo as Portal,ro as Progress,fo as Radio,eo as RadioGroup,mo as Reset,b as ScrollArea,po as SegmentedControl,to as Select,xo as Separator,Ko as Sheet,Jo as Shell,Io as Sidebar,ao as Slider,o as Slot,r as Slottable,f as Spinner,bo as Switch,co as Table,go as Tabs,k as Text,ho as TextArea,io as TextField,g as Theme,c as ThemeContext,p as ThickCheckIcon,x as ThickChevronRightIcon,m as ThickDividerHorizontalIcon,jo as Toggle,yo as Toolbar,no as ToolbarButton,uo as ToolbarFill,lo as ToolbarGroup,qo as ToolbarOverflow,vo as ToolbarSeparator,so as ToolbarSlot,wo as ToolbarTitle,w as Tooltip,v as TooltipProvider,Ho as VirtualMenu,D as VisuallyHidden,Po as appearanceScript,Qo as setAppearance,Ro as useAppearance,l as useBodyPointerEventsCleanup,Mo as useLiveAnnouncer,Co as useNavigator,d as useThemeContext,No as useToggleState,ko as useToolbarOverflow,y as useTooltipWrapper};
3
3
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@womp/kookie-ui",
3
- "version": "0.33.0",
3
+ "version": "0.34.1",
4
4
  "description": "Kookie UI v1: a React component library with design tokens, flexible theming, a full layout shell, and comprehensive documentation.",
5
5
  "keywords": [
6
6
  "react",
@@ -0,0 +1,197 @@
1
+ @import './_internal/base-menu.css';
2
+
3
+ /* Command (from v2, 2026-11-03): a command palette. The dialog's panel paints nothing; it is a
4
+ * column of two panes, the search bar and the results, anchored to the top at every width so a
5
+ * result count never moves the field. Rows are menu rows. No motion. */
6
+
7
+ .kui-CommandOverlay {
8
+ display: flex;
9
+ justify-content: center;
10
+ align-items: flex-start;
11
+ padding: max(var(--space-9), 12vh) var(--space-4) var(--space-4);
12
+ }
13
+
14
+ .kui-CommandContent {
15
+ position: relative;
16
+ z-index: 1;
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: var(--space-3);
20
+ width: 100%;
21
+ max-width: var(--overlay-w);
22
+ max-height: 100%;
23
+ outline: none;
24
+ }
25
+
26
+ .kui-CommandContent:where(.kui-r-size-1) {
27
+ --overlay-w: var(--overlay-w-1);
28
+ }
29
+
30
+ .kui-CommandContent:where(.kui-r-size-2) {
31
+ --overlay-w: var(--overlay-w-2);
32
+ }
33
+
34
+ .kui-CommandContent:where(.kui-r-size-3) {
35
+ --overlay-w: var(--overlay-w-3);
36
+ }
37
+
38
+ .kui-CommandContent:where(.kui-r-size-4) {
39
+ --overlay-w: var(--overlay-w-4);
40
+ }
41
+
42
+ .kui-CommandRoot {
43
+ display: flex;
44
+ flex-direction: column;
45
+ gap: var(--space-3);
46
+ min-height: 0;
47
+ }
48
+
49
+ /* The search bar: a pane of its own, with type one step above the rows it filters. It pads by the
50
+ panel ladder at its index, one rung wider along the line than above and below it. */
51
+ .kui-CommandSearch {
52
+ display: flex;
53
+ align-items: center;
54
+ flex-shrink: 0;
55
+ background-color: var(--color-surface);
56
+ box-shadow: var(--floating-chrome);
57
+ cursor: text;
58
+ }
59
+
60
+ .kui-CommandSearch:where(.kui-r-size-1) {
61
+ gap: var(--space-2);
62
+ padding: var(--panel-p-1) var(--panel-p-2);
63
+ border-radius: var(--radius-panel-1);
64
+ font-size: var(--font-size-3);
65
+ line-height: var(--line-height-3);
66
+ }
67
+
68
+ .kui-CommandSearch:where(.kui-r-size-2) {
69
+ gap: var(--space-3);
70
+ padding: var(--panel-p-2) var(--panel-p-3);
71
+ border-radius: var(--radius-panel-2);
72
+ font-size: var(--font-size-4);
73
+ line-height: var(--line-height-4);
74
+ }
75
+
76
+ .kui-CommandSearch:where(.kui-r-size-3) {
77
+ gap: var(--space-3);
78
+ padding: var(--panel-p-3) var(--panel-p-4);
79
+ border-radius: var(--radius-panel-3);
80
+ font-size: var(--font-size-5);
81
+ line-height: var(--line-height-5);
82
+ }
83
+
84
+ .kui-CommandSearch:where(.kui-r-size-4) {
85
+ gap: var(--space-4);
86
+ padding: var(--panel-p-4) var(--panel-p-4);
87
+ border-radius: var(--radius-panel-4);
88
+ font-size: var(--font-size-6);
89
+ line-height: var(--line-height-6);
90
+ }
91
+
92
+ .kui-CommandSearchSlot {
93
+ display: flex;
94
+ flex-shrink: 0;
95
+ color: var(--neutral-a10);
96
+ }
97
+
98
+ .kui-CommandSearchSlot > :where(*) {
99
+ width: 1em;
100
+ height: 1em;
101
+ }
102
+
103
+ .kui-CommandInput {
104
+ flex: 1;
105
+ min-width: 0;
106
+ font: inherit;
107
+ color: var(--neutral-12);
108
+ background: none;
109
+ outline: none;
110
+ }
111
+
112
+ .kui-CommandInput::placeholder {
113
+ color: var(--neutral-a10);
114
+ }
115
+
116
+ /* The results: a menu pane whose height is its rows, up to what the window leaves. */
117
+ .kui-CommandPanel {
118
+ min-height: 0;
119
+ flex-shrink: 1;
120
+ }
121
+
122
+ .kui-CommandList {
123
+ max-height: min(60vh, calc(480px * var(--scale)));
124
+ overflow: auto;
125
+ overscroll-behavior: contain;
126
+ outline: 0;
127
+ }
128
+
129
+ /* A pane with nothing in it (no rows, no empty state) shows nothing. */
130
+ .kui-CommandPanel:where(:not(:has(.kui-CommandItem, .kui-CommandEmpty))) {
131
+ display: none;
132
+ }
133
+
134
+ .kui-CommandSeat:where(:empty) {
135
+ display: none;
136
+ }
137
+
138
+ /* cmdk marks every enabled row data-disabled="false"; base-menu.css reads any [data-disabled]. */
139
+ .kui-CommandItem:where([data-disabled='false']) {
140
+ color: inherit;
141
+ cursor: var(--cursor-menu-item);
142
+ }
143
+
144
+ /* The highlighted row: cmdk's [data-selected='true'], from the pointer and the keyboard. */
145
+ .kui-CommandItem:where([data-selected='true']) {
146
+ background-color: var(--accent-4);
147
+ }
148
+
149
+ .kui-CommandItem:where([data-tone='destructive']) {
150
+ color: var(--red-11);
151
+ }
152
+
153
+ .kui-CommandItem:where([data-disabled='true']) {
154
+ color: var(--neutral-a8);
155
+ cursor: default;
156
+ pointer-events: none;
157
+ }
158
+
159
+ /* A link row takes the row's look, not a link's. */
160
+ .kui-CommandItem:where(:any-link) {
161
+ text-decoration: none;
162
+ }
163
+
164
+ .kui-CommandItemLabel {
165
+ flex: 1;
166
+ min-width: 0;
167
+ }
168
+
169
+ .kui-CommandItemSlot {
170
+ display: flex;
171
+ flex-shrink: 0;
172
+ }
173
+
174
+ .kui-CommandItemSlot:where([data-slot='leading']) {
175
+ color: var(--neutral-a11);
176
+ }
177
+
178
+ .kui-CommandItemSlot:where([data-slot='trailing']) {
179
+ color: var(--neutral-a10);
180
+ }
181
+
182
+ .kui-CommandEmpty {
183
+ padding: var(--space-5);
184
+ text-align: center;
185
+ color: var(--neutral-a11);
186
+ }
187
+
188
+ @media (forced-colors: active) {
189
+ .kui-CommandItem:where([data-selected='true']) {
190
+ outline: 2px solid Highlight;
191
+ outline-offset: -2px;
192
+ }
193
+
194
+ .kui-CommandSearch {
195
+ border: 1px solid CanvasText;
196
+ }
197
+ }
@@ -0,0 +1,432 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Command, from v2 (2026-11-03): a command palette, one field over everything the app can do.
5
+ *
6
+ * It is a Dialog: it covers the app, traps focus, locks the page and leaves on Escape. Inside are
7
+ * two panes in a column anchored to the top, the search bar and the results, so a result count
8
+ * never moves the field. The keyboard model is the package's: a row is highlighted from the first
9
+ * frame and after every keystroke, the arrows move it, Enter runs it, and running a row closes the
10
+ * palette. What rows exist and what they do is the app's.
11
+ *
12
+ * Filtering keeps the order of `items`: a palette is used by muscle memory, so typing never
13
+ * reorders it. Pass `filter={null}` to narrow and rank `items` yourself.
14
+ *
15
+ * Differs from v2 in its machine only: v1 builds on cmdk where v2 builds on Base UI's Autocomplete.
16
+ * A row that should be a link takes `asChild` around the app's link, where v2 takes `render`.
17
+ */
18
+
19
+ import * as React from 'react';
20
+ import { createPortal } from 'react-dom';
21
+ import classNames from 'classnames';
22
+ import { Command as CommandPrimitive } from 'cmdk';
23
+ import { Dialog as DialogPrimitive } from 'radix-ui';
24
+
25
+ import { PortalTheme } from './_internal/portal-theme.js';
26
+ import { useBodyPointerEventsCleanup } from '../hooks/use-body-pointer-events-cleanup.js';
27
+ import { useThemeSize } from '../hooks/theme-size.js';
28
+
29
+ type Size = '1' | '2' | '3' | '4';
30
+
31
+ /** The rows stand one step above the palette: it is the one object on screen, read from a distance. */
32
+ const ROW_STEP: Record<Size, Size> = { '1': '2', '2': '3', '3': '4', '4': '4' };
33
+
34
+ type CommandOpenChangeReason = 'item-press' | 'dismiss';
35
+ type CommandOpenChangeDetails = { reason: CommandOpenChangeReason };
36
+
37
+ type Filter = (item: unknown, query: string, itemToString: (item: unknown) => string) => boolean;
38
+
39
+ const SIZE_DEF = { values: ['1', '2', '3', '4'], default: '2' } as const;
40
+
41
+ const CommandSizeContext = React.createContext<Size>('2');
42
+ const CommandQueryContext = React.createContext<(query: string) => void>(() => {});
43
+ const CommandItemsContext = React.createContext<readonly unknown[]>([]);
44
+ const CommandCloseContext = React.createContext<(() => void) | null>(null);
45
+ const CommandListContext = React.createContext<{ query: string; filter: Filter | null; itemToString: (item: unknown) => string }>({
46
+ query: '',
47
+ filter: null,
48
+ itemToString: String,
49
+ });
50
+ const CommandSeatContext = React.createContext<{ seat: HTMLElement | null; setSeat: (node: HTMLElement | null) => void } | null>(null);
51
+
52
+ /** What an item says, for the default filter: a string as itself, an object by its label, title or value. */
53
+ function defaultItemToString(item: unknown): string {
54
+ if (typeof item === 'string' || typeof item === 'number') return String(item);
55
+ if (item && typeof item === 'object') {
56
+ const record = item as Record<string, unknown>;
57
+ for (const key of ['label', 'title', 'value', 'name']) {
58
+ if (typeof record[key] === 'string') return record[key] as string;
59
+ }
60
+ }
61
+ return '';
62
+ }
63
+
64
+ const defaultFilter: Filter = (item, query, itemToString) => itemToString(item).toLowerCase().includes(query.trim().toLowerCase());
65
+
66
+ /** A group is an item that carries its own `items`; it stays while any of them matches. */
67
+ function isGroup(item: unknown): item is { items: readonly unknown[] } {
68
+ return Boolean(item && typeof item === 'object' && Array.isArray((item as { items?: unknown }).items));
69
+ }
70
+
71
+ function narrow(items: readonly unknown[], query: string, filter: Filter | null, itemToString: (item: unknown) => string): unknown[] {
72
+ if (filter === null || query.trim() === '') return [...items];
73
+ const out: unknown[] = [];
74
+ for (const item of items) {
75
+ if (isGroup(item)) {
76
+ const kept = item.items.filter((member) => filter(member, query, itemToString));
77
+ if (kept.length > 0) out.push({ ...item, items: kept });
78
+ } else if (filter(item, query, itemToString)) {
79
+ out.push(item);
80
+ }
81
+ }
82
+ return out;
83
+ }
84
+
85
+ interface CommandProps {
86
+ /**
87
+ * The size step of the palette, from `"1"` to `"4"`. The rows are one step larger than the
88
+ * app's controls at the same step, and the search text larger again. The default comes from
89
+ * the theme.
90
+ */
91
+ size?: Size;
92
+ /**
93
+ * Every item the palette can show, before filtering. `CommandList` takes a function that turns
94
+ * an item into a row. An item with its own `items` array is a group. Keep the array stable.
95
+ */
96
+ items: readonly unknown[];
97
+ /** Whether the palette is open, for a controlled palette. Most are, because a key handler opens them. */
98
+ open?: boolean;
99
+ /** Whether the palette is open at the start, for an uncontrolled palette. */
100
+ defaultOpen?: boolean;
101
+ /** Called each time the palette opens or closes. `reason` is `"item-press"` when a row ran. */
102
+ onOpenChange?: (open: boolean, details: CommandOpenChangeDetails) => void;
103
+ /** A `CommandTrigger`, if there is one, and a `CommandContent`. */
104
+ children: React.ReactNode;
105
+ }
106
+
107
+ /** A command palette: a field over a list of rows, in a dialog. Running a row closes it. */
108
+ function Command({ size: sizeProp, items, open, defaultOpen, onOpenChange, children }: CommandProps) {
109
+ const size = useThemeSize<Size>(sizeProp, SIZE_DEF);
110
+ const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false);
111
+ const controlled = open !== undefined;
112
+ const isOpen = controlled ? open : internalOpen;
113
+
114
+ const change = React.useCallback(
115
+ (next: boolean, reason: CommandOpenChangeReason) => {
116
+ onOpenChange?.(next, { reason });
117
+ if (!controlled) setInternalOpen(next);
118
+ },
119
+ [onOpenChange, controlled],
120
+ );
121
+ const close = React.useCallback(() => change(false, 'item-press'), [change]);
122
+
123
+ return (
124
+ <CommandSizeContext.Provider value={size}>
125
+ <CommandItemsContext.Provider value={items}>
126
+ <CommandCloseContext.Provider value={close}>
127
+ <DialogPrimitive.Root open={isOpen} onOpenChange={(next) => change(next, 'dismiss')} modal>
128
+ {children}
129
+ </DialogPrimitive.Root>
130
+ </CommandCloseContext.Provider>
131
+ </CommandItemsContext.Provider>
132
+ </CommandSizeContext.Provider>
133
+ );
134
+ }
135
+
136
+ interface CommandTriggerProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Trigger> {}
137
+
138
+ /** Opens the palette. Most palettes open from a key handler instead, and render none. */
139
+ function CommandTrigger({ children, ...props }: CommandTriggerProps) {
140
+ return (
141
+ <DialogPrimitive.Trigger asChild {...props}>
142
+ {children}
143
+ </DialogPrimitive.Trigger>
144
+ );
145
+ }
146
+
147
+ interface CommandContentProps {
148
+ /** The palette's accessible name, such as "Search the documentation". */
149
+ 'aria-label': string;
150
+ /**
151
+ * How an item matches the query. The default keeps an item whose text contains the query.
152
+ * `null` turns filtering off, for an app that narrows and ranks `items` itself.
153
+ */
154
+ filter?: Filter | null;
155
+ /** What an item says, for the filter. The default reads a string, or an object's label, title or value. */
156
+ itemToString?: (item: unknown) => string;
157
+ /** Called with the query on every keystroke, and with `""` when the palette closes. */
158
+ onQueryChange?: (query: string) => void;
159
+ /** Keep the portal in a container of your own. */
160
+ container?: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Portal>['container'];
161
+ children: React.ReactNode;
162
+ className?: string;
163
+ style?: React.CSSProperties;
164
+ }
165
+
166
+ /** The palette: a column of the search bar over the results, anchored to the top of the window. */
167
+ function CommandContent({ 'aria-label': label, filter = defaultFilter, itemToString = defaultItemToString, onQueryChange, container, children, className, style }: CommandContentProps) {
168
+ const size = React.useContext(CommandSizeContext);
169
+ const [query, setQuery] = React.useState('');
170
+ const [seat, setSeat] = React.useState<HTMLElement | null>(null);
171
+ const report = React.useRef(onQueryChange);
172
+ React.useEffect(() => {
173
+ report.current = onQueryChange;
174
+ });
175
+
176
+ // Closing clears the query, so the palette opens fresh.
177
+ React.useEffect(() => () => report.current?.(''), []);
178
+ useBodyPointerEventsCleanup();
179
+
180
+ const changeQuery = React.useCallback((next: string) => {
181
+ setQuery(next);
182
+ report.current?.(next);
183
+ }, []);
184
+ const listValue = React.useMemo(() => ({ query, filter, itemToString }), [query, filter, itemToString]);
185
+ const seatValue = React.useMemo(() => ({ seat, setSeat }), [seat]);
186
+
187
+ return (
188
+ <DialogPrimitive.Portal container={container}>
189
+ <PortalTheme asChild>
190
+ <DialogPrimitive.Overlay className="kui-BaseDialogOverlay kui-CommandOverlay">
191
+ <DialogPrimitive.Content
192
+ aria-label={label}
193
+ aria-describedby={undefined}
194
+ className={classNames('kui-CommandContent', `kui-r-size-${size}`, className)}
195
+ style={style}
196
+ onCloseAutoFocus={() => {
197
+ document.body.style.pointerEvents = '';
198
+ }}
199
+ >
200
+ <DialogPrimitive.Title className="kui-sr-only">{label}</DialogPrimitive.Title>
201
+ {/* cmdk owns the highlight and Enter; this file owns the filter, so order never changes. */}
202
+ <CommandPrimitive label={label} shouldFilter={false} loop className="kui-CommandRoot">
203
+ <CommandListContext.Provider value={listValue}>
204
+ <CommandQueryContext.Provider value={changeQuery}>
205
+ <CommandSeatContext.Provider value={seatValue}>{children}</CommandSeatContext.Provider>
206
+ </CommandQueryContext.Provider>
207
+ </CommandListContext.Provider>
208
+ </CommandPrimitive>
209
+ </DialogPrimitive.Content>
210
+ </DialogPrimitive.Overlay>
211
+ </PortalTheme>
212
+ </DialogPrimitive.Portal>
213
+ );
214
+ }
215
+
216
+ interface CommandInputProps extends Omit<React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>, 'value' | 'onValueChange' | 'aria-label'> {
217
+ /** The field's accessible name. */
218
+ 'aria-label': string;
219
+ /** An icon before the text, such as a search glyph. */
220
+ leading?: React.ReactNode;
221
+ }
222
+
223
+ /** The search bar, a pane of its own. The palette opens with the caret in it. */
224
+ function CommandInput({ leading, className, ...props }: CommandInputProps) {
225
+ const size = React.useContext(CommandSizeContext);
226
+ const setQuery = React.useContext(CommandQueryContext);
227
+ const inputRef = React.useRef<HTMLInputElement>(null);
228
+ return (
229
+ // A press anywhere on the bar puts the caret in the field.
230
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
231
+ <div
232
+ className={classNames('kui-CommandSearch', `kui-r-size-${size}`)}
233
+ onMouseDown={(event) => {
234
+ if (event.target === inputRef.current) return;
235
+ event.preventDefault();
236
+ inputRef.current?.focus();
237
+ }}
238
+ >
239
+ {leading ? (
240
+ <span className="kui-CommandSearchSlot" aria-hidden>
241
+ {leading}
242
+ </span>
243
+ ) : null}
244
+ {/* `autoFocus` focuses inside the tap's own task, which is what raises the keyboard on iOS. */}
245
+ <CommandPrimitive.Input
246
+ ref={inputRef}
247
+ // The palette is the field: it opens to type into, the one place autofocus is the design.
248
+ // eslint-disable-next-line jsx-a11y/no-autofocus
249
+ autoFocus
250
+ {...props}
251
+ onValueChange={setQuery}
252
+ className={classNames('kui-reset', 'kui-CommandInput', className)}
253
+ />
254
+ </div>
255
+ );
256
+ }
257
+
258
+ interface CommandListProps<T> {
259
+ /** Turns an item into a row (a `CommandItem`) or a group (a `CommandGroup`). */
260
+ children: (item: T) => React.ReactNode;
261
+ className?: string;
262
+ }
263
+
264
+ /** The results, a pane of their own under the search bar. Its height follows the results. */
265
+ function CommandList<T>({ children, className }: CommandListProps<T>) {
266
+ const size = React.useContext(CommandSizeContext);
267
+ const items = React.useContext(CommandItemsContext);
268
+ const { query, filter, itemToString } = React.useContext(CommandListContext);
269
+ const seat = React.useContext(CommandSeatContext);
270
+ const shown = React.useMemo(() => narrow(items, query, filter, itemToString), [items, query, filter, itemToString]);
271
+ return (
272
+ <div className={classNames('kui-BaseMenuContent', 'kui-variant-soft', `kui-r-size-${ROW_STEP[size]}`, 'kui-CommandPanel', className)}>
273
+ <CommandPrimitive.List className={classNames('kui-BaseMenuViewport', 'kui-CommandList')}>
274
+ {shown.map((item) => children(item as T))}
275
+ {/* The empty state's seat, inside this pane, so it is the same box as the results. */}
276
+ <div className="kui-CommandSeat" ref={seat ? seat.setSeat : undefined} />
277
+ </CommandPrimitive.List>
278
+ </div>
279
+ );
280
+ }
281
+
282
+ const CommandGroupItemsContext = React.createContext<readonly unknown[]>([]);
283
+
284
+ interface CommandGroupProps {
285
+ /** The group's own items, after filtering. Render them with `CommandCollection`. */
286
+ items: readonly unknown[];
287
+ children: React.ReactNode;
288
+ className?: string;
289
+ }
290
+
291
+ /** A labelled run of rows. A group with no matching rows is left out. */
292
+ function CommandGroup({ items, children, className }: CommandGroupProps) {
293
+ return (
294
+ <CommandGroupItemsContext.Provider value={items}>
295
+ <CommandPrimitive.Group className={classNames('kui-BaseMenuGroup', 'kui-CommandGroup', className)}>{children}</CommandPrimitive.Group>
296
+ </CommandGroupItemsContext.Provider>
297
+ );
298
+ }
299
+
300
+ interface CommandGroupLabelProps {
301
+ children: React.ReactNode;
302
+ className?: string;
303
+ }
304
+
305
+ /** The group's name, above its rows. */
306
+ function CommandGroupLabel({ children, className }: CommandGroupLabelProps) {
307
+ return (
308
+ <div className={classNames('kui-BaseMenuLabel', 'kui-CommandGroupLabel', className)} aria-hidden>
309
+ {children}
310
+ </div>
311
+ );
312
+ }
313
+
314
+ interface CommandCollectionProps<T> {
315
+ /** Turns one of the group's items into a row. */
316
+ children: (item: T) => React.ReactNode;
317
+ }
318
+
319
+ /** The group's rows. */
320
+ function CommandCollection<T>({ children }: CommandCollectionProps<T>) {
321
+ const items = React.useContext(CommandGroupItemsContext);
322
+ return <>{items.map((item) => children(item as T))}</>;
323
+ }
324
+
325
+ interface CommandItemProps extends Omit<React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>, 'value' | 'onSelect'> {
326
+ /** The item this row shows. A string is also the row's identity for the highlight. */
327
+ value?: unknown;
328
+ /** Runs when the row is pressed or Enter is pressed on it. The palette then closes. */
329
+ onSelect?: () => void;
330
+ /** An icon before the label. */
331
+ leading?: React.ReactNode;
332
+ /** Something after the label, such as a shortcut. */
333
+ trailing?: React.ReactNode;
334
+ /** Set when running the row destroys something. */
335
+ tone?: 'destructive';
336
+ /** Renders the child, such as your router's link, as the row. Enter follows the link. */
337
+ asChild?: boolean;
338
+ }
339
+
340
+ /** One row. Running it, by press or by Enter, runs `onSelect` and closes the palette. */
341
+ function CommandItem({ value, onSelect, leading, trailing, tone, asChild, children, className, ...props }: CommandItemProps) {
342
+ const close = React.useContext(CommandCloseContext);
343
+ const id = React.useId();
344
+ const element = React.useRef<HTMLDivElement>(null);
345
+ const clicked = React.useRef(false);
346
+ const itemValue = typeof value === 'string' ? value : id;
347
+
348
+ const run = () => {
349
+ onSelect?.();
350
+ close?.();
351
+ };
352
+
353
+ // cmdk runs a row from Enter without a click. A link has to be clicked to navigate, so Enter
354
+ // clicks it, and the click comes back through here as the run.
355
+ const handleSelect = () => {
356
+ if (clicked.current) {
357
+ clicked.current = false;
358
+ run();
359
+ return;
360
+ }
361
+ // Found by attribute, not by ref: an app's link component need not forward a ref.
362
+ const node = element.current ?? document.querySelector(`[data-kui-command-item="${CSS.escape(id)}"]`);
363
+ if (asChild && node instanceof HTMLAnchorElement) {
364
+ node.click();
365
+ return;
366
+ }
367
+ run();
368
+ };
369
+
370
+ const child = asChild && React.isValidElement<{ children?: React.ReactNode }>(children) ? children : null;
371
+ const inner = (label: React.ReactNode) => (
372
+ <>
373
+ {leading ? (
374
+ <span className="kui-CommandItemSlot" data-slot="leading" aria-hidden>
375
+ {leading}
376
+ </span>
377
+ ) : null}
378
+ <span className="kui-CommandItemLabel">{label}</span>
379
+ {trailing ? (
380
+ <span className="kui-CommandItemSlot" data-slot="trailing">
381
+ {trailing}
382
+ </span>
383
+ ) : null}
384
+ </>
385
+ );
386
+
387
+ return (
388
+ <CommandPrimitive.Item
389
+ {...props}
390
+ ref={element}
391
+ data-kui-command-item={id}
392
+ value={itemValue}
393
+ asChild={Boolean(child)}
394
+ onSelect={handleSelect}
395
+ onClickCapture={() => {
396
+ clicked.current = true;
397
+ }}
398
+ data-tone={tone}
399
+ className={classNames('kui-reset', 'kui-BaseMenuItem', 'kui-CommandItem', className)}
400
+ >
401
+ {child ? React.cloneElement(child, undefined, inner(child.props.children)) : inner(children)}
402
+ </CommandPrimitive.Item>
403
+ );
404
+ }
405
+
406
+ interface CommandEmptyProps {
407
+ children: React.ReactNode;
408
+ className?: string;
409
+ }
410
+
411
+ /** What the results pane says when nothing matches. It renders inside that pane. */
412
+ function CommandEmpty({ children, className }: CommandEmptyProps) {
413
+ const seat = React.useContext(CommandSeatContext);
414
+ if (!seat?.seat) return null;
415
+ return createPortal(<CommandPrimitive.Empty className={classNames('kui-CommandEmpty', className)}>{children}</CommandPrimitive.Empty>, seat.seat);
416
+ }
417
+
418
+ export { Command, CommandTrigger, CommandContent, CommandInput, CommandList, CommandGroup, CommandGroupLabel, CommandCollection, CommandItem, CommandEmpty, ROW_STEP as COMMAND_ROW_STEP };
419
+ export type {
420
+ CommandProps,
421
+ CommandTriggerProps,
422
+ CommandContentProps,
423
+ CommandInputProps,
424
+ CommandListProps,
425
+ CommandGroupProps,
426
+ CommandGroupLabelProps,
427
+ CommandCollectionProps,
428
+ CommandItemProps,
429
+ CommandEmptyProps,
430
+ CommandOpenChangeDetails,
431
+ CommandOpenChangeReason,
432
+ };
@@ -16,6 +16,7 @@
16
16
  @import './checkbox.css';
17
17
  @import './code.css';
18
18
  @import './context-menu.css';
19
+ @import './command.css';
19
20
  @import './dialog.css';
20
21
  @import './dropdown-menu.css';
21
22
  @import './combobox.css';