{"version":3,"file":"MCPAppsActivityRenderer.mjs","names":[],"sources":["../../src/components/MCPAppsActivityRenderer.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useEffect, useRef, useState, useCallback } from \"react\";\nimport { z } from \"zod\";\nimport type { AbstractAgent, RunAgentResult } from \"@ag-ui/client\";\n\n// Protocol version supported\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n// Build sandbox proxy HTML with optional extra CSP domains from resource metadata\nfunction buildSandboxHTML(extraCspDomains?: string[]): string {\n  const baseScriptSrc =\n    \"'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*\";\n  const baseFrameSrc = \"* blob: data: http://localhost:* https://localhost:*\";\n  const extra = extraCspDomains?.length ? \" \" + extraCspDomains.join(\" \") : \"\";\n  const scriptSrc = baseScriptSrc + extra;\n  const frameSrc = baseFrameSrc + extra;\n\n  return `<!doctype html>\n<html>\n<head>\n<meta charset=\"utf-8\" />\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'self'; img-src * data: blob: 'unsafe-inline'; media-src * blob: data:; font-src * blob: data:; script-src ${scriptSrc}; style-src * blob: data: 'unsafe-inline'; connect-src *; frame-src ${frameSrc}; base-uri 'self';\" />\n<style>html,body{margin:0;padding:0;height:100%;width:100%;overflow:hidden}*{box-sizing:border-box}iframe{background-color:transparent;border:none;padding:0;overflow:hidden;width:100%;height:100%}</style>\n</head>\n<body>\n<script>\nif(window.self===window.top){throw new Error(\"This file must be used in an iframe.\")}\nconst inner=document.createElement(\"iframe\");\ninner.style=\"width:100%;height:100%;border:none;\";\ninner.setAttribute(\"sandbox\",\"allow-scripts allow-same-origin allow-forms\");\ndocument.body.appendChild(inner);\nwindow.addEventListener(\"message\",async(event)=>{\nif(event.source===window.parent){\nif(event.data&&event.data.method===\"ui/notifications/sandbox-resource-ready\"){\nconst{html,sandbox}=event.data.params;\nif(typeof sandbox===\"string\")inner.setAttribute(\"sandbox\",sandbox);\nif(typeof html===\"string\")inner.srcdoc=html;\n}else if(inner&&inner.contentWindow){\ninner.contentWindow.postMessage(event.data,\"*\");\n}\n}else if(event.source===inner.contentWindow){\nwindow.parent.postMessage(event.data,\"*\");\n}\n});\nwindow.parent.postMessage({jsonrpc:\"2.0\",method:\"ui/notifications/sandbox-proxy-ready\",params:{}},\"*\");\n</script>\n</body>\n</html>`;\n}\n\n/**\n * Queue for serializing MCP app requests to an agent.\n * Ensures requests wait for the agent to stop running and are processed one at a time.\n */\nclass MCPAppsRequestQueue {\n  private queues = new Map<\n    string,\n    Array<{\n      execute: () => Promise<RunAgentResult>;\n      resolve: (result: RunAgentResult) => void;\n      reject: (error: Error) => void;\n    }>\n  >();\n  private processing = new Map<string, boolean>();\n\n  /**\n   * Add a request to the queue for a specific agent thread.\n   * Returns a promise that resolves when the request completes.\n   */\n  async enqueue(\n    agent: AbstractAgent,\n    request: () => Promise<RunAgentResult>,\n  ): Promise<RunAgentResult> {\n    const threadId = agent.threadId || \"default\";\n\n    return new Promise((resolve, reject) => {\n      // Get or create queue for this thread\n      let queue = this.queues.get(threadId);\n      if (!queue) {\n        queue = [];\n        this.queues.set(threadId, queue);\n      }\n\n      // Add request to queue\n      queue.push({ execute: request, resolve, reject });\n\n      // Start processing if not already running\n      this.processQueue(threadId, agent);\n    });\n  }\n\n  private async processQueue(\n    threadId: string,\n    agent: AbstractAgent,\n  ): Promise<void> {\n    // If already processing this queue, return\n    if (this.processing.get(threadId)) {\n      return;\n    }\n\n    this.processing.set(threadId, true);\n\n    try {\n      const queue = this.queues.get(threadId);\n      if (!queue) return;\n\n      while (queue.length > 0) {\n        const item = queue[0]!;\n\n        try {\n          // Wait for any active run to complete before processing\n          await this.waitForAgentIdle(agent);\n\n          // Execute the request\n          const result = await item.execute();\n          item.resolve(result);\n        } catch (error) {\n          item.reject(\n            error instanceof Error ? error : new Error(String(error)),\n          );\n        }\n\n        // Remove processed item\n        queue.shift();\n      }\n    } finally {\n      this.processing.set(threadId, false);\n    }\n  }\n\n  private waitForAgentIdle(agent: AbstractAgent): Promise<void> {\n    return new Promise((resolve) => {\n      if (!agent.isRunning) {\n        resolve();\n        return;\n      }\n\n      let done = false;\n      const finish = () => {\n        if (done) return;\n        done = true;\n        clearInterval(checkInterval);\n        sub.unsubscribe();\n        resolve();\n      };\n\n      const sub = agent.subscribe({\n        onRunFinalized: finish,\n        onRunFailed: finish,\n      });\n\n      // Fallback for reconnect scenarios where events don't fire\n      const checkInterval = setInterval(() => {\n        if (!agent.isRunning) finish();\n      }, 500);\n    });\n  }\n}\n\n// Global queue instance for all MCP app requests\nconst mcpAppsRequestQueue = new MCPAppsRequestQueue();\n\n/**\n * Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType\n */\nexport const MCPAppsActivityType = \"mcp-apps\";\n\n// Zod schema for activity content validation (middleware 0.0.2 format)\nexport const MCPAppsActivityContentSchema = z.object({\n  result: z.object({\n    content: z.array(z.any()).optional(),\n    structuredContent: z.any().optional(),\n    isError: z.boolean().optional(),\n  }),\n  // Resource URI to fetch (e.g., \"ui://server/dashboard\")\n  resourceUri: z.string(),\n  // MD5 hash of server config (renamed from serverId in 0.0.1)\n  serverHash: z.string(),\n  // Optional stable server ID from config (takes precedence over serverHash)\n  serverId: z.string().optional(),\n  // Original tool input arguments\n  toolInput: z.record(z.unknown()).optional(),\n});\n\nexport type MCPAppsActivityContent = z.infer<\n  typeof MCPAppsActivityContentSchema\n>;\n\n// Type for the resource fetched from the server\ninterface FetchedResource {\n  uri: string;\n  mimeType?: string;\n  text?: string;\n  blob?: string;\n  _meta?: {\n    ui?: {\n      prefersBorder?: boolean;\n      csp?: {\n        connectDomains?: string[];\n        resourceDomains?: string[];\n      };\n    };\n  };\n}\n\ninterface JSONRPCRequest {\n  jsonrpc: \"2.0\";\n  id: string | number;\n  method: string;\n  params?: Record<string, unknown>;\n}\n\ninterface JSONRPCResponse {\n  jsonrpc: \"2.0\";\n  id: string | number;\n  result?: unknown;\n  error?: { code: number; message: string };\n}\n\ninterface JSONRPCNotification {\n  jsonrpc: \"2.0\";\n  method: string;\n  params?: Record<string, unknown>;\n}\n\ntype JSONRPCMessage = JSONRPCRequest | JSONRPCResponse | JSONRPCNotification;\n\nfunction isRequest(msg: JSONRPCMessage): msg is JSONRPCRequest {\n  return \"id\" in msg && \"method\" in msg;\n}\n\nfunction isNotification(msg: JSONRPCMessage): msg is JSONRPCNotification {\n  return !(\"id\" in msg) && \"method\" in msg;\n}\n\n/**\n * Props for the activity renderer component\n */\ninterface MCPAppsActivityRendererProps {\n  activityType: string;\n  content: MCPAppsActivityContent;\n  message: unknown; // ActivityMessage from @ag-ui/core\n  agent: AbstractAgent | undefined;\n}\n\n/**\n * MCP Apps Extension Activity Renderer\n *\n * Renders MCP Apps UI in a sandboxed iframe with full protocol support.\n * Fetches resource content on-demand via proxied MCP requests.\n */\nexport const MCPAppsActivityRenderer: React.FC<MCPAppsActivityRendererProps> =\n  function MCPAppsActivityRenderer({ content, agent }) {\n    const containerRef = useRef<HTMLDivElement>(null);\n    const iframeRef = useRef<HTMLIFrameElement | null>(null);\n    const [iframeReady, setIframeReady] = useState(false);\n    const [error, setError] = useState<Error | null>(null);\n    const [isLoading, setIsLoading] = useState(true);\n    const [iframeSize, setIframeSize] = useState<{\n      width?: number;\n      height?: number;\n    }>({});\n    const [fetchedResource, setFetchedResource] =\n      useState<FetchedResource | null>(null);\n\n    // Use refs for values that shouldn't trigger re-renders but need latest values\n    const contentRef = useRef(content);\n    contentRef.current = content;\n\n    // Store agent in a ref for use in async handlers\n    const agentRef = useRef(agent);\n    agentRef.current = agent;\n\n    // Ref to track fetch state - survives StrictMode remounts\n    const fetchStateRef = useRef<{\n      inProgress: boolean;\n      promise: Promise<FetchedResource | null> | null;\n      resourceUri: string | null;\n    }>({ inProgress: false, promise: null, resourceUri: null });\n\n    // Callback to send a message to the iframe\n    const sendToIframe = useCallback((msg: JSONRPCMessage) => {\n      if (iframeRef.current?.contentWindow) {\n        console.log(\"[MCPAppsRenderer] Sending to iframe:\", msg);\n        iframeRef.current.contentWindow.postMessage(msg, \"*\");\n      }\n    }, []);\n\n    // Callback to send a JSON-RPC response\n    const sendResponse = useCallback(\n      (id: string | number, result: unknown) => {\n        sendToIframe({\n          jsonrpc: \"2.0\",\n          id,\n          result,\n        });\n      },\n      [sendToIframe],\n    );\n\n    // Callback to send a JSON-RPC error response\n    const sendErrorResponse = useCallback(\n      (id: string | number, code: number, message: string) => {\n        sendToIframe({\n          jsonrpc: \"2.0\",\n          id,\n          error: { code, message },\n        });\n      },\n      [sendToIframe],\n    );\n\n    // Callback to send a notification\n    const sendNotification = useCallback(\n      (method: string, params?: Record<string, unknown>) => {\n        sendToIframe({\n          jsonrpc: \"2.0\",\n          method,\n          params: params || {},\n        });\n      },\n      [sendToIframe],\n    );\n\n    // Effect 0: Fetch the resource content on mount\n    // Uses ref-based deduplication to handle React StrictMode double-mounting\n    useEffect(() => {\n      const { resourceUri, serverHash, serverId } = content;\n\n      // Check if we already have a fetch in progress for this resource\n      // This handles StrictMode double-mounting - second mount reuses first mount's promise\n      if (\n        fetchStateRef.current.inProgress &&\n        fetchStateRef.current.resourceUri === resourceUri\n      ) {\n        // Reuse the existing promise\n        fetchStateRef.current.promise\n          ?.then((resource) => {\n            if (resource) {\n              setFetchedResource(resource);\n              setIsLoading(false);\n            }\n          })\n          .catch((err) => {\n            setError(err instanceof Error ? err : new Error(String(err)));\n            setIsLoading(false);\n          });\n        return;\n      }\n\n      if (!agent) {\n        setError(new Error(\"No agent available to fetch resource\"));\n        setIsLoading(false);\n        return;\n      }\n\n      // Mark fetch as in progress\n      fetchStateRef.current.inProgress = true;\n      fetchStateRef.current.resourceUri = resourceUri;\n\n      // Create the fetch promise using the queue to serialize requests\n      const fetchPromise = (async (): Promise<FetchedResource | null> => {\n        try {\n          // Use queue to wait for agent to be idle and serialize requests\n          const runResult = await mcpAppsRequestQueue.enqueue(agent, () =>\n            agent.runAgent({\n              forwardedProps: {\n                __proxiedMCPRequest: {\n                  serverHash,\n                  serverId, // optional, takes precedence if provided\n                  method: \"resources/read\",\n                  params: { uri: resourceUri },\n                },\n              },\n            }),\n          );\n\n          // Extract resource from result\n          // The response format is: { contents: [{ uri, mimeType, text?, blob?, _meta? }] }\n          const resultData = runResult.result as\n            | { contents?: FetchedResource[] }\n            | undefined;\n          const resource = resultData?.contents?.[0];\n\n          if (!resource) {\n            throw new Error(\"No resource content in response\");\n          }\n\n          return resource;\n        } catch (err) {\n          console.error(\"[MCPAppsRenderer] Failed to fetch resource:\", err);\n          throw err;\n        } finally {\n          // Mark fetch as complete\n          fetchStateRef.current.inProgress = false;\n        }\n      })();\n\n      // Store the promise for potential reuse\n      fetchStateRef.current.promise = fetchPromise;\n\n      // Handle the result\n      fetchPromise\n        .then((resource) => {\n          if (resource) {\n            setFetchedResource(resource);\n            setIsLoading(false);\n          }\n        })\n        .catch((err) => {\n          setError(err instanceof Error ? err : new Error(String(err)));\n          setIsLoading(false);\n        });\n\n      // No cleanup needed - we want the fetch to complete even if StrictMode unmounts\n    }, [agent, content]);\n\n    // Effect 1: Setup sandbox proxy iframe and communication (after resource is fetched)\n    useEffect(() => {\n      // Wait for resource to be fetched\n      if (isLoading || !fetchedResource) {\n        return;\n      }\n\n      // Capture container reference at effect start (refs are cleared during unmount)\n      const container = containerRef.current;\n      if (!container) {\n        return;\n      }\n\n      let mounted = true;\n      let messageHandler: ((event: MessageEvent) => void) | null = null;\n      let initialListener: ((event: MessageEvent) => void) | null = null;\n      let createdIframe: HTMLIFrameElement | null = null;\n\n      const setup = async () => {\n        try {\n          // Create sandbox proxy iframe\n          const iframe = document.createElement(\"iframe\");\n          createdIframe = iframe; // Track for cleanup\n          iframe.style.width = \"100%\";\n          iframe.style.height = \"100px\"; // Start small, will be resized by size-changed notification\n          iframe.style.border = \"none\";\n          iframe.style.backgroundColor = \"transparent\";\n          iframe.style.display = \"block\";\n          iframe.setAttribute(\n            \"sandbox\",\n            \"allow-scripts allow-same-origin allow-forms\",\n          );\n\n          // Wait for sandbox proxy to be ready\n          const sandboxReady = new Promise<void>((resolve) => {\n            initialListener = (event: MessageEvent) => {\n              if (event.source === iframe.contentWindow) {\n                if (\n                  event.data?.method === \"ui/notifications/sandbox-proxy-ready\"\n                ) {\n                  if (initialListener) {\n                    window.removeEventListener(\"message\", initialListener);\n                    initialListener = null;\n                  }\n                  resolve();\n                }\n              }\n            };\n            window.addEventListener(\"message\", initialListener);\n          });\n\n          // Check mounted before adding to DOM (handles StrictMode double-mount)\n          if (!mounted) {\n            if (initialListener) {\n              window.removeEventListener(\"message\", initialListener);\n              initialListener = null;\n            }\n            return;\n          }\n\n          // Build sandbox HTML with CSP domains from resource metadata\n          const cspDomains = fetchedResource._meta?.ui?.csp?.resourceDomains;\n          iframe.srcdoc = buildSandboxHTML(cspDomains);\n          iframeRef.current = iframe;\n          container.appendChild(iframe);\n\n          // Wait for sandbox proxy to signal ready\n          await sandboxReady;\n          if (!mounted) return;\n\n          console.log(\"[MCPAppsRenderer] Sandbox proxy ready\");\n\n          // Setup message handler for JSON-RPC messages from the inner iframe\n          messageHandler = async (event: MessageEvent) => {\n            if (event.source !== iframe.contentWindow) return;\n\n            const msg = event.data as JSONRPCMessage;\n            if (!msg || typeof msg !== \"object\" || msg.jsonrpc !== \"2.0\")\n              return;\n\n            console.log(\"[MCPAppsRenderer] Received from iframe:\", msg);\n\n            // Handle requests (need response)\n            if (isRequest(msg)) {\n              switch (msg.method) {\n                case \"ui/initialize\": {\n                  // Respond with host capabilities\n                  sendResponse(msg.id, {\n                    protocolVersion: PROTOCOL_VERSION,\n                    hostInfo: {\n                      name: \"CopilotKit MCP Apps Host\",\n                      version: \"1.0.0\",\n                    },\n                    hostCapabilities: {\n                      openLinks: {},\n                      logging: {},\n                    },\n                    hostContext: {\n                      theme: \"light\",\n                      platform: \"web\",\n                    },\n                  });\n                  break;\n                }\n\n                case \"ui/message\": {\n                  // Add message to CopilotKit chat\n                  const currentAgent = agentRef.current;\n\n                  if (!currentAgent) {\n                    console.warn(\n                      \"[MCPAppsRenderer] ui/message: No agent available\",\n                    );\n                    sendResponse(msg.id, { isError: false });\n                    break;\n                  }\n\n                  try {\n                    const params = msg.params as {\n                      role?: string;\n                      content?: Array<{ type: string; text?: string }>;\n                    };\n\n                    // Extract text content from the message\n                    const textContent =\n                      params.content\n                        ?.filter((c) => c.type === \"text\" && c.text)\n                        .map((c) => c.text)\n                        .join(\"\\n\") || \"\";\n\n                    if (textContent) {\n                      currentAgent.addMessage({\n                        id: crypto.randomUUID(),\n                        role: (params.role as \"user\" | \"assistant\") || \"user\",\n                        content: textContent,\n                      });\n                    }\n                    sendResponse(msg.id, { isError: false });\n                  } catch (err) {\n                    console.error(\"[MCPAppsRenderer] ui/message error:\", err);\n                    sendResponse(msg.id, { isError: true });\n                  }\n                  break;\n                }\n\n                case \"ui/open-link\": {\n                  // Open URL in new tab\n                  const url = msg.params?.url as string | undefined;\n                  if (url) {\n                    window.open(url, \"_blank\", \"noopener,noreferrer\");\n                    sendResponse(msg.id, { isError: false });\n                  } else {\n                    sendErrorResponse(msg.id, -32602, \"Missing url parameter\");\n                  }\n                  break;\n                }\n\n                case \"tools/call\": {\n                  // Proxy tool call to MCP server via agent.runAgent()\n                  const { serverHash, serverId } = contentRef.current;\n                  const currentAgent = agentRef.current;\n\n                  if (!serverHash) {\n                    sendErrorResponse(\n                      msg.id,\n                      -32603,\n                      \"No server hash available for proxying\",\n                    );\n                    break;\n                  }\n\n                  if (!currentAgent) {\n                    sendErrorResponse(\n                      msg.id,\n                      -32603,\n                      \"No agent available for proxying\",\n                    );\n                    break;\n                  }\n\n                  try {\n                    // Use queue to wait for agent to be idle and serialize requests\n                    const runResult = await mcpAppsRequestQueue.enqueue(\n                      currentAgent,\n                      () =>\n                        currentAgent.runAgent({\n                          forwardedProps: {\n                            __proxiedMCPRequest: {\n                              serverHash,\n                              serverId, // optional, takes precedence if provided\n                              method: \"tools/call\",\n                              params: msg.params,\n                            },\n                          },\n                        }),\n                    );\n\n                    // The result from runAgent contains the MCP response\n                    sendResponse(msg.id, runResult.result || {});\n                  } catch (err) {\n                    console.error(\"[MCPAppsRenderer] tools/call error:\", err);\n                    sendErrorResponse(msg.id, -32603, String(err));\n                  }\n                  break;\n                }\n\n                default:\n                  sendErrorResponse(\n                    msg.id,\n                    -32601,\n                    `Method not found: ${msg.method}`,\n                  );\n              }\n            }\n\n            // Handle notifications (no response needed)\n            if (isNotification(msg)) {\n              switch (msg.method) {\n                case \"ui/notifications/initialized\": {\n                  console.log(\"[MCPAppsRenderer] Inner iframe initialized\");\n                  if (mounted) {\n                    setIframeReady(true);\n                  }\n                  break;\n                }\n\n                case \"ui/notifications/size-changed\": {\n                  const { width, height } = msg.params || {};\n                  console.log(\"[MCPAppsRenderer] Size change:\", {\n                    width,\n                    height,\n                  });\n                  if (mounted) {\n                    setIframeSize({\n                      width: typeof width === \"number\" ? width : undefined,\n                      height: typeof height === \"number\" ? height : undefined,\n                    });\n                  }\n                  break;\n                }\n\n                case \"notifications/message\": {\n                  // Logging notification from the app\n                  console.log(\"[MCPAppsRenderer] App log:\", msg.params);\n                  break;\n                }\n              }\n            }\n          };\n\n          window.addEventListener(\"message\", messageHandler);\n\n          // Extract HTML content from fetched resource\n          let html: string;\n          if (fetchedResource.text) {\n            html = fetchedResource.text;\n          } else if (fetchedResource.blob) {\n            html = atob(fetchedResource.blob);\n          } else {\n            throw new Error(\"Resource has no text or blob content\");\n          }\n\n          // Send the resource content to the sandbox proxy\n          sendNotification(\"ui/notifications/sandbox-resource-ready\", { html });\n        } catch (err) {\n          console.error(\"[MCPAppsRenderer] Setup error:\", err);\n          if (mounted) {\n            setError(err instanceof Error ? err : new Error(String(err)));\n          }\n        }\n      };\n\n      setup();\n\n      return () => {\n        mounted = false;\n        // Clean up initial listener if still active\n        if (initialListener) {\n          window.removeEventListener(\"message\", initialListener);\n          initialListener = null;\n        }\n        if (messageHandler) {\n          window.removeEventListener(\"message\", messageHandler);\n        }\n        // Remove the iframe we created (using tracked reference, not DOM query)\n        // This works even if containerRef.current is null during unmount\n        if (createdIframe) {\n          createdIframe.remove();\n          createdIframe = null;\n        }\n        iframeRef.current = null;\n      };\n    }, [\n      isLoading,\n      fetchedResource,\n      sendNotification,\n      sendResponse,\n      sendErrorResponse,\n    ]);\n\n    // Effect 2: Update iframe size when it changes\n    useEffect(() => {\n      if (iframeRef.current) {\n        if (iframeSize.width !== undefined) {\n          // Use minWidth with min() to allow expansion but cap at 100%\n          iframeRef.current.style.minWidth = `min(${iframeSize.width}px, 100%)`;\n          iframeRef.current.style.width = \"100%\";\n        }\n        if (iframeSize.height !== undefined) {\n          iframeRef.current.style.height = `${iframeSize.height}px`;\n        }\n      }\n    }, [iframeSize]);\n\n    // Effect 3: Send tool input when iframe ready\n    useEffect(() => {\n      if (iframeReady && content.toolInput) {\n        console.log(\"[MCPAppsRenderer] Sending tool input:\", content.toolInput);\n        sendNotification(\"ui/notifications/tool-input\", {\n          arguments: content.toolInput,\n        });\n      }\n    }, [iframeReady, content.toolInput, sendNotification]);\n\n    // Effect 4: Send tool result when iframe ready\n    useEffect(() => {\n      if (iframeReady && content.result) {\n        console.log(\"[MCPAppsRenderer] Sending tool result:\", content.result);\n        sendNotification(\"ui/notifications/tool-result\", content.result);\n      }\n    }, [iframeReady, content.result, sendNotification]);\n\n    // Determine border styling based on prefersBorder metadata from fetched resource\n    // true = show border/background, false = none, undefined = host decides (we default to none)\n    const prefersBorder = fetchedResource?._meta?.ui?.prefersBorder;\n    const borderStyle =\n      prefersBorder === true\n        ? {\n            borderRadius: \"8px\",\n            backgroundColor: \"#f9f9f9\",\n            border: \"1px solid #e0e0e0\",\n          }\n        : {};\n\n    return (\n      <div\n        ref={containerRef}\n        style={{\n          width: \"100%\",\n          height: iframeSize.height ? `${iframeSize.height}px` : \"auto\",\n          minHeight: \"100px\",\n          overflow: \"hidden\",\n          position: \"relative\",\n          ...borderStyle,\n        }}\n      >\n        {isLoading && (\n          <div style={{ padding: \"1rem\", color: \"#666\" }}>Loading...</div>\n        )}\n        {error && (\n          <div style={{ color: \"red\", padding: \"1rem\" }}>\n            Error: {error.message}\n          </div>\n        )}\n      </div>\n    );\n  };\n"],"mappings":"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"}