@oppulence/design-system 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/hooks/use-resize-observer.ts +24 -0
  2. package/lib/ai.ts +31 -0
  3. package/package.json +19 -1
  4. package/src/components/atoms/animated-size-container.tsx +59 -0
  5. package/src/components/atoms/button.tsx +2 -0
  6. package/src/components/atoms/currency-input.tsx +16 -0
  7. package/src/components/atoms/icons.tsx +840 -0
  8. package/src/components/atoms/image.tsx +23 -0
  9. package/src/components/atoms/index.ts +10 -0
  10. package/src/components/atoms/loader.tsx +92 -0
  11. package/src/components/atoms/quantity-input.tsx +103 -0
  12. package/src/components/atoms/record-button.tsx +178 -0
  13. package/src/components/atoms/submit-button.tsx +26 -0
  14. package/src/components/atoms/text-effect.tsx +251 -0
  15. package/src/components/atoms/text-shimmer.tsx +74 -0
  16. package/src/components/molecules/actions.tsx +53 -0
  17. package/src/components/molecules/branch.tsx +192 -0
  18. package/src/components/molecules/code-block.tsx +151 -0
  19. package/src/components/molecules/form.tsx +177 -0
  20. package/src/components/molecules/index.ts +12 -0
  21. package/src/components/molecules/inline-citation.tsx +295 -0
  22. package/src/components/molecules/message.tsx +64 -0
  23. package/src/components/molecules/sources.tsx +116 -0
  24. package/src/components/molecules/suggestion.tsx +53 -0
  25. package/src/components/molecules/task.tsx +74 -0
  26. package/src/components/molecules/time-range-input.tsx +73 -0
  27. package/src/components/molecules/tool-call-indicator.tsx +42 -0
  28. package/src/components/molecules/tool.tsx +130 -0
  29. package/src/components/organisms/combobox-dropdown.tsx +171 -0
  30. package/src/components/organisms/conversation.tsx +98 -0
  31. package/src/components/organisms/date-range-picker.tsx +53 -0
  32. package/src/components/organisms/editor/extentions/bubble-menu/bubble-item.tsx +30 -0
  33. package/src/components/organisms/editor/extentions/bubble-menu/bubble-menu-button.tsx +27 -0
  34. package/src/components/organisms/editor/extentions/bubble-menu/index.tsx +63 -0
  35. package/src/components/organisms/editor/extentions/bubble-menu/link-item.tsx +104 -0
  36. package/src/components/organisms/editor/extentions/register.ts +22 -0
  37. package/src/components/organisms/editor/index.tsx +50 -0
  38. package/src/components/organisms/editor/styles.css +31 -0
  39. package/src/components/organisms/editor/utils.ts +19 -0
  40. package/src/components/organisms/index.ts +11 -0
  41. package/src/components/organisms/multiple-selector.tsx +632 -0
  42. package/src/components/organisms/prompt-input.tsx +747 -0
  43. package/src/components/organisms/reasoning.tsx +170 -0
  44. package/src/components/organisms/response.tsx +121 -0
  45. package/src/components/organisms/toast-toaster.tsx +84 -0
  46. package/src/components/organisms/toast.tsx +124 -0
  47. package/src/components/organisms/use-toast.tsx +206 -0
  48. package/src/styles/globals.css +169 -212
@@ -0,0 +1,74 @@
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
4
+ import React, { useMemo, type JSX } from "react";
5
+ import { cn } from "../../../lib/utils";
6
+
7
+ type TextShimmerSize = "xs" | "sm" | "md" | "lg";
8
+
9
+ export type TextShimmerProps = {
10
+ children: string;
11
+ as?: React.ElementType;
12
+ duration?: number;
13
+ spread?: number;
14
+ size?: TextShimmerSize;
15
+ baseColor?: string;
16
+ gradientColor?: string;
17
+ };
18
+
19
+ const sizeClasses: Record<TextShimmerSize, string> = {
20
+ xs: "text-xs",
21
+ sm: "text-sm",
22
+ md: "text-base",
23
+ lg: "text-lg",
24
+ };
25
+
26
+ function TextShimmerComponent({
27
+ children,
28
+ as: Component = "p",
29
+ duration = 2,
30
+ spread = 2,
31
+ size = "sm",
32
+ baseColor,
33
+ gradientColor,
34
+ }: TextShimmerProps) {
35
+ const MotionComponent = motion.create(
36
+ Component as keyof JSX.IntrinsicElements,
37
+ );
38
+
39
+ const dynamicSpread = useMemo(() => {
40
+ return children.length * spread;
41
+ }, [children, spread]);
42
+
43
+ const style = {
44
+ "--spread": `${dynamicSpread}px`,
45
+ backgroundImage:
46
+ "var(--bg), linear-gradient(var(--base-color), var(--base-color))",
47
+ ...(baseColor ? { "--base-color": baseColor } : null),
48
+ ...(gradientColor ? { "--base-gradient-color": gradientColor } : null),
49
+ } as React.CSSProperties;
50
+
51
+ return (
52
+ <MotionComponent
53
+ className={cn(
54
+ "relative inline-block bg-[length:250%_100%,auto] bg-clip-text",
55
+ "text-transparent [--base-color:#a1a1aa] [--base-gradient-color:#000]",
56
+ "[background-repeat:no-repeat,padding-box] [--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--base-gradient-color),#0000_calc(50%+var(--spread)))]",
57
+ "dark:[--base-color:#71717a] dark:[--base-gradient-color:#ffffff] dark:[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--base-gradient-color),#0000_calc(50%+var(--spread)))]",
58
+ sizeClasses[size],
59
+ )}
60
+ initial={{ backgroundPosition: "100% center" }}
61
+ animate={{ backgroundPosition: "0% center" }}
62
+ transition={{
63
+ repeat: Number.POSITIVE_INFINITY,
64
+ duration,
65
+ ease: "linear",
66
+ }}
67
+ style={style}
68
+ >
69
+ {children}
70
+ </MotionComponent>
71
+ );
72
+ }
73
+
74
+ export const TextShimmer = React.memo(TextShimmerComponent);
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+
5
+ import { Button, type ButtonProps } from "../atoms/button";
6
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip";
7
+
8
+ export type ActionsProps = Omit<ComponentProps<"div">, "className">;
9
+
10
+ export const Actions = ({ children, ...props }: ActionsProps) => (
11
+ <div className="flex items-center gap-1 text-muted-foreground" {...props}>
12
+ {children}
13
+ </div>
14
+ );
15
+
16
+ export type ActionProps = Omit<ButtonProps, "className"> & {
17
+ tooltip?: string;
18
+ label?: string;
19
+ };
20
+
21
+ export const Action = ({
22
+ tooltip,
23
+ children,
24
+ label,
25
+ variant = "ghost",
26
+ size = "icon-lg",
27
+ ...props
28
+ }: ActionProps) => {
29
+ const button = (
30
+ <Button size={size} type="button" variant={variant} {...props}>
31
+ {children}
32
+ <span className="sr-only">{label || tooltip}</span>
33
+ </Button>
34
+ );
35
+
36
+ if (tooltip) {
37
+ return (
38
+ <Tooltip>
39
+ <TooltipTrigger
40
+ render={<Button size={size} type="button" variant={variant} {...props} />}
41
+ >
42
+ {children}
43
+ <span className="sr-only">{label || tooltip}</span>
44
+ </TooltipTrigger>
45
+ <TooltipContent>
46
+ <p>{tooltip}</p>
47
+ </TooltipContent>
48
+ </Tooltip>
49
+ );
50
+ }
51
+
52
+ return button;
53
+ };
@@ -0,0 +1,192 @@
1
+ "use client";
2
+
3
+ import type { ChatRole } from "../../../lib/ai";
4
+ import { cn } from "../../../lib/utils";
5
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
6
+ import type { ButtonHTMLAttributes, HTMLAttributes, ReactElement } from "react";
7
+ import { createContext, useContext, useEffect, useState } from "react";
8
+
9
+ import { buttonVariants } from "../atoms/button";
10
+
11
+ type BranchContextType = {
12
+ currentBranch: number;
13
+ totalBranches: number;
14
+ goToPrevious: () => void;
15
+ goToNext: () => void;
16
+ branches: ReactElement[];
17
+ setBranches: (branches: ReactElement[]) => void;
18
+ };
19
+
20
+ const BranchContext = createContext<BranchContextType | null>(null);
21
+
22
+ const useBranch = () => {
23
+ const context = useContext(BranchContext);
24
+
25
+ if (!context) {
26
+ throw new Error("Branch components must be used within Branch");
27
+ }
28
+
29
+ return context;
30
+ };
31
+
32
+ export type BranchProps = Omit<HTMLAttributes<HTMLDivElement>, "className"> & {
33
+ defaultBranch?: number;
34
+ onBranchChange?: (branchIndex: number) => void;
35
+ };
36
+
37
+ export const Branch = ({
38
+ defaultBranch = 0,
39
+ onBranchChange,
40
+ ...props
41
+ }: BranchProps) => {
42
+ const [currentBranch, setCurrentBranch] = useState(defaultBranch);
43
+ const [branches, setBranches] = useState<ReactElement[]>([]);
44
+
45
+ const handleBranchChange = (newBranch: number) => {
46
+ setCurrentBranch(newBranch);
47
+ onBranchChange?.(newBranch);
48
+ };
49
+
50
+ const goToPrevious = () => {
51
+ const newBranch =
52
+ currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
53
+ handleBranchChange(newBranch);
54
+ };
55
+
56
+ const goToNext = () => {
57
+ const newBranch =
58
+ currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
59
+ handleBranchChange(newBranch);
60
+ };
61
+
62
+ const contextValue: BranchContextType = {
63
+ currentBranch,
64
+ totalBranches: branches.length,
65
+ goToPrevious,
66
+ goToNext,
67
+ branches,
68
+ setBranches,
69
+ };
70
+
71
+ return (
72
+ <BranchContext.Provider value={contextValue}>
73
+ <div className="grid w-full gap-2 [&>div]:pb-0" {...props} />
74
+ </BranchContext.Provider>
75
+ );
76
+ };
77
+
78
+ export type BranchMessagesProps = Omit<HTMLAttributes<HTMLDivElement>, "className">;
79
+
80
+ export const BranchMessages = ({ children, ...props }: BranchMessagesProps) => {
81
+ const { currentBranch, setBranches, branches } = useBranch();
82
+ const childrenArray = Array.isArray(children) ? children : [children];
83
+
84
+ useEffect(() => {
85
+ if (branches.length !== childrenArray.length) {
86
+ setBranches(childrenArray as ReactElement[]);
87
+ }
88
+ }, [childrenArray, branches, setBranches]);
89
+
90
+ return (childrenArray as ReactElement[]).map((branch, index) => (
91
+ <div
92
+ className={cn(
93
+ "grid gap-2 overflow-hidden [&>div]:pb-0",
94
+ index === currentBranch ? "block" : "hidden",
95
+ )}
96
+ key={branch.key}
97
+ {...props}
98
+ >
99
+ {branch}
100
+ </div>
101
+ ));
102
+ };
103
+
104
+ export type BranchSelectorProps = Omit<HTMLAttributes<HTMLDivElement>, "className"> & {
105
+ from: ChatRole;
106
+ };
107
+
108
+ export const BranchSelector = ({ from, ...props }: BranchSelectorProps) => {
109
+ const { totalBranches } = useBranch();
110
+
111
+ if (totalBranches <= 1) {
112
+ return null;
113
+ }
114
+
115
+ return (
116
+ <div
117
+ className={cn(
118
+ "flex items-center gap-2 self-end px-10",
119
+ from === "assistant" ? "justify-start" : "justify-end",
120
+ )}
121
+ {...props}
122
+ />
123
+ );
124
+ };
125
+
126
+ export type BranchPreviousProps = Omit<
127
+ ButtonHTMLAttributes<HTMLButtonElement>,
128
+ "className"
129
+ >;
130
+
131
+ export const BranchPrevious = ({ children, ...props }: BranchPreviousProps) => {
132
+ const { goToPrevious, totalBranches } = useBranch();
133
+
134
+ return (
135
+ <button
136
+ aria-label="Previous branch"
137
+ className={cn(
138
+ buttonVariants({ variant: "ghost", size: "icon-round-sm" }),
139
+ "text-muted-foreground hover:text-foreground",
140
+ )}
141
+ disabled={totalBranches <= 1}
142
+ onClick={goToPrevious}
143
+ type="button"
144
+ {...props}
145
+ >
146
+ {children ?? <ChevronLeftIcon size={14} />}
147
+ </button>
148
+ );
149
+ };
150
+
151
+ export type BranchNextProps = Omit<
152
+ ButtonHTMLAttributes<HTMLButtonElement>,
153
+ "className"
154
+ >;
155
+
156
+ export const BranchNext = ({ children, ...props }: BranchNextProps) => {
157
+ const { goToNext, totalBranches } = useBranch();
158
+
159
+ return (
160
+ <button
161
+ aria-label="Next branch"
162
+ className={cn(
163
+ buttonVariants({ variant: "ghost", size: "icon-round-sm" }),
164
+ "text-muted-foreground hover:text-foreground",
165
+ )}
166
+ disabled={totalBranches <= 1}
167
+ onClick={goToNext}
168
+ type="button"
169
+ {...props}
170
+ >
171
+ {children ?? <ChevronRightIcon size={14} />}
172
+ </button>
173
+ );
174
+ };
175
+
176
+ export type BranchPageProps = Omit<
177
+ HTMLAttributes<HTMLSpanElement>,
178
+ "className"
179
+ >;
180
+
181
+ export const BranchPage = ({ ...props }: BranchPageProps) => {
182
+ const { currentBranch, totalBranches } = useBranch();
183
+
184
+ return (
185
+ <span
186
+ className="font-medium text-muted-foreground text-xs tabular-nums"
187
+ {...props}
188
+ >
189
+ {currentBranch + 1} of {totalBranches}
190
+ </span>
191
+ );
192
+ };
@@ -0,0 +1,151 @@
1
+ "use client";
2
+
3
+ import { CheckIcon, CopyIcon } from "lucide-react";
4
+ import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from "react";
5
+ import { createContext, useContext, useState } from "react";
6
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
7
+ import {
8
+ oneDark,
9
+ oneLight,
10
+ } from "react-syntax-highlighter/dist/esm/styles/prism";
11
+
12
+ import { cn } from "../../../lib/utils";
13
+ import { buttonVariants } from "../atoms/button";
14
+
15
+ type CodeBlockContextType = {
16
+ code: string;
17
+ };
18
+
19
+ const CodeBlockContext = createContext<CodeBlockContextType>({
20
+ code: "",
21
+ });
22
+
23
+ export type CodeBlockProps = Omit<HTMLAttributes<HTMLDivElement>, "className"> & {
24
+ code: string;
25
+ language: string;
26
+ showLineNumbers?: boolean;
27
+ children?: ReactNode;
28
+ };
29
+
30
+ export const CodeBlock = ({
31
+ code,
32
+ language,
33
+ showLineNumbers = false,
34
+ children,
35
+ ...props
36
+ }: CodeBlockProps) => (
37
+ <CodeBlockContext.Provider value={{ code }}>
38
+ <div
39
+ className="relative w-full overflow-hidden rounded-md border bg-background text-foreground"
40
+ {...props}
41
+ >
42
+ <div className="relative">
43
+ {/* @ts-expect-error - SyntaxHighlighter is not a valid JSX component */}
44
+ <SyntaxHighlighter
45
+ className="overflow-hidden dark:hidden"
46
+ codeTagProps={{
47
+ className: "text-sm",
48
+ }}
49
+ customStyle={{
50
+ margin: 0,
51
+ padding: "1rem",
52
+ fontSize: "0.875rem",
53
+ background: "hsl(var(--background))",
54
+ color: "hsl(var(--foreground))",
55
+ }}
56
+ language={language}
57
+ lineNumberStyle={{
58
+ color: "hsl(var(--muted-foreground))",
59
+ paddingRight: "1rem",
60
+ minWidth: "2.5rem",
61
+ }}
62
+ showLineNumbers={showLineNumbers}
63
+ style={oneLight}
64
+ >
65
+ {code}
66
+ </SyntaxHighlighter>
67
+ {/* @ts-expect-error - SyntaxHighlighter is not a valid JSX component */}
68
+ <SyntaxHighlighter
69
+ className="hidden overflow-hidden dark:block"
70
+ codeTagProps={{
71
+ className: "text-sm",
72
+ }}
73
+ customStyle={{
74
+ margin: 0,
75
+ padding: "1rem",
76
+ fontSize: "0.875rem",
77
+ background: "hsl(var(--background))",
78
+ color: "hsl(var(--foreground))",
79
+ }}
80
+ language={language}
81
+ lineNumberStyle={{
82
+ color: "hsl(var(--muted-foreground))",
83
+ paddingRight: "1rem",
84
+ minWidth: "2.5rem",
85
+ }}
86
+ showLineNumbers={showLineNumbers}
87
+ style={oneDark}
88
+ >
89
+ {code}
90
+ </SyntaxHighlighter>
91
+ {children && (
92
+ <div className="absolute top-2 right-2 flex items-center gap-2">
93
+ {children}
94
+ </div>
95
+ )}
96
+ </div>
97
+ </div>
98
+ </CodeBlockContext.Provider>
99
+ );
100
+
101
+ export type CodeBlockCopyButtonProps = Omit<
102
+ ButtonHTMLAttributes<HTMLButtonElement>,
103
+ "className"
104
+ > & {
105
+ onCopy?: () => void;
106
+ onError?: (error: Error) => void;
107
+ timeout?: number;
108
+ };
109
+
110
+ export const CodeBlockCopyButton = ({
111
+ onCopy,
112
+ onError,
113
+ timeout = 2000,
114
+ children,
115
+ ...props
116
+ }: CodeBlockCopyButtonProps) => {
117
+ const [isCopied, setIsCopied] = useState(false);
118
+ const { code } = useContext(CodeBlockContext);
119
+
120
+ const copyToClipboard = async () => {
121
+ if (typeof window === "undefined" || !navigator.clipboard.writeText) {
122
+ onError?.(new Error("Clipboard API not available"));
123
+ return;
124
+ }
125
+
126
+ try {
127
+ await navigator.clipboard.writeText(code);
128
+ setIsCopied(true);
129
+ onCopy?.();
130
+ setTimeout(() => setIsCopied(false), timeout);
131
+ } catch (error) {
132
+ onError?.(error as Error);
133
+ }
134
+ };
135
+
136
+ const Icon = isCopied ? CheckIcon : CopyIcon;
137
+
138
+ return (
139
+ <button
140
+ className={cn(
141
+ buttonVariants({ variant: "ghost", size: "icon-xs" }),
142
+ "shrink-0",
143
+ )}
144
+ onClick={copyToClipboard}
145
+ type="button"
146
+ {...props}
147
+ >
148
+ {children ?? <Icon size={14} />}
149
+ </button>
150
+ );
151
+ };
@@ -0,0 +1,177 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import * as React from "react";
3
+ import {
4
+ Controller,
5
+ type ControllerProps,
6
+ type FieldPath,
7
+ type FieldValues,
8
+ FormProvider,
9
+ useFormContext,
10
+ } from "react-hook-form";
11
+
12
+ const Form = FormProvider;
13
+
14
+ type FormFieldContextValue<
15
+ TFieldValues extends FieldValues = FieldValues,
16
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
17
+ > = {
18
+ name: TName;
19
+ };
20
+
21
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
22
+ {} as FormFieldContextValue,
23
+ );
24
+
25
+ const FormField = <
26
+ TFieldValues extends FieldValues = FieldValues,
27
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
28
+ >({
29
+ ...props
30
+ }: ControllerProps<TFieldValues, TName>) => {
31
+ return (
32
+ <FormFieldContext.Provider value={{ name: props.name }}>
33
+ <Controller {...props} />
34
+ </FormFieldContext.Provider>
35
+ );
36
+ };
37
+
38
+ const useFormField = () => {
39
+ const fieldContext = React.useContext(FormFieldContext);
40
+ const itemContext = React.useContext(FormItemContext);
41
+ const { getFieldState, formState } = useFormContext();
42
+
43
+ const fieldState = getFieldState(fieldContext.name, formState);
44
+
45
+ if (!fieldContext) {
46
+ throw new Error("useFormField should be used within <FormField>");
47
+ }
48
+
49
+ const { id } = itemContext;
50
+
51
+ return {
52
+ id,
53
+ name: fieldContext.name,
54
+ formItemId: `${id}-form-item`,
55
+ formDescriptionId: `${id}-form-item-description`,
56
+ formMessageId: `${id}-form-item-message`,
57
+ ...fieldState,
58
+ };
59
+ };
60
+
61
+ type FormItemContextValue = {
62
+ id: string;
63
+ };
64
+
65
+ const FormItemContext = React.createContext<FormItemContextValue>(
66
+ {} as FormItemContextValue,
67
+ );
68
+
69
+ const FormItem = React.forwardRef<
70
+ HTMLDivElement,
71
+ Omit<React.HTMLAttributes<HTMLDivElement>, "className">
72
+ >(({ ...props }, ref) => {
73
+ const id = React.useId();
74
+
75
+ return (
76
+ <FormItemContext.Provider value={{ id }}>
77
+ <div ref={ref} className="space-y-2" {...props} />
78
+ </FormItemContext.Provider>
79
+ );
80
+ });
81
+ FormItem.displayName = "FormItem";
82
+
83
+ const FormLabel = React.forwardRef<
84
+ HTMLLabelElement,
85
+ Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "className">
86
+ >(({ ...props }, ref) => {
87
+ const { error, formItemId } = useFormField();
88
+
89
+ return (
90
+ <label
91
+ ref={ref}
92
+ htmlFor={formItemId}
93
+ className={
94
+ error
95
+ ? "gap-2 text-sm leading-none font-medium text-destructive group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed"
96
+ : "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed"
97
+ }
98
+ {...props}
99
+ />
100
+ );
101
+ });
102
+ FormLabel.displayName = "FormLabel";
103
+
104
+ const FormControl = React.forwardRef<
105
+ React.ElementRef<typeof Slot>,
106
+ React.ComponentPropsWithoutRef<typeof Slot>
107
+ >(({ ...props }, ref) => {
108
+ const { error, formItemId, formDescriptionId, formMessageId } =
109
+ useFormField();
110
+
111
+ return (
112
+ <Slot
113
+ ref={ref}
114
+ id={formItemId}
115
+ aria-describedby={
116
+ !error
117
+ ? `${formDescriptionId}`
118
+ : `${formDescriptionId} ${formMessageId}`
119
+ }
120
+ aria-invalid={!!error}
121
+ {...props}
122
+ />
123
+ );
124
+ });
125
+ FormControl.displayName = "FormControl";
126
+
127
+ const FormDescription = React.forwardRef<
128
+ HTMLParagraphElement,
129
+ Omit<React.HTMLAttributes<HTMLParagraphElement>, "className">
130
+ >(({ ...props }, ref) => {
131
+ const { formDescriptionId } = useFormField();
132
+
133
+ return (
134
+ <p
135
+ ref={ref}
136
+ id={formDescriptionId}
137
+ className="text-[0.8rem] text-muted-foreground"
138
+ {...props}
139
+ />
140
+ );
141
+ });
142
+ FormDescription.displayName = "FormDescription";
143
+
144
+ const FormMessage = React.forwardRef<
145
+ HTMLParagraphElement,
146
+ Omit<React.HTMLAttributes<HTMLParagraphElement>, "className">
147
+ >(({ children, ...props }, ref) => {
148
+ const { error, formMessageId } = useFormField();
149
+ const body = error ? String(error?.message) : children;
150
+
151
+ if (!body) {
152
+ return null;
153
+ }
154
+
155
+ return (
156
+ <p
157
+ ref={ref}
158
+ id={formMessageId}
159
+ className="text-[0.8rem] font-medium text-destructive"
160
+ {...props}
161
+ >
162
+ {body}
163
+ </p>
164
+ );
165
+ });
166
+ FormMessage.displayName = "FormMessage";
167
+
168
+ export {
169
+ useFormField,
170
+ Form,
171
+ FormItem,
172
+ FormLabel,
173
+ FormControl,
174
+ FormDescription,
175
+ FormMessage,
176
+ FormField,
177
+ };
@@ -1,18 +1,24 @@
1
1
  export * from "./accordion";
2
+ export * from "./actions";
2
3
  export * from "./ai-chat";
3
4
  export * from "./alert";
5
+ export * from "./branch";
4
6
  export * from "./breadcrumb";
5
7
  export * from "./button-group";
6
8
  export * from "./card";
7
9
  export * from "./collapsible";
10
+ export * from "./code-block";
8
11
  export * from "./command-search";
9
12
  export * from "./empty";
10
13
  export * from "./field";
14
+ export * from "./form";
11
15
  export * from "./grid";
12
16
  export * from "./hover-card";
17
+ export * from "./inline-citation";
13
18
  export * from "./input-group";
14
19
  export * from "./input-otp";
15
20
  export * from "./item";
21
+ export * from "./message";
16
22
  export * from "./page-header";
17
23
  export * from "./pagination";
18
24
  export * from "./popover";
@@ -22,8 +28,14 @@ export * from "./scroll-area";
22
28
  export * from "./section";
23
29
  export * from "./select";
24
30
  export * from "./settings";
31
+ export * from "./sources";
32
+ export * from "./suggestion";
33
+ export * from "./task";
25
34
  export * from "./table";
26
35
  export * from "./tabs";
27
36
  export * from "./theme-switcher";
37
+ export * from "./time-range-input";
28
38
  export * from "./toggle-group";
39
+ export * from "./tool";
40
+ export * from "./tool-call-indicator";
29
41
  export * from "./tooltip";