sparkdesign 0.4.7 → 0.4.9

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 (96) hide show
  1. package/cli/registry/AGENTS.md +1 -1
  2. package/cli/registry/agent-manifest.json +3996 -67
  3. package/cli/registry/basic/accordion.tsx +79 -0
  4. package/cli/registry/basic/alert-dialog.tsx +3 -6
  5. package/cli/registry/basic/badge.tsx +49 -0
  6. package/cli/registry/basic/button.tsx +32 -14
  7. package/cli/registry/basic/card.tsx +20 -8
  8. package/cli/registry/basic/collapsible-card.tsx +12 -5
  9. package/cli/registry/basic/combobox.tsx +104 -46
  10. package/cli/registry/basic/context-menu.tsx +2 -3
  11. package/cli/registry/basic/date-picker.tsx +78 -7
  12. package/cli/registry/basic/dialog.tsx +3 -8
  13. package/cli/registry/basic/drawer.tsx +3 -5
  14. package/cli/registry/basic/dropdown-menu.tsx +2 -3
  15. package/cli/registry/basic/ellipsis-text.tsx +151 -0
  16. package/cli/registry/basic/form.tsx +186 -0
  17. package/cli/registry/basic/hover-card.tsx +2 -3
  18. package/cli/registry/basic/icon-button.tsx +29 -14
  19. package/cli/registry/basic/input-group.tsx +4 -4
  20. package/cli/registry/basic/input.tsx +29 -13
  21. package/cli/registry/basic/popover.tsx +2 -3
  22. package/cli/registry/basic/select.tsx +24 -4
  23. package/cli/registry/basic/sidebar.tsx +665 -0
  24. package/cli/registry/basic/sonner.tsx +10 -10
  25. package/cli/registry/basic/spinner.tsx +20 -5
  26. package/cli/registry/basic/textarea.tsx +30 -12
  27. package/cli/registry/basic/tooltip.tsx +2 -1
  28. package/cli/registry/chat/chat-input/compound.tsx +1 -0
  29. package/cli/registry/chat/user-question/compound.tsx +2 -0
  30. package/cli/registry/meta.json +250 -30
  31. package/dist/registry/basic/accordion.d.ts +15 -0
  32. package/dist/registry/basic/alert-dialog.d.ts +1 -1
  33. package/dist/registry/basic/avatar.d.ts +1 -1
  34. package/dist/registry/basic/badge.d.ts +23 -0
  35. package/dist/registry/basic/button.d.ts +3 -1
  36. package/dist/registry/basic/card.d.ts +9 -4
  37. package/dist/registry/basic/combobox.d.ts +20 -9
  38. package/dist/registry/basic/date-picker.d.ts +18 -9
  39. package/dist/registry/basic/dialog.d.ts +1 -1
  40. package/dist/registry/basic/ellipsis-text.d.ts +45 -0
  41. package/dist/registry/basic/form.d.ts +23 -0
  42. package/dist/registry/basic/icon-button.d.ts +17 -3
  43. package/dist/registry/basic/input-group.d.ts +5 -3
  44. package/dist/registry/basic/input.d.ts +8 -3
  45. package/dist/registry/basic/item.d.ts +3 -3
  46. package/dist/registry/basic/resizable.d.ts +48 -48
  47. package/dist/registry/basic/select.d.ts +7 -2
  48. package/dist/registry/basic/sidebar.d.ts +72 -0
  49. package/dist/registry/basic/spinner.d.ts +6 -2
  50. package/dist/registry/basic/tag.d.ts +1 -1
  51. package/dist/registry/basic/textarea.d.ts +9 -3
  52. package/dist/registry/basic/toggle.d.ts +1 -1
  53. package/dist/scale/computed.css +11 -0
  54. package/dist/scale/config.css +11 -0
  55. package/dist/scale/presets/compact.css +7 -0
  56. package/dist/scale/presets/dense.css +7 -0
  57. package/dist/scale/presets/sharp.css +7 -0
  58. package/dist/scale/presets/soft.css +7 -0
  59. package/dist/spark-design.cjs.js +34 -37
  60. package/dist/spark-design.es.js +7200 -4933
  61. package/dist/sparkdesign.css +1 -1
  62. package/dist/src/components/basic/Accordion/index.d.ts +13 -0
  63. package/dist/src/components/basic/Badge/index.d.ts +13 -0
  64. package/dist/src/components/basic/EllipsisText/index.d.ts +4 -36
  65. package/dist/src/components/basic/Form/index.d.ts +12 -0
  66. package/dist/src/components/basic/Sidebar/index.d.ts +13 -0
  67. package/dist/src/components/index.d.ts +7 -3
  68. package/dist/src/lib/index.d.ts +1 -1
  69. package/dist/src/lib/motion.d.ts +79 -0
  70. package/dist/theme-base.css +22 -0
  71. package/dist/themes/dark-mint.css +6 -0
  72. package/dist/themes/dark-parchment.css +6 -0
  73. package/dist/themes/light-parchment.css +6 -0
  74. package/dist/tokens/scale/computed.css +11 -0
  75. package/dist/tokens/scale/config.css +11 -0
  76. package/dist/tokens/scale/presets/compact.css +7 -0
  77. package/dist/tokens/scale/presets/dense.css +7 -0
  78. package/dist/tokens/scale/presets/sharp.css +7 -0
  79. package/dist/tokens/scale/presets/soft.css +7 -0
  80. package/dist/tokens/theme-base.css +22 -0
  81. package/dist/tokens/themes/dark-mint.css +6 -0
  82. package/dist/tokens/themes/dark-parchment.css +6 -0
  83. package/dist/tokens/themes/light-parchment.css +6 -0
  84. package/docs/agent/component-selection.md +106 -4
  85. package/package.json +8 -3
  86. package/registry/agent-manifest.json +3996 -67
  87. package/cli/registry/chat/user-question/UserQuestionCard.tsx +0 -198
  88. package/cli/registry/chat/user-question/UserQuestionFooter.tsx +0 -66
  89. package/cli/registry/chat/user-question/UserQuestionHeader.tsx +0 -64
  90. package/cli/registry/chat/user-question/useUserQuestionState.ts +0 -165
  91. package/dist/registry/chat/user-question/UserQuestionCard.d.ts +0 -36
  92. package/dist/registry/chat/user-question/UserQuestionFooter.d.ts +0 -24
  93. package/dist/registry/chat/user-question/UserQuestionHeader.d.ts +0 -26
  94. package/dist/registry/chat/user-question/useUserQuestionState.d.ts +0 -26
  95. package/dist/src/components/basic/CollapsibleSection/index.d.ts +0 -43
  96. package/dist/src/components/chat/Response/StreamingMarkdownBlock.d.ts +0 -12
@@ -0,0 +1,665 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Slot } from "@radix-ui/react-slot"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+ import { PanelLeft } from "lucide-react"
7
+
8
+ import { cn } from "@/lib/utils"
9
+ import { Button } from "./button"
10
+ import { Input } from "./input"
11
+ import { Separator } from "./separator"
12
+ import { Sheet, SheetContent } from "./sheet"
13
+ import { Skeleton } from "./skeleton"
14
+ import { Tooltip } from "./tooltip"
15
+
16
+ const SIDEBAR_WIDTH = "16rem"
17
+ const SIDEBAR_WIDTH_ICON = "3rem"
18
+ const SIDEBAR_WIDTH_MOBILE = "18rem"
19
+ const MOBILE_BREAKPOINT = "(max-width: 767px)"
20
+
21
+ type SidebarContextValue = {
22
+ state: "expanded" | "collapsed"
23
+ open: boolean
24
+ setOpen: (open: boolean) => void
25
+ openMobile: boolean
26
+ setOpenMobile: (open: boolean) => void
27
+ isMobile: boolean
28
+ toggleSidebar: () => void
29
+ }
30
+
31
+ const SidebarContext = React.createContext<SidebarContextValue | null>(null)
32
+
33
+ function useIsMobile() {
34
+ const [isMobile, setIsMobile] = React.useState(false)
35
+
36
+ React.useEffect(() => {
37
+ const media = window.matchMedia(MOBILE_BREAKPOINT)
38
+ const update = () => setIsMobile(media.matches)
39
+ update()
40
+ media.addEventListener("change", update)
41
+ return () => media.removeEventListener("change", update)
42
+ }, [])
43
+
44
+ return isMobile
45
+ }
46
+
47
+ function useSidebar() {
48
+ const context = React.useContext(SidebarContext)
49
+
50
+ if (!context) {
51
+ throw new Error("useSidebar must be used within a SidebarProvider.")
52
+ }
53
+
54
+ return context
55
+ }
56
+
57
+ function SidebarProvider({
58
+ defaultOpen = true,
59
+ open: openProp,
60
+ onOpenChange: setOpenProp,
61
+ className,
62
+ style,
63
+ children,
64
+ ...props
65
+ }: React.ComponentProps<"div"> & {
66
+ defaultOpen?: boolean
67
+ open?: boolean
68
+ onOpenChange?: (open: boolean) => void
69
+ }) {
70
+ const isMobile = useIsMobile()
71
+ const [openMobile, setOpenMobile] = React.useState(false)
72
+ const [_open, _setOpen] = React.useState(defaultOpen)
73
+ const open = openProp ?? _open
74
+
75
+ const setOpen = React.useCallback(
76
+ (value: boolean) => {
77
+ setOpenProp?.(value)
78
+ if (openProp === undefined) _setOpen(value)
79
+ },
80
+ [openProp, setOpenProp]
81
+ )
82
+
83
+ const toggleSidebar = React.useCallback(() => {
84
+ if (isMobile) {
85
+ setOpenMobile((value) => !value)
86
+ return
87
+ }
88
+
89
+ setOpen(!open)
90
+ }, [isMobile, open, setOpen])
91
+
92
+ const state = open ? "expanded" : "collapsed"
93
+
94
+ const contextValue = React.useMemo<SidebarContextValue>(
95
+ () => ({
96
+ state,
97
+ open,
98
+ setOpen,
99
+ isMobile,
100
+ openMobile,
101
+ setOpenMobile,
102
+ toggleSidebar,
103
+ }),
104
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
105
+ )
106
+
107
+ return (
108
+ <SidebarContext.Provider value={contextValue}>
109
+ <div
110
+ data-slot="sidebar-wrapper"
111
+ style={
112
+ {
113
+ "--sidebar-width": SIDEBAR_WIDTH,
114
+ "--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
115
+ ...style,
116
+ } as React.CSSProperties
117
+ }
118
+ className={cn(
119
+ "group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-bg-base",
120
+ className
121
+ )}
122
+ {...props}
123
+ >
124
+ {children}
125
+ </div>
126
+ </SidebarContext.Provider>
127
+ )
128
+ }
129
+
130
+ function Sidebar({
131
+ side = "left",
132
+ variant = "sidebar",
133
+ collapsible = "offcanvas",
134
+ className,
135
+ children,
136
+ ...props
137
+ }: React.ComponentProps<"div"> & {
138
+ side?: "left" | "right"
139
+ variant?: "sidebar" | "floating" | "inset"
140
+ collapsible?: "offcanvas" | "icon" | "none"
141
+ }) {
142
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
143
+
144
+ if (collapsible === "none") {
145
+ return (
146
+ <div
147
+ data-slot="sidebar"
148
+ className={cn(
149
+ "flex h-full w-[var(--sidebar-width)] flex-col bg-bg-container text-text",
150
+ className
151
+ )}
152
+ {...props}
153
+ >
154
+ {children}
155
+ </div>
156
+ )
157
+ }
158
+
159
+ if (isMobile) {
160
+ return (
161
+ <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
162
+ <SheetContent
163
+ data-sidebar="sidebar"
164
+ data-slot="sidebar"
165
+ side={side}
166
+ showCloseButton={false}
167
+ className="w-[var(--sidebar-width)] border-border-tertiary bg-bg-container p-0 text-text [&>button]:hidden"
168
+ style={{ "--sidebar-width": SIDEBAR_WIDTH_MOBILE } as React.CSSProperties}
169
+ >
170
+ <div className="flex h-full w-full flex-col">{children}</div>
171
+ </SheetContent>
172
+ </Sheet>
173
+ )
174
+ }
175
+
176
+ return (
177
+ <div
178
+ className="group peer hidden text-text md:block"
179
+ data-state={state}
180
+ data-collapsible={state === "collapsed" ? collapsible : ""}
181
+ data-variant={variant}
182
+ data-side={side}
183
+ data-slot="sidebar"
184
+ >
185
+ <div
186
+ className={cn(
187
+ "relative h-svh w-[var(--sidebar-width)] bg-transparent transition-[width] duration-200 ease-linear",
188
+ "group-data-[collapsible=offcanvas]:w-0",
189
+ "group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]"
190
+ )}
191
+ />
192
+ <div
193
+ className={cn(
194
+ "fixed inset-y-0 z-10 hidden h-svh w-[var(--sidebar-width)] transition-[left,right,width] duration-200 ease-linear md:flex",
195
+ side === "left"
196
+ ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
197
+ : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
198
+ "group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]",
199
+ variant === "floating" || variant === "inset"
200
+ ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+1rem)]"
201
+ : "group-data-[side=left]:border-r group-data-[side=right]:border-l border-border-tertiary",
202
+ className
203
+ )}
204
+ {...props}
205
+ >
206
+ <div
207
+ data-sidebar="sidebar"
208
+ className={cn(
209
+ "flex h-full w-full flex-col bg-bg-container",
210
+ variant === "floating" &&
211
+ "rounded-lg border border-border-tertiary shadow-sm"
212
+ )}
213
+ >
214
+ {children}
215
+ </div>
216
+ </div>
217
+ </div>
218
+ )
219
+ }
220
+
221
+ function SidebarTrigger({
222
+ className,
223
+ onClick,
224
+ children,
225
+ ...props
226
+ }: Omit<React.ComponentProps<typeof Button>, "children" | "variant" | "size"> & {
227
+ children?: React.ReactNode
228
+ }) {
229
+ const { toggleSidebar } = useSidebar()
230
+
231
+ return (
232
+ <Button
233
+ data-sidebar="trigger"
234
+ data-slot="sidebar-trigger"
235
+ variant="ghost"
236
+ size="md"
237
+ className={cn("size-8 p-0", className)}
238
+ onClick={(event) => {
239
+ onClick?.(event)
240
+ toggleSidebar()
241
+ }}
242
+ {...props}
243
+ >
244
+ {children ?? (
245
+ <>
246
+ <PanelLeft className="size-4" />
247
+ <span className="sr-only">Toggle Sidebar</span>
248
+ </>
249
+ )}
250
+ </Button>
251
+ )
252
+ }
253
+
254
+ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
255
+ const { toggleSidebar } = useSidebar()
256
+
257
+ return (
258
+ <button
259
+ data-sidebar="rail"
260
+ data-slot="sidebar-rail"
261
+ aria-label="Toggle Sidebar"
262
+ tabIndex={-1}
263
+ onClick={toggleSidebar}
264
+ title="Toggle Sidebar"
265
+ className={cn(
266
+ "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-0.5 hover:after:bg-border md:flex",
267
+ "group-data-[side=left]:-right-4 group-data-[side=right]:left-0",
268
+ className
269
+ )}
270
+ {...props}
271
+ />
272
+ )
273
+ }
274
+
275
+ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
276
+ return (
277
+ <main
278
+ data-slot="sidebar-inset"
279
+ className={cn(
280
+ "relative flex min-h-svh flex-1 flex-col bg-bg-base",
281
+ "peer-data-[variant=inset]:m-2 peer-data-[variant=inset]:rounded-lg peer-data-[variant=inset]:border peer-data-[variant=inset]:border-border-tertiary",
282
+ className
283
+ )}
284
+ {...props}
285
+ />
286
+ )
287
+ }
288
+
289
+ function SidebarInput({
290
+ className,
291
+ ...props
292
+ }: React.ComponentProps<typeof Input>) {
293
+ return (
294
+ <Input
295
+ data-slot="sidebar-input"
296
+ data-sidebar="input"
297
+ className={cn("h-8 bg-bg-base shadow-none", className)}
298
+ {...props}
299
+ />
300
+ )
301
+ }
302
+
303
+ function SidebarHeader({ className, ...props }: React.ComponentProps<"div">) {
304
+ return (
305
+ <div
306
+ data-slot="sidebar-header"
307
+ data-sidebar="header"
308
+ className={cn("flex flex-col gap-2 p-2", className)}
309
+ {...props}
310
+ />
311
+ )
312
+ }
313
+
314
+ function SidebarFooter({ className, ...props }: React.ComponentProps<"div">) {
315
+ return (
316
+ <div
317
+ data-slot="sidebar-footer"
318
+ data-sidebar="footer"
319
+ className={cn("flex flex-col gap-2 p-2", className)}
320
+ {...props}
321
+ />
322
+ )
323
+ }
324
+
325
+ function SidebarSeparator({
326
+ className,
327
+ ...props
328
+ }: React.ComponentProps<typeof Separator>) {
329
+ return (
330
+ <Separator
331
+ data-slot="sidebar-separator"
332
+ data-sidebar="separator"
333
+ className={cn("mx-2 w-auto bg-border-tertiary", className)}
334
+ {...props}
335
+ />
336
+ )
337
+ }
338
+
339
+ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
340
+ return (
341
+ <div
342
+ data-slot="sidebar-content"
343
+ data-sidebar="content"
344
+ className={cn(
345
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
346
+ className
347
+ )}
348
+ {...props}
349
+ />
350
+ )
351
+ }
352
+
353
+ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
354
+ return (
355
+ <div
356
+ data-slot="sidebar-group"
357
+ data-sidebar="group"
358
+ className={cn("relative flex w-full min-w-0 flex-col p-2", className)}
359
+ {...props}
360
+ />
361
+ )
362
+ }
363
+
364
+ function SidebarGroupLabel({
365
+ className,
366
+ asChild = false,
367
+ ...props
368
+ }: React.ComponentProps<"div"> & { asChild?: boolean }) {
369
+ const Comp = asChild ? Slot : "div"
370
+
371
+ return (
372
+ <Comp
373
+ data-slot="sidebar-group-label"
374
+ data-sidebar="group-label"
375
+ className={cn(
376
+ "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-text-tertiary outline-none transition-[margin,opacity] duration-200 ease-linear",
377
+ "group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
378
+ className
379
+ )}
380
+ {...props}
381
+ />
382
+ )
383
+ }
384
+
385
+ function SidebarGroupAction({
386
+ className,
387
+ asChild = false,
388
+ ...props
389
+ }: React.ComponentProps<"button"> & { asChild?: boolean }) {
390
+ const Comp = asChild ? Slot : "button"
391
+
392
+ return (
393
+ <Comp
394
+ data-slot="sidebar-group-action"
395
+ data-sidebar="group-action"
396
+ className={cn(
397
+ "absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-text-tertiary outline-none transition-colors hover:bg-fill-secondary hover:text-text focus-visible:ring-2 focus-visible:ring-primary-border",
398
+ "after:absolute after:-inset-2 md:after:hidden",
399
+ "group-data-[collapsible=icon]:hidden",
400
+ className
401
+ )}
402
+ {...props}
403
+ />
404
+ )
405
+ }
406
+
407
+ function SidebarGroupContent({
408
+ className,
409
+ ...props
410
+ }: React.ComponentProps<"div">) {
411
+ return (
412
+ <div
413
+ data-slot="sidebar-group-content"
414
+ data-sidebar="group-content"
415
+ className={cn("w-full text-sm", className)}
416
+ {...props}
417
+ />
418
+ )
419
+ }
420
+
421
+ function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
422
+ return (
423
+ <ul
424
+ data-slot="sidebar-menu"
425
+ data-sidebar="menu"
426
+ className={cn("flex w-full min-w-0 flex-col gap-1", className)}
427
+ {...props}
428
+ />
429
+ )
430
+ }
431
+
432
+ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
433
+ return (
434
+ <li
435
+ data-slot="sidebar-menu-item"
436
+ data-sidebar="menu-item"
437
+ className={cn("group/menu-item relative", className)}
438
+ {...props}
439
+ />
440
+ )
441
+ }
442
+
443
+ const sidebarMenuButtonVariants = cva(
444
+ "peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none transition-[width,height,padding,color,background-color] hover:bg-fill-secondary hover:text-text focus-visible:ring-2 focus-visible:ring-primary-border active:bg-fill data-[active=true]:bg-fill-secondary data-[active=true]:font-medium data-[active=true]:text-text disabled:pointer-events-none disabled:opacity-50 group-data-[collapsible=icon]:size-8 group-data-[collapsible=icon]:p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
445
+ {
446
+ variants: {
447
+ variant: {
448
+ default: "text-text-secondary",
449
+ outline:
450
+ "border border-border-tertiary bg-bg-container text-text-secondary shadow-sm hover:bg-fill-secondary",
451
+ },
452
+ size: {
453
+ default: "h-8 text-sm",
454
+ sm: "h-7 text-xs",
455
+ lg: "h-12 text-sm group-data-[collapsible=icon]:p-0",
456
+ },
457
+ },
458
+ defaultVariants: {
459
+ variant: "default",
460
+ size: "default",
461
+ },
462
+ }
463
+ )
464
+
465
+ function SidebarMenuButton({
466
+ asChild = false,
467
+ isActive = false,
468
+ variant = "default",
469
+ size = "default",
470
+ tooltip,
471
+ className,
472
+ ...props
473
+ }: React.ComponentProps<"button"> & {
474
+ asChild?: boolean
475
+ isActive?: boolean
476
+ tooltip?: string | Omit<React.ComponentProps<typeof Tooltip>, "children">
477
+ } & VariantProps<typeof sidebarMenuButtonVariants>) {
478
+ const Comp = asChild ? Slot : "button"
479
+ const { state } = useSidebar()
480
+
481
+ const button = (
482
+ <Comp
483
+ data-slot="sidebar-menu-button"
484
+ data-sidebar="menu-button"
485
+ data-size={size}
486
+ data-active={isActive}
487
+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
488
+ {...props}
489
+ />
490
+ )
491
+
492
+ if (!tooltip) return button
493
+
494
+ const tooltipProps =
495
+ typeof tooltip === "string" ? { content: tooltip } : tooltip
496
+
497
+ if (state !== "collapsed") return button
498
+
499
+ return (
500
+ <Tooltip side="right" {...tooltipProps}>
501
+ {button}
502
+ </Tooltip>
503
+ )
504
+ }
505
+
506
+ function SidebarMenuAction({
507
+ className,
508
+ asChild = false,
509
+ showOnHover = false,
510
+ ...props
511
+ }: React.ComponentProps<"button"> & {
512
+ asChild?: boolean
513
+ showOnHover?: boolean
514
+ }) {
515
+ const Comp = asChild ? Slot : "button"
516
+
517
+ return (
518
+ <Comp
519
+ data-slot="sidebar-menu-action"
520
+ data-sidebar="menu-action"
521
+ className={cn(
522
+ "absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-text-tertiary outline-none transition-colors hover:bg-fill-secondary hover:text-text focus-visible:ring-2 focus-visible:ring-primary-border",
523
+ "after:absolute after:-inset-2 md:after:hidden",
524
+ "peer-data-[size=sm]/menu-button:top-1",
525
+ "peer-data-[size=default]/menu-button:top-1.5",
526
+ "peer-data-[size=lg]/menu-button:top-2.5",
527
+ "group-data-[collapsible=icon]:hidden",
528
+ showOnHover &&
529
+ "opacity-0 group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100",
530
+ className
531
+ )}
532
+ {...props}
533
+ />
534
+ )
535
+ }
536
+
537
+ function SidebarMenuBadge({ className, ...props }: React.ComponentProps<"div">) {
538
+ return (
539
+ <div
540
+ data-slot="sidebar-menu-badge"
541
+ data-sidebar="menu-badge"
542
+ className={cn(
543
+ "pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium text-text-tertiary",
544
+ "peer-hover/menu-button:text-text peer-data-[active=true]/menu-button:text-text",
545
+ "peer-data-[size=sm]/menu-button:top-1",
546
+ "peer-data-[size=default]/menu-button:top-1.5",
547
+ "peer-data-[size=lg]/menu-button:top-2.5",
548
+ "group-data-[collapsible=icon]:hidden",
549
+ className
550
+ )}
551
+ {...props}
552
+ />
553
+ )
554
+ }
555
+
556
+ function SidebarMenuSkeleton({
557
+ className,
558
+ showIcon = false,
559
+ width = "70%",
560
+ ...props
561
+ }: React.ComponentProps<"div"> & {
562
+ showIcon?: boolean
563
+ width?: string
564
+ }) {
565
+ return (
566
+ <div
567
+ data-slot="sidebar-menu-skeleton"
568
+ data-sidebar="menu-skeleton"
569
+ className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
570
+ {...props}
571
+ >
572
+ {showIcon && <Skeleton className="size-4 rounded-md" />}
573
+ <Skeleton
574
+ className="h-4 max-w-[var(--skeleton-width)] flex-1"
575
+ style={{ "--skeleton-width": width } as React.CSSProperties}
576
+ />
577
+ </div>
578
+ )
579
+ }
580
+
581
+ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
582
+ return (
583
+ <ul
584
+ data-slot="sidebar-menu-sub"
585
+ data-sidebar="menu-sub"
586
+ className={cn(
587
+ "mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-border-tertiary px-2.5 py-0.5",
588
+ "group-data-[collapsible=icon]:hidden",
589
+ className
590
+ )}
591
+ {...props}
592
+ />
593
+ )
594
+ }
595
+
596
+ function SidebarMenuSubItem({
597
+ className,
598
+ ...props
599
+ }: React.ComponentProps<"li">) {
600
+ return (
601
+ <li
602
+ data-slot="sidebar-menu-sub-item"
603
+ data-sidebar="menu-sub-item"
604
+ className={cn("group/menu-sub-item relative", className)}
605
+ {...props}
606
+ />
607
+ )
608
+ }
609
+
610
+ function SidebarMenuSubButton({
611
+ asChild = false,
612
+ size = "md",
613
+ isActive = false,
614
+ className,
615
+ ...props
616
+ }: React.ComponentProps<"a"> & {
617
+ asChild?: boolean
618
+ size?: "sm" | "md"
619
+ isActive?: boolean
620
+ }) {
621
+ const Comp = asChild ? Slot : "a"
622
+
623
+ return (
624
+ <Comp
625
+ data-slot="sidebar-menu-sub-button"
626
+ data-sidebar="menu-sub-button"
627
+ data-size={size}
628
+ data-active={isActive}
629
+ className={cn(
630
+ "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-text-secondary outline-none hover:bg-fill-secondary hover:text-text focus-visible:ring-2 focus-visible:ring-primary-border active:bg-fill data-[active=true]:bg-fill-secondary data-[active=true]:text-text disabled:pointer-events-none disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
631
+ "data-[size=sm]:text-xs data-[size=md]:text-sm",
632
+ className
633
+ )}
634
+ {...props}
635
+ />
636
+ )
637
+ }
638
+
639
+ export {
640
+ Sidebar,
641
+ SidebarContent,
642
+ SidebarFooter,
643
+ SidebarGroup,
644
+ SidebarGroupAction,
645
+ SidebarGroupContent,
646
+ SidebarGroupLabel,
647
+ SidebarHeader,
648
+ SidebarInput,
649
+ SidebarInset,
650
+ SidebarMenu,
651
+ SidebarMenuAction,
652
+ SidebarMenuBadge,
653
+ SidebarMenuButton,
654
+ SidebarMenuItem,
655
+ SidebarMenuSkeleton,
656
+ SidebarMenuSub,
657
+ SidebarMenuSubButton,
658
+ SidebarMenuSubItem,
659
+ SidebarProvider,
660
+ SidebarRail,
661
+ SidebarSeparator,
662
+ SidebarTrigger,
663
+ sidebarMenuButtonVariants,
664
+ useSidebar,
665
+ }
@@ -14,23 +14,23 @@ export type ToasterProps = Omit<SonnerToasterProps, 'theme'> & {
14
14
  dataTheme?: string
15
15
  }
16
16
 
17
- /** 图标/文字用 2px;按钮与容器边缘、按钮之间用固定 8px */
18
- const TOAST_INNER_GAP_PX = '2px'
19
- const TOAST_EDGE_PADDING_PX = '8px'
20
- const TOAST_BUTTON_GAP_PX = '8px'
17
+ /** 图标/文字用最小 token 间距;按钮与容器边缘、按钮之间用 spacing-2 */
18
+ const TOAST_INNER_GAP = 'calc(var(--spacing-px) * 2)'
19
+ const TOAST_EDGE_PADDING = 'var(--spacing-2)'
20
+ const TOAST_BUTTON_GAP = 'var(--spacing-2)'
21
21
 
22
22
  /** 强制 toast 四边内边距 + 根节点 text-sm 变量 + 非首个按钮左间距;选择器带 data-styled 以覆盖 sonner */
23
- const TOAST_PADDING_TOP_BOTTOM_PX = '12px'
23
+ const TOAST_PADDING_TOP_BOTTOM = 'var(--spacing-3)'
24
24
  /** 根节点用设计系统 text-sm 变量;[data-content] 撑满中间;非首个按钮左间距 */
25
- const TOAST_LAYOUT_FIX_CSS = `[data-sonner-toaster] [data-sonner-toast][data-styled="true"] { padding-left: ${TOAST_EDGE_PADDING_PX} !important; padding-right: ${TOAST_EDGE_PADDING_PX} !important; padding-top: ${TOAST_PADDING_TOP_BOTTOM_PX} !important; padding-bottom: ${TOAST_PADDING_TOP_BOTTOM_PX} !important; font-size: var(--font-size-sm) !important; line-height: var(--font-size-sm--line-height) !important; }
25
+ const TOAST_LAYOUT_FIX_CSS = `[data-sonner-toaster] [data-sonner-toast][data-styled="true"] { padding-left: ${TOAST_EDGE_PADDING} !important; padding-right: ${TOAST_EDGE_PADDING} !important; padding-top: ${TOAST_PADDING_TOP_BOTTOM} !important; padding-bottom: ${TOAST_PADDING_TOP_BOTTOM} !important; font-size: var(--font-size-sm) !important; line-height: var(--font-size-sm--line-height) !important; }
26
26
  [data-sonner-toaster] [data-sonner-toast] [data-content] { flex: 1 !important; min-width: 0 !important; }
27
- [data-sonner-toaster] [data-sonner-toast] [data-button]:not(:first-of-type) { margin-left: ${TOAST_BUTTON_GAP_PX} !important; }`
27
+ [data-sonner-toaster] [data-sonner-toast] [data-button]:not(:first-of-type) { margin-left: ${TOAST_BUTTON_GAP} !important; }`
28
28
 
29
29
  const defaultToastOptions: NonNullable<SonnerToasterProps['toastOptions']> = {
30
30
  className: 'text-sm',
31
31
  style: {
32
- padding: `var(--spacing-3) ${TOAST_EDGE_PADDING_PX}`,
33
- gap: TOAST_INNER_GAP_PX,
32
+ padding: `${TOAST_PADDING_TOP_BOTTOM} ${TOAST_EDGE_PADDING}`,
33
+ gap: TOAST_INNER_GAP,
34
34
  background: 'var(--color-bg-container)',
35
35
  borderColor: 'var(--color-border-tertiary)',
36
36
  color: 'var(--color-text)',
@@ -87,7 +87,7 @@ export function Toaster({ theme, toastOptions, dataStyle, dataTheme, icons, gap
87
87
  fontFamily: 'inherit',
88
88
  '--gap': `${gap}px`,
89
89
  '--toast-icon-margin-start': '0',
90
- '--toast-icon-margin-end': TOAST_INNER_GAP_PX,
90
+ '--toast-icon-margin-end': TOAST_INNER_GAP,
91
91
  /* 控制 action/cancel 按钮间距:按钮间仅靠 toast 根 gap,不再额外 margin */
92
92
  '--toast-button-margin-start': 'auto',
93
93
  '--toast-button-margin-end': '0',