@exxatdesignux/ui 0.0.5

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 (59) hide show
  1. package/package.json +72 -0
  2. package/src/components/ui/avatar.tsx +384 -0
  3. package/src/components/ui/badge.tsx +49 -0
  4. package/src/components/ui/banner.tsx +364 -0
  5. package/src/components/ui/breadcrumb.tsx +120 -0
  6. package/src/components/ui/button.tsx +66 -0
  7. package/src/components/ui/calendar.tsx +220 -0
  8. package/src/components/ui/card.tsx +136 -0
  9. package/src/components/ui/chart.tsx +378 -0
  10. package/src/components/ui/checkbox.tsx +160 -0
  11. package/src/components/ui/coach-mark.tsx +361 -0
  12. package/src/components/ui/collapsible.tsx +33 -0
  13. package/src/components/ui/command.tsx +232 -0
  14. package/src/components/ui/date-picker-field.tsx +186 -0
  15. package/src/components/ui/dialog.tsx +171 -0
  16. package/src/components/ui/drag-handle-grip.tsx +10 -0
  17. package/src/components/ui/drawer.tsx +134 -0
  18. package/src/components/ui/dropdown-menu.tsx +422 -0
  19. package/src/components/ui/field.tsx +238 -0
  20. package/src/components/ui/form.tsx +137 -0
  21. package/src/components/ui/input-group.tsx +156 -0
  22. package/src/components/ui/input-mask.tsx +135 -0
  23. package/src/components/ui/input.tsx +22 -0
  24. package/src/components/ui/kbd.tsx +55 -0
  25. package/src/components/ui/label.tsx +25 -0
  26. package/src/components/ui/payment-card-fields.tsx +65 -0
  27. package/src/components/ui/popover.tsx +46 -0
  28. package/src/components/ui/radio-group.tsx +217 -0
  29. package/src/components/ui/select.tsx +191 -0
  30. package/src/components/ui/selection-tile-grid.tsx +246 -0
  31. package/src/components/ui/separator.tsx +28 -0
  32. package/src/components/ui/sheet.tsx +147 -0
  33. package/src/components/ui/sidebar.tsx +716 -0
  34. package/src/components/ui/skeleton.tsx +13 -0
  35. package/src/components/ui/sonner.tsx +39 -0
  36. package/src/components/ui/status-badge.tsx +109 -0
  37. package/src/components/ui/table.tsx +117 -0
  38. package/src/components/ui/tabs.tsx +90 -0
  39. package/src/components/ui/textarea.tsx +18 -0
  40. package/src/components/ui/tip.tsx +21 -0
  41. package/src/components/ui/toggle-group.tsx +89 -0
  42. package/src/components/ui/toggle-switch.tsx +31 -0
  43. package/src/components/ui/toggle.tsx +48 -0
  44. package/src/components/ui/tooltip.tsx +59 -0
  45. package/src/components/ui/view-segmented-control.tsx +160 -0
  46. package/src/globals.css +1795 -0
  47. package/src/hooks/.gitkeep +0 -0
  48. package/src/hooks/use-app-theme.ts +172 -0
  49. package/src/hooks/use-coach-mark.ts +342 -0
  50. package/src/hooks/use-mobile.ts +31 -0
  51. package/src/hooks/use-mod-key-label.ts +29 -0
  52. package/src/index.ts +55 -0
  53. package/src/lib/compose-refs.ts +15 -0
  54. package/src/lib/date-filter.ts +67 -0
  55. package/src/lib/utils.ts +6 -0
  56. package/src/theme/apply-windows-contrast-theme.ts +29 -0
  57. package/src/theme/windows-contrast-theme.json +147 -0
  58. package/src/theme.css +1130 -0
  59. package/src/types/react-payment-inputs.d.ts +20 -0
@@ -0,0 +1,361 @@
1
+ "use client"
2
+
3
+ /**
4
+ * CoachMark — contextual onboarding / feature-discovery popover
5
+ *
6
+ * Targets elements by CSS selector, scrolls them into view, and positions
7
+ * the popover relative to the target using Radix's virtual anchor.
8
+ *
9
+ * Variants:
10
+ * • single — standalone tip anchored to a target element
11
+ * • flow — multi-step walkthrough with prev/next and step indicator
12
+ * • image — includes a hero image above the content
13
+ * • no-image — text-only (title + description)
14
+ *
15
+ * Brand-colored background with spotlight overlay on the target element.
16
+ *
17
+ * WCAG 2.1 AA:
18
+ * • Focus trapped inside while open
19
+ * • Escape dismisses
20
+ * • aria-labelledby / aria-describedby wired automatically
21
+ * • Step indicator announced via aria-live
22
+ */
23
+
24
+ import * as React from "react"
25
+ import { createPortal } from "react-dom"
26
+ import { Popover as PopoverPrimitive } from "radix-ui"
27
+ import { cva, type VariantProps } from "class-variance-authority"
28
+ import { Button } from "./button"
29
+ import { cn } from "../../lib/utils"
30
+ import type { CoachMarkState } from "../../hooks/use-coach-mark"
31
+
32
+ /* ── Variant styles ─────────────────────────────────────────────────────── */
33
+
34
+ const coachMarkVariants = cva(
35
+ "z-[60] flex flex-col overflow-hidden rounded-xl bg-brand-deep text-white shadow-xl outline-none hc:!bg-background hc:!text-foreground hc:!border-2 hc:!border-foreground hc:!shadow-none",
36
+ {
37
+ variants: {
38
+ size: {
39
+ default: "w-[320px]",
40
+ sm: "w-[260px]",
41
+ lg: "w-[400px]",
42
+ },
43
+ },
44
+ defaultVariants: {
45
+ size: "default",
46
+ },
47
+ }
48
+ )
49
+
50
+ /* ── Sub-components ─────────────────────────────────────────────────────── */
51
+
52
+ function CoachMarkImage({
53
+ src,
54
+ alt,
55
+ }: {
56
+ src: string
57
+ alt: string
58
+ }) {
59
+ return (
60
+ <div className="relative w-full overflow-hidden">
61
+ <img
62
+ src={src}
63
+ alt={alt}
64
+ className="h-[160px] w-full object-cover"
65
+ />
66
+ </div>
67
+ )
68
+ }
69
+
70
+ function CoachMarkStepIndicator({
71
+ current,
72
+ total,
73
+ }: {
74
+ current: number
75
+ total: number
76
+ }) {
77
+ return (
78
+ <div
79
+ className="flex items-center gap-1"
80
+ role="status"
81
+ aria-live="polite"
82
+ aria-label={`Step ${current + 1} of ${total}`}
83
+ >
84
+ {Array.from({ length: total }, (_, i) => (
85
+ <span
86
+ key={i}
87
+ className={cn(
88
+ "h-1.5 rounded-full transition-all duration-200",
89
+ i === current
90
+ ? "w-4 bg-white hc:bg-foreground"
91
+ : "w-1.5 bg-white/30 hc:bg-foreground/40"
92
+ )}
93
+ aria-hidden="true"
94
+ />
95
+ ))}
96
+ </div>
97
+ )
98
+ }
99
+
100
+ /* ── Spotlight overlay — highlights the target element ──────────────────── */
101
+
102
+ function SpotlightOverlay({
103
+ rect,
104
+ maskId,
105
+ }: {
106
+ rect: { x: number; y: number; width: number; height: number }
107
+ /** Unique per coach instance — multiple flows on one page must not duplicate SVG mask ids. */
108
+ maskId: string
109
+ }) {
110
+ const padding = 6
111
+ const borderRadius = 8
112
+ const x = rect.x - padding
113
+ const y = rect.y - padding
114
+ const w = rect.width + padding * 2
115
+ const h = rect.height + padding * 2
116
+ const maskUrl = `url(#${maskId})`
117
+
118
+ return createPortal(
119
+ <div
120
+ className="fixed inset-0 z-[55]"
121
+ aria-hidden="true"
122
+ >
123
+ {/* Semi-transparent overlay with a cutout for the target */}
124
+ <svg className="absolute inset-0 w-full h-full">
125
+ <defs>
126
+ <mask id={maskId}>
127
+ <rect width="100%" height="100%" fill="white" />
128
+ <rect
129
+ x={x}
130
+ y={y}
131
+ width={w}
132
+ height={h}
133
+ rx={borderRadius}
134
+ ry={borderRadius}
135
+ fill="black"
136
+ />
137
+ </mask>
138
+ </defs>
139
+ <rect
140
+ width="100%"
141
+ height="100%"
142
+ fill="rgba(0,0,0,0.5)"
143
+ mask={maskUrl}
144
+ />
145
+ </svg>
146
+
147
+ {/* Highlight ring around the target */}
148
+ <div
149
+ className="absolute rounded-lg ring-2 ring-brand hc:ring-foreground shadow-[0_0_0_4px_rgba(0,0,0,0.3)] hc:shadow-none"
150
+ style={{
151
+ left: x,
152
+ top: y,
153
+ width: w,
154
+ height: h,
155
+ borderRadius,
156
+ }}
157
+ />
158
+ </div>,
159
+ document.body
160
+ )
161
+ }
162
+
163
+ /* ── Main component ─────────────────────────────────────────────────────── */
164
+
165
+ export interface CoachMarkProps
166
+ extends VariantProps<typeof coachMarkVariants> {
167
+ /** State from useCoachMark hook */
168
+ state: CoachMarkState
169
+ /** Default popover placement side (step-level side takes priority) */
170
+ side?: "top" | "bottom" | "left" | "right"
171
+ /** Default popover alignment (step-level align takes priority) */
172
+ align?: "start" | "center" | "end"
173
+ /** Offset from anchor element in px */
174
+ sideOffset?: number
175
+ /** Label for the primary (next/done) button — defaults to "Next" / "Got it" */
176
+ nextLabel?: string
177
+ /** Label for the skip button — defaults to "Skip" */
178
+ skipLabel?: string
179
+ /** Extra className for the content container */
180
+ className?: string
181
+ }
182
+
183
+ export function CoachMark({
184
+ state,
185
+ side = "bottom",
186
+ align = "center",
187
+ sideOffset = 12,
188
+ nextLabel,
189
+ skipLabel = "Skip",
190
+ size,
191
+ className,
192
+ }: CoachMarkProps) {
193
+ const spotlightMaskId = React.useId().replace(/:/g, "")
194
+ const {
195
+ isOpen,
196
+ step,
197
+ currentStep,
198
+ totalSteps,
199
+ isFlow,
200
+ isFirst,
201
+ isLast,
202
+ next,
203
+ prev,
204
+ skip,
205
+ anchorRect,
206
+ } = state
207
+
208
+ if (!isOpen || !step) return null
209
+ if (!anchorRect) return null
210
+
211
+ const titleId = `coach-mark-title-${step.id}`
212
+ const descId = `coach-mark-desc-${step.id}`
213
+ const hasImage = Boolean(step.image)
214
+ const primaryLabel = nextLabel ?? (isLast ? "Got it" : "Next")
215
+ const resolvedSide = step.side ?? side
216
+ const resolvedAlign = step.align ?? align
217
+
218
+ return (
219
+ <>
220
+ {/* Spotlight overlay */}
221
+ <SpotlightOverlay rect={anchorRect} maskId={`coach-spotlight-${spotlightMaskId}`} />
222
+
223
+ {/* Popover with virtual anchor */}
224
+ <PopoverPrimitive.Root open>
225
+ <PopoverPrimitive.Anchor
226
+ virtualRef={{
227
+ current: {
228
+ getBoundingClientRect: () => ({
229
+ x: anchorRect.x,
230
+ y: anchorRect.y,
231
+ top: anchorRect.y,
232
+ left: anchorRect.x,
233
+ bottom: anchorRect.y + anchorRect.height,
234
+ right: anchorRect.x + anchorRect.width,
235
+ width: anchorRect.width,
236
+ height: anchorRect.height,
237
+ toJSON: () => {},
238
+ }),
239
+ },
240
+ }}
241
+ />
242
+
243
+ <PopoverPrimitive.Portal>
244
+ <PopoverPrimitive.Content
245
+ data-slot="coach-mark"
246
+ side={resolvedSide}
247
+ align={resolvedAlign}
248
+ sideOffset={sideOffset}
249
+ onOpenAutoFocus={(e) => e.preventDefault()}
250
+ onInteractOutside={(e) => e.preventDefault()}
251
+ onEscapeKeyDown={() => skip()}
252
+ aria-labelledby={titleId}
253
+ aria-describedby={descId}
254
+ className={cn(
255
+ coachMarkVariants({ size }),
256
+ /* animations */
257
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
258
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
259
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
260
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
261
+ "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
262
+ className
263
+ )}
264
+ >
265
+ {/* Image (optional) */}
266
+ {hasImage && (
267
+ <CoachMarkImage
268
+ src={step.image!}
269
+ alt={step.imageAlt ?? step.title}
270
+ />
271
+ )}
272
+
273
+ {/* Body */}
274
+ <div className="flex flex-col gap-3 p-4">
275
+ {/* Title + close */}
276
+ <div className="flex items-start gap-2">
277
+ <div className="flex-1 min-w-0">
278
+ <h3
279
+ id={titleId}
280
+ className="text-sm font-semibold text-white leading-snug"
281
+ >
282
+ {step.title}
283
+ </h3>
284
+ </div>
285
+ <button
286
+ type="button"
287
+ onClick={skip}
288
+ className="shrink-0 -mt-0.5 -mr-1 flex h-6 w-6 items-center justify-center rounded-md text-white/60 hover:bg-white/15 hover:text-white transition-colors focus-visible:outline-2 focus-visible:outline-white hc:!text-foreground hc:hover:!bg-foreground/10"
289
+ aria-label="Dismiss"
290
+ >
291
+ <i className="fa-light fa-xmark text-xs" aria-hidden="true" />
292
+ </button>
293
+ </div>
294
+
295
+ {/* Description */}
296
+ <p
297
+ id={descId}
298
+ className="text-sm text-white/80 leading-relaxed"
299
+ >
300
+ {step.description}
301
+ </p>
302
+
303
+ {/* Footer: step indicator + actions */}
304
+ <div className="flex items-center justify-between gap-3 pt-1">
305
+ {/* Left: step dots (flow only) */}
306
+ <div className="flex-1">
307
+ {isFlow && (
308
+ <CoachMarkStepIndicator
309
+ current={currentStep}
310
+ total={totalSteps}
311
+ />
312
+ )}
313
+ </div>
314
+
315
+ {/* Right: buttons */}
316
+ <div className="flex items-center gap-2 shrink-0">
317
+ {isFlow && !isLast && (
318
+ <button
319
+ type="button"
320
+ onClick={skip}
321
+ className="h-8 px-3 text-xs font-medium text-white/60 hover:text-white transition-colors rounded-md focus-visible:outline-2 focus-visible:outline-white hc:!text-foreground hc:hover:!text-foreground/80"
322
+ >
323
+ {skipLabel}
324
+ </button>
325
+ )}
326
+ {isFlow && !isFirst && (
327
+ <button
328
+ type="button"
329
+ onClick={prev}
330
+ className="inline-flex items-center gap-1.5 h-8 px-3 text-xs font-medium text-white bg-white/15 hover:bg-white/25 rounded-md border border-white/20 transition-colors focus-visible:outline-2 focus-visible:outline-white hc:!bg-background hc:!text-foreground hc:!border-foreground"
331
+ >
332
+ <i className="fa-light fa-arrow-left text-xs" aria-hidden="true" />
333
+ Back
334
+ </button>
335
+ )}
336
+ <button
337
+ type="button"
338
+ onClick={next}
339
+ className="inline-flex items-center gap-1.5 h-8 px-3 text-xs font-medium text-brand-deep bg-white hover:bg-white/90 rounded-md transition-colors focus-visible:outline-2 focus-visible:outline-white hc:!bg-foreground hc:!text-background hc:border hc:border-foreground"
340
+ >
341
+ {primaryLabel}
342
+ {isFlow && !isLast && (
343
+ <i className="fa-light fa-arrow-right text-xs" aria-hidden="true" />
344
+ )}
345
+ </button>
346
+ </div>
347
+ </div>
348
+ </div>
349
+
350
+ {/* Arrow */}
351
+ <PopoverPrimitive.Arrow
352
+ className="fill-brand-deep drop-shadow-sm hc:fill-background"
353
+ width={12}
354
+ height={6}
355
+ />
356
+ </PopoverPrimitive.Content>
357
+ </PopoverPrimitive.Portal>
358
+ </PopoverPrimitive.Root>
359
+ </>
360
+ )
361
+ }
@@ -0,0 +1,33 @@
1
+ "use client"
2
+
3
+ import { Collapsible as CollapsiblePrimitive } from "radix-ui"
4
+
5
+ function Collapsible({
6
+ ...props
7
+ }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
8
+ return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
9
+ }
10
+
11
+ function CollapsibleTrigger({
12
+ ...props
13
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
14
+ return (
15
+ <CollapsiblePrimitive.CollapsibleTrigger
16
+ data-slot="collapsible-trigger"
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ function CollapsibleContent({
23
+ ...props
24
+ }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
25
+ return (
26
+ <CollapsiblePrimitive.CollapsibleContent
27
+ data-slot="collapsible-content"
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent }
@@ -0,0 +1,232 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Command as CommandPrimitive } from "cmdk"
5
+ import * as DialogPrimitive from "@radix-ui/react-dialog"
6
+
7
+ import { cn } from "../../lib/utils"
8
+ import {
9
+ InputGroup,
10
+ InputGroupAddon,
11
+ } from "./input-group"
12
+ import { SearchIcon, CheckIcon } from "lucide-react"
13
+
14
+ function Command({
15
+ className,
16
+ ...props
17
+ }: React.ComponentProps<typeof CommandPrimitive>) {
18
+ return (
19
+ <CommandPrimitive
20
+ data-slot="command"
21
+ className={cn(
22
+ "flex size-full flex-col overflow-hidden rounded-xl! bg-popover p-1 text-popover-foreground",
23
+ className
24
+ )}
25
+ {...props}
26
+ />
27
+ )
28
+ }
29
+
30
+ function CommandDialog({
31
+ title = "Command Palette",
32
+ description = "Search for a command to run...",
33
+ children,
34
+ className,
35
+ overlayClassName,
36
+ showCloseButton: _showCloseButton = false,
37
+ ...props
38
+ }: React.ComponentProps<typeof DialogPrimitive.Root> & {
39
+ title?: string
40
+ description?: string
41
+ className?: string
42
+ /** Backdrop — default is invisible (no dim/blur) but still captures outside clicks for modal behavior */
43
+ overlayClassName?: string
44
+ showCloseButton?: boolean
45
+ }) {
46
+ return (
47
+ <DialogPrimitive.Root {...props}>
48
+ <DialogPrimitive.Portal>
49
+ <DialogPrimitive.Overlay
50
+ className={cn(
51
+ "fixed inset-0 z-50 bg-transparent data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
52
+ overlayClassName,
53
+ )}
54
+ />
55
+ <DialogPrimitive.Content
56
+ data-slot="dialog-content"
57
+ className={cn(
58
+ "fixed top-[max(1rem,8vh)] start-1/2 z-50 w-full max-w-[min(42rem,calc(100%-2rem))] -translate-x-1/2 rtl:translate-x-1/2 overflow-hidden rounded-xl bg-popover p-0 text-popover-foreground ring-1 ring-foreground/10 shadow-lg sm:max-w-3xl md:max-w-4xl data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
59
+ className,
60
+ )}
61
+ >
62
+ <DialogPrimitive.Title className="sr-only">{title}</DialogPrimitive.Title>
63
+ <DialogPrimitive.Description className="sr-only">{description}</DialogPrimitive.Description>
64
+ {children}
65
+ </DialogPrimitive.Content>
66
+ </DialogPrimitive.Portal>
67
+ </DialogPrimitive.Root>
68
+ )
69
+ }
70
+
71
+ const CommandInput = React.forwardRef<
72
+ HTMLInputElement,
73
+ React.ComponentProps<typeof CommandPrimitive.Input> & {
74
+ /**
75
+ * `palette` — flat header row (icon + field) for ⌘K dialogs; matches blocks.so command-menu-02.
76
+ * `default` — pill `InputGroup` for embedded / library previews.
77
+ */
78
+ variant?: "default" | "palette"
79
+ }
80
+ >(function CommandInput({ className, variant = "default", ...props }, ref) {
81
+ if (variant === "palette") {
82
+ return (
83
+ <div data-slot="command-input-wrapper" className="min-w-0 flex-1">
84
+ <div className="flex min-h-10 w-full items-center gap-2">
85
+ <SearchIcon className="size-4 shrink-0 opacity-50" aria-hidden />
86
+ <CommandPrimitive.Input
87
+ ref={ref}
88
+ data-slot="command-input"
89
+ className={cn(
90
+ // cmdk keeps DOM focus here; rows use accent fill (not rings) so this can match `Input` focus without doubling ring-on-ring with items.
91
+ "flex h-10 w-full min-w-0 flex-1 border-0 bg-transparent p-0 text-[15px] leading-normal text-foreground shadow-none outline-none ring-0 placeholder:text-muted-foreground focus:outline-none focus-visible:border-transparent focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50",
92
+ className
93
+ )}
94
+ {...props}
95
+ />
96
+ </div>
97
+ </div>
98
+ )
99
+ }
100
+
101
+ return (
102
+ <div data-slot="command-input-wrapper" className="p-1 pb-0">
103
+ <InputGroup className="h-8! rounded-lg! border-input/30 bg-input/30 shadow-none! *:data-[slot=input-group-addon]:ps-2!">
104
+ <CommandPrimitive.Input
105
+ ref={ref}
106
+ data-slot="command-input"
107
+ className={cn(
108
+ "w-full text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
109
+ className
110
+ )}
111
+ {...props}
112
+ />
113
+ <InputGroupAddon>
114
+ <SearchIcon className="size-4 shrink-0 opacity-50" />
115
+ </InputGroupAddon>
116
+ </InputGroup>
117
+ </div>
118
+ )
119
+ })
120
+ CommandInput.displayName = "CommandInput"
121
+
122
+ function CommandList({
123
+ className,
124
+ ...props
125
+ }: React.ComponentProps<typeof CommandPrimitive.List>) {
126
+ return (
127
+ <CommandPrimitive.List
128
+ data-slot="command-list"
129
+ className={cn(
130
+ "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
131
+ className
132
+ )}
133
+ {...props}
134
+ />
135
+ )
136
+ }
137
+
138
+ function CommandEmpty({
139
+ className,
140
+ ...props
141
+ }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
142
+ return (
143
+ <CommandPrimitive.Empty
144
+ data-slot="command-empty"
145
+ className={cn("py-6 text-center text-sm", className)}
146
+ {...props}
147
+ />
148
+ )
149
+ }
150
+
151
+ function CommandGroup({
152
+ className,
153
+ ...props
154
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) {
155
+ return (
156
+ <CommandPrimitive.Group
157
+ data-slot="command-group"
158
+ className={cn(
159
+ "overflow-hidden p-1 text-foreground **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground",
160
+ className
161
+ )}
162
+ {...props}
163
+ />
164
+ )
165
+ }
166
+
167
+ function CommandSeparator({
168
+ className,
169
+ ...props
170
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
171
+ return (
172
+ <CommandPrimitive.Separator
173
+ data-slot="command-separator"
174
+ className={cn("-mx-1 h-px bg-border", className)}
175
+ {...props}
176
+ />
177
+ )
178
+ }
179
+
180
+ function CommandItem({
181
+ className,
182
+ children,
183
+ ...props
184
+ }: React.ComponentProps<typeof CommandPrimitive.Item>) {
185
+ return (
186
+ <CommandPrimitive.Item
187
+ data-slot="command-item"
188
+ className={cn(
189
+ // Match popover lists (e.g. DropdownMenuItem): accent surface for hover + cmdk selection — same ring language as sidebar rows (ring-ring) is avoided here so input + row don’t read as double focus.
190
+ // NOTE: cmdk sets `data-selected="false"` on every item and `="true"` only on the highlighted one.
191
+ // Tailwind v4's bare `data-selected:` variant matches attribute PRESENCE, so it applies to every
192
+ // item — in HC mode, accent resolves to brand purple and every row looks selected. Match `=true`
193
+ // explicitly. Also dial HC selection to a border+ring affordance so the palette stays readable
194
+ // when only one row is truly active.
195
+ "group/command-item relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden ring-ring transition-colors select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 hover:bg-accent hover:text-accent-foreground data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[selected=true]:*:[svg]:text-accent-foreground hc:data-[selected=true]:ring-2 hc:data-[selected=true]:ring-ring hc:data-[selected=true]:ring-inset forced-colors:data-[selected=true]:bg-[Highlight] forced-colors:data-[selected=true]:text-[HighlightText]",
196
+ className
197
+ )}
198
+ {...props}
199
+ >
200
+ {children}
201
+ <CheckIcon className="ms-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100" />
202
+ </CommandPrimitive.Item>
203
+ )
204
+ }
205
+
206
+ function CommandShortcut({
207
+ className,
208
+ ...props
209
+ }: React.ComponentProps<"span">) {
210
+ return (
211
+ <span
212
+ data-slot="command-shortcut"
213
+ className={cn(
214
+ "ms-auto text-xs tracking-widest text-muted-foreground group-data-[selected=true]/command-item:text-accent-foreground",
215
+ className
216
+ )}
217
+ {...props}
218
+ />
219
+ )
220
+ }
221
+
222
+ export {
223
+ Command,
224
+ CommandDialog,
225
+ CommandInput,
226
+ CommandList,
227
+ CommandEmpty,
228
+ CommandGroup,
229
+ CommandItem,
230
+ CommandShortcut,
231
+ CommandSeparator,
232
+ }