"use client"; import type { AttachmentData } from "@/components/ai-elements/attachments"; import { Attachment, AttachmentInfo, AttachmentPreview, AttachmentRemove, Attachments, } from "@/components/ai-elements/attachments"; import { ModelSelector, ModelSelectorContent, ModelSelectorEmpty, ModelSelectorGroup, ModelSelectorInput, ModelSelectorItem, ModelSelectorList, ModelSelectorLogo, ModelSelectorLogoGroup, ModelSelectorName, ModelSelectorTrigger, } from "@/components/ai-elements/model-selector"; import type { PromptInputMessage } from "@/components/ai-elements/prompt-input"; import { PromptInput, PromptInputBody, PromptInputButton, PromptInputCommand, PromptInputCommandEmpty, PromptInputCommandGroup, PromptInputCommandInput, PromptInputCommandItem, PromptInputCommandList, PromptInputCommandSeparator, PromptInputFooter, PromptInputHeader, PromptInputHoverCard, PromptInputHoverCardContent, PromptInputHoverCardTrigger, PromptInputProvider, PromptInputSubmit, PromptInputTab, PromptInputTabBody, PromptInputTabItem, PromptInputTabLabel, PromptInputTextarea, PromptInputTools, usePromptInputAttachments, usePromptInputReferencedSources, } from "@/components/ai-elements/prompt-input"; import { Button } from "@/components/ui/button"; import type { SourceDocumentUIPart } from "ai"; import { AtSignIcon, CheckIcon, FilesIcon, GlobeIcon, ImageIcon, RulerIcon, } from "lucide-react"; import { memo, useCallback, useState } from "react"; const models = [ { chef: "OpenAI", chefSlug: "openai", id: "gpt-4o", name: "GPT-4o", providers: ["openai", "azure"], }, { chef: "OpenAI", chefSlug: "openai", id: "gpt-4o-mini", name: "GPT-4o Mini", providers: ["openai", "azure"], }, { chef: "Anthropic", chefSlug: "anthropic", id: "claude-opus-4-20250514", name: "Claude 4 Opus", providers: ["anthropic", "azure", "google", "amazon-bedrock"], }, { chef: "Anthropic", chefSlug: "anthropic", id: "claude-sonnet-4-20250514", name: "Claude 4 Sonnet", providers: ["anthropic", "azure", "google", "amazon-bedrock"], }, { chef: "Google", chefSlug: "google", id: "gemini-2.0-flash-exp", name: "Gemini 2.0 Flash", providers: ["google"], }, ]; const SUBMITTING_TIMEOUT = 200; const STREAMING_TIMEOUT = 2000; interface AttachmentItemProps { attachment: AttachmentData; onRemove: (id: string) => void; } const AttachmentItem = memo(({ attachment, onRemove }: AttachmentItemProps) => { const handleRemove = useCallback( () => onRemove(attachment.id), [onRemove, attachment.id] ); return ( ); }); AttachmentItem.displayName = "AttachmentItem"; interface SourceItemProps { source: AttachmentData; onRemove: (id: string) => void; } const SourceItem = memo(({ source, onRemove }: SourceItemProps) => { const handleRemove = useCallback( () => onRemove(source.id), [onRemove, source.id] ); return ( ); }); SourceItem.displayName = "SourceItem"; interface ModelItemProps { m: (typeof models)[0]; selectedModel: string; onSelect: (id: string) => void; } const ModelItem = memo(({ m, selectedModel, onSelect }: ModelItemProps) => { const handleSelect = useCallback(() => onSelect(m.id), [onSelect, m.id]); return ( {m.name} {m.providers.map((provider) => ( ))} {selectedModel === m.id ? ( ) : (
)} ); }); ModelItem.displayName = "ModelItem"; interface SourceCommandItemProps { source: SourceDocumentUIPart; onAdd: (source: SourceDocumentUIPart) => void; } const SourceCommandItem = memo(({ source, onAdd }: SourceCommandItemProps) => { const handleSelect = useCallback(() => onAdd(source), [onAdd, source]); return (
{source.title} {source.filename}
); }); SourceCommandItem.displayName = "SourceCommandItem"; const sampleSources: SourceDocumentUIPart[] = [ { filename: "packages/elements/src", mediaType: "text/plain", sourceId: "1", title: "prompt-input.tsx", type: "source-document", }, { filename: "apps/test/app/examples", mediaType: "text/plain", sourceId: "2", title: "queue.tsx", type: "source-document", }, { filename: "packages/elements/src", mediaType: "text/plain", sourceId: "3", title: "queue.tsx", type: "source-document", }, ]; const sampleTabs = { active: [{ path: "packages/elements/src/task-queue-panel.tsx" }], recents: [ { path: "apps/test/app/examples/task-queue-panel.tsx" }, { path: "apps/test/app/page.tsx" }, { path: "packages/elements/src/task.tsx" }, { path: "apps/test/app/examples/prompt-input.tsx" }, { path: "packages/elements/src/queue.tsx" }, { path: "apps/test/app/examples/queue.tsx" }, ], }; const PromptInputAttachmentsDisplay = () => { const attachments = usePromptInputAttachments(); const handleRemove = useCallback( (id: string) => attachments.remove(id), [attachments] ); if (attachments.files.length === 0) { return null; } return ( {attachments.files.map((attachment) => ( ))} ); }; const PromptInputReferencedSourcesDisplay = () => { const refs = usePromptInputReferencedSources(); const handleRemove = useCallback((id: string) => refs.remove(id), [refs]); if (refs.sources.length === 0) { return null; } return ( {refs.sources.map((source) => ( ))} ); }; const SampleFilesMenu = () => { const refs = usePromptInputReferencedSources(); const handleAdd = useCallback( (source: SourceDocumentUIPart) => refs.add(source), [refs] ); return ( No results found. Active Tabs {sampleSources .filter( (source) => !refs.sources.some( (s) => s.title === source.title && s.filename === source.filename ) ) .map((source) => ( ))} ); }; const Example = () => { const [model, setModel] = useState(models[0].id); const [modelSelectorOpen, setModelSelectorOpen] = useState(false); const [status, setStatus] = useState< "submitted" | "streaming" | "ready" | "error" >("ready"); const selectedModelData = models.find((m) => m.id === model); const handleModelSelect = useCallback((id: string) => { setModel(id); setModelSelectorOpen(false); }, []); const handleSubmit = useCallback((message: PromptInputMessage) => { const hasText = Boolean(message.text); const hasAttachments = Boolean(message.files?.length); if (!(hasText || hasAttachments)) { return; } setStatus("submitted"); setTimeout(() => { setStatus("streaming"); }, SUBMITTING_TIMEOUT); setTimeout(() => { setStatus("ready"); }, STREAMING_TIMEOUT); }, []); return (
1

Attached Project Rules

Always Apply:

ultracite.mdc

Click to manage

1 Tab Active Tabs {sampleTabs.active.map((tab) => ( {tab.path} ))} Recents {sampleTabs.recents.map((tab) => ( {tab.path} ))}
Only file paths are included
{selectedModelData?.chefSlug && ( )} {selectedModelData?.name && ( {selectedModelData.name} )} No models found. {["OpenAI", "Anthropic", "Google"].map((chef) => ( {models .filter((m) => m.chef === chef) .map((m) => ( ))} ))}
); }; export default Example;