@hilum/ui 3.4.0 → 3.5.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 (65) hide show
  1. package/README.md +4 -0
  2. package/dist/{chunk-MAHPAP65.js → chunk-RI6QJR2L.js} +6 -3
  3. package/dist/chunk-RI6QJR2L.js.map +1 -0
  4. package/dist/index.d.ts +789 -115
  5. package/dist/index.js +10404 -1099
  6. package/dist/index.js.map +1 -1
  7. package/dist/tokens.css +17 -0
  8. package/dist/tokens.d.ts +3 -0
  9. package/dist/tokens.js +1 -1
  10. package/llms.txt +18 -4
  11. package/package.json +19 -1
  12. package/src/components/accordion.tsx +746 -58
  13. package/src/components/alert-dialog.tsx +11 -5
  14. package/src/components/ask-user-questions.tsx +1366 -0
  15. package/src/components/badge.tsx +105 -24
  16. package/src/components/button.tsx +207 -38
  17. package/src/components/card.tsx +3 -2
  18. package/src/components/chat-message.tsx +116 -0
  19. package/src/components/checkbox-group.tsx +403 -0
  20. package/src/components/checkbox.tsx +6 -3
  21. package/src/components/color-input.tsx +3 -1
  22. package/src/components/color-picker.tsx +1992 -80
  23. package/src/components/dialog.tsx +141 -81
  24. package/src/components/dropdown-menu.tsx +21 -13
  25. package/src/components/dropdown.tsx +245 -0
  26. package/src/components/file-thumbnail.tsx +156 -0
  27. package/src/components/hover-card.tsx +2 -0
  28. package/src/components/input-copy.tsx +307 -0
  29. package/src/components/input-group.tsx +303 -108
  30. package/src/components/input-message.tsx +883 -0
  31. package/src/components/input.tsx +6 -3
  32. package/src/components/menu-item.tsx +156 -0
  33. package/src/components/mobile-drawer.tsx +50 -0
  34. package/src/components/nav-item.tsx +43 -0
  35. package/src/components/nav-menu.tsx +47 -0
  36. package/src/components/popover.tsx +3 -1
  37. package/src/components/radio-group.tsx +381 -35
  38. package/src/components/scroll-area.tsx +9 -2
  39. package/src/components/select.tsx +708 -121
  40. package/src/components/sheet.tsx +15 -6
  41. package/src/components/slider.tsx +1583 -20
  42. package/src/components/status-badge.tsx +2 -0
  43. package/src/components/switch.tsx +245 -28
  44. package/src/components/table.tsx +191 -77
  45. package/src/components/tabs-subtle.tsx +411 -0
  46. package/src/components/tabs.tsx +459 -49
  47. package/src/components/textarea.tsx +7 -3
  48. package/src/components/thinking-indicator.tsx +106 -0
  49. package/src/components/thinking-steps.tsx +382 -0
  50. package/src/components/tooltip.tsx +158 -33
  51. package/src/hooks/use-merge-split.tsx +359 -0
  52. package/src/hooks/use-proximity-hover.ts +208 -0
  53. package/src/index.ts +14 -0
  54. package/src/lib/elevated.tsx +52 -0
  55. package/src/lib/font-weight.ts +18 -0
  56. package/src/lib/icon-context.tsx +98 -0
  57. package/src/lib/icon-map.tsx +669 -0
  58. package/src/lib/interaction.ts +102 -0
  59. package/src/lib/scroll-fade.tsx +241 -0
  60. package/src/lib/shape-context.tsx +121 -0
  61. package/src/lib/springs.ts +30 -0
  62. package/src/lib/surface-classes.ts +43 -0
  63. package/src/lib/surface-context.tsx +17 -0
  64. package/src/tokens/tokens.ts +3 -0
  65. package/dist/chunk-MAHPAP65.js.map +0 -1
@@ -0,0 +1,382 @@
1
+ "use client";
2
+
3
+ import { useRef, forwardRef, type ReactNode, type HTMLAttributes } from "react";
4
+ import { motion } from "framer-motion";
5
+ import { cn } from "../lib/utils";
6
+ import { useIcon } from "../lib/icon-context";
7
+ import type { IconName } from "../lib/icon-context";
8
+ import { spring } from "../lib/springs";
9
+ import { fontWeights } from "../lib/font-weight";
10
+ import { useShape } from "../lib/shape-context";
11
+ import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "./accordion";
12
+ import { Badge } from "./badge";
13
+ import type { BadgeColor } from "./badge";
14
+
15
+ // ─── ThinkingSteps (root) ───────────────────────────────────────────────────
16
+
17
+ interface ThinkingStepsProps extends HTMLAttributes<HTMLDivElement> {
18
+ defaultOpen?: boolean;
19
+ open?: boolean;
20
+ onOpenChange?: (open: boolean) => void;
21
+ children?: ReactNode;
22
+ steps?: Array<{
23
+ label: string;
24
+ status?: ThinkingStepStatus | "current";
25
+ description?: string;
26
+ }>;
27
+ }
28
+
29
+ const ThinkingSteps = forwardRef<HTMLDivElement, ThinkingStepsProps>(
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ (
32
+ {
33
+ defaultOpen = true,
34
+ open,
35
+ onOpenChange,
36
+ children,
37
+ steps,
38
+ className,
39
+ defaultValue: _,
40
+ ...props
41
+ },
42
+ ref,
43
+ ) => {
44
+ const controlled = open !== undefined;
45
+ const content =
46
+ children ??
47
+ (steps ? (
48
+ <>
49
+ <ThinkingStepsHeader>Thinking</ThinkingStepsHeader>
50
+ <ThinkingStepsContent>
51
+ {steps.map((step, index) => (
52
+ <ThinkingStep
53
+ key={`${step.label}-${index}`}
54
+ label={step.label}
55
+ {...(step.description !== undefined ? { description: step.description } : {})}
56
+ {...(step.status !== undefined
57
+ ? { status: step.status === "current" ? "active" : step.status }
58
+ : {})}
59
+ index={index}
60
+ isLast={index === steps.length - 1}
61
+ />
62
+ ))}
63
+ </ThinkingStepsContent>
64
+ </>
65
+ ) : null);
66
+ return (
67
+ <Accordion
68
+ ref={ref}
69
+ type="single"
70
+ collapsible
71
+ {...(controlled
72
+ ? { value: open ? "thinking" : "" }
73
+ : { defaultValue: defaultOpen ? "thinking" : "" })}
74
+ {...(onOpenChange
75
+ ? { onValueChange: (v: string | string[]) => onOpenChange(v === "thinking") }
76
+ : {})}
77
+ className={cn("w-80 max-w-full", className)}
78
+ {...props}
79
+ >
80
+ {/* Hide standalone accordion expanded bg */}
81
+ <AccordionItem value="thinking" className="[&>.absolute]:hidden">
82
+ {content}
83
+ </AccordionItem>
84
+ </Accordion>
85
+ );
86
+ },
87
+ );
88
+ ThinkingSteps.displayName = "ThinkingSteps";
89
+
90
+ // ─── ThinkingStepsHeader ────────────────────────────────────────────────────
91
+
92
+ interface ThinkingStepsHeaderProps extends HTMLAttributes<HTMLButtonElement> {
93
+ children?: ReactNode;
94
+ }
95
+
96
+ const ThinkingStepsHeader = forwardRef<HTMLButtonElement, ThinkingStepsHeaderProps>(
97
+ ({ children = "Thinking", className, ...props }, ref) => {
98
+ return (
99
+ <div className="w-fit">
100
+ <AccordionTrigger
101
+ ref={ref}
102
+ className={cn("[&>span:first-child]:flex-none w-auto", className)}
103
+ {...props}
104
+ >
105
+ {children}
106
+ </AccordionTrigger>
107
+ </div>
108
+ );
109
+ },
110
+ );
111
+ ThinkingStepsHeader.displayName = "ThinkingStepsHeader";
112
+
113
+ // ─── ThinkingStepsContent ───────────────────────────────────────────────────
114
+
115
+ interface ThinkingStepsContentProps extends HTMLAttributes<HTMLDivElement> {
116
+ children: ReactNode;
117
+ }
118
+
119
+ const ThinkingStepsContent = forwardRef<HTMLDivElement, ThinkingStepsContentProps>(
120
+ ({ children, className, ...props }, ref) => {
121
+ return (
122
+ <AccordionContent>
123
+ <div ref={ref} className={cn("flex flex-col", className)} {...props}>
124
+ {children}
125
+ </div>
126
+ </AccordionContent>
127
+ );
128
+ },
129
+ );
130
+ ThinkingStepsContent.displayName = "ThinkingStepsContent";
131
+
132
+ // ─── ThinkingStep ───────────────────────────────────────────────────────────
133
+
134
+ type ThinkingStepStatus = "complete" | "active" | "pending";
135
+
136
+ interface ThinkingStepProps {
137
+ icon?: IconName;
138
+ showIcon?: boolean;
139
+ label: string;
140
+ description?: string;
141
+ status?: ThinkingStepStatus;
142
+ index: number;
143
+ delay?: number;
144
+ isLast?: boolean;
145
+ children?: ReactNode;
146
+ className?: string;
147
+ }
148
+
149
+ function ThinkingStep({
150
+ icon = "dot",
151
+ showIcon = true,
152
+ label,
153
+ description,
154
+ status = "complete",
155
+ index,
156
+ delay = 0,
157
+ isLast = false,
158
+ children,
159
+ className,
160
+ }: ThinkingStepProps) {
161
+ const Icon = useIcon(icon);
162
+ const shape = useShape();
163
+
164
+ if (status === "pending") return null;
165
+
166
+ const isActive = status === "active";
167
+
168
+ return (
169
+ /* Outer: animates height to create space smoothly */
170
+ <motion.div
171
+ className={cn("relative z-10 overflow-hidden", className)}
172
+ initial={{ height: 0 }}
173
+ animate={{ height: "auto" }}
174
+ transition={spring.slow}
175
+ >
176
+ {/* Inner: fades content in after space starts opening */}
177
+ <motion.div
178
+ initial={{ opacity: 0 }}
179
+ animate={{ opacity: 1 }}
180
+ transition={{ duration: 0.24, delay: 0.08, ease: "easeOut" }}
181
+ >
182
+ {/* Content row — this is the proximity hover target */}
183
+ <div className={cn("flex gap-2.5 px-2 py-1.5", shape.item)}>
184
+ {/* Icon column with continuous connector line */}
185
+ <div className="flex flex-col items-center shrink-0 w-[14px]">
186
+ <div className="pt-0.5">
187
+ {showIcon ? (
188
+ <Icon size={14} strokeWidth={1.5} className="text-muted-foreground" />
189
+ ) : (
190
+ <div className="w-[14px] h-[14px] flex items-center justify-center">
191
+ <div className="w-1.5 h-1.5 rounded-full bg-muted-foreground/60" />
192
+ </div>
193
+ )}
194
+ </div>
195
+ {/* Line stretches from icon to bottom of this step */}
196
+ {!isLast && <div className="flex-1 w-px bg-border/60 mt-1" />}
197
+ </div>
198
+
199
+ {/* Text content */}
200
+ <div className="flex-1 flex flex-col gap-1 min-w-0">
201
+ <span
202
+ className={cn(
203
+ "text-[13px] leading-tight text-foreground",
204
+ isActive && "shimmer-text",
205
+ )}
206
+ style={{ fontVariationSettings: fontWeights.medium }}
207
+ >
208
+ {label}
209
+ </span>
210
+ {description && (
211
+ <span className="text-[13px] text-muted-foreground leading-snug">{description}</span>
212
+ )}
213
+ {children}
214
+ </div>
215
+ </div>
216
+ </motion.div>
217
+ </motion.div>
218
+ );
219
+ }
220
+
221
+ // ─── ThinkingStepDetails (nested accordion) ────────────────────────────────
222
+
223
+ interface ThinkingStepDetailsProps {
224
+ summary: string;
225
+ details?: string[];
226
+ defaultOpen?: boolean;
227
+ children?: ReactNode;
228
+ className?: string;
229
+ }
230
+
231
+ function ThinkingStepDetails({
232
+ summary,
233
+ details,
234
+ defaultOpen = false,
235
+ children,
236
+ className,
237
+ }: ThinkingStepDetailsProps) {
238
+ const shape = useShape();
239
+
240
+ return (
241
+ <Accordion
242
+ type="single"
243
+ collapsible
244
+ defaultValue={defaultOpen ? "details" : ""}
245
+ className={cn("mt-1 -ml-3", className)}
246
+ >
247
+ <AccordionItem value="details" className="[&>.absolute]:hidden">
248
+ <div className="w-fit">
249
+ <AccordionTrigger
250
+ className={cn("[&>span:first-child]:flex-none w-auto py-1 px-3 gap-1.5", shape.item)}
251
+ >
252
+ {summary}
253
+ </AccordionTrigger>
254
+ </div>
255
+ <AccordionContent>
256
+ <div className="flex flex-col gap-0.5 pt-0.5">
257
+ {details?.map((item, i) => (
258
+ <span key={i} className="text-[12px] text-muted-foreground leading-snug">
259
+ {item}
260
+ </span>
261
+ ))}
262
+ {children}
263
+ </div>
264
+ </AccordionContent>
265
+ </AccordionItem>
266
+ </Accordion>
267
+ );
268
+ }
269
+
270
+ // ─── ThinkingStepSources ────────────────────────────────────────────────────
271
+
272
+ interface ThinkingStepSourcesProps extends HTMLAttributes<HTMLDivElement> {
273
+ children: ReactNode;
274
+ }
275
+
276
+ const ThinkingStepSources = forwardRef<HTMLDivElement, ThinkingStepSourcesProps>(
277
+ ({ children, className, ...props }, ref) => {
278
+ return (
279
+ <div ref={ref} className={cn("flex flex-wrap gap-1.5 mt-1", className)} {...props}>
280
+ {children}
281
+ </div>
282
+ );
283
+ },
284
+ );
285
+ ThinkingStepSources.displayName = "ThinkingStepSources";
286
+
287
+ // ─── ThinkingStepSource ─────────────────────────────────────────────────────
288
+
289
+ interface ThinkingStepSourceProps {
290
+ color?: BadgeColor;
291
+ delay?: number;
292
+ children: ReactNode;
293
+ className?: string;
294
+ }
295
+
296
+ function ThinkingStepSource({
297
+ color = "gray",
298
+ delay = 0,
299
+ children,
300
+ className,
301
+ }: ThinkingStepSourceProps) {
302
+ return (
303
+ <motion.span
304
+ initial={{ opacity: 0, scale: 0.85, filter: "blur(4px)" }}
305
+ animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
306
+ transition={{
307
+ ...spring.moderate,
308
+ delay,
309
+ filter: { duration: 0.12, delay },
310
+ }}
311
+ >
312
+ <Badge variant="solid" size="sm" color={color} className={className}>
313
+ {children}
314
+ </Badge>
315
+ </motion.span>
316
+ );
317
+ }
318
+ ThinkingStepSource.displayName = "ThinkingStepSource";
319
+
320
+ // ─── ThinkingStepImage ──────────────────────────────────────────────────────
321
+
322
+ interface ThinkingStepImageProps {
323
+ src: string;
324
+ alt?: string;
325
+ caption?: string;
326
+ delay?: number;
327
+ className?: string;
328
+ }
329
+
330
+ function ThinkingStepImage({
331
+ src,
332
+ alt = "",
333
+ caption,
334
+ delay = 0,
335
+ className,
336
+ }: ThinkingStepImageProps) {
337
+ const shape = useShape();
338
+ return (
339
+ <motion.div
340
+ className={cn("mt-1.5", className)}
341
+ initial={{ opacity: 0, filter: "blur(4px)" }}
342
+ animate={{ opacity: 1, filter: "blur(0px)" }}
343
+ transition={{
344
+ opacity: { duration: 0.2, delay, ease: "easeOut" },
345
+ filter: { duration: 0.15, delay },
346
+ }}
347
+ >
348
+ <img
349
+ src={src}
350
+ alt={alt}
351
+ className={cn("w-full max-w-[200px] object-cover", shape.container)}
352
+ />
353
+ {caption && <span className="text-[11px] text-muted-foreground mt-1 block">{caption}</span>}
354
+ </motion.div>
355
+ );
356
+ }
357
+ ThinkingStepImage.displayName = "ThinkingStepImage";
358
+
359
+ // ─── Exports ────────────────────────────────────────────────────────────────
360
+
361
+ export {
362
+ ThinkingSteps,
363
+ ThinkingStepsHeader,
364
+ ThinkingStepsContent,
365
+ ThinkingStep,
366
+ ThinkingStepDetails,
367
+ ThinkingStepSources,
368
+ ThinkingStepSource,
369
+ ThinkingStepImage,
370
+ };
371
+
372
+ export type {
373
+ ThinkingStepsProps,
374
+ ThinkingStepsHeaderProps,
375
+ ThinkingStepsContentProps,
376
+ ThinkingStepProps,
377
+ ThinkingStepDetailsProps,
378
+ ThinkingStepSourcesProps,
379
+ ThinkingStepSourceProps,
380
+ ThinkingStepImageProps,
381
+ ThinkingStepStatus,
382
+ };
@@ -1,43 +1,168 @@
1
1
  "use client";
2
2
 
3
- import * as React from "react";
4
- import { Tooltip } from "radix-ui";
3
+ import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
4
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5
+ import { motion } from "framer-motion";
5
6
  import { cn } from "../lib/utils";
7
+ import { spring } from "../lib/springs";
8
+ import { fontWeights } from "../lib/font-weight";
9
+ import { useShape } from "../lib/shape-context";
6
10
 
7
- function TooltipProvider({
11
+ // ---------------------------------------------------------------------------
12
+ // Portal container context
13
+ // ---------------------------------------------------------------------------
14
+
15
+ const TooltipPortalContainerContext = createContext<HTMLElement | null>(null);
16
+
17
+ function TooltipPortalContainer({
18
+ value,
8
19
  children,
9
- ...props
10
- }: React.ComponentPropsWithoutRef<typeof Tooltip.Provider>) {
20
+ }: {
21
+ value: HTMLElement | null;
22
+ children: ReactNode;
23
+ }) {
11
24
  return (
12
- <Tooltip.Provider delayDuration={300} {...props}>
25
+ <TooltipPortalContainerContext.Provider value={value}>
13
26
  {children}
14
- </Tooltip.Provider>
27
+ </TooltipPortalContainerContext.Provider>
28
+ );
29
+ }
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Types
33
+ // ---------------------------------------------------------------------------
34
+
35
+ type TooltipSide = "top" | "right" | "bottom" | "left";
36
+
37
+ interface TooltipProps {
38
+ content?: ReactNode;
39
+ children: ReactNode;
40
+ side?: TooltipSide;
41
+ sideOffset?: number;
42
+ delayDuration?: number;
43
+ className?: string;
44
+ open?: boolean | undefined;
45
+ /** When true, forces the tooltip open. When false, forces it closed. When undefined, uses default hover/focus behavior. */
46
+ forceOpen?: boolean | undefined;
47
+ /** Called when the tooltip's internal open state changes (before forceOpen is applied). */
48
+ onOpenChange?: (open: boolean) => void;
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Animation helpers
53
+ // ---------------------------------------------------------------------------
54
+
55
+ function getSlideOffset(side: TooltipSide) {
56
+ switch (side) {
57
+ case "top":
58
+ return { y: 4 };
59
+ case "bottom":
60
+ return { y: -4 };
61
+ case "left":
62
+ return { x: 4 };
63
+ case "right":
64
+ return { x: -4 };
65
+ }
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Tooltip
70
+ // ---------------------------------------------------------------------------
71
+
72
+ function Tooltip({
73
+ content,
74
+ children,
75
+ side = "top",
76
+ sideOffset = 8,
77
+ delayDuration = 200,
78
+ className,
79
+ open: openProp,
80
+ forceOpen,
81
+ onOpenChange: onOpenChangeProp,
82
+ }: TooltipProps) {
83
+ const [internalOpen, setInternalOpen] = useState(false);
84
+ if (content === undefined) {
85
+ return (
86
+ <TooltipPrimitive.Root
87
+ {...(openProp !== undefined ? { open: openProp } : {})}
88
+ {...(onOpenChangeProp ? { onOpenChange: onOpenChangeProp } : {})}
89
+ >
90
+ {children}
91
+ </TooltipPrimitive.Root>
92
+ );
93
+ }
94
+ const open = forceOpen ?? openProp ?? internalOpen;
95
+ const [mounted, setMounted] = useState(false);
96
+ const shape = useShape();
97
+ const portalContainer = useContext(TooltipPortalContainerContext);
98
+
99
+ useEffect(() => {
100
+ if (open) setMounted(true);
101
+ }, [open]);
102
+
103
+ const handleExitComplete = () => {
104
+ if (!open) setMounted(false);
105
+ };
106
+
107
+ const slideOffset = getSlideOffset(side);
108
+
109
+ return (
110
+ <TooltipPrimitive.Provider delayDuration={delayDuration}>
111
+ <TooltipPrimitive.Root
112
+ open={open}
113
+ onOpenChange={(v) => {
114
+ setInternalOpen(v);
115
+ onOpenChangeProp?.(v);
116
+ }}
117
+ >
118
+ <TooltipPrimitive.Trigger asChild>
119
+ {children as React.ReactElement}
120
+ </TooltipPrimitive.Trigger>
121
+ {mounted && (
122
+ <TooltipPrimitive.Portal forceMount container={portalContainer ?? undefined}>
123
+ <TooltipPrimitive.Content
124
+ side={side}
125
+ sideOffset={sideOffset}
126
+ forceMount
127
+ className="z-50"
128
+ >
129
+ <motion.div
130
+ className={cn(
131
+ "bg-foreground text-background text-[12px] px-2 py-1",
132
+ shape.bg,
133
+ className,
134
+ )}
135
+ style={{ fontVariationSettings: fontWeights.medium }}
136
+ initial={{ opacity: 0, ...slideOffset }}
137
+ animate={{
138
+ opacity: open ? 1 : 0,
139
+ x: 0,
140
+ y: 0,
141
+ }}
142
+ transition={open ? spring.fast : spring.fast.exit}
143
+ onAnimationComplete={handleExitComplete}
144
+ >
145
+ {content}
146
+ </motion.div>
147
+ </TooltipPrimitive.Content>
148
+ </TooltipPrimitive.Portal>
149
+ )}
150
+ </TooltipPrimitive.Root>
151
+ </TooltipPrimitive.Provider>
15
152
  );
16
153
  }
17
154
 
18
- const TooltipRoot = Tooltip.Root;
19
- const TooltipTrigger = Tooltip.Trigger;
20
-
21
- const TooltipContent = React.forwardRef<
22
- React.ComponentRef<typeof Tooltip.Content>,
23
- React.ComponentPropsWithoutRef<typeof Tooltip.Content>
24
- >(({ className, sideOffset = 4, ...props }, ref) => (
25
- <Tooltip.Portal>
26
- <Tooltip.Content
27
- ref={ref}
28
- sideOffset={sideOffset}
29
- className={cn(
30
- "z-50 rounded-lg bg-foreground px-2.5 py-1.5 caption text-background",
31
- "animate-in fade-in-0 zoom-in-95",
32
- "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
33
- "data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1",
34
- "data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1",
35
- className,
36
- )}
37
- {...props}
38
- />
39
- </Tooltip.Portal>
40
- ));
41
- TooltipContent.displayName = "TooltipContent";
42
-
43
- export { TooltipProvider, TooltipRoot as Tooltip, TooltipTrigger, TooltipContent };
155
+ const TooltipProvider = TooltipPrimitive.Provider;
156
+ const TooltipRoot = TooltipPrimitive.Root;
157
+ const TooltipTrigger = TooltipPrimitive.Trigger;
158
+ const TooltipContent = TooltipPrimitive.Content;
159
+
160
+ export {
161
+ Tooltip,
162
+ TooltipPortalContainer,
163
+ TooltipProvider,
164
+ TooltipRoot,
165
+ TooltipTrigger,
166
+ TooltipContent,
167
+ };
168
+ export type { TooltipProps, TooltipSide };