diff --git a/packages/elements/src/prompt-input.tsx b/packages/elements/src/prompt-input.tsx index 84f5b2a..e5735bf 100644 --- a/packages/elements/src/prompt-input.tsx +++ b/packages/elements/src/prompt-input.tsx @@ -34,11 +34,6 @@ import { SelectTrigger, SelectValue, } from "@repo/shadcn-ui/components/ui/select"; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@repo/shadcn-ui/components/ui/tooltip"; import { cn } from "@repo/shadcn-ui/lib/utils"; import type { ChatStatus, FileUIPart } from "ai"; import { @@ -70,7 +65,6 @@ import { useCallback, useContext, useEffect, - useLayoutEffect, useMemo, useRef, useState, @@ -270,58 +264,87 @@ export function PromptInputAttachment({ }: PromptInputAttachmentProps) { const attachments = usePromptInputAttachments(); + const filename = data.filename || ""; + const mediaType = data.mediaType?.startsWith("image/") && data.url ? "image" : "file"; + const isImage = mediaType === "image"; + + const attachmentLabel = filename || (isImage ? "Image" : "Attachment"); return ( -
- {mediaType === "image" ? ( - {data.filename - ) : ( -
- - - -

- {data.filename || "Unknown file"} + + +
+
+
+ {isImage ? ( + {filename + ) : ( +
+ +
+ )} +
+ +
+ + {attachmentLabel} +
+
+ +
+ {isImage && ( +
+ {filename +
+ )} +
+
+

+ {filename || (isImage ? "Image" : "Attachment")}

- - -
-

- {data.filename || "Unknown file"} -

- {data.mediaType &&
{data.mediaType}
} -
-
- + {data.mediaType && ( +

+ {data.mediaType} +

+ )} +
+
- )} - -

+ + ); } @@ -333,69 +356,15 @@ export type PromptInputAttachmentsProps = Omit< }; export function PromptInputAttachments({ - className, children, - ...props }: PromptInputAttachmentsProps) { const attachments = usePromptInputAttachments(); - const [height, setHeight] = useState(0); - const contentRef = useRef(null); - useLayoutEffect(() => { - const el = contentRef.current; - if (!el) { - return; - } - const ro = new ResizeObserver(() => { - setHeight(el.getBoundingClientRect().height); - }); - ro.observe(el); - setHeight(el.getBoundingClientRect().height); - return () => ro.disconnect(); - }, []); - - // biome-ignore lint/correctness/useExhaustiveDependencies: Force height measurement when attachments change - useLayoutEffect(() => { - const el = contentRef.current; - if (!el) { - return; - } - setHeight(el.getBoundingClientRect().height); - }, [attachments.files.length]); - - if (attachments.files.length === 0) { - return null; - } - - return ( - -
-
- {attachments.files - .filter((f) => !(f.mediaType?.startsWith("image/") && f.url)) - .map((file) => ( - {children(file)} - ))} -
-
- {attachments.files - .filter((f) => f.mediaType?.startsWith("image/") && f.url) - .map((file) => ( - {children(file)} - ))} -
-
-
- ); + return attachments?.files.length > 0 + ? attachments?.files.map((file) => ( + {children(file)} + )) + : null; } export type PromptInputActionAddAttachmentsProps = ComponentProps< @@ -890,7 +859,7 @@ export const PromptInputHeader = ({ }: PromptInputHeaderProps) => ( ); diff --git a/packages/examples/src/chatbot.tsx b/packages/examples/src/chatbot.tsx index 12593b4..6ed8c8b 100644 --- a/packages/examples/src/chatbot.tsx +++ b/packages/examples/src/chatbot.tsx @@ -24,7 +24,7 @@ import { PromptInputAttachments, PromptInputBody, PromptInputButton, - PromptInputFooter, + PromptInputHeader, type PromptInputMessage, PromptInputModelSelect, PromptInputModelSelectContent, @@ -162,14 +162,14 @@ React hooks are a powerful feature that let you use state and other React featur \`\`\`jsx function ProfilePage({ userId }) { const [user, setUser] = useState(null); - + useEffect(() => { // This runs after render and when userId changes fetchUser(userId).then(userData => { setUser(userData); }); }, [userId]); - + return user ? : ; } \`\`\` @@ -208,7 +208,7 @@ Would you like me to explain any specific hook in more detail?`, from: "assistant", reasoning: { content: `The user is asking for a detailed explanation of useCallback and useMemo. I should provide a clear and concise explanation of each hook's purpose and how they differ. - + The useCallback hook is used to memoize functions to prevent unnecessary re-renders of child components that receive functions as props. The useMemo hook is used to memoize values to avoid expensive recalculations on every render. @@ -485,10 +485,12 @@ const Example = () => {
- + {(attachment) => } + + setText(event.target.value)} value={text}