{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-shimmer-demo",
  "registryDependencies": [
    "@delta/ai-elements"
  ],
  "files": [
    {
      "path": "examples/chat-shimmer-demo.tsx",
      "content": "\"use client\"\n\nimport { useCallback, useRef, useState } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChatContainer } from \"@/components/ui/ai-elements/chat-container\"\nimport {\n  Conversation,\n  ConversationContent,\n  ConversationScrollButton,\n} from \"@/components/ui/ai-elements/conversation\"\nimport {\n  Message,\n  MessageContent,\n} from \"@/components/ui/ai-elements/message\"\nimport {\n  PromptInput,\n  PromptInputActionAddAttachments,\n  PromptInputActionMenu,\n  PromptInputActionMenuContent,\n  PromptInputActionMenuTrigger,\n  PromptInputAttachment,\n  PromptInputAttachments,\n  PromptInputBody,\n  PromptInputFooter,\n  PromptInputModelSelect,\n  PromptInputModelSelectContent,\n  PromptInputModelSelectItem,\n  PromptInputModelSelectTrigger,\n  PromptInputModelSelectValue,\n  PromptInputSubmit,\n  PromptInputTextarea,\n  PromptInputTools,\n  type PromptInputMessage,\n} from \"@/components/ui/ai-elements/prompt-input\"\nimport { Response } from \"@/components/ui/ai-elements/response\"\nimport { Shimmer } from \"@/components/ui/ai-elements/shimmer\"\n\ntype MessageType = {\n  key: string\n  from: \"user\" | \"assistant\"\n  content: string\n  isShimmering?: boolean\n  avatar: string\n  name: string\n}\n\nconst initialMessages: MessageType[] = []\n\nconst models = [\n  { id: \"gpt-4\", name: \"GPT-4\" },\n  { id: \"claude-2\", name: \"Claude 2\" },\n  { id: \"palm-2\", name: \"PaLM 2\" },\n]\n\nconst Example = () => {\n  const [model, setModel] = useState<string>(models[0].id)\n  const [text, setText] = useState<string>(\"\")\n  const [status, setStatus] = useState<\n    \"submitted\" | \"streaming\" | \"ready\" | \"error\"\n  >(\"ready\")\n  const [messages, setMessages] = useState<MessageType[]>(initialMessages)\n  const shouldCancelRef = useRef<boolean>(false)\n  const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n  const stop = useCallback(() => {\n    shouldCancelRef.current = true\n    setStatus(\"ready\")\n  }, [])\n\n  const addUserMessage = useCallback((content: string) => {\n    const userMessage: MessageType = {\n      key: `user-${Date.now()}`,\n      from: \"user\",\n      content,\n      avatar: \"https://patrickprunty.com/icon.webp\",\n      name: \"User\",\n    }\n\n    setMessages((prev) => [...prev, userMessage])\n\n    // Add shimmer message immediately\n    const shimmerMessage: MessageType = {\n      key: `assistant-${Date.now()}`,\n      from: \"assistant\",\n      content: \"Processing your request...\",\n      isShimmering: true,\n      avatar: \"https://github.com/openai.png\",\n      name: \"Assistant\",\n    }\n\n    setMessages((prev) => [...prev, shimmerMessage])\n    setStatus(\"streaming\")\n\n    // Simulate processing time then replace with actual response\n    setTimeout(() => {\n      if (!shouldCancelRef.current) {\n        const responseMessage: MessageType = {\n          key: shimmerMessage.key,\n          from: \"assistant\",\n          content: `I received your message: \"${content}\". This demonstrates the shimmer effect which is useful for showing loading states while AI processes requests.`,\n          isShimmering: false,\n          avatar: \"https://github.com/openai.png\",\n          name: \"Assistant\",\n        }\n\n        setMessages((prev) =>\n          prev.map((msg) =>\n            msg.key === shimmerMessage.key ? responseMessage : msg\n          )\n        )\n        setStatus(\"ready\")\n      }\n    }, 3000)\n  }, [])\n\n  const handleSubmit = (message: PromptInputMessage) => {\n    if (status === \"streaming\") {\n      stop()\n      return\n    }\n\n    const hasText = Boolean(message.text)\n    const hasAttachments = Boolean(message.files?.length)\n\n    if (!(hasText || hasAttachments)) {\n      return\n    }\n\n    setStatus(\"submitted\")\n    addUserMessage(message.text || \"Sent with attachments\")\n    setText(\"\")\n  }\n\n  return (\n    <ChatContainer>\n      <Conversation>\n        <ConversationContent>\n          {messages.map((message) => (\n            <Message\n              from={message.from}\n              key={message.key}\n              className={cn(\n                message.from === \"user\" ? \"items-end justify-end\" : undefined,\n                \"group/message\"\n              )}\n            >\n              <div>\n                <MessageContent\n                  className={cn(\n                    message.from === \"assistant\" ? \"max-w-full\" : \"\",\n                    message.from === \"user\" && \"ml-auto w-fit\"\n                  )}\n                >\n                  <div className=\"text-base leading-[1.65rem]\">\n                    {message.isShimmering ? (\n                      <Shimmer duration={2} spread={3}>\n                        {message.content}\n                      </Shimmer>\n                    ) : (\n                      <Response>{message.content}</Response>\n                    )}\n                  </div>\n                </MessageContent>\n              </div>\n            </Message>\n          ))}\n        </ConversationContent>\n        <ConversationScrollButton />\n      </Conversation>\n      <div className=\"grid shrink-0 gap-4\">\n        <div className=\"w-full px-4 pb-4\">\n          <PromptInput globalDrop multiple onSubmit={handleSubmit}>\n            <PromptInputBody>\n              <PromptInputAttachments>\n                {(attachment: any) => (\n                  <PromptInputAttachment data={attachment} />\n                )}\n              </PromptInputAttachments>\n              <PromptInputTextarea\n                onChange={(event: any) => setText(event.target.value)}\n                ref={textareaRef}\n                value={text}\n                className=\"text-base leading-[1.65rem]\"\n                placeholder=\"Ask anything...\"\n              />\n            </PromptInputBody>\n            <PromptInputFooter>\n              <PromptInputTools>\n                <PromptInputActionMenu>\n                  <PromptInputActionMenuTrigger />\n                  <PromptInputActionMenuContent>\n                    <PromptInputActionAddAttachments />\n                  </PromptInputActionMenuContent>\n                </PromptInputActionMenu>\n              </PromptInputTools>\n              <PromptInputTools>\n                <PromptInputModelSelect onValueChange={setModel} value={model}>\n                  <PromptInputModelSelectTrigger>\n                    <PromptInputModelSelectValue />\n                  </PromptInputModelSelectTrigger>\n                  <PromptInputModelSelectContent>\n                    {models.map((model: any) => (\n                      <PromptInputModelSelectItem\n                        key={model.id || model.name}\n                        value={model.id || model.name}\n                      >\n                        {model.name || model.id}\n                      </PromptInputModelSelectItem>\n                    ))}\n                  </PromptInputModelSelectContent>\n                </PromptInputModelSelect>\n                <PromptInputSubmit\n                  disabled={(!text.trim() && !status) || status === \"streaming\"}\n                  status={status}\n                />\n              </PromptInputTools>\n            </PromptInputFooter>\n          </PromptInput>\n        </div>\n      </div>\n    </ChatContainer>\n  )\n}\n\nexport default Example\n",
      "type": "registry:example"
    }
  ],
  "type": "registry:example"
}