@sero-ai/ui 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.turbo/turbo-typecheck.log +1 -1
  2. package/LICENSE +201 -0
  3. package/dist/index.cjs +15527 -0
  4. package/dist/index.d.cts +2246 -0
  5. package/dist/index.d.ts +2246 -0
  6. package/dist/index.js +14985 -0
  7. package/package.json +27 -22
  8. package/src/components/ai-elements/attachments.tsx +4 -4
  9. package/src/components/ai-elements/audio-player.tsx +1 -1
  10. package/src/components/ai-elements/chain-of-thought.tsx +2 -2
  11. package/src/components/ai-elements/confirmation.tsx +2 -2
  12. package/src/components/ai-elements/context.tsx +2 -2
  13. package/src/components/ai-elements/environment-variables.tsx +6 -6
  14. package/src/components/ai-elements/file-tree.tsx +3 -3
  15. package/src/components/ai-elements/inline-citation.tsx +2 -2
  16. package/src/components/ai-elements/jsx-preview.tsx +2 -2
  17. package/src/components/ai-elements/message.tsx +2 -2
  18. package/src/components/ai-elements/mic-selector.tsx +8 -8
  19. package/src/components/ai-elements/open-in-chat.tsx +2 -2
  20. package/src/components/ai-elements/package-info.tsx +4 -4
  21. package/src/components/ai-elements/plan.tsx +2 -2
  22. package/src/components/ai-elements/prompt-input-context.tsx +8 -8
  23. package/src/components/ai-elements/queue.tsx +1 -1
  24. package/src/components/ai-elements/reasoning.tsx +3 -3
  25. package/src/components/ai-elements/schema-display.tsx +7 -7
  26. package/src/components/ai-elements/snippet.tsx +3 -3
  27. package/src/components/ai-elements/sources.tsx +2 -2
  28. package/src/components/ai-elements/terminal.tsx +5 -5
  29. package/src/components/ai-elements/test-results.tsx +8 -8
  30. package/src/components/ai-elements/transcription.tsx +2 -2
  31. package/src/components/ai-elements/voice-selector.tsx +2 -2
  32. package/src/components/ai-elements/web-preview.tsx +5 -6
  33. package/src/components/model-selection/available-model-picker.tsx +1 -1
  34. package/src/components/ui/calendar.tsx +1 -1
  35. package/src/components/ui/carousel.tsx +1 -1
  36. package/src/components/ui/chart.tsx +3 -3
  37. package/src/components/ui/command.tsx +1 -1
  38. package/src/components/ui/field.tsx +2 -2
  39. package/src/components/ui/form.tsx +2 -2
  40. package/src/components/ui/input-otp.tsx +2 -2
  41. package/src/components/ui/input.tsx +3 -1
  42. package/src/components/ui/menubar.tsx +1 -1
  43. package/src/components/ui/native-select.tsx +2 -0
  44. package/src/components/ui/navigation-menu.tsx +1 -1
  45. package/src/components/ui/plugin-safety-disclaimer.tsx +3 -3
  46. package/src/components/ui/progress.tsx +1 -1
  47. package/src/components/ui/resizable.tsx +1 -1
  48. package/src/components/ui/search-input.tsx +26 -27
  49. package/src/components/ui/slider.tsx +7 -2
  50. package/src/components/ui/textarea.tsx +3 -1
  51. package/src/components/ui/toggle-group.tsx +1 -1
  52. package/src/components/ui/tree.tsx +3 -3
  53. package/src/index.ts +3 -0
  54. package/src/styles/globals.css +91 -26
  55. package/src/styles/plugin.css +4 -0
  56. package/src/theme/apply-theme.ts +250 -0
  57. package/src/theme/index.ts +2 -0
  58. package/src/theme/types.ts +253 -0
@@ -9,7 +9,7 @@ import { CheckIcon, CopyIcon, TerminalIcon, Trash2Icon } from "lucide-react";
9
9
  import {
10
10
  createContext,
11
11
  useCallback,
12
- useContext,
12
+ use,
13
13
  useEffect,
14
14
  useMemo,
15
15
  useRef,
@@ -122,7 +122,7 @@ export const TerminalStatus = ({
122
122
  children,
123
123
  ...props
124
124
  }: TerminalStatusProps) => {
125
- const { isStreaming } = useContext(TerminalContext);
125
+ const { isStreaming } = use(TerminalContext);
126
126
 
127
127
  if (!isStreaming) {
128
128
  return null;
@@ -166,7 +166,7 @@ export const TerminalCopyButton = ({
166
166
  }: TerminalCopyButtonProps) => {
167
167
  const [isCopied, setIsCopied] = useState(false);
168
168
  const timeoutRef = useRef<number>(0);
169
- const { output } = useContext(TerminalContext);
169
+ const { output } = use(TerminalContext);
170
170
 
171
171
  const copyToClipboard = useCallback(async () => {
172
172
  if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
@@ -216,7 +216,7 @@ export const TerminalClearButton = ({
216
216
  className,
217
217
  ...props
218
218
  }: TerminalClearButtonProps) => {
219
- const { onClear } = useContext(TerminalContext);
219
+ const { onClear } = use(TerminalContext);
220
220
 
221
221
  if (!onClear) {
222
222
  return null;
@@ -245,7 +245,7 @@ export const TerminalContent = ({
245
245
  children,
246
246
  ...props
247
247
  }: TerminalContentProps) => {
248
- const { output, isStreaming, autoScroll } = useContext(TerminalContext);
248
+ const { output, isStreaming, autoScroll } = use(TerminalContext);
249
249
  const containerRef = useRef<HTMLDivElement>(null);
250
250
 
251
251
  // biome-ignore lint/correctness/useExhaustiveDependencies: output triggers auto-scroll when new content arrives
@@ -16,7 +16,7 @@ import {
16
16
  CircleIcon,
17
17
  XCircleIcon,
18
18
  } from "lucide-react";
19
- import { createContext, useContext, useMemo } from "react";
19
+ import { createContext, use, useMemo } from "react";
20
20
 
21
21
  type TestStatus = "passed" | "failed" | "skipped" | "running";
22
22
 
@@ -96,7 +96,7 @@ export const TestResultsSummary = ({
96
96
  children,
97
97
  ...props
98
98
  }: TestResultsSummaryProps) => {
99
- const { summary } = useContext(TestResultsContext);
99
+ const { summary } = use(TestResultsContext);
100
100
 
101
101
  if (!summary) {
102
102
  return null;
@@ -144,7 +144,7 @@ export const TestResultsDuration = ({
144
144
  children,
145
145
  ...props
146
146
  }: TestResultsDurationProps) => {
147
- const { summary } = useContext(TestResultsContext);
147
+ const { summary } = use(TestResultsContext);
148
148
 
149
149
  if (!summary?.duration) {
150
150
  return null;
@@ -164,7 +164,7 @@ export const TestResultsProgress = ({
164
164
  children,
165
165
  ...props
166
166
  }: TestResultsProgressProps) => {
167
- const { summary } = useContext(TestResultsContext);
167
+ const { summary } = use(TestResultsContext);
168
168
 
169
169
  if (!summary) {
170
170
  return null;
@@ -251,7 +251,7 @@ export const TestSuiteName = ({
251
251
  children,
252
252
  ...props
253
253
  }: TestSuiteNameProps) => {
254
- const { name, status } = useContext(TestSuiteContext);
254
+ const { name, status } = use(TestSuiteContext);
255
255
 
256
256
  return (
257
257
  <CollapsibleTrigger
@@ -395,7 +395,7 @@ export const TestStatus = ({
395
395
  children,
396
396
  ...props
397
397
  }: TestStatusProps) => {
398
- const { status } = useContext(TestContext);
398
+ const { status } = use(TestContext);
399
399
 
400
400
  return (
401
401
  <span
@@ -410,7 +410,7 @@ export const TestStatus = ({
410
410
  export type TestNameProps = HTMLAttributes<HTMLSpanElement>;
411
411
 
412
412
  export const TestName = ({ className, children, ...props }: TestNameProps) => {
413
- const { name } = useContext(TestContext);
413
+ const { name } = use(TestContext);
414
414
 
415
415
  return (
416
416
  <span className={cn("flex-1", className)} {...props}>
@@ -426,7 +426,7 @@ export const TestDuration = ({
426
426
  children,
427
427
  ...props
428
428
  }: TestDurationProps) => {
429
- const { duration } = useContext(TestContext);
429
+ const { duration } = use(TestContext);
430
430
 
431
431
  if (duration === undefined) {
432
432
  return null;
@@ -5,7 +5,7 @@ import type { ComponentProps, ReactNode } from "react";
5
5
 
6
6
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
7
7
  import { cn } from "../../lib/utils";
8
- import { createContext, useCallback, useContext, useMemo } from "react";
8
+ import { createContext, useCallback, use, useMemo } from "react";
9
9
 
10
10
  type TranscriptionSegment = TranscriptionResult["segments"][number];
11
11
 
@@ -21,7 +21,7 @@ const TranscriptionContext = createContext<TranscriptionContextValue | null>(
21
21
  );
22
22
 
23
23
  const useTranscription = () => {
24
- const context = useContext(TranscriptionContext);
24
+ const context = use(TranscriptionContext);
25
25
  if (!context) {
26
26
  throw new Error(
27
27
  "Transcription components must be used within Transcription"
@@ -38,7 +38,7 @@ import {
38
38
  VenusAndMarsIcon,
39
39
  VenusIcon,
40
40
  } from "lucide-react";
41
- import { createContext, useCallback, useContext, useMemo } from "react";
41
+ import { createContext, useCallback, use, useMemo } from "react";
42
42
 
43
43
  interface VoiceSelectorContextValue {
44
44
  value: string | undefined;
@@ -52,7 +52,7 @@ const VoiceSelectorContext = createContext<VoiceSelectorContextValue | null>(
52
52
  );
53
53
 
54
54
  export const useVoiceSelector = () => {
55
- const context = useContext(VoiceSelectorContext);
55
+ const context = use(VoiceSelectorContext);
56
56
  if (!context) {
57
57
  throw new Error(
58
58
  "VoiceSelector components must be used within VoiceSelector"
@@ -20,7 +20,7 @@ import { ChevronDownIcon } from "lucide-react";
20
20
  import {
21
21
  createContext,
22
22
  useCallback,
23
- useContext,
23
+ use,
24
24
  useMemo,
25
25
  useState,
26
26
  } from "react";
@@ -35,7 +35,7 @@ export interface WebPreviewContextValue {
35
35
  const WebPreviewContext = createContext<WebPreviewContextValue | null>(null);
36
36
 
37
37
  const useWebPreview = () => {
38
- const context = useContext(WebPreviewContext);
38
+ const context = use(WebPreviewContext);
39
39
  if (!context) {
40
40
  throw new Error("WebPreview components must be used within a WebPreview");
41
41
  }
@@ -120,7 +120,7 @@ export const WebPreviewNavigationButton = ({
120
120
  <Tooltip>
121
121
  <TooltipTrigger asChild>
122
122
  <Button
123
- className="h-8 w-8 p-0 hover:text-foreground"
123
+ className="size-8 p-0 hover:text-foreground"
124
124
  disabled={disabled}
125
125
  onClick={onClick}
126
126
  size="sm"
@@ -199,7 +199,6 @@ export const WebPreviewBody = ({
199
199
  <div className="flex-1">
200
200
  <iframe
201
201
  className={cn("size-full", className)}
202
- // oxlint-disable-next-line eslint-plugin-react(iframe-missing-sandbox)
203
202
  sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-presentation"
204
203
  src={(src ?? url) || undefined}
205
204
  title="Preview"
@@ -241,7 +240,7 @@ export const WebPreviewConsole = ({
241
240
  Console
242
241
  <ChevronDownIcon
243
242
  className={cn(
244
- "h-4 w-4 transition-transform duration-200",
243
+ "size-4 transition-transform duration-200",
245
244
  consoleOpen && "rotate-180"
246
245
  )}
247
246
  />
@@ -265,7 +264,7 @@ export const WebPreviewConsole = ({
265
264
  log.level === "warn" && "text-yellow-600",
266
265
  log.level === "log" && "text-foreground"
267
266
  )}
268
- key={`${log.timestamp.getTime()}-${index}`}
267
+ key={`${log.timestamp.getTime()}-${log.level}-${log.message}`}
269
268
  >
270
269
  <span className="text-muted-foreground">
271
270
  {log.timestamp.toLocaleTimeString()}
@@ -37,7 +37,7 @@ export function AvailableModelPicker<
37
37
  value,
38
38
  onChange,
39
39
  placeholder = 'Choose a model',
40
- searchPlaceholder = 'Search models',
40
+ searchPlaceholder = 'Search models...',
41
41
  emptyLabel = 'No matching models',
42
42
  noModelsLabel = 'No models available',
43
43
  allowClear = false,
@@ -102,7 +102,7 @@ function Calendar({
102
102
  defaultClassNames.week_number
103
103
  ),
104
104
  day: cn(
105
- "relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
105
+ "relative size-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none",
106
106
  props.showWeekNumber
107
107
  ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
108
108
  : "[&:first-child[data-selected=true]_button]:rounded-l-md",
@@ -31,7 +31,7 @@ type CarouselContextProps = {
31
31
  const CarouselContext = React.createContext<CarouselContextProps | null>(null)
32
32
 
33
33
  function useCarousel() {
34
- const context = React.useContext(CarouselContext)
34
+ const context = React.use(CarouselContext)
35
35
 
36
36
  if (!context) {
37
37
  throw new Error("useCarousel must be used within a <Carousel />")
@@ -23,7 +23,7 @@ type ChartContextProps = {
23
23
  const ChartContext = React.createContext<ChartContextProps | null>(null)
24
24
 
25
25
  function useChart() {
26
- const context = React.useContext(ChartContext)
26
+ const context = React.use(ChartContext)
27
27
 
28
28
  if (!context) {
29
29
  throw new Error("useChart must be used within a <ChartContainer />")
@@ -204,7 +204,7 @@ function ChartTooltipContent({
204
204
  className={cn(
205
205
  "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",
206
206
  {
207
- "h-2.5 w-2.5": indicator === "dot",
207
+ "size-2.5": indicator === "dot",
208
208
  "w-1": indicator === "line",
209
209
  "w-0 border-[1.5px] border-dashed bg-transparent":
210
210
  indicator === "dashed",
@@ -292,7 +292,7 @@ function ChartLegendContent({
292
292
  <itemConfig.icon />
293
293
  ) : (
294
294
  <div
295
- className="h-2 w-2 shrink-0 rounded-[2px]"
295
+ className="size-2 shrink-0 rounded-[2px]"
296
296
  style={{
297
297
  backgroundColor: item.color,
298
298
  }}
@@ -21,7 +21,7 @@ function Command({
21
21
  <CommandPrimitive
22
22
  data-slot="command"
23
23
  className={cn(
24
- "bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
24
+ "bg-popover text-popover-foreground flex size-full flex-col overflow-hidden rounded-md",
25
25
  className
26
26
  )}
27
27
  {...props}
@@ -211,8 +211,8 @@ function FieldError({
211
211
  return (
212
212
  <ul className="ml-4 flex list-disc flex-col gap-1">
213
213
  {uniqueErrors.map(
214
- (error, index) =>
215
- error?.message && <li key={index}>{error.message}</li>
214
+ (error) =>
215
+ error?.message && <li key={error.message}>{error.message}</li>
216
216
  )}
217
217
  </ul>
218
218
  )
@@ -41,8 +41,8 @@ const FormField = <
41
41
  }
42
42
 
43
43
  const useFormField = () => {
44
- const fieldContext = React.useContext(FormFieldContext)
45
- const itemContext = React.useContext(FormItemContext)
44
+ const fieldContext = React.use(FormFieldContext)
45
+ const itemContext = React.use(FormItemContext)
46
46
  const { getFieldState } = useFormContext()
47
47
  const formState = useFormState({ name: fieldContext.name })
48
48
  const fieldState = getFieldState(fieldContext.name, formState)
@@ -43,7 +43,7 @@ function InputOTPSlot({
43
43
  }: React.ComponentProps<"div"> & {
44
44
  index: number
45
45
  }) {
46
- const inputOTPContext = React.useContext(OTPInputContext)
46
+ const inputOTPContext = React.use(OTPInputContext)
47
47
  const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
48
48
 
49
49
  return (
@@ -51,7 +51,7 @@ function InputOTPSlot({
51
51
  data-slot="input-otp-slot"
52
52
  data-active={isActive}
53
53
  className={cn(
54
- "data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
54
+ "data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex size-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
55
55
  className
56
56
  )}
57
57
  {...props}
@@ -2,9 +2,11 @@ import * as React from "react"
2
2
 
3
3
  import { cn } from "../../lib/utils"
4
4
 
5
- function Input({ className, type, ...props }: React.ComponentProps<"input">) {
5
+ function Input({ className, type, placeholder, "aria-label": ariaLabel, ...props }: React.ComponentProps<"input">) {
6
6
  return (
7
7
  <input
8
+ aria-label={ariaLabel ?? (typeof placeholder === "string" ? placeholder : undefined)}
9
+ placeholder={placeholder}
8
10
  type={type}
9
11
  data-slot="input"
10
12
  className={cn(
@@ -235,7 +235,7 @@ function MenubarSubTrigger({
235
235
  {...props}
236
236
  >
237
237
  {children}
238
- <ChevronRightIcon className="ml-auto h-4 w-4" />
238
+ <ChevronRightIcon className="ml-auto size-4" />
239
239
  </MenubarPrimitive.SubTrigger>
240
240
  )
241
241
  }
@@ -6,6 +6,7 @@ import { cn } from "../../lib/utils"
6
6
  function NativeSelect({
7
7
  className,
8
8
  size = "default",
9
+ "aria-label": ariaLabel,
9
10
  ...props
10
11
  }: Omit<React.ComponentProps<"select">, "size"> & { size?: "sm" | "default" }) {
11
12
  return (
@@ -14,6 +15,7 @@ function NativeSelect({
14
15
  data-slot="native-select-wrapper"
15
16
  >
16
17
  <select
18
+ aria-label={ariaLabel}
17
19
  data-slot="native-select"
18
20
  data-size={size}
19
21
  className={cn(
@@ -150,7 +150,7 @@ function NavigationMenuIndicator({
150
150
  )}
151
151
  {...props}
152
152
  >
153
- <div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
153
+ <div className="bg-border relative top-[60%] size-2 rotate-45 rounded-tl-sm shadow-md" />
154
154
  </NavigationMenuPrimitive.Indicator>
155
155
  )
156
156
  }
@@ -10,13 +10,13 @@ export function PluginSafetyDisclaimer({ className }: PluginSafetyDisclaimerProp
10
10
  <div
11
11
  role="note"
12
12
  className={cn(
13
- 'flex shrink-0 items-start gap-2 border-t border-[var(--status-warning-border)] bg-[var(--status-warning-faint)] px-4 py-2.5 text-[11px] leading-5 text-[var(--text-secondary)]',
13
+ 'flex shrink-0 items-start gap-2 border-t border-status-warning-border bg-status-warning-faint px-4 py-2.5 text-[11px] leading-5 text-[var(--text-secondary)]',
14
14
  className,
15
15
  )}
16
16
  >
17
- <ShieldAlert className="mt-0.5 size-3.5 shrink-0 text-[var(--status-warning)]" />
17
+ <ShieldAlert className="mt-0.5 size-3.5 shrink-0 text-status-warning" />
18
18
  <p>
19
- <span className="font-medium text-[var(--status-warning)]">Heads up:</span>{' '}
19
+ <span className="font-medium text-status-warning">Heads up:</span>{' '}
20
20
  Plugins run with full access to this workspace and can execute code on your device.
21
21
  Only install plugins from sources you trust.
22
22
  </p>
@@ -21,7 +21,7 @@ function Progress({
21
21
  >
22
22
  <ProgressPrimitive.Indicator
23
23
  data-slot="progress-indicator"
24
- className="bg-primary h-full w-full flex-1 transition-all"
24
+ className="bg-primary size-full flex-1 transition-all"
25
25
  style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
26
26
  />
27
27
  </ProgressPrimitive.Root>
@@ -13,7 +13,7 @@ function ResizablePanelGroup({
13
13
  <ResizablePrimitive.Group
14
14
  data-slot="resizable-panel-group"
15
15
  className={cn(
16
- "flex h-full w-full aria-[orientation=vertical]:flex-col",
16
+ "flex size-full aria-[orientation=vertical]:flex-col",
17
17
  className
18
18
  )}
19
19
  {...props}
@@ -7,34 +7,33 @@ interface SearchInputProps extends React.ComponentProps<"input"> {
7
7
  containerClassName?: string
8
8
  endAdornment?: React.ReactNode
9
9
  iconClassName?: string
10
+ ref?: React.Ref<HTMLInputElement>
10
11
  }
11
12
 
12
- const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
13
- ({ containerClassName, className, endAdornment, iconClassName, ...props }, ref) => {
14
- return (
15
- <div
16
- data-slot="search-input"
17
- className={cn("flex items-center gap-2 px-3", containerClassName)}
18
- >
19
- <SearchIcon
20
- data-slot="search-input-icon"
21
- className={cn("text-muted-foreground size-3.5 shrink-0", iconClassName)}
22
- />
23
- <input
24
- ref={ref}
25
- data-slot="search-input-control"
26
- className={cn(
27
- "placeholder:text-muted-foreground h-9 w-full min-w-0 bg-transparent pl-1.5 text-xs text-foreground outline-none disabled:cursor-not-allowed disabled:opacity-50",
28
- className
29
- )}
30
- {...props}
31
- />
32
- {endAdornment}
33
- </div>
34
- )
35
- }
36
- )
37
-
38
- SearchInput.displayName = "SearchInput"
13
+ function SearchInput({ containerClassName, className, endAdornment, iconClassName, placeholder, "aria-label": ariaLabel, ref, ...props }: SearchInputProps) {
14
+ return (
15
+ <div
16
+ data-slot="search-input"
17
+ className={cn("flex items-center gap-2 px-3", containerClassName)}
18
+ >
19
+ <SearchIcon
20
+ data-slot="search-input-icon"
21
+ className={cn("text-muted-foreground size-3.5 shrink-0", iconClassName)}
22
+ />
23
+ <input
24
+ aria-label={ariaLabel ?? (typeof placeholder === "string" ? placeholder : "Search")}
25
+ placeholder={placeholder}
26
+ ref={ref}
27
+ data-slot="search-input-control"
28
+ className={cn(
29
+ "placeholder:text-muted-foreground h-9 w-full min-w-0 bg-transparent pl-1.5 text-xs text-foreground outline-none disabled:cursor-not-allowed disabled:opacity-50",
30
+ className
31
+ )}
32
+ {...props}
33
+ />
34
+ {endAdornment}
35
+ </div>
36
+ )
37
+ }
39
38
 
40
39
  export { SearchInput }
@@ -20,6 +20,11 @@ function Slider({
20
20
  : [min, max],
21
21
  [value, defaultValue, min, max]
22
22
  )
23
+ const thumbKeysRef = React.useRef<string[]>([])
24
+ while (thumbKeysRef.current.length < _values.length) {
25
+ thumbKeysRef.current.push(`thumb-${thumbKeysRef.current.length}`)
26
+ }
27
+ const thumbKeys = thumbKeysRef.current.slice(0, _values.length)
23
28
 
24
29
  return (
25
30
  <SliderPrimitive.Root
@@ -47,10 +52,10 @@ function Slider({
47
52
  )}
48
53
  />
49
54
  </SliderPrimitive.Track>
50
- {Array.from({ length: _values.length }, (_, index) => (
55
+ {_values.map((_, index) => (
51
56
  <SliderPrimitive.Thumb
52
57
  data-slot="slider-thumb"
53
- key={index}
58
+ key={thumbKeys[index]}
54
59
  className="border-primary ring-ring/50 block size-4 shrink-0 rounded-full border bg-white shadow-sm transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
55
60
  />
56
61
  ))}
@@ -2,9 +2,11 @@ import * as React from "react"
2
2
 
3
3
  import { cn } from "../../lib/utils"
4
4
 
5
- function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
5
+ function Textarea({ className, placeholder, "aria-label": ariaLabel, ...props }: React.ComponentProps<"textarea">) {
6
6
  return (
7
7
  <textarea
8
+ aria-label={ariaLabel ?? (typeof placeholder === "string" ? placeholder : undefined)}
9
+ placeholder={placeholder}
8
10
  data-slot="textarea"
9
11
  className={cn(
10
12
  "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
@@ -56,7 +56,7 @@ function ToggleGroupItem({
56
56
  ...props
57
57
  }: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
58
58
  VariantProps<typeof toggleVariants>) {
59
- const context = React.useContext(ToggleGroupContext)
59
+ const context = React.use(ToggleGroupContext)
60
60
 
61
61
  return (
62
62
  <ToggleGroupPrimitive.Item
@@ -20,7 +20,7 @@ const TreeContext = React.createContext<TreeContextValue>({
20
20
  });
21
21
 
22
22
  function useTreeContext<T = any>() {
23
- return React.useContext(TreeContext) as TreeContextValue<T>;
23
+ return React.use(TreeContext) as TreeContextValue<T>;
24
24
  }
25
25
 
26
26
  interface TreeProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -61,7 +61,7 @@ interface TreeItemProps<T = any>
61
61
  }
62
62
 
63
63
  /**
64
- * TreeItem full-width row button.
64
+ * TreeItem, full-width row button.
65
65
  *
66
66
  * The row carries hover / selected / drag-target backgrounds so the
67
67
  * highlight spans edge-to-edge (VSCode-style). Indentation is applied
@@ -140,7 +140,7 @@ interface TreeItemLabelProps<T = any>
140
140
  }
141
141
 
142
142
  /**
143
- * TreeItemLabel content wrapper inside a TreeItem.
143
+ * TreeItemLabel, content wrapper inside a TreeItem.
144
144
  *
145
145
  * Layout-only (chevron + children). Background lives on the parent
146
146
  * TreeItem so it spans the full row width.
package/src/index.ts CHANGED
@@ -14,6 +14,9 @@ export { cn } from "./lib/utils";
14
14
  // ── Hooks ──
15
15
  export { useIsMobile } from "./hooks/use-mobile";
16
16
 
17
+ // ── Theme ──
18
+ export * from "./theme";
19
+
17
20
  // ── UI Components ──
18
21
  export * from "./components/ui/accordion";
19
22
  export * from "./components/ui/alert-dialog";