@preset.nz/ux-kit 0.1.0 → 0.1.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 (55) hide show
  1. package/README.md +27 -0
  2. package/THIRD_PARTY_NOTICES.md +253 -0
  3. package/package.json +9 -10
  4. package/src/chat/index.ts +9 -0
  5. package/src/chat/message.tsx +80 -0
  6. package/src/chat/prompt-input.tsx +121 -0
  7. package/src/chat/reasoning.tsx +182 -0
  8. package/src/chat/scroller.tsx +147 -0
  9. package/src/chat/suggestion.tsx +65 -0
  10. package/src/chat/tool.tsx +143 -0
  11. package/src/chat/types.ts +8 -0
  12. package/src/components/avatar.tsx +1 -1
  13. package/src/components/badge.tsx +4 -6
  14. package/src/components/breadcrumb.tsx +8 -10
  15. package/src/components/button.tsx +1 -1
  16. package/src/components/card.tsx +4 -21
  17. package/src/components/checkbox.tsx +3 -5
  18. package/src/components/color-field.tsx +20 -7
  19. package/src/components/color-swatch.tsx +3 -3
  20. package/src/components/context-menu.tsx +13 -13
  21. package/src/components/dialog.tsx +5 -9
  22. package/src/components/dropdown-menu.tsx +13 -13
  23. package/src/components/empty-state.tsx +6 -3
  24. package/src/components/input.tsx +2 -2
  25. package/src/components/label.tsx +3 -2
  26. package/src/components/navigation-menu.tsx +16 -10
  27. package/src/components/number-field.tsx +30 -26
  28. package/src/components/popover.tsx +3 -10
  29. package/src/components/select.tsx +16 -28
  30. package/src/components/separator.tsx +2 -6
  31. package/src/components/sheet.tsx +16 -8
  32. package/src/components/side-panel.tsx +31 -8
  33. package/src/components/sidebar.tsx +15 -52
  34. package/src/components/skeleton.tsx +1 -1
  35. package/src/components/slider.tsx +4 -8
  36. package/src/components/snackbar.tsx +36 -34
  37. package/src/components/status-bar.tsx +10 -3
  38. package/src/components/tabs.tsx +3 -6
  39. package/src/components/textarea.tsx +2 -2
  40. package/src/components/toggle-group.tsx +4 -4
  41. package/src/components/toggle.tsx +1 -1
  42. package/src/components/toolbar.tsx +8 -11
  43. package/src/components/tooltip.tsx +1 -1
  44. package/src/graphics/adsr.ts +21 -13
  45. package/src/graphics/curve-editor.tsx +56 -14
  46. package/src/graphics/curve-edits.ts +20 -3
  47. package/src/graphics/index.ts +6 -6
  48. package/src/graphics/scale.ts +4 -1
  49. package/src/graphics/use-canvas.ts +1 -1
  50. package/src/graphics/use-tokens.ts +4 -1
  51. package/src/index.css +1 -1
  52. package/src/index.ts +3 -5
  53. package/src/lib/use-mobile.ts +1 -1
  54. package/src/lib/utils.ts +1 -1
  55. package/src/shadcn-tailwind.css +665 -0
@@ -1,8 +1,7 @@
1
- import * as React from "react"
2
1
  import { NumberField as NumberFieldPrimitive } from "@base-ui/react/number-field"
3
-
4
- import { cn } from "../lib/utils"
2
+ import * as React from "react"
5
3
  import { ArrowsHorizontalIcon } from "../icons"
4
+ import { cn } from "../lib/utils"
6
5
 
7
6
  // NumberField: a number you can drag. The label is the scrub handle (Base UI's ScrubArea);
8
7
  // click it, or tab in, to type. Behaviour is Oblique's InputNumber on Base UI's NumberField:
@@ -29,10 +28,7 @@ interface NumberChange {
29
28
  }
30
29
 
31
30
  interface NumberFieldProps
32
- extends Omit<
33
- React.ComponentProps<"div">,
34
- "onChange" | "defaultValue" | "children" | "value"
35
- > {
31
+ extends Omit<React.ComponentProps<"div">, "onChange" | "defaultValue" | "children" | "value"> {
36
32
  value: number
37
33
  onValueChange?: (value: number, details: NumberChange) => void
38
34
  min?: number
@@ -112,7 +108,7 @@ function NumberField({
112
108
  const digits = integer ? 0 : (precision ?? placesOf(small))
113
109
  const options = React.useMemo<Intl.NumberFormatOptions>(
114
110
  () => ({ maximumFractionDigits: digits, useGrouping: false, ...format }),
115
- [digits, format]
111
+ [digits, format],
116
112
  )
117
113
 
118
114
  // The field's own value, so a scrub follows the pointer without waiting for the app's round
@@ -165,6 +161,7 @@ function NumberField({
165
161
  })
166
162
  }
167
163
 
164
+ // biome-ignore lint/correctness/useExhaustiveDependencies: the effect follows `value` alone; `show` and `clearSettle` are per-render closures over refs and state setters
168
165
  React.useEffect(() => {
169
166
  valueRef.current = value
170
167
  if (scrub.current.started) return
@@ -174,15 +171,17 @@ function NumberField({
174
171
  show(value)
175
172
  }, [value])
176
173
 
174
+ // biome-ignore lint/correctness/useExhaustiveDependencies: unmount cleanup only; `clearSettle` is a per-render closure over refs
177
175
  React.useEffect(
178
176
  () => () => {
179
177
  clearSettle()
180
178
  cancelAnimationFrame(pending.current.raf)
181
179
  },
182
- []
180
+ [],
183
181
  )
184
182
 
185
183
  // Escape while scrubbing. Captured so the app's own Escape (clear selection) doesn't also run.
184
+ // biome-ignore lint/correctness/useExhaustiveDependencies: the listener lives while a scrub does; `show` and `dropPending` are per-render closures over refs
186
185
  React.useEffect(() => {
187
186
  if (!scrubbing || !onScrubCancel) return
188
187
  const onKey = (e: KeyboardEvent) => {
@@ -297,7 +296,10 @@ function NumberField({
297
296
  />
298
297
  )
299
298
  const unit = suffix != null && (
300
- <span data-slot="number-field-suffix" className="flex shrink-0 items-center pr-2 text-muted-foreground select-none">
299
+ <span
300
+ data-slot="number-field-suffix"
301
+ className="flex shrink-0 items-center pr-2 text-muted-foreground select-none"
302
+ >
301
303
  {suffix}
302
304
  </span>
303
305
  )
@@ -322,9 +324,10 @@ function NumberField({
322
324
  className={cn(
323
325
  "min-w-0",
324
326
  !outside && `group/number-field relative ${boxClass}`,
325
- labelPlacement === "column" && "col-span-2 grid grid-cols-subgrid items-center data-disabled:opacity-50",
327
+ labelPlacement === "column" &&
328
+ "col-span-2 grid grid-cols-subgrid items-center data-disabled:opacity-50",
326
329
  labelPlacement === "above" && "flex flex-col gap-1 data-disabled:opacity-50",
327
- className
330
+ className,
328
331
  )}
329
332
  {...props}
330
333
  >
@@ -340,8 +343,8 @@ function NumberField({
340
343
  ? "cursor-default"
341
344
  : cn(
342
345
  "cursor-ew-resize hover:text-foreground data-scrubbing:text-foreground",
343
- !outside && "hover:bg-muted data-scrubbing:bg-selection/20"
344
- )
346
+ !outside && "hover:bg-muted data-scrubbing:bg-selection/20",
347
+ ),
345
348
  )}
346
349
  >
347
350
  {label}
@@ -367,10 +370,7 @@ function NumberField({
367
370
  const AXES = ["X", "Y", "Z", "W"]
368
371
 
369
372
  interface VectorFieldProps
370
- extends Omit<
371
- NumberFieldProps,
372
- "value" | "onValueChange" | "label" | "className"
373
- > {
373
+ extends Omit<NumberFieldProps, "value" | "onValueChange" | "label" | "className"> {
374
374
  value: number[]
375
375
  /** The whole vector with the changed component replaced. */
376
376
  onValueChange?: (value: number[], details: NumberChange) => void
@@ -393,20 +393,20 @@ function VectorField({
393
393
  return (
394
394
  <div
395
395
  data-slot="vector-field"
396
- className={cn(
397
- "flex min-w-0 gap-1",
398
- orientation === "column" && "flex-col",
399
- className
400
- )}
396
+ className={cn("flex min-w-0 gap-1", orientation === "column" && "flex-col", className)}
401
397
  >
402
398
  {value.map((x, i) => (
403
399
  <NumberField
400
+ // biome-ignore lint/suspicious/noArrayIndexKey: one field per axis; the index is the axis
404
401
  key={i}
405
402
  {...props}
406
403
  label={labels?.[i] ?? (value.length <= AXES.length ? AXES[i] : String(i + 1))}
407
404
  value={x}
408
405
  onValueChange={(n, d) =>
409
- onValueChange?.(value.map((v, j) => (j === i ? n : v)), d)
406
+ onValueChange?.(
407
+ value.map((v, j) => (j === i ? n : v)),
408
+ d,
409
+ )
410
410
  }
411
411
  />
412
412
  ))}
@@ -414,15 +414,19 @@ function VectorField({
414
414
  )
415
415
  }
416
416
 
417
- export { NumberField, VectorField }
418
417
  export type { NumberChange, NumberFieldProps, VectorFieldProps }
418
+ export { NumberField, VectorField }
419
419
 
420
420
  /**
421
421
  * Pixels of drag per step, so a full-range scrub is about 300px whatever the step: a 0–8 field
422
422
  * with 0.25 steps moves one step per ~9px instead of per pixel, which read as jumping.
423
423
  * Unbounded ranges keep one step per pixel.
424
424
  */
425
- function pixelsPerStep(min: number | undefined, max: number | undefined, step: number | undefined): number {
425
+ function pixelsPerStep(
426
+ min: number | undefined,
427
+ max: number | undefined,
428
+ step: number | undefined,
429
+ ): number {
426
430
  if (min == null || max == null || !step || max <= min) return 1
427
431
  return Math.min(12, Math.max(1, Math.round((300 * step) / (max - min))))
428
432
  }
@@ -1,5 +1,5 @@
1
- import * as React from "react"
2
1
  import { Popover as PopoverPrimitive } from "@base-ui/react/popover"
2
+ import type * as React from "react"
3
3
 
4
4
  import { cn } from "../lib/utils"
5
5
 
@@ -33,7 +33,7 @@ function PopoverContent({
33
33
  data-slot="popover-content"
34
34
  className={cn(
35
35
  "z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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",
36
- className
36
+ className,
37
37
  )}
38
38
  {...props}
39
39
  />
@@ -72,11 +72,4 @@ function PopoverDescription({ className, ...props }: PopoverPrimitive.Descriptio
72
72
  )
73
73
  }
74
74
 
75
- export {
76
- Popover,
77
- PopoverContent,
78
- PopoverDescription,
79
- PopoverHeader,
80
- PopoverTitle,
81
- PopoverTrigger,
82
- }
75
+ export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger }
@@ -1,10 +1,9 @@
1
1
  "use client"
2
2
 
3
- import * as React from "react"
4
3
  import { Select as SelectPrimitive } from "@base-ui/react/select"
5
-
4
+ import { CaretDownIcon, CaretUpIcon, CheckIcon } from "@phosphor-icons/react"
5
+ import type * as React from "react"
6
6
  import { cn } from "../lib/utils"
7
- import { CaretDownIcon, CheckIcon, CaretUpIcon } from "@phosphor-icons/react"
8
7
 
9
8
  const Select = SelectPrimitive.Root
10
9
 
@@ -42,15 +41,13 @@ function SelectTrigger({
42
41
  data-size={size}
43
42
  className={cn(
44
43
  "flex w-fit items-center justify-between gap-1.5 rounded-none border border-input bg-transparent py-2 pr-2 pl-2.5 text-xs whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
45
- className
44
+ className,
46
45
  )}
47
46
  {...props}
48
47
  >
49
48
  {children}
50
49
  <SelectPrimitive.Icon
51
- render={
52
- <CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />
53
- }
50
+ render={<CaretDownIcon className="pointer-events-none size-4 text-muted-foreground" />}
54
51
  />
55
52
  </SelectPrimitive.Trigger>
56
53
  )
@@ -83,7 +80,10 @@ function SelectContent({
83
80
  <SelectPrimitive.Popup
84
81
  data-slot="select-content"
85
82
  data-align-trigger={alignItemWithTrigger}
86
- className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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", className )}
83
+ className={cn(
84
+ "relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 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",
85
+ className,
86
+ )}
87
87
  {...props}
88
88
  >
89
89
  <SelectScrollUpButton />
@@ -95,10 +95,7 @@ function SelectContent({
95
95
  )
96
96
  }
97
97
 
98
- function SelectLabel({
99
- className,
100
- ...props
101
- }: SelectPrimitive.GroupLabel.Props) {
98
+ function SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props) {
102
99
  return (
103
100
  <SelectPrimitive.GroupLabel
104
101
  data-slot="select-label"
@@ -108,17 +105,13 @@ function SelectLabel({
108
105
  )
109
106
  }
110
107
 
111
- function SelectItem({
112
- className,
113
- children,
114
- ...props
115
- }: SelectPrimitive.Item.Props) {
108
+ function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Props) {
116
109
  return (
117
110
  <SelectPrimitive.Item
118
111
  data-slot="select-item"
119
112
  className={cn(
120
113
  "relative flex w-full cursor-default items-center gap-2 rounded-none py-2 pr-8 pl-2 text-xs outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
121
- className
114
+ className,
122
115
  )}
123
116
  {...props}
124
117
  >
@@ -136,10 +129,7 @@ function SelectItem({
136
129
  )
137
130
  }
138
131
 
139
- function SelectSeparator({
140
- className,
141
- ...props
142
- }: SelectPrimitive.Separator.Props) {
132
+ function SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props) {
143
133
  return (
144
134
  <SelectPrimitive.Separator
145
135
  data-slot="select-separator"
@@ -158,12 +148,11 @@ function SelectScrollUpButton({
158
148
  data-slot="select-scroll-up-button"
159
149
  className={cn(
160
150
  "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
161
- className
151
+ className,
162
152
  )}
163
153
  {...props}
164
154
  >
165
- <CaretUpIcon
166
- />
155
+ <CaretUpIcon />
167
156
  </SelectPrimitive.ScrollUpArrow>
168
157
  )
169
158
  }
@@ -177,12 +166,11 @@ function SelectScrollDownButton({
177
166
  data-slot="select-scroll-down-button"
178
167
  className={cn(
179
168
  "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
180
- className
169
+ className,
181
170
  )}
182
171
  {...props}
183
172
  >
184
- <CaretDownIcon
185
- />
173
+ <CaretDownIcon />
186
174
  </SelectPrimitive.ScrollDownArrow>
187
175
  )
188
176
  }
@@ -4,18 +4,14 @@ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
4
4
 
5
5
  import { cn } from "../lib/utils"
6
6
 
7
- function Separator({
8
- className,
9
- orientation = "horizontal",
10
- ...props
11
- }: SeparatorPrimitive.Props) {
7
+ function Separator({ className, orientation = "horizontal", ...props }: SeparatorPrimitive.Props) {
12
8
  return (
13
9
  <SeparatorPrimitive
14
10
  data-slot="separator"
15
11
  orientation={orientation}
16
12
  className={cn(
17
13
  "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
18
- className
14
+ className,
19
15
  )}
20
16
  {...props}
21
17
  />
@@ -1,6 +1,6 @@
1
- import * as React from "react"
2
1
  import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
3
2
  import { XIcon } from "@phosphor-icons/react"
3
+ import type * as React from "react"
4
4
 
5
5
  import { cn } from "../lib/utils"
6
6
  import { Button } from "./button"
@@ -28,7 +28,7 @@ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
28
28
  data-slot="sheet-overlay"
29
29
  className={cn(
30
30
  "fixed inset-0 z-50 bg-foreground/40 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
31
- className
31
+ className,
32
32
  )}
33
33
  {...props}
34
34
  />
@@ -61,7 +61,7 @@ function SheetContent({
61
61
  "inset-x-0 top-0 h-auto border-b data-closed:slide-out-to-top data-open:slide-in-from-top",
62
62
  side === "bottom" &&
63
63
  "inset-x-0 bottom-0 h-auto border-t data-closed:slide-out-to-bottom data-open:slide-in-from-bottom",
64
- className
64
+ className,
65
65
  )}
66
66
  {...props}
67
67
  >
@@ -82,13 +82,21 @@ function SheetContent({
82
82
 
83
83
  function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
84
84
  return (
85
- <div data-slot="sheet-header" className={cn("flex flex-col gap-1.5 p-4", className)} {...props} />
85
+ <div
86
+ data-slot="sheet-header"
87
+ className={cn("flex flex-col gap-1.5 p-4", className)}
88
+ {...props}
89
+ />
86
90
  )
87
91
  }
88
92
 
89
93
  function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
90
94
  return (
91
- <div data-slot="sheet-footer" className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />
95
+ <div
96
+ data-slot="sheet-footer"
97
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
98
+ {...props}
99
+ />
92
100
  )
93
101
  }
94
102
 
@@ -114,11 +122,11 @@ function SheetDescription({ className, ...props }: SheetPrimitive.Description.Pr
114
122
 
115
123
  export {
116
124
  Sheet,
117
- SheetTrigger,
118
125
  SheetClose,
119
126
  SheetContent,
120
- SheetHeader,
127
+ SheetDescription,
121
128
  SheetFooter,
129
+ SheetHeader,
122
130
  SheetTitle,
123
- SheetDescription,
131
+ SheetTrigger,
124
132
  }
@@ -1,4 +1,4 @@
1
- import * as React from "react"
1
+ import type * as React from "react"
2
2
 
3
3
  import { cn } from "../lib/utils"
4
4
  import { buttonVariants } from "./button"
@@ -58,7 +58,12 @@ function SidePanel({
58
58
  data-slot="side-panel"
59
59
  data-state="closed"
60
60
  aria-label={title}
61
- className={cn("flex w-10 shrink-0 flex-col items-center bg-sidebar py-2 text-sidebar-foreground", border, "border-sidebar-border", className)}
61
+ className={cn(
62
+ "flex w-10 shrink-0 flex-col items-center bg-sidebar py-2 text-sidebar-foreground",
63
+ border,
64
+ "border-sidebar-border",
65
+ className,
66
+ )}
62
67
  {...props}
63
68
  >
64
69
  <Tooltip>
@@ -77,7 +82,10 @@ function SidePanel({
77
82
  <TooltipContent side={side === "left" ? "right" : "left"}>
78
83
  <span>Show {title}</span>
79
84
  {shortcut ? (
80
- <kbd data-slot="kbd" className="rounded-sm bg-background/20 px-1 font-sans text-[11px]">
85
+ <kbd
86
+ data-slot="kbd"
87
+ className="rounded-sm bg-background/20 px-1 font-sans text-[11px]"
88
+ >
81
89
  {shortcut}
82
90
  </kbd>
83
91
  ) : null}
@@ -121,10 +129,16 @@ function SidePanel({
121
129
  data-state="open"
122
130
  aria-label={title}
123
131
  style={{ width: w }}
124
- className={cn("relative flex shrink-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground", border, "border-sidebar-border", className)}
132
+ className={cn(
133
+ "relative flex shrink-0 flex-col overflow-hidden bg-sidebar text-sidebar-foreground",
134
+ border,
135
+ "border-sidebar-border",
136
+ className,
137
+ )}
125
138
  {...props}
126
139
  >
127
140
  {children}
141
+ {/* biome-ignore lint/a11y/useSemanticElements: a focusable, keyboard-operable splitter (WAI-ARIA window splitter); <hr> is not focusable */}
128
142
  <div
129
143
  role="separator"
130
144
  aria-orientation="vertical"
@@ -137,7 +151,7 @@ function SidePanel({
137
151
  onKeyDown={onKey}
138
152
  className={cn(
139
153
  "absolute inset-y-0 z-10 w-1 cursor-col-resize touch-none hover:bg-primary/30 focus-visible:bg-primary/40 focus-visible:outline-none",
140
- side === "left" ? "right-0" : "left-0"
154
+ side === "left" ? "right-0" : "left-0",
141
155
  )}
142
156
  />
143
157
  </aside>
@@ -149,7 +163,10 @@ function SidePanelHeader({ className, children, ...props }: React.ComponentProps
149
163
  return (
150
164
  <div
151
165
  data-slot="side-panel-header"
152
- className={cn("flex h-9 shrink-0 items-center gap-2 border-b border-sidebar-border px-3 text-xs font-medium uppercase tracking-wide text-muted-foreground", className)}
166
+ className={cn(
167
+ "flex h-9 shrink-0 items-center gap-2 border-b border-sidebar-border px-3 text-xs font-medium uppercase tracking-wide text-muted-foreground",
168
+ className,
169
+ )}
153
170
  {...props}
154
171
  >
155
172
  {children}
@@ -159,8 +176,14 @@ function SidePanelHeader({ className, children, ...props }: React.ComponentProps
159
176
 
160
177
  /** The scrolling body under the header. */
161
178
  function SidePanelContent({ className, ...props }: React.ComponentProps<"div">) {
162
- return <div data-slot="side-panel-content" className={cn("min-h-0 flex-1 overflow-auto", className)} {...props} />
179
+ return (
180
+ <div
181
+ data-slot="side-panel-content"
182
+ className={cn("min-h-0 flex-1 overflow-auto", className)}
183
+ {...props}
184
+ />
185
+ )
163
186
  }
164
187
 
165
- export { SidePanel, SidePanelContent, SidePanelHeader, clampWidth }
166
188
  export type { SidePanelProps }
189
+ export { clampWidth, SidePanel, SidePanelContent, SidePanelHeader }
@@ -1,29 +1,18 @@
1
1
  "use client"
2
2
 
3
- import * as React from "react"
4
3
  import { useRender } from "@base-ui/react/use-render"
5
- import { cva, type VariantProps } from "class-variance-authority"
6
4
  import { SidebarSimpleIcon } from "@phosphor-icons/react"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import * as React from "react"
7
7
 
8
8
  import { useIsMobile } from "../lib/use-mobile"
9
9
  import { cn } from "../lib/utils"
10
10
  import { Button } from "./button"
11
11
  import { Input } from "./input"
12
12
  import { Separator } from "./separator"
13
- import {
14
- Sheet,
15
- SheetContent,
16
- SheetDescription,
17
- SheetHeader,
18
- SheetTitle,
19
- } from "./sheet"
13
+ import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "./sheet"
20
14
  import { Skeleton } from "./skeleton"
21
- import {
22
- Tooltip,
23
- TooltipContent,
24
- TooltipProvider,
25
- TooltipTrigger,
26
- } from "./tooltip"
15
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip"
27
16
 
28
17
  // Base UI's replacement for Radix's asChild: pass `render={<a href="..." />}`
29
18
  // to swap the element, props and ref are merged onto it.
@@ -93,6 +82,7 @@ function SidebarProvider({
93
82
  }
94
83
 
95
84
  // This sets the cookie to keep the sidebar state.
85
+ // biome-ignore lint/suspicious/noDocumentCookie: the Cookie Store API is not in every webview, and the cookie is written for hosts that read it server-side
96
86
  document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
97
87
  },
98
88
  [setOpenProp, open],
@@ -101,15 +91,12 @@ function SidebarProvider({
101
91
  // Helper to toggle the sidebar.
102
92
  const toggleSidebar = React.useCallback(() => {
103
93
  return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
104
- }, [isMobile, setOpen, setOpenMobile])
94
+ }, [isMobile, setOpen])
105
95
 
106
96
  // Adds a keyboard shortcut to toggle the sidebar.
107
97
  React.useEffect(() => {
108
98
  const handleKeyDown = (event: KeyboardEvent) => {
109
- if (
110
- event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
111
- (event.metaKey || event.ctrlKey)
112
- ) {
99
+ if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
113
100
  event.preventDefault()
114
101
  toggleSidebar()
115
102
  }
@@ -133,7 +120,7 @@ function SidebarProvider({
133
120
  setOpenMobile,
134
121
  toggleSidebar,
135
122
  }),
136
- [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
123
+ [state, open, setOpen, isMobile, openMobile, toggleSidebar],
137
124
  )
138
125
 
139
126
  return (
@@ -263,11 +250,7 @@ function Sidebar({
263
250
  )
264
251
  }
265
252
 
266
- function SidebarTrigger({
267
- className,
268
- onClick,
269
- ...props
270
- }: React.ComponentProps<typeof Button>) {
253
+ function SidebarTrigger({ className, onClick, ...props }: React.ComponentProps<typeof Button>) {
271
254
  const { toggleSidebar } = useSidebar()
272
255
 
273
256
  return (
@@ -328,10 +311,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
328
311
  )
329
312
  }
330
313
 
331
- function SidebarInput({
332
- className,
333
- ...props
334
- }: React.ComponentProps<typeof Input>) {
314
+ function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>) {
335
315
  return (
336
316
  <Input
337
317
  data-slot="sidebar-input"
@@ -364,10 +344,7 @@ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
364
344
  )
365
345
  }
366
346
 
367
- function SidebarSeparator({
368
- className,
369
- ...props
370
- }: React.ComponentProps<typeof Separator>) {
347
+ function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
371
348
  return (
372
349
  <Separator
373
350
  data-slot="sidebar-separator"
@@ -447,10 +424,7 @@ function SidebarGroupAction({
447
424
  )
448
425
  }
449
426
 
450
- function SidebarGroupContent({
451
- className,
452
- ...props
453
- }: React.ComponentProps<"div">) {
427
+ function SidebarGroupContent({ className, ...props }: React.ComponentProps<"div">) {
454
428
  return (
455
429
  <div
456
430
  data-slot="sidebar-group-content"
@@ -588,10 +562,7 @@ function SidebarMenuAction({
588
562
  )
589
563
  }
590
564
 
591
- function SidebarMenuBadge({
592
- className,
593
- ...props
594
- }: React.ComponentProps<"div">) {
565
+ function SidebarMenuBadge({ className, ...props }: React.ComponentProps<"div">) {
595
566
  return (
596
567
  <div
597
568
  data-slot="sidebar-menu-badge"
@@ -627,12 +598,7 @@ function SidebarMenuSkeleton({
627
598
  className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
628
599
  {...props}
629
600
  >
630
- {showIcon && (
631
- <Skeleton
632
- className="size-4 rounded-md"
633
- data-sidebar="menu-skeleton-icon"
634
- />
635
- )}
601
+ {showIcon && <Skeleton className="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
636
602
  <Skeleton
637
603
  className="h-4 max-w-(--skeleton-width) flex-1"
638
604
  data-sidebar="menu-skeleton-text"
@@ -661,10 +627,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
661
627
  )
662
628
  }
663
629
 
664
- function SidebarMenuSubItem({
665
- className,
666
- ...props
667
- }: React.ComponentProps<"li">) {
630
+ function SidebarMenuSubItem({ className, ...props }: React.ComponentProps<"li">) {
668
631
  return (
669
632
  <li
670
633
  data-slot="sidebar-menu-sub-item"
@@ -1,4 +1,4 @@
1
- import * as React from "react"
1
+ import type * as React from "react"
2
2
  import { cn } from "../lib/utils"
3
3
 
4
4
  function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
@@ -1,5 +1,5 @@
1
- import * as React from "react"
2
1
  import { Slider as SliderPrimitive } from "@base-ui/react/slider"
2
+ import * as React from "react"
3
3
 
4
4
  import { cn } from "../lib/utils"
5
5
 
@@ -21,13 +21,8 @@ function Slider({
21
21
  const [live, setLive] = React.useState<typeof valueProp | null>(null)
22
22
  const value = live ?? valueProp
23
23
  const _values = React.useMemo(
24
- () =>
25
- Array.isArray(value)
26
- ? value
27
- : Array.isArray(defaultValue)
28
- ? defaultValue
29
- : [min],
30
- [value, defaultValue, min]
24
+ () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min]),
25
+ [value, defaultValue, min],
31
26
  )
32
27
 
33
28
  return (
@@ -62,6 +57,7 @@ function Slider({
62
57
  {Array.from({ length: _values.length }, (_, index) => (
63
58
  <SliderPrimitive.Thumb
64
59
  data-slot="slider-thumb"
60
+ // biome-ignore lint/suspicious/noArrayIndexKey: thumbs are positional; the count is fixed by the value array and they have no identity
65
61
  key={index}
66
62
  index={index}
67
63
  className="block h-4 w-2 shrink-0 rounded-[1px] border border-muted-foreground bg-background ring-ring/50 transition-[color,box-shadow] select-none hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden has-[:focus-visible]:ring-4 data-disabled:pointer-events-none data-disabled:opacity-50"