{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "firebase-image-generation",
  "type": "registry:block",
  "title": "Firebase Image Generation",
  "description": "A chat interface for generating images using Firebase Gemini with interleaved text and image output",
  "dependencies": [
    "@ai-sdk/react",
    "@mui/material",
    "firebase",
    "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-image-generation/firebase-image-generation.tsx",
      "target": "src/mui-treasury/firebase/firebase-image-generation/firebase-image-generation.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 { ResponseModality } from 'firebase/ai';\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  PromptInputBody,\n  type PromptInputMessage,\n  PromptInputSubmit,\n  PromptInputTextarea,\n  PromptInputToolbar,\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  'Draw a cute cat wearing a space helmet',\n  'Create a watercolor painting of a sunset over mountains',\n  'Generate an illustration of a cozy coffee shop interior',\n  \"Design a logo for a tech startup called 'NexGen'\",\n];\n\nexport default function FirebaseImageGeneration() {\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-image',\n              generationConfig: {\n                responseModalities: [\n                  ResponseModality.TEXT,\n                  ResponseModality.IMAGE,\n                ],\n              },\n            },\n          })\n        : null,\n    [],\n  );\n\n  const { messages, status, error, sendMessage, stop, regenerate } =\n    useChat<UIMessage>({\n      id: 'firebase-image-generation',\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\n    if (hasText) {\n      sendMessage({ text: message.text! });\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 (${\n          err instanceof Error ? err.message : 'Unknown error'\n        })`,\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                  Image Generation\n                </Typography>\n                <Typography\n                  sx={{\n                    color: 'text.secondary',\n                  }}\n                >\n                  Describe an image you&apos;d like to generate\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=\"Generated 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                          Generating 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                  Generating...\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}>\n        <PromptInputBody>\n          <PromptInputTextarea\n            placeholder=\"Describe the image you want to generate...\"\n            value={inputValue}\n            onChange={(e) => setInputValue(e.target.value)}\n            disabled={status === 'submitted' || error != null}\n          />\n        </PromptInputBody>\n        <PromptInputToolbar>\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-image-generation/index.ts",
      "target": "src/mui-treasury/firebase/firebase-image-generation/index.ts",
      "content": "export * from './firebase-image-generation';\nexport { default as FirebaseImageGeneration } from './firebase-image-generation';\n",
      "type": "registry:item"
    }
  ],
  "meta": {
    "category": "ai",
    "subcategory": "firebase",
    "previewClassName": "h-[600px!important]"
  }
}