{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "firebase-analyze-image",
  "type": "registry:block",
  "title": "Firebase Analyze Image",
  "description": "A chat interface for analyzing images using Firebase Gemini with multimodal input support",
  "dependencies": [
    "@ai-sdk/react",
    "@mui/material",
    "lucide-react",
    "sonner",
    "@emotion/react",
    "@emotion/styled"
  ],
  "registryDependencies": [
    "https://www.mui-treasury.com/r/ai-actions.json",
    "https://www.mui-treasury.com/r/ai-conversation.json",
    "https://www.mui-treasury.com/r/ai-loader.json",
    "https://www.mui-treasury.com/r/ai-message.json",
    "https://www.mui-treasury.com/r/ai-prompt-input.json",
    "https://www.mui-treasury.com/r/ai-response.json",
    "https://www.mui-treasury.com/r/ai-suggestion.json",
    "https://www.mui-treasury.com/r/firebase-chat-transport.json"
  ],
  "files": [
    {
      "path": "firebase/firebase-analyze-image/firebase-analyze-image.tsx",
      "target": "src/mui-treasury/firebase/firebase-analyze-image/firebase-analyze-image.tsx",
      "content": "'use client';\n\nimport React, { useState } from 'react';\n\nimport { UIMessage, useChat } from '@ai-sdk/react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport CircularProgress from '@mui/material/CircularProgress';\nimport Typography from '@mui/material/Typography';\nimport { CopyIcon, ImageIcon, RefreshCwIcon, SquareIcon } from 'lucide-react';\nimport { toast } from 'sonner';\n\nimport { app } from '@/lib/firebase-setup';\nimport { Action, Actions } from '@/registry/components/ai-actions/ai-actions';\nimport {\n  Conversation,\n  ConversationContent,\n  ConversationScrollButton,\n} from '@/registry/components/ai-conversation/ai-conversation';\nimport { Loader } from '@/registry/components/ai-loader/ai-loader';\nimport {\n  Message,\n  MessageAvatar,\n  MessageContent,\n} from '@/registry/components/ai-message/ai-message';\nimport {\n  PromptInput,\n  PromptInputActionAddAttachments,\n  PromptInputActionMenu,\n  PromptInputActionMenuContent,\n  PromptInputActionMenuTrigger,\n  PromptInputAttachment,\n  PromptInputAttachments,\n  PromptInputBody,\n  type PromptInputMessage,\n  PromptInputSubmit,\n  PromptInputTextarea,\n  PromptInputToolbar,\n  PromptInputTools,\n} from '@/registry/components/ai-prompt-input/ai-prompt-input';\nimport { Response } from '@/registry/components/ai-response/ai-response';\nimport {\n  Suggestion,\n  Suggestions,\n} from '@/registry/components/ai-suggestion/ai-suggestion';\nimport { FirebaseChatTransport } from '@/registry/firebase/firebase-chat-transport';\n\nconst SUGGESTED_PROMPTS = [\n  'What objects can you identify in this image?',\n  'Describe this image in detail',\n  'Extract any text visible in this image',\n  'What emotions or mood does this image convey?',\n];\n\nexport default function FirebaseAnalyzeImage() {\n  const [inputValue, setInputValue] = useState('');\n\n  const transport = React.useMemo(\n    () =>\n      app\n        ? new FirebaseChatTransport({\n            firebaseApp: app,\n            modelParams: {\n              model: 'gemini-2.5-flash',\n              systemInstruction: `You are a helpful AI assistant specialized in image analysis.\nWhen the user shares an image, analyze it thoroughly and provide detailed insights.\nIf no image is provided, politely ask the user to share an image for analysis.\nAlways respond in a clear and organized manner using Markdown formatting.`,\n            },\n          })\n        : null,\n    [],\n  );\n\n  const { messages, status, error, sendMessage, stop, regenerate } =\n    useChat<UIMessage>({\n      id: 'firebase-analyze-image',\n      transport: transport!,\n    });\n\n  const handleSubmit = (\n    message: PromptInputMessage,\n    event: React.FormEvent,\n  ) => {\n    event.preventDefault();\n    const hasText = message.text?.trim();\n    const hasFiles = message.files && message.files.length > 0;\n\n    if (hasText && hasFiles) {\n      sendMessage({ text: message.text!, files: message.files });\n    } else if (hasText) {\n      sendMessage({ text: message.text! });\n    } else if (hasFiles) {\n      sendMessage({ files: message.files! });\n    }\n    setInputValue('');\n  };\n\n  const handleSuggestionClick = (suggestion: string) => {\n    setInputValue(suggestion);\n  };\n\n  const handleCopy = async (text: string) => {\n    try {\n      await navigator.clipboard.writeText(text);\n      toast.success('Copied to clipboard');\n    } catch (err) {\n      toast.error(\n        `Failed to copy (${err instanceof Error ? err.message : 'Unknown error'})`,\n      );\n    }\n  };\n\n  const showSuggestions = messages.length === 0 && status === 'ready';\n\n  if (!app) {\n    return (\n      <Box\n        sx={{\n          height: '100%',\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          p: 2,\n        }}\n      >\n        <Typography\n          sx={{\n            color: 'text.secondary',\n          }}\n        >\n          Firebase not configured. Please set up Firebase config at the top of\n          the page.\n        </Typography>\n      </Box>\n    );\n  }\n\n  return (\n    <Box\n      sx={{\n        height: '100%',\n        width: '100%',\n        display: 'flex',\n        flexDirection: 'column',\n        maxWidth: 768,\n        mx: 'auto',\n      }}\n    >\n      <Box\n        sx={{\n          flex: 1,\n          display: 'flex',\n          flexDirection: 'column',\n          overflow: 'hidden',\n        }}\n      >\n        <Conversation>\n          <ConversationContent>\n            {messages.length === 0 && status === 'ready' ? (\n              <Box\n                sx={{\n                  flex: 1,\n                  display: 'flex',\n                  alignItems: 'center',\n                  justifyContent: 'center',\n                  flexDirection: 'column',\n                  gap: 2,\n                  color: 'text.tertiary',\n                }}\n              >\n                <ImageIcon size={48} />\n                <Typography variant=\"h4\" sx={{ fontWeight: 500 }}>\n                  Analyze Image\n                </Typography>\n                <Typography\n                  sx={{\n                    color: 'text.secondary',\n                  }}\n                >\n                  Upload an image to analyze\n                </Typography>\n              </Box>\n            ) : (\n              <Box sx={{ display: 'flex', flexDirection: 'column' }}>\n                {messages.map((message) => {\n                  const messageText = message.parts\n                    ?.filter((part) => part.type === 'text')\n                    .map((part) => part.text)\n                    .join('\\n');\n\n                  return (\n                    <Message key={message.id} from={message.role}>\n                      <MessageAvatar\n                        name={message.role === 'user' ? 'You' : 'AI'}\n                      />\n                      <MessageContent variant=\"flat\">\n                        {message.parts?.map((part, index: number) => {\n                          if (part.type === 'text') {\n                            if (\n                              message.role === 'assistant' &&\n                              part.state !== 'done' &&\n                              !part.text\n                            ) {\n                              return null;\n                            }\n                            return message.role === 'assistant' ? (\n                              <Response key={index}>{part.text}</Response>\n                            ) : (\n                              <Box key={index}>{part.text}</Box>\n                            );\n                          }\n                          if (\n                            part.type === 'file' &&\n                            part.mediaType?.startsWith('image/')\n                          ) {\n                            return (\n                              <Box\n                                key={index}\n                                component=\"img\"\n                                src={part.url}\n                                alt={part.filename || 'uploaded image'}\n                                sx={{\n                                  maxWidth: '100%',\n                                  height: 'auto',\n                                  borderRadius: 2,\n                                  my: 1,\n                                }}\n                              />\n                            );\n                          }\n                          return null;\n                        })}\n                        {message.role === 'assistant' && messageText && (\n                          <Actions>\n                            <Action\n                              tooltip=\"Copy\"\n                              onClick={() => handleCopy(messageText)}\n                            >\n                              <CopyIcon size={16} />\n                            </Action>\n                            <Action\n                              tooltip=\"Regenerate\"\n                              onClick={() => regenerate()}\n                            >\n                              <RefreshCwIcon size={16} />\n                            </Action>\n                          </Actions>\n                        )}\n                      </MessageContent>\n                    </Message>\n                  );\n                })}\n\n                {status === 'submitted' && (\n                  <Message from=\"assistant\">\n                    <MessageAvatar name=\"AI\" />\n                    <MessageContent variant=\"flat\">\n                      <Box\n                        sx={{ display: 'flex', alignItems: 'center', gap: 1 }}\n                      >\n                        <CircularProgress size={20} />\n                        <Typography sx={{ color: 'text.secondary' }}>\n                          Analyzing image...\n                        </Typography>\n                      </Box>\n                    </MessageContent>\n                  </Message>\n                )}\n\n                {error && (\n                  <Message from=\"assistant\">\n                    <MessageAvatar name=\"AI\" />\n                    <MessageContent variant=\"flat\">\n                      <Typography sx={{ color: 'error.text' }}>\n                        {error.message ||\n                          'An error occurred. Please try again.'}\n                      </Typography>\n                    </MessageContent>\n                  </Message>\n                )}\n              </Box>\n            )}\n            {status === 'streaming' && (\n              <Box\n                sx={{\n                  display: 'flex',\n                  alignItems: 'center',\n                  gap: 1,\n                  mt: 1,\n                }}\n              >\n                <Loader />\n                <Typography sx={{ color: 'text.secondary' }}>\n                  Analyzing...\n                </Typography>\n              </Box>\n            )}\n          </ConversationContent>\n\n          <ConversationScrollButton />\n        </Conversation>\n      </Box>\n      {showSuggestions && (\n        <Box sx={{ mb: 2 }}>\n          <Suggestions>\n            {SUGGESTED_PROMPTS.map((prompt, index) => (\n              <Suggestion\n                key={index}\n                suggestion={prompt}\n                onClick={handleSuggestionClick}\n              />\n            ))}\n          </Suggestions>\n        </Box>\n      )}\n      <PromptInput onSubmit={handleSubmit} accept=\"image/*\" multiple>\n        <PromptInputAttachments>\n          {(attachment) => <PromptInputAttachment data={attachment} />}\n        </PromptInputAttachments>\n        <PromptInputBody>\n          <PromptInputTextarea\n            placeholder=\"Ask about the image or upload one to analyze...\"\n            value={inputValue}\n            onChange={(e) => setInputValue(e.target.value)}\n            disabled={status === 'submitted' || error != null}\n          />\n        </PromptInputBody>\n        <PromptInputToolbar>\n          <PromptInputTools>\n            <PromptInputActionMenu>\n              <PromptInputActionMenuTrigger />\n              <PromptInputActionMenuContent>\n                <PromptInputActionAddAttachments />\n              </PromptInputActionMenuContent>\n            </PromptInputActionMenu>\n          </PromptInputTools>\n          {status === 'streaming' || status === 'submitted' ? (\n            <Button\n              variant=\"outlined\"\n              onClick={(e) => {\n                e.preventDefault();\n                stop();\n              }}\n              sx={{\n                minWidth: 'auto',\n                borderRadius: 2,\n                p: 1,\n              }}\n            >\n              <SquareIcon size={16} />\n            </Button>\n          ) : (\n            <PromptInputSubmit\n              status={status as 'ready' | 'submitted' | 'streaming' | 'error'}\n              disabled={error != null}\n            />\n          )}\n        </PromptInputToolbar>\n      </PromptInput>\n    </Box>\n  );\n}\n",
      "type": "registry:item"
    },
    {
      "path": "firebase/firebase-analyze-image/index.ts",
      "target": "src/mui-treasury/firebase/firebase-analyze-image/index.ts",
      "content": "export * from './firebase-analyze-image';\nexport { default as FirebaseAnalyzeImage } from './firebase-analyze-image';\n",
      "type": "registry:item"
    }
  ],
  "meta": {
    "category": "ai",
    "subcategory": "firebase",
    "previewClassName": "h-[600px!important]"
  }
}