@frankieai/react 0.2.19 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/FrankieProvider.tsx","../src/lib/identity.ts","../src/lib/client.ts","../src/lib/theme-inherit.ts","../src/hooks/useFrankieChat.ts","../src/lib/messages.ts","../src/components/chat/FrankieWidget.tsx","../src/lib/styles-ready.ts","../src/generated/css-content.ts","../src/lib/ensure-frankie-styles.ts","../src/lib/bottom-bars.ts","../src/lib/theme.ts","../src/lib/utils.ts","../src/components/ui/button.tsx","../src/components/chat/ChatPanel.tsx","../src/components/ui/avatar.tsx","../src/components/ui/card.tsx","../src/components/ui/scroll-area.tsx","../src/components/chat/AgentContext.tsx","../src/components/chat/ChatEmptyState.tsx","../src/components/chat/Composer.tsx","../src/components/ui/textarea.tsx","../src/components/chat/MessageBubble.tsx","../src/lib/choices.ts","../src/components/chat/Choices.tsx","../src/components/chat/Markdown.tsx","../src/components/chat/ToolActivityList.tsx","../src/components/ui/badge.tsx","../src/components/chat/FrankieInline.tsx","../src/components/chat/FrankieChatPage.tsx","../src/lib/viewport-fill.ts","../src/components/chat/SuggestedPrompts.tsx","../src/components/ui/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { PublicWidgetConfig, WidgetTheme } from \"@frankieai/core\";\nimport { FrankieClient, type FrankieClientOptions, type PageContext } from \"../lib/client.js\";\nimport { inferHostTheme, watchHostTheme } from \"../lib/theme-inherit.js\";\nimport { FrankieChatContext, useFrankieChatState } from \"./useFrankieChat.js\";\n\nexport interface FrankieProviderProps extends FrankieClientOptions {\n children: ReactNode;\n context?: PageContext;\n theme?: WidgetTheme;\n config?: PublicWidgetConfig;\n /**\n * Set to `false` if the host compiles Frankie's classes itself instead of\n * loading `@frankieai/react/styles.css` — e.g. a Tailwind v4 host that\n * `@source`s this package — so it isn't given a second, independently-themed\n * copy of the stylesheet when its own build never sets the styles-ready\n * sentinel. Defaults to `true`: a host that simply forgot the import, or\n * whose CSS chunk hasn't landed yet, still gets a correctly-styled widget.\n */\n injectStyles?: boolean;\n}\n\ninterface FrankieContextValue {\n client: FrankieClient;\n config?: PublicWidgetConfig;\n configError?: string;\n loading: boolean;\n context?: PageContext;\n theme?: WidgetTheme;\n injectStyles: boolean;\n /** The host page's colors, sampled once for \"inherit my site's theme\" (null: not inheriting, or unreadable). */\n hostTheme: Partial<WidgetTheme> | null;\n /**\n * True once the chat knows its colors: the config (or its error) has arrived\n * and, when inheriting, the host page has been sampled. Until then the chat\n * stays invisible, so a visitor in OS dark mode never sees a black flash on\n * a light site before the real theme lands.\n */\n themeReady: boolean;\n}\n\n/** Layout effects warn on the server; there's nothing to paint there anyway. */\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nconst FrankieContext = createContext<FrankieContextValue | null>(null);\n\nexport function FrankieProvider({\n children,\n context,\n theme,\n config: providedConfig,\n injectStyles = true,\n ...clientOptions\n}: FrankieProviderProps) {\n const tokenCallback = useRef(clientOptions.getUserToken);\n tokenCallback.current = clientOptions.getUserToken;\n const client = useMemo(\n () => new FrankieClient({ ...clientOptions, ...(clientOptions.getUserToken ? { getUserToken: () => tokenCallback.current?.() } : {}) }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientOptions.baseUrl, clientOptions.publishableKey, clientOptions.storageNamespace, clientOptions.conversationId, clientOptions.persist, clientOptions.fetchImpl, clientOptions.api?.baseUrl, clientOptions.api?.credentials, Boolean(clientOptions.getUserToken)],\n );\n\n const [config, setConfig] = useState<PublicWidgetConfig | undefined>(providedConfig);\n const [configError, setConfigError] = useState<string | undefined>(undefined);\n const [loading, setLoading] = useState(!providedConfig);\n\n // The widget config is the workspace's, not the conversation's: fetch it\n // once per workspace. Fetching it again whenever the client was rebuilt for\n // another conversation flipped `loading` back on, and the panel replaced the\n // transcript with \"Starting Frankie…\" for a moment on every history switch.\n const configKey = `${clientOptions.baseUrl ?? \"\"}|${clientOptions.publishableKey}`;\n const clientRef = useRef(client);\n clientRef.current = client;\n useEffect(() => {\n if (providedConfig) return;\n const abort = new AbortController();\n setLoading(true);\n clientRef.current\n .getConfig(abort.signal)\n .then((next) => {\n setConfig(next);\n setConfigError(undefined);\n })\n .catch((error: unknown) => {\n if (abort.signal.aborted) return;\n setConfigError(error instanceof Error ? error.message : \"Could not load the assistant.\");\n })\n .finally(() => {\n if (!abort.signal.aborted) setLoading(false);\n });\n return () => abort.abort();\n }, [configKey, providedConfig]);\n\n // \"Inherit my site's theme\": sample the host page once, here, and share it.\n // Each chat component sampling on its own mount read the page at different\n // moments — the panel, opening later, found Frankie's own (fallback-colored)\n // send button and inherited itself while the launcher had the site's color.\n // Sampling reads the live DOM, so it runs post-mount and never on the server.\n const inheriting = (theme?.source ?? config?.theme?.source) === \"inherit\";\n const [hostTheme, setHostTheme] = useState<Partial<WidgetTheme> | null>(null);\n const [hostSampled, setHostSampled] = useState(false);\n // Before paint, so the first frame already has the site's colors.\n useIsomorphicLayoutEffect(() => {\n if (!inheriting) { setHostTheme(null); return; }\n // Keep the previous object when nothing changed, so a re-sample that finds\n // the same colors doesn't re-render every chat component.\n const sample = () => setHostTheme((previous) => {\n const next = inferHostTheme();\n return JSON.stringify(previous) === JSON.stringify(next) ? previous : next;\n });\n sample();\n setHostSampled(true);\n // Follow the host when it switches between its own light and dark themes.\n return watchHostTheme(sample);\n }, [inheriting]);\n\n const value = useMemo<FrankieContextValue>(\n () => ({\n client,\n ...(config ? { config } : {}),\n ...(configError ? { configError } : {}),\n loading,\n ...(context ? { context } : {}),\n ...(theme ? { theme } : {}),\n injectStyles,\n hostTheme,\n themeReady: Boolean(config || configError) && (!inheriting || hostSampled),\n }),\n [client, config, configError, loading, context, theme, injectStyles, hostTheme, inheriting, hostSampled],\n );\n\n return <FrankieContext.Provider value={value}><ConversationState key={client.namespace + \":\" + (client.conversationId ?? \"\")}>{children}</ConversationState></FrankieContext.Provider>;\n}\n\nfunction ConversationState({ children }: { children: ReactNode }) {\n const chat = useFrankieChatState();\n return <FrankieChatContext.Provider value={chat}>{children}</FrankieChatContext.Provider>;\n}\n\nexport function useFrankie(): FrankieContextValue {\n const value = useContext(FrankieContext);\n if (!value) throw new Error(\"Frankie components must be rendered inside <FrankieProvider>.\");\n return value;\n}\n\nexport function useFrankieTheme(): WidgetTheme | undefined {\n const { config, theme, hostTheme: inferred } = useFrankie();\n const merged = useMemo<WidgetTheme | undefined>(() => ({ ...config?.theme, ...theme }), [config?.theme, theme]);\n const inheriting = merged?.source === \"inherit\";\n return useMemo(() => {\n if (!inheriting || !inferred) return merged;\n // Detection succeeded: the host's own colors replace the configured\n // palette, which stays as the fallback already folded into `merged`\n // wherever a specific inferred value is missing.\n return { ...merged, ...inferred };\n }, [merged, inheriting, inferred]);\n}\n","/**\n * The stable identity a host-signed user token carries, used only to decide\n * whether a cached chat session still belongs to the same person.\n *\n * The client never verifies the token — it has no secret, and the server\n * re-verifies on every `/v1/session` anyway. It only needs a key that stays\n * constant while the same user's token is re-minted. A JWT's `sub` is that\n * key: hosts mint with `setIssuedAt()`, so the raw string changes every\n * second even though nothing about the user did.\n *\n * Anything that isn't a readable JWT falls back to the raw token, which is\n * exactly the previous behaviour — an opaque token that changes still resets\n * the session, so this can only ever reuse a session, never leak one.\n */\nexport function sessionIdentity(token: string | undefined): string | undefined {\n if (!token) return undefined;\n return jwtSubject(token) ?? token;\n}\n\nfunction jwtSubject(token: string): string | undefined {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[1]) return undefined;\n try {\n const payload = JSON.parse(base64UrlDecode(parts[1])) as { sub?: unknown };\n return typeof payload.sub === \"string\" && payload.sub ? payload.sub : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction base64UrlDecode(segment: string): string {\n const base64 = segment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n if (typeof atob === \"function\") {\n const bytes = atob(padded);\n return decodeURIComponent(Array.from(bytes, (c) => \"%\" + c.charCodeAt(0).toString(16).padStart(2, \"0\")).join(\"\"));\n }\n return Buffer.from(padded, \"base64\").toString(\"utf8\");\n}\n","import type {\n ChatAttachment,\n ChatRequestBody,\n CreateSessionResponse,\n FrankieServerEvent,\n HostToolRequest,\n HostToolResult,\n PublicWidgetConfig,\n} from \"@frankieai/core\";\nimport { sessionIdentity } from \"./identity.js\";\n\nexport type PageContext = Record<string, string | number | boolean | null>;\n\n/**\n * How the widget reaches the host's own API for client-side tool execution.\n *\n * When a workspace marks an API source `execution: \"client\"`, Frankie asks the\n * browser to make the call itself — from the user's existing session — instead\n * of calling the API from Frankie's servers. This says where that API lives and\n * how the browser authenticates to it: normally the app's own cookies\n * (`credentials: \"include\"` on a same-origin or CORS-allowed API), or a bearer\n * token the app already holds, added via `headers`. Frankie sends no credential\n * of its own; the host API authorizes the call exactly as it would any request\n * from the host's frontend.\n */\nexport interface HostApiClientOptions {\n /** Base URL that operation paths resolve against, e.g. \"/api\" or \"https://api.acme.com\". */\n baseUrl: string;\n /** fetch credentials mode. \"include\" sends the app's cookies. Defaults to \"same-origin\". */\n credentials?: RequestCredentials;\n /** Extra headers for every host call (e.g. an app access token). Awaited per call. */\n headers?: () => Promise<Record<string, string>> | Record<string, string>;\n}\n\n/**\n * The hosted Frankie data plane. Embedding needs only a publishable key, so\n * `baseUrl` defaults here — override it only when self-hosting the server.\n */\nexport const FRANKIE_CLOUD_URL = \"https://frankie-control-665333762719.us-central1.run.app\";\n\nexport interface FrankieClientOptions {\n /** Where the Frankie server is mounted. Defaults to the hosted cloud. */\n baseUrl?: string;\n /** Safe to ship to the browser; the origin allowlist is what authorises it. */\n publishableKey: string;\n /**\n * Fetches a short-lived JWT for the signed-in user from the host's own\n * backend. Called before each turn; return undefined after sign-out.\n * Omit only for tenants that allow anonymous chat.\n */\n getUserToken?: () => Promise<string | undefined> | string | undefined;\n fetchImpl?: typeof fetch;\n /**\n * Reaches the host's own API for `execution: \"client\"` tool calls (see\n * `HostApiClientOptions`). Required only for workspaces that use client-side\n * execution; omit it and a client-mode tool reports that no host API is\n * configured rather than calling anything.\n */\n api?: HostApiClientOptions;\n /**\n * Remember the session (and, in the hook, the transcript) in localStorage so a\n * page reload resumes the same anonymous conversation. Authenticated chat\n * never writes tokens or transcripts to localStorage; it uses server history.\n */\n persist?: boolean;\n /** Override localStorage namespace for session and transcript. */\n storageNamespace?: string;\n /** Stable id for a server-backed conversation. */\n conversationId?: string;\n}\n\nexport interface ChatConversation {\n id: string;\n title: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport class FrankieApiError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly code: string,\n ) {\n super(message);\n }\n}\n\nexport class FrankieClient {\n private readonly baseUrl: string;\n private readonly doFetch: typeof fetch;\n private session?: CreateSessionResponse;\n /** Identity (JWT `sub`, else the raw token) the cached session was issued for. */\n private sessionUserKey?: string;\n\n constructor(private readonly options: FrankieClientOptions) {\n this.baseUrl = (options.baseUrl ?? FRANKIE_CLOUD_URL).replace(/\\/+$/, \"\");\n this.doFetch = options.fetchImpl ?? globalThis.fetch.bind(globalThis);\n this.session = this.readStored();\n if (options.getUserToken && typeof window !== \"undefined\") {\n try { for (const suffix of [\"session\", \"messages\"]) window.localStorage.removeItem(`${this.namespace}.${suffix}`); } catch { /* storage may be unavailable */ }\n }\n }\n\n get sessionId(): string | undefined {\n return this.session?.sessionId;\n }\n\n get conversationId(): string | undefined {\n return this.options.conversationId;\n }\n\n /** Whether this client persists across reloads (browser only, opt-out via `persist:false`). */\n get persist(): boolean {\n // Authenticated history comes from the identity-checked server. Do not leave\n // a previous user's bearer token or transcript in shared browser storage.\n return !this.options.getUserToken && this.options.persist !== false && typeof window !== \"undefined\";\n }\n\n /** localStorage namespace for this tenant/conversation, shared with the chat hook. */\n get namespace(): string {\n return this.options.storageNamespace ?? `frankie.${this.options.publishableKey}`;\n }\n\n private readStored(): CreateSessionResponse | undefined {\n if (!this.persist) return undefined;\n try {\n const raw = window.localStorage.getItem(`${this.namespace}.session`);\n if (!raw) return undefined;\n const session = JSON.parse(raw) as CreateSessionResponse;\n return new Date(session.expiresAt).getTime() > Date.now() + 30_000 ? session : undefined;\n } catch {\n return undefined;\n }\n }\n\n private writeStored(): void {\n if (!this.persist) return;\n try {\n if (this.session) window.localStorage.setItem(`${this.namespace}.session`, JSON.stringify(this.session));\n else window.localStorage.removeItem(`${this.namespace}.session`);\n } catch {\n /* storage may be unavailable (private mode); the chat still works in memory */\n }\n }\n\n async getConfig(signal?: AbortSignal): Promise<PublicWidgetConfig> {\n const response = await this.doFetch(`${this.baseUrl}/v1/config`, {\n headers: { \"x-frankie-key\": this.options.publishableKey },\n ...(signal ? { signal } : {}),\n });\n return (await this.unwrap(response)) as PublicWidgetConfig;\n }\n\n async ensureSession(context?: PageContext): Promise<CreateSessionResponse> {\n // The host's token endpoint knows why it failed (not signed in, secret\n // missing, lookup not wired); a thrown Error carries that reason to the user.\n let userToken: string | undefined;\n try {\n userToken = await this.options.getUserToken?.();\n } catch (error) {\n this.resetSession();\n const reason = error instanceof Error && error.message.trim() ? error.message.trim() : undefined;\n throw new FrankieApiError(\n reason ?? \"Your signed-in Frankie session could not be verified. Refresh the page and sign in again.\",\n 401,\n \"user_token_unavailable\",\n );\n }\n if (this.options.getUserToken && !userToken) {\n this.resetSession();\n throw new FrankieApiError(\n \"Your signed-in Frankie session could not be verified. Refresh the page and sign in again.\",\n 401,\n \"user_token_unavailable\",\n );\n }\n // Reset only when the *person* changed, not when the same person's token\n // was re-minted. Hosts mint a fresh JWT (with `iat`) before every turn, so\n // comparing raw bytes threw the session away on every message and started\n // a new, empty transcript each time — the agent never saw the last turn.\n // Sign-out is handled above; a different user resets here as before.\n const userKey = sessionIdentity(userToken);\n if (userKey !== this.sessionUserKey) this.resetSession();\n if (this.session && new Date(this.session.expiresAt).getTime() > Date.now() + 30_000) return this.session;\n const response = await this.doFetch(`${this.baseUrl}/v1/session`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"x-frankie-key\": this.options.publishableKey },\n body: JSON.stringify({\n ...(userToken ? { userToken } : {}),\n ...(context ? { context } : {}),\n ...(this.options.conversationId ? { conversationId: this.options.conversationId } : {}),\n }),\n });\n this.session = (await this.unwrap(response)) as CreateSessionResponse;\n this.sessionUserKey = userKey;\n this.writeStored();\n return this.session;\n }\n\n async listConversations(): Promise<ChatConversation[]> {\n const userToken = await this.requireUserToken();\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations`, {\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${userToken}` },\n });\n const body = (await this.unwrap(response)) as { conversations?: ChatConversation[] };\n return Array.isArray(body.conversations) ? body.conversations : [];\n }\n\n /** Whether a conversation can be reloaded from the server (a signed-in user and a conversation id). */\n get canResume(): boolean {\n return Boolean(this.options.conversationId && this.options.getUserToken);\n }\n\n /**\n * Ask the server to stop the running turn. Closing the stream no longer\n * stops it (a closed tab shouldn't lose the reply), so Stop says so\n * explicitly. Best effort: the stream is closed right after either way.\n */\n async stopTurn(): Promise<void> {\n if (!this.session) return;\n await this.doFetch(`${this.baseUrl}/v1/chat/stop`, {\n method: \"POST\",\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${this.session.sessionToken}` },\n }).catch(() => undefined);\n }\n\n async resumeConversation(id = this.options.conversationId): Promise<{ messages: unknown[]; title: string; busy: boolean } | undefined> {\n if (!id || !this.options.getUserToken) return undefined;\n const userToken = await this.requireUserToken();\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations/${encodeURIComponent(id)}/resume`, {\n method: \"POST\",\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${userToken}` },\n });\n if (response.status === 404) return undefined;\n const body = (await this.unwrap(response)) as { session: CreateSessionResponse; messages?: unknown[]; title?: string; busy?: boolean };\n this.session = body.session;\n this.sessionUserKey = sessionIdentity(userToken);\n this.writeStored();\n return { messages: Array.isArray(body.messages) ? body.messages : [], title: body.title ?? \"New chat\", busy: body.busy === true };\n }\n\n async syncConversation(messages: unknown[], title?: string): Promise<void> {\n if (!this.session || !this.options.conversationId) return;\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations/${encodeURIComponent(this.options.conversationId)}`, {\n method: \"PUT\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-frankie-key\": this.options.publishableKey,\n authorization: `Bearer ${this.session.sessionToken}`,\n },\n body: JSON.stringify({ messages, ...(title ? { title } : {}) }),\n });\n await this.unwrap(response);\n }\n\n async deleteConversation(id: string): Promise<void> {\n const userToken = await this.requireUserToken();\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${userToken}` },\n });\n if (response.status !== 404) await this.unwrap(response);\n }\n\n private async requireUserToken(): Promise<string> {\n const token = await this.options.getUserToken?.();\n if (!token) throw new FrankieApiError(\"Sign in to access your chat history.\", 401, \"unauthorized\");\n return token;\n }\n\n resetSession(): void {\n this.session = undefined;\n this.writeStored();\n }\n\n async send(\n input: {\n message: string;\n context?: PageContext;\n model?: string;\n attachments?: ChatAttachment[];\n signal?: AbortSignal;\n },\n onEvent: (event: FrankieServerEvent) => void,\n ): Promise<void> {\n const session = await this.ensureSession(input.context);\n const body: ChatRequestBody = {\n sessionId: session.sessionId,\n message: input.message,\n ...(input.context ? { context: input.context } : {}),\n ...(input.model ? { model: input.model } : {}),\n ...(input.attachments?.length ? { attachments: input.attachments } : {}),\n };\n\n const response = await this.doFetch(`${this.baseUrl}/v1/chat`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-frankie-key\": this.options.publishableKey,\n authorization: `Bearer ${session.sessionToken}`,\n },\n body: JSON.stringify(body),\n ...(input.signal ? { signal: input.signal } : {}),\n });\n\n if (!response.ok || !response.body) {\n if (response.status === 401 || response.status === 404) this.resetSession();\n await this.unwrap(response);\n return;\n }\n\n for await (const event of readEventStream(response.body)) {\n // A client-side tool call is handled here, not surfaced to the UI: run it\n // against the host API on the user's own session and POST the outcome\n // back. It runs detached so reading the stream never blocks on a host call\n // (the server's agent loop is what waits, via the tool-result endpoint).\n if (event.type === \"tool_call_request\") {\n void this.runClientTool(session, event.callId, event.request);\n continue;\n }\n onEvent(event);\n }\n }\n\n /**\n * Execute one host API call in the browser and report the outcome back to the\n * server. Never throws: a transport failure or a missing `api` client becomes\n * a result the model can see and explain, exactly like a non-2xx status.\n */\n private async runClientTool(\n session: CreateSessionResponse,\n callId: string,\n request: HostToolRequest,\n ): Promise<void> {\n const result = await this.callHostApi(request);\n await this.postToolResult(session, callId, result).catch(() => undefined);\n }\n\n private async callHostApi(request: HostToolRequest): Promise<HostToolResult> {\n const api = this.options.api;\n if (!api) {\n return {\n status: 0,\n error:\n \"This assistant tried to call your API from the browser, but no host API client is configured \" +\n \"in the Frankie SDK (the `api` option).\",\n };\n }\n try {\n const extra = api.headers ? await api.headers() : {};\n const response = await this.doFetch(joinHostUrl(api.baseUrl, request.path), {\n method: request.method,\n credentials: api.credentials ?? \"same-origin\",\n headers: {\n accept: \"application/json, text/plain;q=0.9, */*;q=0.8\",\n ...(request.body !== undefined ? { \"content-type\": \"application/json\" } : {}),\n ...extra,\n },\n ...(request.body !== undefined ? { body: JSON.stringify(request.body) } : {}),\n });\n const body = await response.text().catch(() => \"\");\n return { status: response.status, body };\n } catch (error) {\n return { status: 0, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n private async postToolResult(\n session: CreateSessionResponse,\n callId: string,\n result: HostToolResult,\n ): Promise<void> {\n await this.doFetch(`${this.baseUrl}/v1/chat/tool-result`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-frankie-key\": this.options.publishableKey,\n authorization: `Bearer ${session.sessionToken}`,\n },\n body: JSON.stringify({ callId, result }),\n });\n }\n\n private async unwrap(response: Response): Promise<unknown> {\n if (response.ok) return response.json();\n let code = \"internal\";\n let message = `Request failed with status ${response.status}.`;\n try {\n const body = (await response.json()) as { error?: { code?: string; message?: string } };\n code = body.error?.code ?? code;\n message = body.error?.message ?? message;\n } catch {\n /* keep the default message */\n }\n throw new FrankieApiError(message, response.status, code);\n }\n}\n\n/**\n * Join the host's configured base URL with a server-supplied relative path. The\n * server always sends a relative path (params interpolated, no scheme or host),\n * and this refuses anything that looks absolute or protocol-relative — so a tool\n * call can never be steered to a different origin than the one the host set.\n */\nexport function joinHostUrl(baseUrl: string, path: string): string {\n if (/^[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith(\"//\")) {\n throw new Error(\"A client tool path must be relative to the configured host API base URL.\");\n }\n return `${baseUrl.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nexport async function* readEventStream(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<FrankieServerEvent> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n let split = buffer.indexOf(\"\\n\\n\");\n while (split !== -1) {\n const frame = buffer.slice(0, split);\n buffer = buffer.slice(split + 2);\n const event = parseFrame(frame);\n if (event) yield event;\n split = buffer.indexOf(\"\\n\\n\");\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction parseFrame(frame: string): FrankieServerEvent | null {\n const data = frame\n .split(\"\\n\")\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).replace(/^ /, \"\"))\n .join(\"\\n\");\n if (!data) return null;\n try {\n return JSON.parse(data) as FrankieServerEvent;\n } catch {\n return null;\n }\n}\n","import type { WidgetTheme } from \"@frankieai/core\";\n\ntype Rgba = [number, number, number, number];\n\n/** Computed colors always come back as `rgb()`/`rgba()`, never a hex or name. */\nfunction parseRgb(value: string): Rgba | null {\n const match = value.match(/rgba?\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)/i);\n if (!match) return null;\n const alpha = match[4] === undefined ? 1 : match[4].endsWith(\"%\") ? Number(match[4].slice(0, -1)) / 100 : Number(match[4]);\n return [Number(match[1]), Number(match[2]), Number(match[3]), alpha];\n}\n\nfunction toCss([r, g, b, a]: Rgba): string {\n return a >= 1 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\nfunction sameColor(a: Rgba, b: Rgba): boolean {\n return Math.abs(a[0] - b[0]) < 12 && Math.abs(a[1] - b[1]) < 12 && Math.abs(a[2] - b[2]) < 12;\n}\n\n/** Relative luminance (WCAG), 0 = black, 1 = white. */\nfunction luminance([r, g, b]: Rgba): number {\n const channel = (c: number) => {\n const v = c / 255;\n return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);\n}\n\nfunction contrastRatio(a: Rgba, b: Rgba): number {\n const la = luminance(a), lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/**\n * Elements likely to carry a site's own accent color, checked in order.\n * `data-frankie-accent` is an explicit opt-in hook a host can add to any\n * element (e.g. their own primary button) to make detection reliable instead\n * of guessed; everything after it is a heuristic guess at a \"branded\"\n * element, weakest last.\n */\nconst ACCENT_SELECTORS = [\"button[type=submit]\", \"a[href]\", \"button\", \"[class*=primary]\", \"[class*=btn]\"];\n\n/**\n * Brand-color custom properties, in the same order the dashboard's\n * server-side preview reads them (`inferTheme` in @frankieai/core), so the\n * live widget and the Appearance preview agree.\n */\nconst PRIMARY_VARS = [\"--primary\", \"--color-primary\", \"--brand\", \"--color-brand\", \"--accent\", \"--color-accent\"];\n\n/**\n * Frankie's own DOM is never \"the host's\" color: once the chat is open, its\n * send button (a `button[type=submit]` filled with the fallback accent) would\n * otherwise be the first match and the widget would inherit itself.\n */\nconst OWN_DOM = \".frankie-root, [data-frankie-ignore]\";\n\nfunction firstHostElement(selector: string): Element | null {\n for (const element of document.querySelectorAll(selector)) if (!element.closest(OWN_DOM)) return element;\n return null;\n}\n\n/** Resolve any CSS color (hex, hsl, oklch, shadcn's bare \"221 83% 53%\") to RGBA, or null. */\nfunction resolveColor(raw: string): Rgba | null {\n const value = raw.trim();\n if (!value) return null;\n const candidate = /^[\\d.]+(?:deg)?\\s+[\\d.]+%\\s+[\\d.]+%(?:\\s*\\/\\s*[\\d.]+%?)?$/.test(value) ? `hsl(${value})` : value;\n const probe = document.createElement(\"span\");\n probe.style.color = candidate;\n if (!probe.style.color) return null; // not a color\n probe.style.display = \"none\";\n probe.setAttribute(\"data-frankie-ignore\", \"\");\n document.body.appendChild(probe);\n const computed = getComputedStyle(probe).color;\n probe.remove();\n const rgb = parseRgb(computed);\n if (rgb) return rgb;\n // Newer browsers keep oklch()/lab() as-is in computed styles: rasterize one pixel instead.\n try {\n const context = document.createElement(\"canvas\").getContext(\"2d\", { willReadFrequently: true });\n if (!context) return null;\n context.fillStyle = computed;\n context.fillRect(0, 0, 1, 1);\n const [r, g, b, a] = context.getImageData(0, 0, 1, 1).data;\n return a ? [r!, g!, b!, a / 255] : null;\n } catch {\n return null;\n }\n}\n\n/** The page's declared brand color (`--primary` and friends on :root or body), if any. */\nfunction declaredAccent(background: Rgba): Rgba | null {\n for (const element of [document.documentElement, document.body]) {\n const style = getComputedStyle(element);\n if (typeof style.getPropertyValue !== \"function\") continue;\n for (const name of PRIMARY_VARS) {\n const color = resolveColor(style.getPropertyValue(name));\n if (color && color[3] > 0.5 && !sameColor(color, background)) return color;\n }\n }\n return null;\n}\n\n/** A color custom property declared on <html> or <body> (shadcn's `--background`, Tailwind v4's `--color-background`). */\nfunction declaredColor(names: string[]): Rgba | null {\n for (const element of [document.documentElement, document.body]) {\n const style = getComputedStyle(element);\n if (typeof style.getPropertyValue !== \"function\") continue;\n for (const name of names) {\n const color = resolveColor(style.getPropertyValue(name));\n if (color && color[3] >= 0.5) return color;\n }\n }\n return null;\n}\n\n/** An element's own background, when it's opaque enough to be a page surface. */\nfunction solidBackground(element: Element | null | undefined): Rgba | null {\n if (!element || element.closest(OWN_DOM)) return null;\n const color = parseRgb(getComputedStyle(element).backgroundColor ?? \"\");\n return color && color[3] >= 0.5 ? color : null;\n}\n\n/**\n * The page's real background and text. Many sites leave <body> transparent\n * and paint <html>, a top-level wrapper (`<div class=\"min-h-screen bg-…\">`)\n * or only declare `--background`, so each is tried in turn; <body>'s own\n * text color stays the default unless the background came from elsewhere\n * and the page declares `--foreground`.\n */\nfunction pageColors(bodyStyle: CSSStyleDeclaration): { background: Rgba; foreground: Rgba } | null {\n const bodyText = parseRgb(bodyStyle.color);\n const fromBody = parseRgb(bodyStyle.backgroundColor);\n if (fromBody && fromBody[3] >= 0.5) return bodyText ? { background: fromBody, foreground: bodyText } : null;\n const wrapper = document.body.firstElementChild;\n const background = solidBackground(document.documentElement) ?? solidBackground(wrapper) ?? solidBackground(wrapper?.firstElementChild) ?? declaredColor([\"--background\", \"--color-background\"]);\n const foreground = declaredColor([\"--foreground\", \"--color-foreground\"]) ?? bodyText;\n return background && foreground ? { background, foreground } : null;\n}\n\n/** `a` moved `amount` of the way toward `b`: a surface or hairline in the page's own palette. */\nfunction mix(a: Rgba, b: Rgba, amount: number): Rgba {\n return [0, 1, 2].map((i) => Math.round(a[i]! + (b[i]! - a[i]!) * amount)).concat(1) as Rgba;\n}\n\n/** The page's corner radius in rem, from a shadcn-style `--radius` (rem or px), if it declares one. */\nfunction declaredRadius(): string | undefined {\n const raw = getComputedStyle(document.documentElement).getPropertyValue?.(\"--radius\").trim();\n const match = raw?.match(/^([\\d.]+)(rem|px)$/);\n if (!match) return undefined;\n const rem = match[2] === \"px\" ? Number(match[1]) / 16 : Number(match[1]);\n return rem >= 0 && rem <= 2 ? String(Math.round(rem * 1000) / 1000) : undefined;\n}\n\n/**\n * Best-effort read of the host page's own background/text/accent colors, for\n * a workspace that chose \"inherit my site's theme\" over a manually configured\n * palette. Conservative: returns `null` — never a guess — when nothing\n * sampled is reliable (no DOM to read, no opaque surface on <body>, <html>,\n * the top wrapper or a declared `--background`, illegible contrast), so the\n * caller falls back to the configured theme rather than shipping a widget\n * that looks broken. With no distinct accent it still returns the page's\n * surfaces and leaves the configured accent in place.\n *\n * Never attempts full brand-color extraction (logos, images, gradients) —\n * only colors real elements are already rendering with, via\n * `getComputedStyle`. The provider re-runs it when the host switches its own\n * theme (see `watchHostTheme`).\n */\nexport function inferHostTheme(): Partial<WidgetTheme> | null {\n if (typeof document === \"undefined\" || typeof getComputedStyle !== \"function\") return null;\n try {\n const bodyStyle = getComputedStyle(document.body);\n const colors = pageColors(bodyStyle);\n if (!colors) return null; // no opaque surface anywhere: nothing real to read\n const { background, foreground } = colors;\n if (contrastRatio(background, foreground) < 2) return null; // too low-contrast to trust as a real reading\n\n // An explicit opt-in wins, then the page's declared brand variable, then\n // a guess from rendered elements (never Frankie's own).\n let accent: Rgba | null = null;\n const marked = firstHostElement(\"[data-frankie-accent]\");\n if (marked) {\n const style = getComputedStyle(marked);\n const surface = parseRgb(style.backgroundColor);\n accent = surface && surface[3] > 0.5 && !sameColor(surface, background) ? surface : parseRgb(style.color);\n }\n accent ??= declaredAccent(background);\n for (const selector of accent ? [] : ACCENT_SELECTORS) {\n const element = firstHostElement(selector);\n if (!element) continue;\n const style = getComputedStyle(element);\n const surface = parseRgb(style.backgroundColor);\n if (surface && surface[3] > 0.5 && !sameColor(surface, background)) { accent = surface; break; }\n const text = parseRgb(style.color);\n if (text && text[3] > 0.5 && !sameColor(text, foreground)) { accent = text; break; }\n }\n\n // The page's light/dark decides the widget's, not the visitor's OS: a dark\n // site on a light-mode machine must not get light borders and buttons.\n // Muted surfaces and hairlines come from the page's own two colors so\n // nothing in the widget keeps the default palette.\n const font = bodyStyle.fontFamily?.trim();\n const radius = declaredRadius();\n // No accent distinct from the page: the page's surfaces still apply, and\n // the configured accent (the fallback) stays for buttons and links.\n return {\n mode: luminance(background) < 0.4 ? \"dark\" : \"light\",\n background: toCss(background),\n foreground: toCss(foreground),\n ...(accent ? {\n primary: toCss(accent),\n // Whichever of near-black or near-white reads better on the accent.\n primaryForeground: contrastRatio(accent, [10, 10, 10, 1]) > contrastRatio(accent, [250, 250, 250, 1]) ? \"#0a0a0a\" : \"#fafafa\",\n } : {}),\n muted: toCss(mix(background, foreground, 0.07)),\n border: toCss(mix(background, foreground, 0.14)),\n ...(font ? { fontFamily: font } : {}),\n ...(radius ? { radius } : {}),\n };\n } catch {\n return null; // any DOM access failure (unusual host environment): never guess\n }\n}\n\n/**\n * Call `onChange` when the host page may have switched its theme: a class,\n * `data-*` or inline style change on <html> or <body> (how sites toggle light\n * and dark), or the OS color scheme flipping. Coalesced to one call per\n * frame. Watches only those two elements' own attributes, never the subtree,\n * so the widget's own rendering can't trigger it. Returns a cleanup.\n */\nexport function watchHostTheme(onChange: () => void): () => void {\n if (typeof document === \"undefined\" || typeof MutationObserver === \"undefined\") return () => {};\n let frame = 0;\n const schedule = () => {\n if (frame) return;\n frame = (typeof requestAnimationFrame === \"function\" ? requestAnimationFrame : (fn: FrameRequestCallback) => setTimeout(fn, 16) as unknown as number)(() => {\n frame = 0;\n onChange();\n });\n };\n const observer = new MutationObserver(schedule);\n for (const element of [document.documentElement, document.body]) {\n if (element) observer.observe(element, { attributes: true, attributeFilter: [\"class\", \"style\", \"data-theme\", \"data-mode\", \"data-color-scheme\", \"data-color-mode\"] });\n }\n const media = typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(\"(prefers-color-scheme: dark)\") : undefined;\n media?.addEventListener?.(\"change\", schedule);\n return () => {\n observer.disconnect();\n media?.removeEventListener?.(\"change\", schedule);\n if (frame && typeof cancelAnimationFrame === \"function\") cancelAnimationFrame(frame);\n };\n}\n","\"use client\";\n\nimport { createContext, useContext, useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ApiToolInventory, ChatAttachment, LoadedSkill } from \"@frankieai/core\";\nimport { FrankieApiError } from \"../lib/client.js\";\nimport {\n applyEvent,\n newAssistantMessage,\n newUserMessage,\n type AssistantChatMessage,\n type ChatMessage,\n} from \"../lib/messages.js\";\nimport { useFrankie } from \"./FrankieProvider.jsx\";\n\n/** Extra inputs a turn can carry: attached files and a chosen model. */\nexport interface SendOptions {\n attachments?: ChatAttachment[];\n model?: string;\n}\n\nexport interface UseFrankieChatResult {\n loadedSkills: LoadedSkill[] | null;\n /** Which OpenAPI sources are live for this turn, and a content fingerprint of each. */\n activeApis: ApiToolInventory[] | null;\n restoring: boolean;\n messages: ChatMessage[];\n /** True from the moment a message is sent until the turn ends. */\n busy: boolean;\n /** Set when the turn failed outright (network, auth, rate limit). */\n error?: string;\n send: (text: string, opts?: SendOptions) => Promise<void>;\n /** Abort the in-flight turn. The partial answer stays in the transcript. */\n stop: () => void;\n /** Clear the transcript and start a brand-new server session. */\n reset: () => void;\n}\n\nlet counter = 0;\nconst nextId = (prefix: string) => `${prefix}_${Date.now().toString(36)}_${(counter += 1)}`;\n\nexport const FrankieChatContext = createContext<UseFrankieChatResult | null>(null);\nexport function useFrankieChat(): UseFrankieChatResult {\n const value = useContext(FrankieChatContext);\n if (!value) throw new Error(\"useFrankieChat requires FrankieProvider.\");\n return value;\n}\n\nexport function useFrankieChatState(): UseFrankieChatResult {\n const { client, context } = useFrankie();\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n const [restored, setRestored] = useState(false);\n const [loadedSkills, setLoadedSkills] = useState<LoadedSkill[] | null>(null);\n const [activeApis, setActiveApis] = useState<ApiToolInventory[] | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const mounted = useRef(true);\n // State updates can be batched while a stream is arriving. Keep the latest\n // transcript synchronously so the final server sync cannot save a stale copy.\n const messagesRef = useRef<ChatMessage[]>([]);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n abortRef.current?.abort();\n };\n }, []);\n\n const storageKey = `${client.namespace}.messages`;\n\n // A reply the server is still writing after this browser lost the stream (a\n // closed tab, a phone that backgrounded the page): show it as working and\n // check back until it lands, then show the server's finished copy.\n const followingRef = useRef(false);\n const followRunningTurn = useCallback(async () => {\n if (followingRef.current || !client.canResume) return;\n followingRef.current = true;\n setBusy(true);\n try {\n const deadline = Date.now() + 3 * 60_000;\n while (mounted.current && Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 2500));\n const remote = await client.resumeConversation().catch(() => undefined);\n if (!remote || remote.busy) continue;\n if (remote.messages.length) {\n const finished = remote.messages as ChatMessage[];\n messagesRef.current = finished;\n if (mounted.current) setMessages(finished);\n }\n if (mounted.current) setError(undefined);\n return;\n }\n } finally {\n followingRef.current = false;\n if (mounted.current) setBusy(false);\n }\n }, [client]);\n\n // Restore local storage immediately, then hydrate the authoritative server\n // transcript. The server path makes the same signed-in user's chats available\n // on another browser or device.\n useEffect(() => {\n let active = true;\n const restore = async () => {\n if (client.persist) {\n try {\n const raw = window.localStorage.getItem(storageKey);\n const stored = raw ? (JSON.parse(raw) as ChatMessage[]) : [];\n if (Array.isArray(stored) && stored.length && active) {\n messagesRef.current = stored;\n setMessages(stored);\n }\n } catch {\n /* storage unavailable; server history can still restore */\n }\n }\n try {\n const remote = await client.resumeConversation();\n if (active && remote?.messages.length) {\n const restoredMessages = remote.busy ? stillWorking(remote.messages as ChatMessage[]) : (remote.messages as ChatMessage[]);\n messagesRef.current = restoredMessages;\n setMessages(restoredMessages);\n }\n if (active && remote?.busy) void followRunningTurn();\n } catch (caught) {\n if (active) {\n const message =\n caught instanceof FrankieApiError\n ? caught.message\n : caught instanceof Error && caught.message.startsWith(\"Frankie\")\n ? caught.message\n : \"Could not restore this conversation. Check your sign-in and connection, then try opening it again.\";\n setError(message);\n }\n } finally {\n if (active) setRestored(true);\n }\n };\n void restore();\n return () => { active = false; };\n }, [client, storageKey, followRunningTurn]);\n\n // Persist once a turn settles — never mid-stream — so the saved copy is clean.\n // Wait until the initial restore has completed so an empty initial state never\n // deletes an existing transcript.\n useEffect(() => {\n if (!client.persist || !restored || busy) return;\n try {\n if (messages.length) window.localStorage.setItem(storageKey, JSON.stringify(messages));\n else window.localStorage.removeItem(storageKey);\n } catch {\n /* ignore */\n }\n if (messages.length) void client.syncConversation(messages, conversationTitle(messages)).catch(() => undefined);\n }, [messages, busy, client, storageKey, restored]);\n\n const patchAssistant = useCallback((id: string, update: (m: AssistantChatMessage) => AssistantChatMessage) => {\n const next = messagesRef.current.map((message) =>\n message.id === id && message.role === \"assistant\" ? update(message) : message,\n );\n messagesRef.current = next;\n setMessages(next);\n }, []);\n\n const send = useCallback(\n async (text: string, opts?: SendOptions) => {\n const trimmed = text.trim();\n const attachments = opts?.attachments ?? [];\n if ((!trimmed && attachments.length === 0) || abortRef.current || !restored) return;\n // The server needs a message; an attachment-only turn gets a stand-in.\n const outgoing = trimmed || \"Please take a look at the attached file(s).\";\n\n const assistantId = nextId(\"a\");\n setError(undefined);\n setLoadedSkills(null);\n setActiveApis(null);\n setBusy(true);\n const next = [\n ...messagesRef.current,\n newUserMessage(\n nextId(\"u\"),\n trimmed,\n attachments.map((file) => ({ name: file.name, mimeType: file.mimeType })),\n ),\n newAssistantMessage(assistantId),\n ];\n messagesRef.current = next;\n setMessages(next);\n\n const abort = new AbortController();\n abortRef.current = abort;\n\n // Models stream text in uneven bursts — a few characters, then a lull,\n // then a whole paragraph at once — which reads as a stutter no matter how\n // efficiently React renders it. So we don't paint what arrives; we buffer\n // it and *reveal* it at a steady cadence, a slice per animation frame, so\n // the answer types out smoothly regardless of how lumpy the network is.\n // The slice is a fraction of what's waiting, so a big burst drains fast\n // (and never lags far behind) while a trickle still advances a few chars a\n // frame. Thinking isn't rendered, so it commits in full without pacing.\n // Any structural event, the stream's end, or an abort flushes the whole\n // remaining buffer at once, so ordering holds and no token is dropped.\n let bufferedText = \"\";\n let pendingThinking = \"\";\n let frame: number | null = null;\n const raf = typeof requestAnimationFrame === \"function\" ? requestAnimationFrame : (cb: FrameRequestCallback) => setTimeout(() => cb(performance.now()), 16) as unknown as number;\n const cancelRaf = typeof cancelAnimationFrame === \"function\" ? cancelAnimationFrame : (clearTimeout as (id: number) => void);\n const revealTick = () => {\n frame = null;\n let addText = \"\";\n if (bufferedText) {\n // Reveal ~1/6 of what's waiting (at least a few chars) so the pace\n // eases with the backlog: snappy on a burst, gentle on a trickle.\n const step = Math.max(3, Math.ceil(bufferedText.length / 6));\n addText = bufferedText.slice(0, step);\n bufferedText = bufferedText.slice(step);\n }\n const addThinking = pendingThinking;\n pendingThinking = \"\";\n if (addText || addThinking) {\n patchAssistant(assistantId, (message) => ({ ...message, text: message.text + addText, thinking: message.thinking + addThinking }));\n }\n if (bufferedText) scheduleReveal();\n };\n const scheduleReveal = () => { if (frame === null) frame = raf(revealTick); };\n const flushDeltas = () => {\n if (frame !== null) { cancelRaf(frame); frame = null; }\n if (!bufferedText && !pendingThinking) return;\n const addText = bufferedText;\n const addThinking = pendingThinking;\n bufferedText = \"\";\n pendingThinking = \"\";\n patchAssistant(assistantId, (message) => ({ ...message, text: message.text + addText, thinking: message.thinking + addThinking }));\n };\n\n let lostStream = false;\n try {\n let receivedText = false;\n let receivedToolActivity = false;\n await client.send(\n {\n message: outgoing,\n ...(context ? { context } : {}),\n ...(opts?.model ? { model: opts.model } : {}),\n ...(attachments.length ? { attachments } : {}),\n signal: abort.signal,\n },\n (event) => {\n if (event.type === \"skills_loaded\") { setLoadedSkills(event.skills); return; }\n if (event.type === \"tools_active\") { setActiveApis(event.apis); return; }\n if (event.type === \"ping\" || event.type === \"session\") return;\n if (event.type === \"text_delta\") { receivedText = true; bufferedText += event.text; scheduleReveal(); return; }\n if (event.type === \"thinking_delta\") { pendingThinking += event.text; scheduleReveal(); return; }\n if (event.type === \"tool_start\" || event.type === \"tool_end\") receivedToolActivity = true;\n // A structural event: land the buffered text before it so the tool\n // row or terminal status sits in the right place in the transcript.\n flushDeltas();\n patchAssistant(assistantId, (message) => applyEvent(message, event));\n },\n );\n // Commit anything still buffered when the stream ends, so the closing\n // words appear at once instead of trickling after the reply is done.\n flushDeltas();\n // A terminated stream with no text previously looked like Frankie did\n // nothing. Preserve visible tool activity, but never leave an empty\n // assistant bubble for the user to interpret.\n if (!receivedText) {\n patchAssistant(assistantId, (message) => ({\n ...message,\n status: \"done\",\n text: receivedToolActivity\n ? \"Completed. Open Agent activity for the details.\"\n : \"Frankie didn’t return an answer. Please try again.\",\n }));\n }\n } catch (caught) {\n // Land any tokens buffered when the stream broke off, so a stopped or\n // failed turn still shows everything that had arrived.\n flushDeltas();\n if (abort.signal.aborted) {\n patchAssistant(assistantId, (message) => ({\n ...message,\n status: \"done\",\n text: message.text || \"Stopped.\",\n }));\n } else if (!(caught instanceof FrankieApiError) && client.canResume) {\n // The connection dropped, not the turn: the server keeps going and\n // saves the reply. Keep this message working and pick it up below.\n lostStream = true;\n } else {\n const message =\n caught instanceof FrankieApiError\n ? caught.message\n : caught instanceof Error && caught.message.startsWith(\"Frankie\")\n ? caught.message\n : \"Could not reach the assistant. Check your connection and try again.\";\n if (mounted.current) setError(message);\n patchAssistant(assistantId, (current) => ({ ...current, status: \"error\", error: message }));\n }\n } finally {\n // Ensure no frame is left pending and every buffered token is committed\n // before the turn settles and the transcript is persisted.\n flushDeltas();\n // A turn that ended without a `turn_end` event still has to settle,\n // unless the server is still writing it (followed below).\n if (!lostStream) {\n patchAssistant(assistantId, (message) =>\n message.status === \"streaming\" ? { ...message, status: \"done\" } : message,\n );\n }\n // Persist before send() resolves. This closes the previous gap where a\n // route change or tab close could leave a server conversation with no\n // rendered messages even though the agent transcript existed.\n const transcript = messagesRef.current;\n if (transcript.length) {\n await client.syncConversation(transcript, conversationTitle(transcript)).catch(() => undefined);\n }\n if (mounted.current) setBusy(false);\n abortRef.current = null;\n if (lostStream) void followRunningTurn();\n }\n },\n [busy, client, context, patchAssistant, restored, followRunningTurn],\n );\n\n // Closing the stream no longer stops a turn (a closed tab keeps its reply),\n // so Stop tells the server first, then closes it.\n const stop = useCallback(() => {\n if (!abortRef.current) return;\n void client.stopTurn();\n abortRef.current.abort();\n }, [client]);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n client.resetSession();\n messagesRef.current = [];\n setMessages([]);\n setError(undefined);\n setBusy(false);\n setRestored(true);\n setLoadedSkills(null);\n setActiveApis(null);\n try {\n if (client.persist) window.localStorage.removeItem(`${client.namespace}.messages`);\n } catch {\n /* ignore */\n }\n }, [client]);\n\n return { messages, busy, loadedSkills, activeApis, restoring: !restored, ...(error ? { error } : {}), send, stop, reset };\n}\n\n\nfunction conversationTitle(messages: ChatMessage[]): string | undefined {\n const first = messages.find((message) => message.role === \"user\" && message.text.trim());\n if (!first) return undefined;\n const title = first.text.trim().replace(/\\s+/g, \" \");\n return title.length > 42 ? `${title.slice(0, 42)}…` : title;\n}\n\n/** Show the last reply as still being written: reopen a broken one, or add an empty one. */\nfunction stillWorking(messages: ChatMessage[]): ChatMessage[] {\n const last = messages[messages.length - 1];\n if (last?.role === \"assistant\") return [...messages.slice(0, -1), { ...last, status: \"streaming\", error: undefined }];\n return [...messages, { id: \"pending_\" + Date.now(), role: \"assistant\", text: \"\", thinking: \"\", tools: [], status: \"streaming\", createdAt: Date.now() }];\n}\n","import type { FrankieServerEvent } from \"@frankieai/core\";\n\nexport interface ToolActivity {\n id: string;\n name: string;\n label: string;\n status: \"running\" | \"done\" | \"failed\" | \"denied\";\n detail?: string;\n}\n\n/** A file shown on a user message (name/type only — the bytes aren't kept in the transcript). */\nexport interface MessageAttachment {\n name: string;\n mimeType: string;\n}\n\nexport interface UserChatMessage {\n id: string;\n role: \"user\";\n text: string;\n attachments?: MessageAttachment[];\n createdAt: number;\n}\n\nexport interface AssistantChatMessage {\n id: string;\n role: \"assistant\";\n text: string;\n thinking: string;\n tools: ToolActivity[];\n status: \"streaming\" | \"done\" | \"error\";\n error?: string;\n createdAt: number;\n}\n\nexport type ChatMessage = UserChatMessage | AssistantChatMessage;\n\n/**\n * Fold one server event into the assistant message being built.\n *\n * Kept pure and separate from React so the streaming logic is testable without\n * a DOM, and so a host writing its own UI can reuse it.\n */\nexport function applyEvent(message: AssistantChatMessage, event: FrankieServerEvent): AssistantChatMessage {\n switch (event.type) {\n case \"text_delta\":\n return { ...message, text: message.text + event.text };\n\n case \"thinking_delta\":\n return { ...message, thinking: message.thinking + event.text };\n\n case \"tool_start\":\n return {\n ...message,\n tools: [\n ...message.tools,\n { id: event.toolUseId, name: event.name, label: event.label, status: \"running\" },\n ],\n };\n\n case \"tool_end\":\n return {\n ...message,\n tools: message.tools.map((tool) =>\n tool.id === event.toolUseId\n ? {\n ...tool,\n status: event.ok ? \"done\" : \"failed\",\n ...(event.preview ? { detail: event.preview } : {}),\n }\n : tool,\n ),\n };\n\n case \"tool_denied\":\n return {\n ...message,\n tools: upsertTool(message.tools, {\n id: event.toolUseId,\n name: event.name,\n label: event.name,\n status: \"denied\",\n detail: event.reason,\n }),\n };\n\n case \"turn_end\":\n return {\n ...message,\n status: message.status === \"error\" ? \"error\" : \"done\",\n // A turn that produced nothing still needs to say something.\n text: message.text || fallbackText(event.stopReason),\n };\n\n case \"error\":\n return { ...message, status: \"error\", error: event.message };\n\n default:\n return message;\n }\n}\n\nfunction upsertTool(tools: ToolActivity[], next: ToolActivity): ToolActivity[] {\n return tools.some((tool) => tool.id === next.id)\n ? tools.map((tool) => (tool.id === next.id ? { ...tool, ...next } : tool))\n : [...tools, next];\n}\n\nfunction fallbackText(stopReason: string): string {\n switch (stopReason) {\n case \"max_turns\":\n return \"I got stuck working on that. Could you narrow it down a little?\";\n case \"aborted\":\n return \"Stopped.\";\n case \"error\":\n return \"Something went wrong before I could answer. Try again?\";\n default:\n return \"\";\n }\n}\n\nexport function newAssistantMessage(id: string): AssistantChatMessage {\n return { id, role: \"assistant\", text: \"\", thinking: \"\", tools: [], status: \"streaming\", createdAt: Date.now() };\n}\n\nexport function newUserMessage(id: string, text: string, attachments?: MessageAttachment[]): UserChatMessage {\n return { id, role: \"user\", text, ...(attachments?.length ? { attachments } : {}), createdAt: Date.now() };\n}\n","\"use client\";\n\nimport { MessageCircle, X } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useStylesReady } from \"../../lib/styles-ready.js\";\nimport { bottomObstruction } from \"../../lib/bottom-bars.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { Button } from \"../ui/button.js\";\nimport { ChatPanel, type ChatPanelProps } from \"./ChatPanel.js\";\n\nexport interface FrankieWidgetProps extends ChatPanelProps {\n position?: \"bottom-right\" | \"bottom-left\";\n /** Distance from the viewport edges, in pixels. */\n offset?: number;\n defaultOpen?: boolean;\n /** Host overlays should sit above the assistant. Override for your layer system. */\n zIndex?: number;\n /** Controlled mode: pass both to drive open state from the host app. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Replace the floating launcher button. */\n renderLauncher?: (props: { open: boolean; toggle: () => void; label: string }) => React.ReactNode;\n /**\n * `false` hides the floating button, for apps with their own entry point:\n * give any element `data-frankie-open` (a nav tab, a \"Help\" link) and a\n * click on it opens the chat. Defaults to `true`.\n */\n launcher?: boolean;\n /**\n * Sit above fixed bars along the bottom of the page (a mobile tab bar, a\n * cookie banner) instead of underneath them. Defaults to `true`.\n */\n avoidBottomBars?: boolean;\n}\n\n/**\n * The drop-in embed: a floating launcher plus the chat panel.\n *\n * Renders `position: fixed` at the document level, so it does not care what\n * layout the host page uses.\n */\nexport function FrankieWidget({\n position = \"bottom-right\",\n offset = 24,\n defaultOpen = false,\n zIndex = 20,\n open: controlledOpen,\n onOpenChange,\n renderLauncher,\n launcher = true,\n avoidBottomBars = true,\n ...panelProps\n}: FrankieWidgetProps) {\n const { config, injectStyles, themeReady } = useFrankie();\n const theme = useFrankieTheme();\n const rootRef = useRef<HTMLDivElement>(null);\n // Never paint the panel as raw HTML while the host's CSS chunk is still in flight.\n const stylesReady = useStylesReady(rootRef, undefined, injectStyles);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const open = controlledOpen ?? uncontrolledOpen;\n\n const setOpen = (next: boolean) => {\n if (controlledOpen === undefined) setUncontrolledOpen(next);\n onOpenChange?.(next);\n };\n\n // Escape closes the panel, matching every other overlay on the host's page.\n useEffect(() => {\n if (!open) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setOpen(false);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n // Any element marked `data-frankie-open` opens the chat, so a host's own\n // \"Ask\" tab or help link can be the entry point.\n useEffect(() => {\n const onClick = (event: MouseEvent) => {\n const target = event.target instanceof Element ? event.target.closest(\"[data-frankie-open]\") : null;\n if (!target || rootRef.current?.contains(target)) return;\n if (target instanceof HTMLAnchorElement && !target.getAttribute(\"href\")?.startsWith(\"#\")) event.preventDefault();\n setOpen(true);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Lift above a fixed bar along the bottom (a mobile tab bar) rather than\n // hiding beneath it. Measured after paint, on resize, and as the page\n // changes (throttled), since apps often mount their nav after the widget.\n const [lift, setLift] = useState(0);\n useEffect(() => {\n if (!avoidBottomBars || typeof window === \"undefined\") { setLift(0); return; }\n let frame = 0;\n let last = 0;\n const measure = () => {\n frame = 0;\n last = Date.now();\n setLift(bottomObstruction(rootRef.current, position, offset));\n };\n const schedule = () => {\n if (frame) return;\n const wait = Math.max(0, 400 - (Date.now() - last));\n frame = window.setTimeout(() => requestAnimationFrame(measure), wait) as unknown as number;\n };\n schedule();\n window.addEventListener(\"resize\", schedule);\n // The chat's own updates (a reply streaming in) never move the host's bars.\n const observer = typeof MutationObserver === \"function\"\n ? new MutationObserver((records) => { if (records.some((record) => !rootRef.current?.contains(record.target))) schedule(); })\n : undefined;\n observer?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [\"class\", \"style\"] });\n return () => {\n window.clearTimeout(frame);\n window.removeEventListener(\"resize\", schedule);\n observer?.disconnect();\n };\n }, [avoidBottomBars, position, offset]);\n\n const label = config?.branding.launcherLabel ?? \"Need a hand?\";\n const mode = useResolvedMode(theme?.mode);\n const side = position === \"bottom-right\" ? { right: offset } : { left: offset };\n\n return (\n <div\n ref={rootRef}\n className={cn(\"frankie-root frankie-widget\", mode === \"dark\" && \"dark\")}\n // `opacity` rather than `visibility` or unmounting: the sentinel has to stay\n // measurable on this element, and the two alternatives both drop the widget\n // out of the accessibility tree for the wait.\n style={{ ...themeToCssVars(theme), position: \"fixed\", bottom: lift ? lift + Math.min(offset, 12) : offset, ...side, zIndex, pointerEvents: \"none\", [\"--frankie-lift\" as string]: `${lift}px`, ...(stylesReady && themeReady ? null : { opacity: 0 as const }) }}\n data-frankie-mode={mode}\n data-frankie-styles={stylesReady ? \"ready\" : \"pending\"}\n >\n <div className={cn(\"flex flex-col gap-3\", position === \"bottom-left\" ? \"items-start\" : \"items-end\")}>\n {/* No clipping wrapper: `.frankie-widget-panel` caps its own height, and an\n `overflow: hidden` box here cut the panel's shadow off (all that showed was\n faint square corners). */}\n {open ? <div style={{ pointerEvents: \"auto\" }}><ChatPanel {...panelProps} className={cn(\"frankie-widget-panel\", panelProps.className)} onClose={() => setOpen(false)} autoFocus /></div> : null}\n {launcher || renderLauncher ? <div style={{ pointerEvents: \"auto\" }}>\n {renderLauncher ? (\n renderLauncher({ open, toggle: () => setOpen(!open), label })\n ) : (\n <Button\n size=\"icon\"\n className=\"size-12 rounded-full shadow-lg\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n aria-label={open ? \"Close the assistant\" : label}\n title={label}\n >\n {open ? <X className=\"size-5\" /> : <MessageCircle className=\"size-5\" />}\n </Button>\n )}</div> : null}\n </div>\n </div>\n );\n}\n","import { useEffect, useLayoutEffect, useState, type RefObject } from \"react\";\nimport { ensureFrankieStyles } from \"./ensure-frankie-styles.js\";\n\n/** `useLayoutEffect` warns during SSR, where there is nothing to measure anyway. */\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** The property `frankie.css` sets on `.frankie-root`, and the value it sets. */\nconst SENTINEL = \"--frankie-styles-ready\";\n\n/**\n * Whether this widget's stylesheet has actually been applied to `node`.\n *\n * The widget is mounted inside someone else's application and bundler. A host\n * that imports `@frankieai/react/styles.css` from a client component ships it\n * in that component's chunk, and on a client-side navigation React can render\n * the panel before the CSS arrives — the panel then paints as raw HTML: native\n * `<details>` markers, no pills, browser-default type. A full reload hides this\n * because the stylesheet is in `<head>` before first paint.\n *\n * Probing a custom property costs nothing and asks the only question that\n * matters — are our rules in effect for this element — rather than guessing at\n * `<link>` elements the host may have inlined, bundled or renamed.\n *\n * Callers hide the root with `opacity` for the wait rather than `visibility` or\n * unmounting: the sentinel must stay measurable on that element, and both of the\n * alternatives would also drop the widget out of the accessibility tree.\n *\n * The wait is deliberately bounded. After `timeoutMs`, with no sentinel found,\n * this injects the stylesheet itself (`ensureFrankieStyles`) as a fallback for\n * a host that never loaded `styles.css` — the far more common way this shows\n * up in practice than the one case it must not clobber: a Tailwind v4 host\n * told it may skip our stylesheet and compile the same classes itself, which\n * also never sets the sentinel. That host opts out with `injectStyles={false}`\n * so it doesn't get a second, independently-themed copy of the stylesheet.\n */\nexport function useStylesReady(ref: RefObject<HTMLElement | null>, timeoutMs = 600, injectFallback = true): boolean {\n const [ready, setReady] = useState(false);\n\n useIsomorphicLayoutEffect(() => {\n if (ready) return;\n const node = ref.current;\n if (!node || typeof window === \"undefined\" || typeof window.getComputedStyle !== \"function\") {\n setReady(true);\n return;\n }\n\n const applied = () => window.getComputedStyle(node).getPropertyValue(SENTINEL).trim() === \"1\";\n // The common case — stylesheet already in `<head>` — resolves here, inside a\n // layout effect, so nothing unstyled is ever painted and nothing flickers.\n if (applied()) {\n setReady(true);\n return;\n }\n\n const deadline = Date.now() + timeoutMs;\n let frame = 0;\n const poll = () => {\n if (applied()) {\n setReady(true);\n return;\n }\n if (Date.now() >= deadline) {\n // Inline `<style>` insertion applies synchronously, so re-check right\n // after — but set `ready` regardless, matching the pre-existing\n // \"never hide the widget forever\" guarantee.\n if (injectFallback) ensureFrankieStyles();\n setReady(true);\n return;\n }\n frame = window.requestAnimationFrame(poll);\n };\n // `requestAnimationFrame` is missing in some test environments; fall back to\n // rendering rather than hiding the widget forever.\n if (typeof window.requestAnimationFrame !== \"function\") {\n setReady(true);\n return;\n }\n frame = window.requestAnimationFrame(poll);\n return () => window.cancelAnimationFrame(frame);\n }, [ref, ready, timeoutMs, injectFallback]);\n\n return ready;\n}\n","// Generated by scripts/embed-css.mjs from dist/frankie.css — do not edit by hand.\nexport const FRANKIE_CSS_CONTENT: string = \"/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\\n@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-ease:initial}}}@layer theme{.frankie-root{--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--leading-tight:1.25;--radius-md:.375rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--color-background:var(--frankie-background);--color-foreground:var(--frankie-foreground);--color-primary:var(--frankie-primary)}}@layer components{.frankie-page-shell{background:var(--frankie-background);height:100%;min-height:0;color:var(--frankie-foreground);flex:1;display:flex}.frankie-page-main{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.frankie-history-desktop{border-right:1px solid var(--frankie-border);background:var(--frankie-secondary);flex-direction:column;flex-shrink:0;gap:16px;width:260px;padding:20px 14px;transition:width .16s,padding .16s;display:flex;overflow:auto}.frankie-history-toggle{border:1px solid var(--frankie-border);background:var(--frankie-background);width:32px;height:32px;color:var(--frankie-muted-foreground);cursor:pointer;border-radius:8px;flex-shrink:0;justify-content:center;align-self:flex-end;align-items:center;display:flex}.frankie-history-toggle:hover{color:var(--frankie-foreground)}.frankie-history-collapsed{width:56px;padding:20px 8px;overflow:visible}.frankie-history-collapsed .frankie-history-toggle{align-self:center}.frankie-history-heading{justify-content:space-between;align-items:center;font-size:14px;display:flex}.frankie-new-chat{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;border-radius:10px;justify-content:center;align-items:center;gap:8px;min-height:44px;font-weight:600;display:flex}.frankie-history-search>span{color:var(--frankie-muted-foreground);margin-bottom:6px;font-size:12px;display:block}.frankie-history-search input{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:8px;width:100%;min-height:40px;padding:8px 10px}.frankie-history-list{flex-direction:column;flex:1;gap:4px;display:flex}.frankie-history-list button{text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:8px;flex-direction:column;align-items:flex-start;gap:5px;min-height:54px;padding:10px;display:flex}.frankie-history-list button span{text-overflow:ellipsis;white-space:nowrap;width:100%;font-size:13px;overflow:hidden}.frankie-history-list small{color:var(--frankie-muted-foreground);font-size:11px}.frankie-history-list button:hover,.frankie-history-list button[aria-current]{background:var(--frankie-background);border-color:var(--frankie-border)}.frankie-history-desktop>p,.frankie-history-dialog p{color:var(--frankie-muted-foreground);font-size:13px}.frankie-header-caption{color:var(--frankie-muted-foreground);font-size:12px}.frankie-page-header button{cursor:pointer;justify-content:center;align-items:center;gap:6px;min-width:40px;min-height:40px;display:inline-flex}.frankie-chat-settings{position:relative}.frankie-chat-settings summary{cursor:pointer;border-radius:8px;place-items:center;width:40px;height:40px;list-style:none;display:grid}.frankie-chat-settings summary::-webkit-details-marker{display:none}.frankie-chat-settings>div{z-index:5;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:12px;gap:12px;width:min(280px,80vw);padding:16px;font-size:13px;display:grid;position:absolute;top:46px;right:0;box-shadow:0 10px 30px #0002}.frankie-chat-settings p{color:var(--frankie-muted-foreground);margin:0}.frankie-loaded-skills{width:100%;max-width:760px;color:var(--frankie-muted-foreground);font-size:12px}.frankie-loaded-skills summary{cursor:pointer;padding:6px 0}.frankie-loaded-skills ul{margin:0;padding:4px 0;list-style:none}.frankie-loaded-skills li{gap:8px;padding:4px 0;display:flex}.frankie-loaded-skills small{opacity:.7}.frankie-history-dialog{background:var(--frankie-secondary);width:min(320px,86vw);max-width:none;height:100dvh;max-height:none;color:var(--frankie-foreground);border:0;margin:0;padding:0}.frankie-history-dialog::backdrop{background:#0008}.frankie-history-dialog>aside{height:100%;padding:calc(20px + env(safe-area-inset-top)) 16px calc(20px + env(safe-area-inset-bottom));flex-direction:column;gap:16px;display:flex;overflow:auto}.frankie-mobile-only{display:none!important}.frankie-widget-panel{max-height:calc(100dvh - 100px - var(--frankie-lift,0px))}.frankie-root button:focus-visible,.frankie-root summary:focus-visible,.frankie-root input:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:3px}.frankie-root button:disabled{opacity:.5;cursor:not-allowed}@media (max-width:760px){.frankie-history-desktop{display:none}.frankie-mobile-only{display:inline-flex!important}.frankie-header-caption{display:none}.frankie-page-header{padding:6px 10px}.frankie-history-heading button{background:0 0;border:0;justify-content:center;align-items:center;min-width:44px;min-height:44px}.frankie-page-prompts{gap:8px}.frankie-widget-panel{width:calc(100vw - 32px)}}.frankie-root{--frankie-styles-ready:1;--frankie-background:#fff;--frankie-foreground:#0a0a0a;--frankie-primary:#171717;--frankie-primary-foreground:#fafafa;--frankie-secondary:#f5f5f5;--frankie-secondary-foreground:#171717;--frankie-muted:#f5f5f5;--frankie-muted-foreground:#737373;--frankie-border:#e5e5e5;--frankie-radius:.625rem;--frankie-font:ui-sans-serif, system-ui, -apple-system, \\\"Segoe UI\\\", Roboto, \\\"Helvetica Neue\\\", Arial, sans-serif;font-family:var(--frankie-font);color:var(--frankie-foreground);-webkit-font-smoothing:antialiased;line-height:1.5}.frankie-root.dark{--frankie-background:#0a0a0a;--frankie-foreground:#fafafa;--frankie-primary:#fafafa;--frankie-primary-foreground:#171717;--frankie-secondary:#262626;--frankie-secondary-foreground:#fafafa;--frankie-muted:#262626;--frankie-muted-foreground:#a3a3a3;--frankie-border:#262626}.frankie-root,.frankie-root *,.frankie-root :before,.frankie-root :after{box-sizing:border-box}.frankie-root button,.frankie-root textarea,.frankie-root input{font:inherit;color:inherit;margin:0}.frankie-root button{background-color:#0000;border:0 solid}.frankie-root textarea{background-color:var(--frankie-background)}.frankie-focus:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:2px}.frankie-scroll{scrollbar-width:thin;scrollbar-color:var(--frankie-border) transparent}.frankie-prose>*+*{margin-top:.6rem}.frankie-prose p{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;line-height:1.6}.frankie-prose>.frankie-h{letter-spacing:-.01em;margin-top:1.15rem;line-height:1.3}.frankie-prose>.frankie-h:first-child{margin-top:0}.frankie-prose h3.frankie-h{font-size:1.06em;font-weight:700}.frankie-prose h4.frankie-h{font-size:1em;font-weight:600}.frankie-prose h5.frankie-h{color:var(--frankie-muted-foreground);font-size:.92em;font-weight:600}.frankie-prose strong{font-weight:600}.frankie-prose ul,.frankie-prose ol{flex-direction:column;gap:.35rem;margin:0;padding-left:1.4rem;display:flex}.frankie-prose li{padding-left:.15rem;line-height:1.55}.frankie-prose li::marker{color:var(--frankie-muted-foreground)}.frankie-prose ul{list-style:outside}.frankie-prose ol{list-style:decimal}.frankie-prose a{color:var(--frankie-primary);text-underline-offset:2px;text-decoration:underline}.frankie-prose code{background-color:var(--frankie-muted);border-radius:.25rem;padding:.1em .3em;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;font-size:.85em}.frankie-prose pre{border-radius:var(--frankie-radius);background-color:var(--frankie-muted);margin:0;padding:.625rem .75rem;overflow-x:auto}.frankie-prose pre code{background:0 0;padding:0}.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, var(--frankie-primary), transparent 65%), var(--frankie-background);flex-direction:column;min-width:0;display:flex;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, color-mix(in srgb, var(--frankie-primary) 8%, transparent), transparent 65%), var(--frankie-background)}}.frankie-chat-page{color:var(--frankie-foreground)}.frankie-page-header{border-bottom:1px solid var(--frankie-border);background:var(--frankie-background);flex:0 0 56px;justify-content:space-between;align-items:center;gap:16px;min-height:56px;padding:0 24px;display:flex}@supports (color:color-mix(in lab, red, red)){.frankie-page-header{background:color-mix(in srgb, var(--frankie-background) 92%, transparent)}}.frankie-page-header{-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.frankie-page-header>div{align-items:center;gap:10px;display:flex}.frankie-page-header strong{font-size:.925rem;font-weight:650}.frankie-page-status{background:var(--frankie-primary);width:10px;height:10px;box-shadow:0 0 0 4px var(--frankie-primary);border-radius:999px}@supports (color:color-mix(in lab, red, red)){.frankie-page-status{box-shadow:0 0 0 4px color-mix(in srgb, var(--frankie-primary) 16%, transparent)}}.frankie-page-header button{border:1px solid var(--frankie-border);border-radius:calc(var(--frankie-radius) * .9);background:var(--frankie-secondary);cursor:pointer;min-height:36px;padding:0 13px}.frankie-page-header button:hover{border-color:var(--frankie-primary)}.frankie-page-canvas{flex:1;min-width:0;min-height:0;display:flex}.frankie-page-canvas>.frankie-inline{width:100%;max-width:56rem;margin:0 auto}.frankie-page-canvas .frankie-scroll{padding:2rem clamp(1rem,4vw,4rem) 1.25rem}.frankie-page-canvas .frankie-root>.border-t{background:linear-gradient(180deg, transparent, var(--frankie-background) 30%);border-top:0;padding:.75rem clamp(1rem,4vw,4rem) 1.5rem}.frankie-page-empty{text-align:center;flex-direction:column;flex-shrink:0;justify-content:safe center;align-items:center;gap:2rem;width:min(100%,44rem);min-height:100%;margin:auto;padding:clamp(2rem,7vh,4.5rem) 1rem 2rem;display:flex;container-type:inline-size}.frankie-page-welcome h1{letter-spacing:-.02em;margin:0;font-size:clamp(1.55rem,3.4vw,2rem);font-weight:700;line-height:1.15}.frankie-page-welcome p{max-width:34rem;color:var(--frankie-muted-foreground);margin:.6rem auto 0;font-size:1rem}.frankie-page-suggest{flex-direction:column;align-items:center;gap:1rem;width:100%;display:flex}.frankie-page-suggest-label{color:var(--frankie-muted-foreground);letter-spacing:.12em;text-transform:uppercase;margin:0;font-size:.72rem;font-weight:600}.frankie-page-prompts{grid-template-columns:1fr;gap:.75rem;width:100%;display:grid}@container (min-width:30rem){.frankie-page-prompts{grid-template-columns:repeat(2,minmax(0,1fr))}}.frankie-page-prompts button{border:1px solid var(--frankie-border);background:var(--frankie-background);text-align:left;cursor:pointer;border-radius:14px;align-items:flex-start;gap:.85rem;min-height:64px;padding:.9rem 1rem;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-page-prompts button:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 45%, var(--frankie-border))}}.frankie-page-prompts button:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 4%, var(--frankie-background))}}.frankie-page-prompts button:hover:not(:disabled){transform:translateY(-1px)}.frankie-prompt-icon{background:var(--frankie-muted);width:34px;height:34px;color:var(--frankie-foreground);border-radius:9px;flex:none;place-items:center;display:grid}.frankie-prompt-text{flex-direction:column;gap:2px;min-width:0;display:flex}.frankie-prompt-text strong{font-size:.9rem;font-weight:600;line-height:1.3}.frankie-prompt-text small{color:var(--frankie-muted-foreground);font-size:.8rem;line-height:1.35;overflow:hidden}.frankie-page-suggest-hint{color:var(--frankie-muted-foreground);margin:0;font-size:.85rem}.frankie-widget-panel .frankie-page-empty{gap:1rem;padding:1rem .25rem}.frankie-widget-panel .frankie-page-welcome h1{font-size:1.25rem}.frankie-widget-panel .frankie-page-welcome p{margin-top:.35rem;font-size:.88rem;line-height:1.45}.frankie-widget-panel .frankie-page-suggest{gap:.55rem}.frankie-widget-panel .frankie-page-suggest-label{font-size:.66rem}.frankie-widget-panel .frankie-page-prompts{flex-wrap:wrap;justify-content:center;gap:.4rem;display:flex}.frankie-widget-panel .frankie-page-prompts button{border-radius:999px;align-items:center;gap:.4rem;max-width:100%;min-height:34px;padding:.3rem .8rem .3rem .45rem}.frankie-widget-panel .frankie-prompt-icon{border-radius:999px;width:22px;height:22px}.frankie-widget-panel .frankie-prompt-icon svg{width:13px;height:13px}.frankie-widget-panel .frankie-prompt-text strong{white-space:nowrap;text-overflow:ellipsis;font-size:.82rem;font-weight:550;overflow:hidden}.frankie-widget-panel .frankie-prompt-text small,.frankie-widget-panel .frankie-page-suggest-hint{display:none}@media (max-width:640px){.frankie-page-header{flex-basis:52px;min-height:52px;padding:0 12px}.frankie-page-canvas .frankie-scroll{padding:1.25rem .75rem .5rem}.frankie-page-canvas .frankie-root>.border-t{padding:.65rem .75rem calc(.65rem + env(safe-area-inset-bottom))}}}@layer utilities;.frankie-root .\\\\@container{container-type:inline-size}.frankie-root .invisible{visibility:hidden}.frankie-root .visible{visibility:visible}.frankie-root .absolute{position:absolute}.frankie-root .fixed{position:fixed}.frankie-root .relative{position:relative}.frankie-root .static{position:static}.frankie-root .sticky{position:sticky}.frankie-root .isolate{isolation:isolate}.frankie-root .container{width:100%}@media (min-width:40rem){.frankie-root .container{max-width:40rem}}@media (min-width:48rem){.frankie-root .container{max-width:48rem}}@media (min-width:64rem){.frankie-root .container{max-width:64rem}}@media (min-width:80rem){.frankie-root .container{max-width:80rem}}@media (min-width:96rem){.frankie-root .container{max-width:96rem}}.frankie-root .mt-0{margin-top:0}.frankie-root .mt-0\\\\.5{margin-top:calc(var(--spacing) * .5)}.frankie-root .mt-1{margin-top:var(--spacing)}.frankie-root .mt-2{margin-top:calc(var(--spacing) * 2)}.frankie-root .block{display:block}.frankie-root .contents{display:contents}.frankie-root .flex{display:flex}.frankie-root .grid{display:grid}.frankie-root .hidden{display:none}.frankie-root .inline{display:inline}.frankie-root .inline-flex{display:inline-flex}.frankie-root .table{display:table}.frankie-root .size-1{width:var(--spacing);height:var(--spacing)}.frankie-root .size-1\\\\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.frankie-root .size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.frankie-root .size-3\\\\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.frankie-root .size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.frankie-root .size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.frankie-root .size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.frankie-root .size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.frankie-root .size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.frankie-root .size-full{width:100%;height:100%}.frankie-root .h-8{height:calc(var(--spacing) * 8)}.frankie-root .h-9{height:calc(var(--spacing) * 9)}.frankie-root .h-10{height:calc(var(--spacing) * 10)}.frankie-root .h-\\\\[32rem\\\\]{height:32rem}.frankie-root .h-full{height:100%}.frankie-root .h-px{height:1px}.frankie-root .min-h-7{min-height:calc(var(--spacing) * 7)}.frankie-root .min-h-\\\\[2\\\\.25rem\\\\]{min-height:2.25rem}.frankie-root .min-h-screen{min-height:100vh}.frankie-root .w-\\\\[22rem\\\\]{width:22rem}.frankie-root .w-full{width:100%}.frankie-root .max-w-\\\\[12rem\\\\]{max-width:12rem}.frankie-root .max-w-\\\\[14rem\\\\]{max-width:14rem}.frankie-root .max-w-\\\\[85\\\\%\\\\]{max-width:85%}.frankie-root .max-w-\\\\[calc\\\\(100vw-2rem\\\\)\\\\]{max-width:calc(100vw - 2rem)}.frankie-root .min-w-0{min-width:0}.frankie-root .flex-1{flex:1}.frankie-root .shrink-0{flex-shrink:0}.frankie-root .grow{flex-grow:1}.frankie-root .animate-pulse{animation:var(--animate-pulse)}.frankie-root .animate-spin{animation:var(--animate-spin)}.frankie-root .cursor-pointer{cursor:pointer}.frankie-root .resize{resize:both}.frankie-root .resize-none{resize:none}.frankie-root .list-none{list-style-type:none}.frankie-root .flex-col{flex-direction:column}.frankie-root .flex-wrap{flex-wrap:wrap}.frankie-root .items-center{align-items:center}.frankie-root .items-end{align-items:flex-end}.frankie-root .items-start{align-items:flex-start}.frankie-root .justify-center{justify-content:center}.frankie-root .justify-end{justify-content:flex-end}.frankie-root .gap-1{gap:var(--spacing)}.frankie-root .gap-1\\\\.5{gap:calc(var(--spacing) * 1.5)}.frankie-root .gap-2{gap:calc(var(--spacing) * 2)}.frankie-root .gap-3{gap:calc(var(--spacing) * 3)}.frankie-root .gap-4{gap:calc(var(--spacing) * 4)}.frankie-root .truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.frankie-root .overflow-hidden{overflow:hidden}.frankie-root .overflow-y-auto{overflow-y:auto}.frankie-root .overscroll-contain{overscroll-behavior:contain}.frankie-root .rounded{border-radius:.25rem}.frankie-root .rounded-\\\\[calc\\\\(var\\\\(--frankie-radius\\\\)\\\\+4px\\\\)\\\\]{border-radius:calc(var(--frankie-radius) + 4px)}.frankie-root .rounded-\\\\[calc\\\\(var\\\\(--frankie-radius\\\\)-2px\\\\)\\\\]{border-radius:calc(var(--frankie-radius) - 2px)}.frankie-root .rounded-\\\\[var\\\\(--frankie-radius\\\\)\\\\]{border-radius:var(--frankie-radius)}.frankie-root .rounded-full{border-radius:3.40282e38px}.frankie-root .rounded-md{border-radius:var(--radius-md)}.frankie-root .rounded-none{border-radius:0}.frankie-root .border{border-style:var(--tw-border-style);border-width:1px}.frankie-root .border-0{border-style:var(--tw-border-style);border-width:0}.frankie-root .border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.frankie-root .border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.frankie-root .border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.frankie-root .border-border{border-color:var(--frankie-border)}.frankie-root .border-transparent{border-color:#0000}.frankie-root .bg-background{background-color:var(--frankie-background)}.frankie-root .bg-border{background-color:var(--frankie-border)}.frankie-root .bg-emerald-500{background-color:var(--color-emerald-500)}.frankie-root .bg-emerald-500\\\\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-emerald-500\\\\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.frankie-root .bg-muted{background-color:var(--frankie-muted)}.frankie-root .bg-muted-foreground{background-color:var(--frankie-muted-foreground)}.frankie-root .bg-primary{background-color:var(--frankie-primary)}.frankie-root .bg-red-500{background-color:var(--color-red-500)}.frankie-root .bg-red-500\\\\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-red-500\\\\/10{background-color:color-mix(in oklab, var(--color-red-500) 10%, transparent)}}.frankie-root .bg-secondary{background-color:var(--frankie-secondary)}.frankie-root .object-cover{object-fit:cover}.frankie-root .p-3{padding:calc(var(--spacing) * 3)}.frankie-root .p-4{padding:calc(var(--spacing) * 4)}.frankie-root .px-1{padding-inline:var(--spacing)}.frankie-root .px-2{padding-inline:calc(var(--spacing) * 2)}.frankie-root .px-3{padding-inline:calc(var(--spacing) * 3)}.frankie-root .px-4{padding-inline:calc(var(--spacing) * 4)}.frankie-root .px-6{padding-inline:calc(var(--spacing) * 6)}.frankie-root .py-0{padding-block:0}.frankie-root .py-0\\\\.5{padding-block:calc(var(--spacing) * .5)}.frankie-root .py-1{padding-block:var(--spacing)}.frankie-root .py-2{padding-block:calc(var(--spacing) * 2)}.frankie-root .py-3{padding-block:calc(var(--spacing) * 3)}.frankie-root .pl-2{padding-left:calc(var(--spacing) * 2)}.frankie-root .text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.frankie-root .text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.frankie-root .text-\\\\[0\\\\.625rem\\\\]{font-size:.625rem}.frankie-root .text-\\\\[0\\\\.6875rem\\\\]{font-size:.6875rem}.frankie-root .text-\\\\[11px\\\\]{font-size:11px}.frankie-root .leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.frankie-root .font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.frankie-root .font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.frankie-root .whitespace-nowrap{white-space:nowrap}.frankie-root .text-emerald-600{color:var(--color-emerald-600)}.frankie-root .text-foreground{color:var(--frankie-foreground)}.frankie-root .text-muted-foreground{color:var(--frankie-muted-foreground)}.frankie-root .text-primary{color:var(--frankie-primary)}.frankie-root .text-primary-foreground{color:var(--frankie-primary-foreground)}.frankie-root .text-red-600{color:var(--color-red-600)}.frankie-root .text-secondary-foreground{color:var(--frankie-secondary-foreground)}.frankie-root .italic{font-style:italic}.frankie-root .underline-offset-4{text-underline-offset:4px}.frankie-root .opacity-60{opacity:.6}.frankie-root .opacity-70{opacity:.7}.frankie-root .shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .outline{outline-style:var(--tw-outline-style);outline-width:1px}.frankie-root .transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.frankie-root .select-all{-webkit-user-select:all;user-select:all}.frankie-root .placeholder\\\\:text-muted-foreground::placeholder{color:var(--frankie-muted-foreground)}@media (hover:hover){.frankie-root .hover\\\\:border-border:hover{border-color:var(--frankie-border)}.frankie-root .hover\\\\:bg-muted:hover{background-color:var(--frankie-muted)}.frankie-root .hover\\\\:text-foreground:hover{color:var(--frankie-foreground)}.frankie-root .hover\\\\:underline:hover{text-decoration-line:underline}.frankie-root .hover\\\\:opacity-90:hover{opacity:.9}.frankie-root .hover\\\\:opacity-100:hover{opacity:1}}.frankie-root .focus-visible\\\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .focus-visible\\\\:ring-primary:focus-visible{--tw-ring-color:var(--frankie-primary)}.frankie-root .focus-visible\\\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.frankie-root .disabled\\\\:pointer-events-none:disabled{pointer-events:none}.frankie-root .disabled\\\\:cursor-not-allowed:disabled{cursor:not-allowed}.frankie-root .disabled\\\\:opacity-50:disabled{opacity:.5}.frankie-root .dark\\\\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.frankie-root .dark\\\\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.frankie-root .\\\\[\\\\&_svg\\\\]\\\\:pointer-events-none svg{pointer-events:none}.frankie-root .\\\\[\\\\&_svg\\\\]\\\\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .\\\\[\\\\&_svg\\\\]\\\\:shrink-0 svg{flex-shrink:0}.frankie-root .\\\\[\\\\&\\\\:\\\\:-webkit-details-marker\\\\]\\\\:hidden::-webkit-details-marker{display:none}.frankie-root.frankie-chat-panel{min-height:0;position:relative}.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px var(--frankie-primary), 0 12px 28px -12px #0f172a47, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px color-mix(in srgb, var(--frankie-primary) 22%, #0f172a6b), 0 12px 28px -12px #0f172a47, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 8%, transparent)}}.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 12%, transparent)}}.frankie-root .frankie-agent-context-wrap{flex:none;justify-content:center;padding:12px 10px;display:flex}.frankie-root .frankie-agent-context{max-width:100%;color:var(--frankie-foreground);font-size:13px;position:relative}.frankie-root .frankie-agent-context>summary{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;white-space:nowrap;border-radius:999px;justify-content:center;align-items:center;gap:10px;min-height:44px;padding:7px 13px;list-style:none;display:flex;box-shadow:0 1px 3px #0000000d}.frankie-root .frankie-agent-context>summary::-webkit-details-marker{display:none}.frankie-root .frankie-agent-context>summary>svg{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-context-count{background:var(--frankie-muted);color:var(--frankie-muted-foreground);border-radius:999px;padding:2px 10px;font-size:12px}.frankie-root .frankie-context-status{color:var(--frankie-muted-foreground);align-items:center;gap:6px;font-size:12px;display:flex}.frankie-root .frankie-context-status i{background:currentColor;border-radius:50%;width:7px;height:7px}.frankie-root .frankie-context-status[data-active=true]{color:#00976b}.frankie-root .frankie-context-detail{z-index:10;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:16px;width:340px;max-width:calc(100vw - 32px);padding:18px;position:absolute;top:calc(100% + 10px);left:50%;transform:translate(-50%);box-shadow:0 12px 32px #00000016}.frankie-root .frankie-context-detail p{color:var(--frankie-muted-foreground);margin:10px 0 0;font-size:12px;line-height:1.6}.frankie-root .frankie-context-detail ul{max-height:220px;margin:12px 0;padding:0;list-style:none;overflow:auto}.frankie-root .frankie-context-detail li{border-bottom:1px solid var(--frankie-border);overflow-wrap:anywhere;justify-content:space-between;gap:12px;padding:8px 0;display:flex}.frankie-root .frankie-context-detail small{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.frankie-root.frankie-chat-panel-fill{--frankie-canvas:var(--frankie-background)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill{--frankie-canvas:color-mix(in srgb,var(--frankie-background) 60%,var(--frankie-muted))}}.frankie-root.frankie-chat-panel-fill{background:var(--frankie-canvas)}.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:44px 16px 26px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:14px max(24px,50% - 480px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-message-list{gap:38px}.frankie-root.frankie-chat-panel-fill .frankie-user-bubble{background:var(--frankie-muted);color:var(--frankie-foreground);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:24px;padding:12px 20px;font-size:15px}.frankie-root.frankie-chat-panel-fill .frankie-assistant-content{flex:1;max-width:100%;font-size:15px;line-height:1.7}.frankie-root.frankie-chat-panel-fill .frankie-assistant-message{gap:14px}.frankie-root .frankie-thinking{color:var(--frankie-muted-foreground);gap:5px}.frankie-root .frankie-thinking-label{margin-right:6px;font-size:14px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{background:0 0;border:0;flex:none;padding:16px max(24px,50% - 500px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-composer{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:28px;padding:14px;box-shadow:0 2px 4px #0000000a}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:color-mix(in srgb,var(--frankie-foreground) 32%,var(--frankie-border))}}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px color-mix(in srgb,var(--frankie-primary) 8%,transparent)}}.frankie-root.frankie-chat-panel-fill .frankie-composer-controls{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.frankie-root.frankie-chat-panel-fill .frankie-composer textarea{resize:none;width:100%;min-height:32px;max-height:140px;color:var(--frankie-foreground);box-shadow:none;background:0 0;border:0;border-radius:0;outline:none;flex:0 0 100%;order:-1;margin-bottom:4px;padding:2px 6px;font-size:16px}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Send],.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Stop]{background:var(--frankie-primary);width:42px;height:42px;color:var(--frankie-primary-foreground);border:0;border-radius:50%;margin-left:auto}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Attach\\\\ files]{border-radius:50%;width:42px;height:42px}.frankie-root .frankie-composer-actions{align-items:center;gap:6px;min-width:0;display:flex}.frankie-root .frankie-composer-meta{align-items:center;gap:8px;margin-top:6px;padding:0 2px;display:flex}.frankie-root .frankie-model-picker{align-items:center;min-width:0;display:inline-flex}.frankie-root .frankie-model-select{appearance:none;max-width:12rem;color:var(--frankie-muted-foreground);font:inherit;cursor:pointer;text-overflow:ellipsis;background-color:#0000;background-image:url(\\\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b8b8b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>\\\");background-position:right 6px center;background-repeat:no-repeat;background-size:11px;border:0;border-radius:8px;padding:5px 22px 5px 8px;font-size:12px;font-weight:500;line-height:1;transition:background-color .12s,color .12s}.frankie-root .frankie-model-select:hover{background-color:var(--frankie-muted);color:var(--frankie-foreground)}.frankie-root .frankie-model-select:disabled{opacity:.5;cursor:not-allowed}.frankie-root .frankie-model-select option{color:var(--frankie-foreground);background:var(--frankie-background);font-weight:400}.frankie-root .frankie-history-desktop{background:var(--frankie-background);width:290px;padding:20px 12px}.frankie-root .frankie-history-collapsed{width:56px;padding:20px 8px}.frankie-root .frankie-new-chat{background:0 0;border:0;justify-content:flex-start;padding:8px 12px;font-size:15px}.frankie-root .frankie-history-heading{color:var(--frankie-muted-foreground);padding:0 12px;font-size:12px;font-weight:400}.frankie-root .frankie-history-list button{min-height:44px;padding:10px 12px}.frankie-root .frankie-history-list button span{font-size:14px}.frankie-root .frankie-history-list button[aria-current],.frankie-root .frankie-history-list button:hover{background:var(--frankie-muted);border-color:#0000}.frankie-root .frankie-history-list small{display:none}.frankie-root .frankie-history-group{flex-direction:column;gap:2px;display:flex}.frankie-root .frankie-history-group+.frankie-history-group{margin-top:12px}.frankie-root .frankie-history-group-label{letter-spacing:.04em;color:var(--frankie-muted-foreground);margin:0;padding:4px 12px;font-size:11px;font-weight:600}.frankie-root .frankie-page-header{background:var(--frankie-background);height:56px;min-height:56px}.frankie-root .frankie-page-header>div{min-width:0}.frankie-root .frankie-thread-title{text-overflow:ellipsis;white-space:nowrap;max-width:55vw;font-weight:500;overflow:hidden}.frankie-root .frankie-page-header button{background:0 0;border-color:#0000}.frankie-root .frankie-page-canvas>.frankie-inline{max-width:none}@media (max-width:760px){.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:20px 8px 14px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:12px 16px 16px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{padding:10px 12px calc(12px + env(safe-area-inset-bottom))}.frankie-root .frankie-agent-context>summary{gap:6px;padding:7px 9px;font-size:12px}.frankie-root .frankie-context-count{padding:2px 7px;font-size:11px}.frankie-root .frankie-context-status{gap:4px;font-size:11px}.frankie-root .frankie-agent-context>summary>svg{width:14px}.frankie-root .frankie-thread-title{max-width:42vw}}.frankie-root .frankie-agent-context>summary>svg:first-child{box-sizing:content-box;background:var(--frankie-muted);border-radius:50%;padding:4px}.frankie-root.frankie-chat-panel-fill .frankie-agent-context>summary{font-size:14px}.frankie-root .frankie-agent-context>summary>span:first-of-type{border-right:1px solid var(--frankie-border);padding-right:10px}@media (max-width:760px){.frankie-root .frankie-page-header button>span{display:none}}.frankie-root .frankie-choices{flex-direction:column;gap:8px;margin-top:12px;display:flex}.frankie-root .frankie-choice{text-align:left;cursor:pointer;border:1px solid var(--frankie-border);border-radius:var(--frankie-radius);background:var(--frankie-background);width:100%;color:var(--frankie-foreground);font:inherit;align-items:center;gap:12px;padding:11px 14px;font-size:14px;line-height:1.4;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-root .frankie-choice:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 55%, var(--frankie-border))}}.frankie-root .frankie-choice:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 6%, var(--frankie-background))}}.frankie-root .frankie-choice:active:not(:disabled){transform:scale(.99)}.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px var(--frankie-primary);outline:none}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, var(--frankie-primary) 40%, transparent)}}.frankie-root .frankie-choice:disabled{opacity:.6;cursor:default}.frankie-root .frankie-choice-index{border:1px solid var(--frankie-border);background:var(--frankie-muted);width:24px;height:24px;color:var(--frankie-muted-foreground);border-radius:50%;flex:none;place-items:center;font-size:12px;font-weight:600;transition:background-color .15s,color .15s,border-color .15s;display:grid}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-primary);border-color:var(--frankie-primary);color:var(--frankie-primary-foreground)}.frankie-root .frankie-choice-label{flex:1;min-width:0}.frankie-root .frankie-choice-go{color:var(--frankie-muted-foreground);opacity:0;flex:none;transition:opacity .15s,transform .15s;transform:translate(-4px)}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-go{opacity:1;color:var(--frankie-primary);transform:none}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-choice,.frankie-root .frankie-choice-go,.frankie-root .frankie-choice-index{transition:none}}.frankie-root .frankie-choice-other{color:var(--frankie-muted-foreground);border-style:dashed}.frankie-root .frankie-choice-other .frankie-choice-index{background:0 0}.frankie-root .frankie-choice-other:hover:not(:disabled){color:var(--frankie-foreground)}.frankie-root .frankie-choice-other:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-muted);border-color:var(--frankie-border);color:var(--frankie-foreground)}.frankie-root .frankie-choice-hint{color:var(--frankie-muted-foreground);flex:none;font-size:12px}.frankie-root .frankie-skeleton{flex-direction:column;gap:8px;max-width:min(420px,100%);padding:4px 0 6px;display:flex}.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, var(--frankie-muted-foreground) 25%, var(--frankie-muted-foreground) 37%, var(--frankie-muted-foreground) 63%);border-radius:6px;height:12px}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 25%, color-mix(in srgb, var(--frankie-muted-foreground) 19%, transparent) 37%, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 63%)}}.frankie-root .frankie-skeleton-line{background-size:400% 100%;animation:2.2s ease-in-out infinite frankie-shimmer}.frankie-root .frankie-skeleton.is-delayed{animation:.25s .35s both frankie-skeleton-in}@keyframes frankie-skeleton-in{0%{opacity:0}to{opacity:1}}@keyframes frankie-shimmer{0%{background-position:100%}to{background-position:0}}@keyframes frankie-skeleton-pulse{0%,to{opacity:.45}50%{opacity:.85}}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-skeleton-line{background:var(--frankie-muted-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:color-mix(in srgb, var(--frankie-muted-foreground) 16%, transparent)}}.frankie-root .frankie-skeleton-line{animation:1.6s ease-in-out infinite frankie-skeleton-pulse}}@property --tw-border-style{syntax:\\\"*\\\";inherits:false;initial-value:solid}@property --tw-leading{syntax:\\\"*\\\";inherits:false}@property --tw-font-weight{syntax:\\\"*\\\";inherits:false}@property --tw-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\\\"*\\\";inherits:false}@property --tw-shadow-alpha{syntax:\\\"<percentage>\\\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\\\"*\\\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\\\"<percentage>\\\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\\\"*\\\";inherits:false}@property --tw-ring-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\\\"*\\\";inherits:false}@property --tw-inset-ring-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\\\"*\\\";inherits:false}@property --tw-ring-offset-width{syntax:\\\"<length>\\\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\\\"*\\\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:\\\"*\\\";inherits:false;initial-value:solid}@property --tw-ease{syntax:\\\"*\\\";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}\";\n","import { FRANKIE_CSS_CONTENT } from \"../generated/css-content.js\";\n\nconst STYLE_ELEMENT_ID = \"frankie-fallback-styles\";\nlet attempted = false;\n\n/**\n * Last resort for a host that never loaded `@frankieai/react/styles.css` —\n * forgot the import, or its CSS chunk hasn't landed by the time\n * `useStylesReady`'s bounded wait gives up. Injects the same stylesheet the\n * package ships as `./styles.css`, so the widget still renders correctly\n * instead of as raw, unstyled HTML.\n *\n * Only called when a host hasn't opted out via `injectStyles={false}` — the\n * documented escape hatch for a host that compiles Frankie's classes itself\n * (e.g. a Tailwind v4 host `@source`-ing this package) and would not want a\n * second, independently-themed copy of the stylesheet appended.\n */\nexport function ensureFrankieStyles(): void {\n if (attempted || typeof document === \"undefined\") return;\n attempted = true;\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ELEMENT_ID;\n style.textContent = FRANKIE_CSS_CONTENT;\n document.head.appendChild(style);\n}\n\n/** Test-only: clears the injected-once guard so each test starts clean. */\nexport function __resetFrankieStylesForTests(): void {\n attempted = false;\n document.getElementById(STYLE_ELEMENT_ID)?.remove();\n}\n","/**\n * How far a fixed or sticky bar along the bottom of the viewport (a mobile tab\n * bar, a cookie banner, a sticky \"Buy\" bar) rises over the widget's corner, in\n * pixels. The launcher sits above it instead of hiding beneath it.\n *\n * It samples the corner where the launcher sits, ignoring the widget itself,\n * and counts only elements anchored to the bottom edge and shorter than 40% of\n * the viewport, so a full-height sidebar or a modal never pushes it away.\n */\nexport function bottomObstruction(root: Element | null, position: \"bottom-right\" | \"bottom-left\", offset: number): number {\n if (typeof document === \"undefined\" || typeof window === \"undefined\" || typeof document.elementsFromPoint !== \"function\") return 0;\n const width = window.innerWidth;\n const height = window.innerHeight;\n if (!width || !height) return 0;\n const xs = position === \"bottom-right\" ? [width - offset - 24, width - 8] : [offset + 24, 8];\n const ys = [height - offset - 24, height - 4];\n let lift = 0;\n const seen = new Set<Element>();\n for (const x of xs) for (const y of ys) {\n for (const hit of document.elementsFromPoint(x, y)) {\n if (root?.contains(hit)) continue;\n const bar = anchoredAncestor(hit);\n if (!bar || seen.has(bar)) continue;\n seen.add(bar);\n const rect = bar.getBoundingClientRect();\n const anchoredToBottom = rect.bottom >= height - 4;\n const short = rect.height > 0 && rect.height < height * 0.4;\n if (anchoredToBottom && short) lift = Math.max(lift, height - rect.top);\n }\n }\n return Math.round(lift);\n}\n\n/** The nearest fixed or sticky element at or above `element` (a few levels up at most). */\nfunction anchoredAncestor(element: Element): Element | null {\n let node: Element | null = element;\n for (let depth = 0; node && depth < 8 && node !== document.body && node !== document.documentElement; depth += 1) {\n const position = getComputedStyle(node).position;\n if (position === \"fixed\" || position === \"sticky\") return node;\n node = node.parentElement;\n }\n return null;\n}\n","import type { WidgetTheme } from \"@frankieai/core\";\nimport { useSyncExternalStore, type CSSProperties } from \"react\";\n\n/**\n * Map a theme to the CSS custom properties the widget's styles read.\n *\n * Anything not set falls through to the defaults in `frankie.css`, which are\n * themselves shadcn/ui token names — so a host that already runs shadcn can\n * drop the stylesheet and the widget inherits their design system instead.\n */\nexport function themeToCssVars(theme: WidgetTheme | undefined): CSSProperties {\n if (!theme) return {};\n const vars: Record<string, string> = {};\n if (theme.primary) vars[\"--frankie-primary\"] = theme.primary;\n if (theme.primaryForeground) vars[\"--frankie-primary-foreground\"] = theme.primaryForeground;\n if (theme.background) vars[\"--frankie-background\"] = theme.background;\n if (theme.foreground) vars[\"--frankie-foreground\"] = theme.foreground;\n if (theme.muted) vars[\"--frankie-muted\"] = theme.muted;\n if (theme.border) vars[\"--frankie-border\"] = theme.border;\n if (theme.radius) vars[\"--frankie-radius\"] = `${theme.radius}rem`;\n if (theme.fontFamily) vars[\"--frankie-font\"] = theme.fontFamily;\n return vars as CSSProperties;\n}\n\n/** Resolve `mode: \"system\"` against the browser, so the widget can hard-set a class. */\nexport function resolveMode(mode: WidgetTheme[\"mode\"]): \"light\" | \"dark\" {\n if (mode === \"light\" || mode === \"dark\") return mode;\n if (typeof window === \"undefined\" || !window.matchMedia) return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\nfunction subscribeToScheme(onChange: () => void): () => void {\n const media = typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(DARK_QUERY) : undefined;\n media?.addEventListener?.(\"change\", onChange);\n return () => media?.removeEventListener?.(\"change\", onChange);\n}\nconst prefersDark = () => typeof window !== \"undefined\" && Boolean(window.matchMedia?.(DARK_QUERY).matches);\n\n/**\n * `resolveMode` as a hook. Server-rendered pages can't know the visitor's\n * scheme, and a hydrated attribute that differs from the server's is never\n * patched, so a plain `resolveMode` call could leave \"system\" stuck on light.\n * This renders light on the server and during hydration, then the visitor's\n * scheme, and follows it when they switch.\n */\nexport function useResolvedMode(mode: WidgetTheme[\"mode\"]): \"light\" | \"dark\" {\n const dark = useSyncExternalStore(subscribeToScheme, prefersDark, () => false);\n if (mode === \"light\" || mode === \"dark\") return mode;\n return dark ? \"dark\" : \"light\";\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/** shadcn/ui's class helper: conditional classes with Tailwind conflict resolution. */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ButtonHTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport const buttonVariants = cva(\n \"frankie-focus inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--frankie-radius)] text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:opacity-90\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-muted\",\n outline: \"border border-border bg-background hover:bg-muted\",\n ghost: \"hover:bg-muted\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-[calc(var(--frankie-radius)-2px)] px-3 text-xs\",\n lg: \"h-10 px-6\",\n icon: \"size-9\",\n },\n },\n defaultVariants: { variant: \"default\", size: \"default\" },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nexport function Button({ className, variant, size, asChild = false, ...props }: ButtonProps) {\n const Comp = asChild ? Slot : \"button\";\n return <Comp className={cn(buttonVariants({ variant, size }), className)} {...props} />;\n}\n","\"use client\";\n\nimport { RotateCcw, X } from \"lucide-react\";\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\nimport type { ChatMessage, ToolActivity } from \"../../lib/messages.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { Avatar } from \"../ui/avatar.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from \"../ui/card.js\";\nimport { ScrollArea } from \"../ui/scroll-area.js\";\nimport { AgentContext } from \"./AgentContext.js\";\nimport { ChatEmptyState } from \"./ChatEmptyState.js\";\nimport { Composer } from \"./Composer.js\";\nimport { MessageBubble } from \"./MessageBubble.js\";\n\nexport interface ChatPanelProps {\n className?: string;\n /** Replace the header entirely, or pass `false` to drop it. */\n header?: ReactNode | false;\n /** Rendered under the composer — good for a \"powered by\" or a privacy note. */\n footer?: ReactNode;\n /** Shown before the first message, in place of the default greeting block. */\n emptyState?: ReactNode;\n /** Take over message rendering completely. */\n renderMessage?: (message: ChatMessage) => ReactNode;\n /** Take over the tool-activity rows. */\n renderTool?: (tool: ToolActivity) => ReactNode;\n placeholder?: string;\n /** Shows a close button in the header when provided. */\n onClose?: () => void;\n autoFocus?: boolean;\n /** Fill the parent instead of the fixed floating size — for full-page use. */\n fill?: boolean;\n}\n\nexport function ChatPanel({\n className,\n header,\n footer,\n emptyState,\n renderMessage,\n renderTool,\n placeholder,\n onClose,\n autoFocus,\n fill,\n}: ChatPanelProps) {\n const { config, configError, loading } = useFrankie();\n const theme = useFrankieTheme();\n const { messages, busy, restoring, error, send, stop, reset } = useFrankieChat();\n const scrollRef = useRef<HTMLDivElement>(null);\n // Tracks whether the reader was at the bottom just before this render, not\n // where the bottom ends up after it. Measuring post-render (against the\n // already-grown scrollHeight) breaks as soon as one update — a sent message\n // plus the reply's opening burst of text, say — adds more than the near-\n // bottom threshold in a single paint: the check then reads \"scrolled away\"\n // even though the reader never touched the scrollbar, and every later\n // delta stops following. A live scroll listener captures the real, current\n // position instead of re-deriving a stale one from updated content.\n const atBottomRef = useRef(true);\n\n const features = config?.features;\n const pickerModels = features?.modelPicker.enabled ? features.modelPicker.models : [];\n const [model, setModel] = useState<string | undefined>(undefined);\n // A \"Something else\" choice focuses the composer through this handle.\n const composerFocus = useRef<(() => void) | null>(null);\n const focusComposer = () => composerFocus.current?.();\n\n useEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n const onScroll = () => {\n atBottomRef.current = node.scrollHeight - node.scrollTop - node.clientHeight < 160;\n };\n node.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => node.removeEventListener(\"scroll\", onScroll);\n }, []);\n\n // Follow the stream, but only while the reader is already near the bottom.\n // With no messages yet the welcome screen starts at its top: scrolling it to\n // the bottom cut its heading off in the small floating panel.\n useEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n if (messages.length === 0) { node.scrollTop = 0; return; }\n if (!atBottomRef.current) return;\n node.scrollTop = node.scrollHeight;\n }, [messages]);\n\n const name = config?.branding.name ?? \"Assistant\";\n const mode = useResolvedMode(theme?.mode);\n\n return (\n <Card\n className={cn(\n \"frankie-root frankie-chat-panel\",\n fill && \"frankie-chat-panel-fill\",\n fill ? \"h-full w-full rounded-none border-0 shadow-none\" : \"h-[32rem] w-[22rem] max-w-[calc(100vw-2rem)]\",\n mode === \"dark\" && \"dark\",\n className,\n )}\n style={themeToCssVars(theme)}\n data-frankie-mode={mode}\n >\n {header === false ? null : header !== undefined ? (\n header\n ) : (\n <CardHeader>\n <Avatar {...(config?.branding.avatarUrl ? { src: config.branding.avatarUrl } : {})} name={name} />\n <div className=\"min-w-0 flex-1\">\n <CardTitle>{name}</CardTitle>\n {config?.branding.tagline ? <CardDescription>{config.branding.tagline}</CardDescription> : null}\n </div>\n <Button size=\"icon\" variant=\"ghost\" onClick={reset} aria-label=\"Start a new conversation\" title=\"New conversation\">\n <RotateCcw />\n </Button>\n {onClose ? (\n <Button size=\"icon\" variant=\"ghost\" onClick={onClose} aria-label=\"Close\">\n <X />\n </Button>\n ) : null}\n </CardHeader>\n )}\n\n <AgentContext />\n <CardContent className=\"frankie-conversation-content\">\n <ScrollArea ref={scrollRef} className=\"frankie-transcript flex h-full flex-col gap-4 p-4\" aria-live=\"polite\">\n {configError ? (\n <div className=\"frankie-config-error\" role=\"alert\">\n <strong>Frankie couldn’t start</strong>\n <p>{configError}</p>\n <p>Refresh this page after checking the workspace’s allowed origin and sign-in settings.</p>\n </div>\n ) : loading ? (\n <p className=\"text-sm text-muted-foreground\">Starting Frankie…</p>\n ) : messages.length === 0 ? (\n (emptyState ?? <ChatEmptyState />)\n ) : (\n <div className=\"frankie-message-list flex flex-col gap-4\">\n {messages.map((message, index) =>\n renderMessage ? (\n <div key={message.id}>{renderMessage(message)}</div>\n ) : (\n <MessageBubble\n key={message.id}\n message={message}\n assistantName={name}\n {...(config?.branding.avatarUrl ? { avatarUrl: config.branding.avatarUrl } : {})}\n showToolActivity={config?.showToolActivity ?? true}\n {...(renderTool ? { renderTool } : {})}\n onChoose={send}\n onRequestInput={focusComposer}\n active={index === messages.length - 1}\n busy={busy}\n />\n ),\n )}\n </div>\n )}\n </ScrollArea>\n </CardContent>\n\n <CardFooter className=\"frankie-composer-footer flex flex-col gap-2\">\n {error ? <p className=\"w-full text-xs text-red-600 dark:text-red-400\">{error}</p> : null}\n <div className=\"w-full\">\n <Composer\n onSend={send}\n onStop={stop}\n busy={busy}\n focusHandleRef={composerFocus}\n disabled={loading || restoring || Boolean(configError)}\n {...(placeholder ? { placeholder } : {})}\n {...(autoFocus ? { autoFocus } : {})}\n {...(features?.attachments.enabled ? { attachments: features.attachments } : {})}\n {...(pickerModels.length\n ? { modelPicker: { models: pickerModels, value: model ?? pickerModels[0]?.id, onChange: setModel } }\n : {})}\n />\n </div>\n {footer}\n </CardFooter>\n </Card>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils.js\";\n\nexport interface AvatarProps {\n src?: string;\n name: string;\n className?: string;\n}\n\n/** Image avatar with an initials fallback — no image request when `src` is absent. */\nexport function Avatar({ src, name, className }: AvatarProps) {\n const initials = name\n .split(/\\s+/)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n\n return (\n <span\n className={cn(\n \"inline-flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary text-[0.625rem] font-semibold text-primary-foreground\",\n className,\n )}\n aria-hidden=\"true\"\n >\n {src ? <img src={src} alt=\"\" className=\"size-full object-cover\" /> : initials}\n </span>\n );\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\n \"flex flex-col rounded-[calc(var(--frankie-radius)+4px)] border border-border bg-background text-foreground shadow-lg\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function CardHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"flex items-center gap-3 border-b border-border px-4 py-3\", className)} {...props} />;\n}\n\nexport function CardTitle({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"text-sm font-semibold leading-tight\", className)} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"text-xs text-muted-foreground\", className)} {...props} />;\n}\n\nexport function CardContent({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"flex-1 overflow-hidden\", className)} {...props} />;\n}\n\nexport function CardFooter({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"border-t border-border p-3\", className)} {...props} />;\n}\n","\"use client\";\n\nimport { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A plain overflow container rather than Radix's ScrollArea: the widget lives\n * inside someone else's page, and a native scroller inherits their scrollbar\n * behaviour instead of fighting it.\n */\nexport const ScrollArea = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n function ScrollArea({ className, ...props }, ref) {\n return <div ref={ref} className={cn(\"frankie-scroll overflow-y-auto overscroll-contain\", className)} {...props} />;\n },\n);\n","\"use client\";\nimport { ArrowUpRight, Sparkles } from \"lucide-react\";\nimport { useFrankie } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\n\n/** Reports server-confirmed skills for the current turn; never implies drafts are active. */\nexport function AgentContext() {\n const { loading, configError } = useFrankie();\n const { loadedSkills, activeApis, busy, restoring, error } = useFrankieChat();\n const status = configError || error ? \"Unavailable\" : loading || restoring ? \"Connecting\" : busy && loadedSkills === null ? \"Checking\" : loadedSkills?.length ? \"Active\" : loadedSkills ? \"Tools only\" : \"Ready\";\n return <div className=\"frankie-agent-context-wrap\">\n <details className=\"frankie-agent-context\">\n <summary aria-label=\"Agent context\"><Sparkles size={17} aria-hidden=\"true\" /><span>Agent context</span><span className=\"frankie-context-count\">{loadedSkills === null ? \"Not checked\" : `${loadedSkills.length} ${loadedSkills.length === 1 ? \"skill\" : \"skills\"}`}</span><span className=\"frankie-context-status\" data-active={status === \"Active\"}><i aria-hidden=\"true\" />{status}</span><ArrowUpRight size={15} aria-hidden=\"true\" /></summary>\n <div className=\"frankie-context-detail\">\n <strong>{loadedSkills === null ? \"Skills are checked when you send a message\" : loadedSkills.length ? `${loadedSkills.length} ${loadedSkills.length === 1 ? \"skill\" : \"skills\"} loaded` : \"No skills loaded for this turn\"}</strong>\n {loadedSkills?.length ? <ul>{loadedSkills.map(skill => <li key={skill.id}><span>{skill.name}</span><small>v{skill.revision}</small></li>)}</ul> : <p>Frankie can use the tools approved for this workspace. Drafts and skills without available tools are not loaded.</p>}\n <p>Published skills and updates are checked on every message. Only approved, available tools can run.</p>\n {activeApis?.length ? <ul className=\"frankie-active-apis\">{activeApis.map(api => <li key={api.name}><span>{api.name}</span><small>spec {api.specHash} · {api.operationCount} {api.operationCount === 1 ? \"operation\" : \"operations\"}</small></li>)}</ul> : null}\n </div>\n </details>\n <span className=\"frankie-sr-only\" role=\"status\">{loadedSkills === null ? status : `${loadedSkills.length} ${loadedSkills.length === 1 ? \"skill\" : \"skills\"} loaded. ${status}.`}</span>\n </div>;\n}\n","\"use client\";\n\nimport { BarChart3, BookOpen, Compass, FileText, Image as ImageIcon, LayoutGrid, Lightbulb, MessageSquare, Mic, Music, Sparkles, Wand2, Zap, type LucideIcon } from \"lucide-react\";\nimport { useFrankie } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\n\n/** Named icons a starter card may choose (mirrors core's STARTER_PROMPT_ICONS). */\nconst NAMED_ICONS: Record<string, LucideIcon> = {\n sparkles: Sparkles, wand: Wand2, book: BookOpen, chat: MessageSquare, chart: BarChart3, bolt: Zap,\n music: Music, mic: Mic, image: ImageIcon, compass: Compass, lightbulb: Lightbulb, file: FileText,\n};\n\n/** Rotating icon set for cards without an explicit icon, so each reads distinctly. */\nconst PROMPT_ICONS = [Sparkles, Wand2, BarChart3, Zap, MessageSquare, LayoutGrid];\n\n/** A prompt may pack a short description after an em/en dash or a hyphen (\"Do X — the details\"); split it so the card can show a title and subtitle. Falls back to title-only. */\nfunction splitPrompt(prompt: string): { title: string; description?: string } {\n const match = prompt.match(/^(.+?)\\s+[—–]\\s+(.+)$/) ?? prompt.match(/^(.+?)\\s+-\\s+(.+)$/);\n if (match) return { title: match[1]!.trim(), description: match[2]!.trim() };\n return { title: prompt.trim() };\n}\n\n/**\n * The empty state shown before the first message on every chat surface — the\n * floating widget, the inline panel, and the full page. A \"Welcome to {name}\"\n * line plus a grid of suggestion cards (title + optional subtitle). The prompts\n * are workspace-driven: an authored `suggestedPrompts` list, or ones the server\n * derived from the workspace's published skills. Clicking a card sends only the\n * title. The card grid collapses to one column in the narrow widget via a\n * container query (see `.frankie-page-prompts`).\n */\nexport function ChatEmptyState() {\n const { config } = useFrankie();\n const { send, busy, restoring } = useFrankieChat();\n const name = config?.branding.name ?? \"Frankie\";\n // Explicit starter cards (configured in the dashboard) win; each sends its own\n // message on click. Otherwise fall back to the string suggestions (authored or\n // skill-derived), where the card title is also the message that's sent.\n const starters = config?.starterPrompts?.length\n ? config.starterPrompts.slice(0, 6).map(card => ({ title: card.title, description: card.description, message: card.message, icon: card.icon }))\n : (config?.suggestedPrompts ?? []).slice(0, 6).map(prompt => {\n const { title, description } = splitPrompt(prompt);\n return { title, description, message: title, icon: undefined as string | undefined };\n });\n return <section className=\"frankie-page-empty\" aria-label=\"Start a conversation\">\n <div className=\"frankie-page-welcome\">\n <h1>Welcome to {name}</h1>\n <p>{config?.greeting ?? `Ask me anything about your ${name} workspace`}</p>\n </div>\n {starters.length ? <div className=\"frankie-page-suggest\">\n <p className=\"frankie-page-suggest-label\">Try asking me to</p>\n <div className=\"frankie-page-prompts\">{starters.map((card, index) => {\n const Icon = (card.icon && NAMED_ICONS[card.icon]) || PROMPT_ICONS[index % PROMPT_ICONS.length]!;\n return <button key={card.title + index} type=\"button\" disabled={busy || restoring} onClick={() => void send(card.message)} title={card.description || undefined}>\n <span className=\"frankie-prompt-icon\" aria-hidden><Icon size={18} /></span>\n <span className=\"frankie-prompt-text\"><strong>{card.title}</strong>{card.description ? <small>{card.description}</small> : null}</span>\n </button>;\n })}</div>\n <p className=\"frankie-page-suggest-hint\">Click a suggestion or type your own question below</p>\n </div> : null}\n </section>;\n}\n","\"use client\";\n\nimport { ArrowUp, Paperclip, Plus, Square, X } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState, type KeyboardEvent, type MutableRefObject } from \"react\";\nimport type { ChatAttachment } from \"@frankieai/core\";\nimport { Button } from \"../ui/button.js\";\nimport { Textarea } from \"../ui/textarea.js\";\n\nexport interface ComposerAttachmentsConfig {\n enabled: boolean;\n /** Accepted MIME patterns, e.g. \"image/*\". */\n accept: string[];\n maxCount: number;\n maxMb: number;\n}\n\nexport interface ComposerModelPicker {\n models: { id: string; label: string }[];\n value?: string;\n onChange: (id: string) => void;\n}\n\nexport interface ComposerProps {\n onSend: (text: string, opts?: { attachments?: ChatAttachment[]; model?: string }) => void;\n onStop: () => void;\n busy: boolean;\n placeholder?: string;\n disabled?: boolean;\n autoFocus?: boolean;\n /** Enables the attach button when provided and `enabled`. */\n attachments?: ComposerAttachmentsConfig;\n /** Enables a model dropdown when provided with at least one model. */\n modelPicker?: ComposerModelPicker;\n /** Populated with a function that focuses the input, so a \"type your own\" choice can jump here. */\n focusHandleRef?: MutableRefObject<(() => void) | null>;\n}\n\ninterface PickedFile extends ChatAttachment {\n id: string;\n}\n\nconst MAX_ROWS_PX = 140;\n\nexport function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker, focusHandleRef }: ComposerProps) {\n const [value, setValue] = useState(\"\");\n const [files, setFiles] = useState<PickedFile[]>([]);\n const [fileError, setFileError] = useState<string | undefined>(undefined);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Publish a focus handle so a \"Something else\" choice elsewhere in the panel\n // can move the caret here without reaching into the DOM.\n useEffect(() => {\n if (!focusHandleRef) return;\n focusHandleRef.current = () => textareaRef.current?.focus();\n return () => { focusHandleRef.current = null; };\n }, [focusHandleRef]);\n\n const canAttach = Boolean(attachments?.enabled);\n const models = modelPicker?.models ?? [];\n\n // Grow with the content up to a cap, then scroll.\n useEffect(() => {\n const node = textareaRef.current;\n if (!node) return;\n node.style.height = \"auto\";\n node.style.height = `${Math.min(node.scrollHeight, MAX_ROWS_PX)}px`;\n }, [value]);\n\n const addFiles = useCallback(\n async (list: FileList | null) => {\n if (!list || !attachments) return;\n setFileError(undefined);\n const room = attachments.maxCount - files.length;\n const chosen = Array.from(list).slice(0, Math.max(0, room));\n if (Array.from(list).length > room) setFileError(`You can attach up to ${attachments.maxCount} file(s).`);\n\n const next: PickedFile[] = [];\n for (const file of chosen) {\n if (!mimeAccepted(file.type || \"application/octet-stream\", attachments.accept)) {\n setFileError(`Files of type ${file.type || \"unknown\"} aren't allowed.`);\n continue;\n }\n if (file.size > attachments.maxMb * 1024 * 1024) {\n setFileError(`Each file must be under ${attachments.maxMb} MB.`);\n continue;\n }\n try {\n next.push({\n id: `${file.name}-${file.size}-${Math.random().toString(36).slice(2, 7)}`,\n name: file.name,\n mimeType: file.type || \"application/octet-stream\",\n data: await readAsBase64(file),\n });\n } catch {\n setFileError(\"Couldn't read that file.\");\n }\n }\n if (next.length) setFiles((current) => [...current, ...next]);\n },\n [attachments, files.length],\n );\n\n const removeFile = (id: string) => setFiles((current) => current.filter((file) => file.id !== id));\n\n const submit = useCallback(() => {\n const text = value.trim();\n if ((!text && files.length === 0) || busy || disabled) return;\n setValue(\"\");\n const attached = files.map(({ name, mimeType, data }) => ({ name, mimeType, data }));\n setFiles([]);\n setFileError(undefined);\n onSend(text, {\n ...(attached.length ? { attachments: attached } : {}),\n ...(modelPicker?.value ? { model: modelPicker.value } : {}),\n });\n }, [busy, disabled, files, modelPicker?.value, onSend, value]);\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === \"Enter\" && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n submit();\n }\n };\n\n const nothingToSend = value.trim().length === 0 && files.length === 0;\n\n return (\n <form\n className=\"frankie-composer flex flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n {files.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {files.map((file) => (\n <span\n key={file.id}\n className=\"inline-flex max-w-[14rem] items-center gap-1.5 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs\"\n >\n <Paperclip className=\"size-3 shrink-0 opacity-70\" />\n <span className=\"truncate\">{file.name}</span>\n <button\n type=\"button\"\n onClick={() => removeFile(file.id)}\n aria-label={`Remove ${file.name}`}\n className=\"frankie-focus shrink-0 opacity-60 hover:opacity-100\"\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n </div>\n ) : null}\n\n {fileError ? <p className=\"text-xs text-red-600 dark:text-red-400\">{fileError}</p> : null}\n\n <div className=\"frankie-composer-controls flex items-end gap-2\">\n <Textarea\n ref={textareaRef}\n rows={1}\n value={value}\n disabled={disabled}\n autoFocus={autoFocus}\n onChange={(event) => setValue(event.target.value)}\n onKeyDown={onKeyDown}\n placeholder={placeholder ?? \"Ask anything…\"}\n aria-label=\"Message\"\n />\n\n {/* One toolbar row: attach + model picker on the left, send on the right —\n instead of stacking the model picker on its own line below. */}\n <div className=\"frankie-composer-actions\">\n {canAttach ? (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n hidden\n accept={attachments?.accept.join(\",\")}\n onChange={(event) => {\n void addFiles(event.target.files);\n event.target.value = \"\";\n }}\n />\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"outline\"\n disabled={disabled || files.length >= (attachments?.maxCount ?? 0)}\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Attach files\"\n title=\"Attach files\"\n >\n <Plus />\n </Button>\n </>\n ) : null}\n\n {models.length > 0 ? (\n <label className=\"frankie-model-picker\" title=\"Model\">\n <span className=\"frankie-sr-only\">Model</span>\n <select\n value={modelPicker?.value ?? models[0]?.id}\n onChange={(event) => modelPicker?.onChange(event.target.value)}\n disabled={disabled}\n aria-label=\"Model\"\n className=\"frankie-model-select\"\n >\n {models.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label}\n </option>\n ))}\n </select>\n </label>\n ) : null}\n </div>\n\n {busy ? (\n <Button type=\"button\" size=\"icon\" variant=\"outline\" onClick={onStop} aria-label=\"Stop\">\n <Square />\n </Button>\n ) : (\n <Button type=\"submit\" size=\"icon\" disabled={disabled || nothingToSend} aria-label=\"Send\">\n <ArrowUp />\n </Button>\n )}\n </div>\n </form>\n );\n}\n\nfunction mimeAccepted(mimeType: string, accept: string[]): boolean {\n return accept.some((pattern) =>\n pattern.endsWith(\"/*\") ? mimeType.startsWith(pattern.slice(0, -1)) : pattern === mimeType,\n );\n}\n\n/** Read a File to a base64 string (no data-URL prefix), for the wire protocol. */\nfunction readAsBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = String(reader.result);\n const comma = result.indexOf(\",\");\n resolve(comma >= 0 ? result.slice(comma + 1) : result);\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(file);\n });\n}\n\n","\"use client\";\n\nimport { forwardRef, type TextareaHTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** `forwardRef` rather than React 19's ref-as-prop, so React 18 hosts work too. */\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>(\n function Textarea({ className, ...props }, ref) {\n return (\n <textarea\n ref={ref}\n className={cn(\n \"frankie-focus flex min-h-[2.25rem] w-full resize-none rounded-[var(--frankie-radius)] border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n },\n);\n","\"use client\";\n\nimport { Check, Copy, Paperclip } from \"lucide-react\";\nimport \"./message-actions.css\";\nimport { memo, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport { extractChoices } from \"../../lib/choices.js\";\nimport type { ChatMessage, ToolActivity } from \"../../lib/messages.js\";\nimport { Avatar } from \"../ui/avatar.js\";\nimport { Choices } from \"./Choices.js\";\nimport { Markdown } from \"./Markdown.js\";\nimport { ToolActivityList } from \"./ToolActivityList.js\";\n\nexport interface MessageBubbleProps {\n message: ChatMessage;\n assistantName: string;\n avatarUrl?: string;\n showToolActivity: boolean;\n renderTool?: (tool: ToolActivity) => ReactNode;\n /** Send a picked choice as the next message. Omitted when the host renders its own. */\n onChoose?: (choice: string) => void;\n /** Focus the composer so the user can type their own answer instead of picking. */\n onRequestInput?: () => void;\n /** True only for the newest message, so stale choices from earlier turns aren't clickable. */\n active?: boolean;\n /** A turn is in flight, so choices should be disabled. */\n busy?: boolean;\n}\n\n// Memoized: during a stream only the message whose object identity changed (the\n// one being written) re-renders. Every earlier bubble keeps its parsed markdown,\n// which is what keeps a long transcript from re-laying-out on every token.\nexport const MessageBubble = memo(function MessageBubble({\n message,\n assistantName,\n avatarUrl,\n showToolActivity,\n renderTool,\n onChoose,\n onRequestInput,\n active,\n busy,\n}: MessageBubbleProps) {\n if (message.role === \"user\") {\n return (\n <div className=\"frankie-user-message flex flex-col items-end gap-1\">\n {message.attachments?.length ? (\n <div className=\"flex max-w-[85%] flex-wrap justify-end gap-1\">\n {message.attachments.map((file, index) => (\n <span\n key={`${file.name}-${index}`}\n className=\"inline-flex max-w-[12rem] items-center gap-1 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs\"\n >\n <Paperclip className=\"size-3 shrink-0 opacity-70\" />\n <span className=\"truncate\">{file.name}</span>\n </span>\n ))}\n </div>\n ) : null}\n {message.text ? (\n <div className=\"flex max-w-[85%] items-end gap-1\">\n <div className=\"frankie-user-bubble rounded-[var(--frankie-radius)] bg-primary px-3 py-2 text-sm text-primary-foreground\">{message.text}</div>\n <MessageCopyButton text={message.text} />\n </div>\n ) : null}\n </div>\n );\n }\n\n // Lift any offered options out of the prose. While the reply is still\n // streaming, a half-written block is hidden (options stays null) so the raw\n // fence never flashes. Options become clickable only on the newest, settled\n // message so answering an earlier turn's question can't jump the transcript.\n const { body, options } = extractChoices(message.text);\n const showChoices = Boolean(active && onChoose && options && message.status === \"done\");\n // A running tool row is its own progress indicator; otherwise, while the turn\n // is still streaming with no visible text yet, the model is thinking (before\n // its first words, or in the pause after a tool result while it composes the\n // answer). Show a skeleton there so those gaps never look frozen.\n // Only before the first action: once tools have run, the \"Working\" list is\n // the progress indicator, and a skeleton toggling in the gaps between calls\n // reads as flashing.\n const waiting = message.status === \"streaming\" && body.length === 0 && message.tools.length === 0;\n\n return (\n <div className=\"frankie-assistant-message flex gap-2\">\n <div className=\"frankie-assistant-actions mt-0.5 flex shrink-0 flex-col items-center gap-1\">\n <Avatar {...(avatarUrl ? { src: avatarUrl } : {})} name={assistantName} />\n {body ? <MessageCopyButton text={body} /> : null}\n </div>\n <div className=\"frankie-assistant-content min-w-0 max-w-[85%]\">\n {message.status === \"error\" ? (\n <p className=\"rounded-[var(--frankie-radius)] bg-red-500/10 px-3 py-2 text-sm text-red-600 dark:text-red-400\">\n {message.error}\n </p>\n ) : body ? (\n <Markdown text={body} className=\"text-sm\" />\n ) : null}\n {showChoices ? <Choices options={options ?? []} onPick={onChoose!} {...(onRequestInput ? { onSomethingElse: onRequestInput } : {})} disabled={busy} /> : null}\n {showToolActivity ? (\n <ToolActivityList tools={message.tools} {...(renderTool ? { renderTool } : {})} />\n ) : null}\n {waiting ? <Thinking delayed /> : null}\n </div>\n </div>\n );\n});\n\nfunction MessageCopyButton({ text }: { text: string }) {\n const [copied, setCopied] = useState(false);\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n } catch { /* Clipboard access can be disabled by an embedding host. */ }\n };\n return <button type=\"button\" onClick={() => void copy()} className=\"frankie-message-copy inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-transparent text-muted-foreground transition hover:border-border hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\" aria-label={copied ? \"Copied\" : \"Copy message\"} title={copied ? \"Copied\" : \"Copy message\"}>{copied ? <Check className=\"size-3.5\" aria-hidden=\"true\" /> : <Copy className=\"size-3.5\" aria-hidden=\"true\" />}</button>;\n}\n\nexport function TypingDots() {\n return (\n <span className=\"frankie-thinking inline-flex items-center gap-1 py-2\" role=\"status\" aria-label=\"Thinking\">\n <span className=\"frankie-thinking-label\">Thinking</span>\n {[0, 1, 2].map((index) => (\n <span\n key={index}\n className={cn(\"size-1.5 animate-pulse rounded-full bg-muted-foreground\")}\n style={{ animationDelay: `${index * 150}ms` }}\n />\n ))}\n </span>\n );\n}\n\n/**\n * A shimmering skeleton shown while the reply is still being produced — before\n * the first words, or during the pause after a tool result while the model\n * composes its answer — so those gaps read as \"loading\" rather than frozen.\n */\nexport function Thinking({ delayed = false }: { delayed?: boolean }) {\n return (\n <div className={\"frankie-skeleton\" + (delayed ? \" is-delayed\" : \"\")} role=\"status\" aria-label=\"Thinking\">\n <span className=\"frankie-skeleton-line\" style={{ width: \"62%\" }} />\n <span className=\"frankie-skeleton-line\" style={{ width: \"92%\" }} />\n <span className=\"frankie-skeleton-line\" style={{ width: \"48%\" }} />\n </div>\n );\n}\n\n","/**\n * Interactive choice blocks.\n *\n * The agent can end a reply with a fenced block to offer the user a small set\n * of discrete answers:\n *\n * ```frankie:choices\n * British\n * Indian\n * Australian\n * ```\n *\n * The SDK lifts that block out of the prose and renders it as clickable options\n * (the composer stays open, so the user can always type their own answer). The\n * block is a plain fenced code block, so a host that doesn't understand it just\n * shows a harmless code listing instead — never broken markup.\n */\nexport interface ExtractedChoices {\n /** The reply text with the choices block removed. */\n body: string;\n /** The offered options, or null when there is no complete block. */\n options: string[] | null;\n}\n\nconst CLOSED = /```frankie:choices[ \\t]*\\r?\\n([\\s\\S]*?)```/;\nconst OPEN = /```frankie:choices[ \\t]*\\r?\\n?[\\s\\S]*$/;\n\nexport function extractChoices(text: string): ExtractedChoices {\n const closed = CLOSED.exec(text);\n if (closed) {\n const options = parseOptionLines(closed[1] ?? \"\");\n const body = (text.slice(0, closed.index) + text.slice(closed.index + closed[0].length))\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trimEnd();\n return { body, options: options.length ? options : null };\n }\n // An unterminated block means the reply is still streaming the options in.\n // Hide the half-written fence so the user never sees the raw ``` marker.\n const open = OPEN.exec(text);\n if (open) return { body: text.slice(0, open.index).trimEnd(), options: null };\n return { body: text, options: null };\n}\n\nfunction parseOptionLines(block: string): string[] {\n return block\n .split(\"\\n\")\n .map((line) => line.trim().replace(/^([-*+]|\\d+[.)])\\s+/, \"\").trim())\n .filter(Boolean);\n}\n","\"use client\";\n\nimport { ArrowRight, PencilLine } from \"lucide-react\";\n\n/**\n * Clickable answer options the agent offered via a `frankie:choices` block.\n * Picking one sends it as the next message. A trailing \"Something else\" row\n * doesn't send — it moves the caret to the composer so the user can type their\n * own answer, while the offered options stay put in case they change their mind.\n */\nexport function Choices({\n options,\n onPick,\n onSomethingElse,\n disabled,\n}: {\n options: string[];\n onPick: (choice: string) => void;\n onSomethingElse?: () => void;\n disabled?: boolean;\n}) {\n if (options.length === 0) return null;\n return (\n <div className=\"frankie-choices\" role=\"group\" aria-label=\"Choose an option\">\n {options.map((option, index) => (\n <button\n key={`${index}-${option}`}\n type=\"button\"\n className=\"frankie-choice\"\n disabled={disabled}\n onClick={() => onPick(option)}\n >\n <span className=\"frankie-choice-index\" aria-hidden>{index + 1}</span>\n <span className=\"frankie-choice-label\">{option}</span>\n <ArrowRight className=\"frankie-choice-go size-4\" aria-hidden />\n </button>\n ))}\n {onSomethingElse ? (\n <button\n type=\"button\"\n className=\"frankie-choice frankie-choice-other\"\n disabled={disabled}\n onClick={onSomethingElse}\n >\n <span className=\"frankie-choice-index\" aria-hidden><PencilLine className=\"size-3.5\" /></span>\n <span className=\"frankie-choice-label\">Something else…</span>\n <span className=\"frankie-choice-hint\" aria-hidden>Type your own</span>\n </button>\n ) : null}\n </div>\n );\n}\n","\"use client\";\n\nimport { Fragment, memo, useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A deliberately small Markdown subset — headings, paragraphs, lists, fenced\n * code, bold, italic, inline code and links.\n *\n * Everything is rendered as React elements; nothing goes through\n * `dangerouslySetInnerHTML`. Model output lands in someone else's production\n * page, so it is never trusted as markup.\n *\n * Memoized on `text`: while a reply streams, the parent re-renders every frame,\n * but the block tree is only recomputed when the text actually grows — the\n * settled messages above never re-parse at all.\n */\nexport const Markdown = memo(function Markdown({ text, className }: { text: string; className?: string }) {\n const blocks = useMemo(() => renderBlocks(text), [text]);\n return <div className={cn(\"frankie-prose\", className)}>{blocks}</div>;\n});\n\nfunction renderBlocks(text: string): ReactNode[] {\n const lines = text.split(\"\\n\");\n const blocks: ReactNode[] = [];\n let paragraph: string[] = [];\n let list: { ordered: boolean; items: string[] } | null = null;\n let code: { lang: string; lines: string[] } | null = null;\n\n const flushParagraph = () => {\n if (paragraph.length === 0) return;\n blocks.push(<p key={`p${blocks.length}`}>{renderInline(paragraph.join(\" \"))}</p>);\n paragraph = [];\n };\n const flushList = () => {\n if (!list) return;\n const items = list.items.map((item, index) => <li key={index}>{renderInline(item)}</li>);\n blocks.push(list.ordered ? <ol key={`l${blocks.length}`}>{items}</ol> : <ul key={`l${blocks.length}`}>{items}</ul>);\n list = null;\n };\n\n for (const line of lines) {\n const fence = /^```(\\w*)\\s*$/.exec(line);\n if (fence) {\n if (code) {\n blocks.push(\n <pre key={`c${blocks.length}`}>\n <code>{code.lines.join(\"\\n\")}</code>\n </pre>,\n );\n code = null;\n } else {\n flushParagraph();\n flushList();\n code = { lang: fence[1] ?? \"\", lines: [] };\n }\n continue;\n }\n if (code) {\n code.lines.push(line);\n continue;\n }\n\n const bullet = /^\\s*[-*+]\\s+(.*)$/.exec(line);\n const ordered = /^\\s*\\d+[.)]\\s+(.*)$/.exec(line);\n if (bullet ?? ordered) {\n flushParagraph();\n const isOrdered = Boolean(ordered);\n if (!list || list.ordered !== isOrdered) {\n flushList();\n list = { ordered: isOrdered, items: [] };\n }\n list.items.push((ordered?.[1] ?? bullet?.[1]) ?? \"\");\n continue;\n }\n\n if (line.trim() === \"\") {\n flushParagraph();\n flushList();\n continue;\n }\n\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line);\n if (heading) {\n flushParagraph();\n flushList();\n const level = (heading[1] ?? \"#\").length;\n const Tag = level <= 1 ? \"h3\" : level === 2 ? \"h4\" : \"h5\";\n blocks.push(<Tag key={`h${blocks.length}`} className=\"frankie-h\">{renderInline(heading[2] ?? \"\")}</Tag>);\n continue;\n }\n\n flushList();\n paragraph.push(line.trim());\n }\n\n if (code) {\n blocks.push(\n <pre key={`c${blocks.length}`}>\n <code>{code.lines.join(\"\\n\")}</code>\n </pre>,\n );\n }\n flushParagraph();\n flushList();\n return blocks;\n}\n\n// `*italic*` needs a non-space, non-asterisk character right inside each\n// asterisk, so \"2 * 3\" and a \"***\" divider stay text.\nconst INLINE = /(\\*\\*[^*]+\\*\\*|\\*(?=[^\\s*])[^*\\n]*?[^\\s*]\\*(?!\\*)|`[^`]+`|\\[[^\\]]+\\]\\((?:https?:\\/\\/|\\/)[^)\\s]+\\)|\\bhttps?:\\/\\/[^\\s<>()]+)/g;\n\nfunction renderInline(text: string): ReactNode[] {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n\n for (const match of text.matchAll(INLINE)) {\n const token = match[0];\n const start = match.index;\n if (start > lastIndex) nodes.push(text.slice(lastIndex, start));\n nodes.push(<Fragment key={start}>{renderToken(token)}</Fragment>);\n lastIndex = start + token.length;\n }\n\n if (lastIndex < text.length) nodes.push(text.slice(lastIndex));\n return nodes;\n}\n\nfunction renderToken(token: string): ReactNode {\n if (token.startsWith(\"**\")) return <strong>{token.slice(2, -2)}</strong>;\n if (token.startsWith(\"*\")) return <em>{token.slice(1, -1)}</em>;\n if (token.startsWith(\"`\")) return <code>{token.slice(1, -1)}</code>;\n\n const link = /^\\[([^\\]]+)\\]\\(([^)]+)\\)$/.exec(token);\n const href = link ? (link[2] ?? \"\") : token;\n const label = link ? (link[1] ?? \"\") : token;\n\n // Only http(s) and same-site paths become links; anything else stays text,\n // so a model can never emit a `javascript:` href into the host page.\n if (!/^(https?:\\/\\/|\\/)/.test(href)) return token;\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer nofollow\">\n {label}\n </a>\n );\n}\n","\"use client\";\n\nimport { Check, ChevronDown, Loader2, ShieldAlert, Sparkles, Wrench, X } from \"lucide-react\";\nimport { SKILL_TOOL_NAME } from \"@frankieai/core\";\nimport { Badge } from \"../ui/badge.js\";\nimport type { ToolActivity } from \"../../lib/messages.js\";\n\nexport interface ToolActivityListProps {\n tools: ToolActivity[];\n /** Replace the default row rendering entirely. */\n renderTool?: (tool: ToolActivity) => React.ReactNode;\n}\n\n/** The \"what the assistant is doing\" strip under a streaming answer. */\nexport function ToolActivityList({ tools, renderTool }: ToolActivityListProps) {\n if (tools.length === 0) return null;\n const running = tools.some((tool) => tool.status === \"running\");\n return (\n <details className=\"frankie-tool-activity mt-2\" open={running}>\n <summary className=\"inline-flex min-h-7 cursor-pointer list-none items-center gap-1.5 rounded-md px-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden\">\n <ChevronDown className=\"frankie-tool-chevron size-3.5 transition-transform\" aria-hidden=\"true\" />\n {running ? <Loader2 className=\"size-3.5 animate-spin\" aria-hidden=\"true\" /> : <Wrench className=\"size-3.5\" aria-hidden=\"true\" />}\n <span>{running ? \"Working\" : \"Activity\"}</span>\n <span className=\"text-[11px]\">· {tools.length} tool call{tools.length === 1 ? \"\" : \"s\"}</span>\n </summary>\n <ul className=\"mt-1 flex flex-col gap-1 border-l border-border pl-2\">{tools.map((tool) => <li key={tool.id}>{renderTool ? renderTool(tool) : <ToolActivityRow tool={tool} />}</li>)}</ul>\n </details>\n );\n}\n\nexport function ToolActivityRow({ tool }: { tool: ToolActivity }) {\n // A skill-activation call carries its own icon so \"Using skill: X\" reads as a\n // skill being applied, not just another tool; the spinner still wins while it\n // runs so its status stays legible.\n const isSkill = tool.name === SKILL_TOOL_NAME;\n return (\n <Badge variant={badgeVariant(tool.status)} title={tool.detail ?? tool.name}>\n {isSkill && tool.status !== \"running\" ? <Sparkles className=\"size-3\" aria-hidden=\"true\" /> : <ToolIcon status={tool.status} />}\n <span className=\"truncate\">{tool.label}</span>\n </Badge>\n );\n}\n\nfunction ToolIcon({ status }: { status: ToolActivity[\"status\"] }) {\n switch (status) {\n case \"running\":\n return <Loader2 className=\"size-3 animate-spin\" aria-hidden=\"true\" />;\n case \"done\":\n return <Check className=\"size-3\" aria-hidden=\"true\" />;\n case \"denied\":\n return <ShieldAlert className=\"size-3\" aria-hidden=\"true\" />;\n default:\n return <X className=\"size-3\" aria-hidden=\"true\" />;\n }\n}\n\nfunction badgeVariant(status: ToolActivity[\"status\"]): \"default\" | \"success\" | \"danger\" | \"outline\" {\n if (status === \"done\") return \"success\";\n if (status === \"failed\") return \"danger\";\n if (status === \"denied\") return \"outline\";\n return \"default\";\n}\n","\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst badgeVariants = cva(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[0.6875rem] font-medium\",\n {\n variants: {\n variant: {\n default: \"border-transparent bg-muted text-muted-foreground\",\n success: \"border-transparent bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\",\n danger: \"border-transparent bg-red-500/10 text-red-600 dark:text-red-400\",\n outline: \"border-border text-muted-foreground\",\n },\n },\n defaultVariants: { variant: \"default\" },\n },\n);\n\nexport function Badge({\n className,\n variant,\n ...props\n}: HTMLAttributes<HTMLSpanElement> & VariantProps<typeof badgeVariants>) {\n return <span className={cn(badgeVariants({ variant }), className)} {...props} />;\n}\n","\"use client\";\n\nimport { useRef, type CSSProperties } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useStylesReady } from \"../../lib/styles-ready.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { ChatPanel, type ChatPanelProps } from \"./ChatPanel.js\";\n\nexport interface FrankieInlineProps extends ChatPanelProps {\n /** Extra styles on the filling wrapper — e.g. a `maxWidth` to centre the column. */\n style?: CSSProperties;\n}\n\n/**\n * Full-page (or any-container) chat: the panel fills its parent instead of\n * floating in a corner. Drop it into a route or a sized element — it brings its\n * own theme root, so it styles correctly wherever it lands.\n *\n * <div style={{ height: \"100dvh\" }}>\n * <FrankieProvider …><FrankieInline /></FrankieProvider>\n * </div>\n */\nexport function FrankieInline({ className, style, ...panelProps }: FrankieInlineProps) {\n const { injectStyles, themeReady } = useFrankie();\n const theme = useFrankieTheme();\n const mode = useResolvedMode(theme?.mode);\n const rootRef = useRef<HTMLDivElement>(null);\n const stylesReady = useStylesReady(rootRef, undefined, injectStyles);\n return (\n <div\n ref={rootRef}\n className={cn(\"frankie-root frankie-inline\", mode === \"dark\" && \"dark\", className)}\n style={{ ...themeToCssVars(theme), display: \"flex\", height: \"100%\", width: \"100%\", ...style, ...(stylesReady && themeReady ? null : { opacity: 0 as const }) }}\n data-frankie-mode={mode}\n data-frankie-styles={stylesReady ? \"ready\" : \"pending\"}\n >\n <ChatPanel {...panelProps} fill />\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState, type CSSProperties } from \"react\";\nimport { ChevronsLeft, ChevronsRight, History, Menu, Plus, Settings, X } from \"lucide-react\";\nimport type { FrankieProviderProps } from \"../../hooks/FrankieProvider.jsx\";\nimport { FrankieProvider, useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\nimport { useViewportFill } from \"../../lib/viewport-fill.js\";\nimport { useStylesReady } from \"../../lib/styles-ready.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { FrankieApiError, type ChatConversation } from \"../../lib/client.js\";\nimport { FrankieInline } from \"./FrankieInline.js\";\nimport type { ChatPanelProps } from \"./ChatPanel.js\";\n\nexport interface FrankieChatPageProps extends Omit<FrankieProviderProps, \"children\">,\n Pick<ChatPanelProps, \"header\" | \"footer\" | \"emptyState\" | \"renderMessage\" | \"renderTool\" | \"placeholder\" | \"autoFocus\"> {\n className?: string;\n style?: CSSProperties;\n /** Include the built-in conversation sidebar. Defaults to true. */\n showHistory?: boolean;\n /** Optional host navigation, e.g. a Back to dashboard link. */\n navigation?: React.ReactNode;\n /**\n * Grow to fill the screen from where the chat starts down to the bottom\n * edge, or to the top of a fixed bottom bar. Defaults to true; set false\n * when a sized parent should decide the height.\n */\n fillViewport?: boolean;\n}\n\n/** Buckets conversations into relative-date groups (\"Today\", \"Yesterday\", …) like the reference sidebar. Order is preserved within each group; empty groups are dropped. */\nfunction groupByDate(items: ChatConversation[]): { label: string; items: ChatConversation[] }[] {\n const now = new Date();\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const day = 86400000;\n const buckets: { label: string; items: ChatConversation[] }[] = [\n { label: \"Today\", items: [] }, { label: \"Yesterday\", items: [] }, { label: \"Previous 7 days\", items: [] }, { label: \"Older\", items: [] },\n ];\n for (const item of items) {\n const at = new Date(item.updatedAt).getTime();\n const bucket = at >= startOfToday ? 0 : at >= startOfToday - day ? 1 : at >= startOfToday - 7 * day ? 2 : 3;\n buckets[bucket]!.items.push(item);\n }\n return buckets.filter(bucket => bucket.items.length);\n}\n\n/**\n * The sidebar's state. It is owned by `FrankieChatPage`, above the provider,\n * because everything inside the provider remounts when the conversation\n * changes (that is how each chat gets fresh state). Keeping the list, its\n * loading/error state and the search box here is what stops the sidebar from\n * emptying and re-fetching every time a conversation is clicked.\n */\ninterface HistoryState {\n conversations: ChatConversation[];\n setConversations: (items: ChatConversation[]) => void;\n historyError: string;\n setHistoryError: (message: string) => void;\n historyLoading: boolean;\n setHistoryLoading: (loading: boolean) => void;\n query: string;\n setQuery: (query: string) => void;\n}\n\nfunction useHistoryState(): HistoryState {\n const [conversations, setConversations] = useState<ChatConversation[]>([]);\n const [historyError, setHistoryError] = useState(\"\");\n const [historyLoading, setHistoryLoading] = useState(false);\n const [query, setQuery] = useState(\"\");\n return { conversations, setConversations, historyError, setHistoryError, historyLoading, setHistoryLoading, query, setQuery };\n}\n\nfunction PageChrome({ showHistory, navigation, currentId, onSelect, onNew, history: state, ...panel }: Pick<FrankieChatPageProps, \"header\" | \"footer\" | \"emptyState\" | \"renderMessage\" | \"renderTool\" | \"placeholder\" | \"autoFocus\" | \"navigation\"> & { showHistory: boolean; currentId: string; onSelect: (id: string) => void; onNew: () => void; history: HistoryState }) {\n const { client, config, themeReady } = useFrankie();\n const { busy, messages } = useFrankieChat();\n const theme = useFrankieTheme();\n const mode = useResolvedMode(theme?.mode);\n const { conversations, setConversations, historyError, setHistoryError, historyLoading, setHistoryLoading, query, setQuery } = state;\n const [drawerOpen, setDrawerOpen] = useState(false);\n const [collapsed, setCollapsed] = useState(() => {\n try { return window.localStorage.getItem(\"frankie.history-collapsed\") === \"1\"; }\n catch { return false; }\n });\n const dialog = useRef<HTMLDialogElement>(null);\n const menu = useRef<HTMLButtonElement>(null);\n const requestVersion = useRef(0);\n const refresh = useCallback(async () => {\n if (!showHistory || !config || config.allowAnonymous) { setHistoryError(\"\"); return; }\n const version = ++requestVersion.current;\n setHistoryLoading(true); setHistoryError(\"\");\n try { const items = await client.listConversations(); if (version === requestVersion.current) setConversations(items); }\n catch (caught) {\n if (version !== requestVersion.current) { /* superseded by a newer request */ }\n else {\n const message =\n caught instanceof FrankieApiError ? caught.message\n : caught instanceof Error && caught.message.startsWith(\"Frankie\") ? caught.message\n : \"History couldn’t load. Check your sign-in and try again.\";\n setHistoryError(message);\n }\n }\n finally { if (version === requestVersion.current) setHistoryLoading(false); }\n }, [client, showHistory, config?.allowAnonymous, setConversations, setHistoryError, setHistoryLoading]);\n useEffect(() => { if (!busy) void refresh(); return () => { requestVersion.current++; }; }, [refresh, busy]);\n useEffect(() => {\n try { window.localStorage.setItem(\"frankie.history-collapsed\", collapsed ? \"1\" : \"0\"); }\n catch { /* storage unavailable */ }\n }, [collapsed]);\n useEffect(() => {\n const node = dialog.current;\n if (drawerOpen) { node?.showModal?.(); }\n else { node?.close?.(); }\n }, [drawerOpen]);\n const close = () => { setDrawerOpen(false); menu.current?.focus(); };\n const select = (id: string) => { if (!busy) { onSelect(id); close(); } };\n const history = <>\n <div className=\"frankie-history-heading\"><strong>Conversations</strong><button type=\"button\" className=\"frankie-mobile-only\" aria-label=\"Close conversations\" onClick={close}><X size={18} /></button></div>\n <button type=\"button\" className=\"frankie-new-chat\" disabled={busy} onClick={() => { onNew(); close(); }}><Plus size={17} />New chat</button>\n <label className=\"frankie-history-search\"><span>Search conversations</span><input type=\"search\" placeholder=\"Search chats…\" value={query} onChange={event => setQuery(event.target.value)} /></label>\n <nav className=\"frankie-history-list\" aria-label=\"Conversations\">{groupByDate(conversations.filter(item => item.title.toLowerCase().includes(query.toLowerCase()))).map(group => <div className=\"frankie-history-group\" key={group.label}><p className=\"frankie-history-group-label\">{group.label}</p>{group.items.map(item => <button type=\"button\" key={item.id} aria-current={item.id === currentId ? \"page\" : undefined} disabled={busy} onClick={() => select(item.id)}><span>{item.title}</span><small>{new Date(item.updatedAt).toLocaleDateString()}</small></button>)}</div>)}</nav>\n {historyLoading && !conversations.length ? <p role=\"status\">Loading history…</p> : historyError ? <p role=\"status\">{historyError}<button type=\"button\" onClick={() => void refresh()}>Try again</button></p> : !conversations.length && !historyLoading ? <p>{config?.allowAnonymous ? \"Sign in to sync conversations across devices.\" : \"Your conversations will appear here.\"}</p> : null}\n {navigation ? <div className=\"frankie-host-navigation\">{navigation}</div> : null}\n </>;\n // Hidden until the theme is known, so the page never flashes the OS's dark mode first.\n return <div className={\"frankie-page-shell\" + (mode === \"dark\" ? \" dark\" : \"\")} style={{ ...themeToCssVars(theme), ...(themeReady ? null : { opacity: 0 }) }}>\n {showHistory ? <aside className={\"frankie-history-desktop\" + (collapsed ? \" frankie-history-collapsed\" : \"\")}>\n <button type=\"button\" className=\"frankie-history-toggle\" aria-label={collapsed ? \"Expand conversations\" : \"Collapse conversations\"} aria-expanded={!collapsed} onClick={() => setCollapsed(value => !value)}>{collapsed ? <ChevronsRight size={16} /> : <ChevronsLeft size={16} />}</button>\n {collapsed ? null : history}\n </aside> : null}\n <div className=\"frankie-page-main\">\n <header className=\"frankie-page-header\"><div>{showHistory ? <button type=\"button\" className=\"frankie-mobile-only\" ref={menu} aria-label=\"Open conversations\" aria-haspopup=\"dialog\" aria-expanded={drawerOpen} onClick={() => setDrawerOpen(true)}><Menu size={20} /></button> : null}<strong className=\"frankie-thread-title\">{conversations.find(item => item.id === currentId)?.title ?? messages.find(message => message.role === \"user\")?.text ?? \"New chat\"}</strong></div><div>\n <button type=\"button\" disabled={busy} onClick={onNew} aria-label=\"Start a new conversation\"><Plus size={16} /><span>New chat</span></button>\n <details className=\"frankie-chat-settings\"><summary aria-label=\"Chat settings\"><Settings size={19} /></summary><div><strong>Chat settings</strong><p>History syncs automatically for signed-in users.</p><button type=\"button\" onClick={() => void refresh()} disabled={historyLoading || !showHistory}><History size={16} />Refresh history</button><small>Skills are checked at the start of each message.</small></div></details>\n </div></header>\n <div className=\"frankie-page-canvas\"><FrankieInline {...panel} header={panel.header ?? false} placeholder={panel.placeholder ?? `Ask anything about your ${config?.branding.name ?? \"Frankie\"} workspace…`} emptyState={panel.emptyState} /></div>\n </div>\n {showHistory ? <dialog ref={dialog} className=\"frankie-history-dialog frankie-root\" aria-label=\"Chat conversations\" style={themeToCssVars(theme)} onCancel={close} onClick={event => { if (event.target === event.currentTarget) close(); }}><aside>{history}</aside></dialog> : null}\n </div>;\n}\n\n/** Reusable product chat with one shared conversation state and scoped styling. */\nexport function FrankieChatPage({ className, style, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus, showHistory = true, navigation, fillViewport = true, ...provider }: FrankieChatPageProps) {\n const [id, setId] = useState(() => provider.conversationId ?? crypto.randomUUID());\n const onNew = () => setId(crypto.randomUUID());\n const history = useHistoryState();\n const pageRef = useRef<HTMLDivElement>(null);\n const stylesReady = useStylesReady(pageRef, undefined, provider.injectStyles);\n // Fill the screen down to the host's bottom bar even when no parent sets a\n // height (a route inside an app layout), instead of stopping at 36rem.\n const fill = useViewportFill(pageRef, fillViewport);\n return <div ref={pageRef} className={[\"frankie-root\", \"frankie-chat-page\", className].filter(Boolean).join(\" \")} style={{ height: \"100%\", minHeight: fill > 360 ? `${fill}px` : \"min(36rem, 100dvh)\", width: \"100%\", ...style, ...(stylesReady ? null : { opacity: 0 as const }) }} data-frankie-styles={stylesReady ? \"ready\" : \"pending\"}>\n <FrankieProvider {...provider} conversationId={id} storageNamespace={(provider.storageNamespace ?? \"frankie.\" + provider.publishableKey) + \".\" + id}>\n <PageChrome showHistory={showHistory} navigation={navigation} currentId={id} onSelect={setId} onNew={onNew} history={history} header={header} footer={footer} emptyState={emptyState} renderMessage={renderMessage} renderTool={renderTool} placeholder={placeholder} autoFocus={autoFocus} />\n </FrankieProvider>\n </div>;\n}\n","import { useEffect, useState, type RefObject } from \"react\";\nimport { bottomObstruction } from \"./bottom-bars.js\";\n\n/**\n * The height that fills the viewport from where `ref` starts down to the\n * bottom edge, less any fixed bar along the bottom (a mobile tab bar). A\n * full-page chat inside a host layout with no sized parent otherwise stops at\n * its minimum height and leaves a gap above the host's navigation.\n * Re-measured on resize and, throttled, as the page changes.\n */\nexport function useViewportFill(ref: RefObject<HTMLElement | null>, enabled: boolean): number {\n const [fill, setFill] = useState(0);\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") { setFill(0); return; }\n let timer = 0;\n let last = 0;\n const measure = () => {\n timer = 0;\n last = Date.now();\n const node = ref.current;\n if (!node) return;\n const top = node.getBoundingClientRect().top + window.scrollY;\n const lift = bottomObstruction(node, \"bottom-right\", 0);\n setFill(Math.max(0, Math.round(window.innerHeight - top - lift)));\n };\n const schedule = () => {\n if (timer) return;\n timer = window.setTimeout(() => requestAnimationFrame(measure), Math.max(0, 300 - (Date.now() - last)));\n };\n measure();\n window.addEventListener(\"resize\", schedule);\n const observer = typeof MutationObserver === \"function\"\n ? new MutationObserver((records) => { if (records.some((record) => !ref.current?.contains(record.target))) schedule(); })\n : undefined;\n observer?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [\"class\", \"style\"] });\n return () => { window.clearTimeout(timer); window.removeEventListener(\"resize\", schedule); observer?.disconnect(); };\n }, [ref, enabled]);\n return fill;\n}\n","\"use client\";\n\nimport { Button } from \"../ui/button.js\";\n\nexport function SuggestedPrompts({\n prompts,\n onPick,\n disabled,\n}: {\n prompts: string[];\n onPick: (prompt: string) => void;\n disabled?: boolean;\n}) {\n if (prompts.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-2\">\n {prompts.map((prompt) => (\n <Button\n key={prompt}\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={disabled}\n onClick={() => onPick(prompt)}\n >\n {prompt}\n </Button>\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils.js\";\n\nexport function Separator({ className }: { className?: string }) {\n return <div role=\"separator\" className={cn(\"h-px w-full bg-border\", className)} />;\n}\n"],"mappings":"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","names":["createContext","useContext","useEffect","useRef","useState","useEffect","createContext","useRef","useState","useContext","X","useEffect","useRef","useState","useEffect","useLayoutEffect","useState","useIsomorphicLayoutEffect","useEffect","useLayoutEffect","useState","jsx","X","useEffect","useRef","useState","jsx","jsx","jsx","ScrollArea","jsx","Sparkles","jsx","jsxs","Sparkles","useCallback","useEffect","useRef","useState","forwardRef","jsx","forwardRef","Textarea","jsx","jsxs","useState","useRef","useEffect","useCallback","Check","Paperclip","memo","useState","jsx","jsxs","Fragment","useMemo","jsx","Markdown","useMemo","Fragment","Sparkles","X","cva","jsx","cva","jsx","jsxs","Sparkles","X","jsx","jsxs","memo","MessageBubble","Paperclip","useState","Check","jsx","jsxs","useRef","useState","useEffect","X","jsx","jsxs","useRef","useState","useEffect","X","useRef","jsx","useRef","useCallback","useEffect","useRef","useState","Plus","X","useEffect","useState","useState","useEffect","Fragment","jsx","jsxs","useState","useRef","useCallback","useEffect","X","Plus","jsx","jsx"]}
1
+ {"version":3,"sources":["../src/hooks/FrankieProvider.tsx","../src/lib/identity.ts","../src/lib/client.ts","../src/lib/theme-inherit.ts","../src/hooks/useFrankieChat.ts","../src/lib/messages.ts","../src/components/chat/FrankieWidget.tsx","../src/lib/styles-ready.ts","../src/generated/css-content.ts","../src/lib/ensure-frankie-styles.ts","../src/lib/bottom-bars.ts","../src/lib/theme.ts","../src/lib/utils.ts","../src/components/ui/button.tsx","../src/components/chat/ChatPanel.tsx","../src/components/ui/avatar.tsx","../src/components/ui/card.tsx","../src/components/ui/scroll-area.tsx","../src/components/chat/AgentContext.tsx","../src/components/chat/ChatEmptyState.tsx","../src/components/chat/Composer.tsx","../src/components/ui/textarea.tsx","../src/components/chat/MessageBubble.tsx","../src/lib/choices.ts","../src/components/chat/ActionConfirm.tsx","../src/components/chat/Choices.tsx","../src/components/chat/Markdown.tsx","../src/components/chat/ToolActivityList.tsx","../src/components/ui/badge.tsx","../src/components/chat/FrankieInline.tsx","../src/components/chat/FrankieChatPage.tsx","../src/lib/viewport-fill.ts","../src/components/chat/SuggestedPrompts.tsx","../src/components/ui/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport type { PublicWidgetConfig, WidgetTheme } from \"@frankieai/core\";\nimport { FrankieClient, type FrankieClientOptions, type PageContext } from \"../lib/client.js\";\nimport { inferHostTheme, watchHostTheme } from \"../lib/theme-inherit.js\";\nimport { FrankieChatContext, useFrankieChatState } from \"./useFrankieChat.js\";\n\nexport interface FrankieProviderProps extends FrankieClientOptions {\n children: ReactNode;\n context?: PageContext;\n theme?: WidgetTheme;\n config?: PublicWidgetConfig;\n /**\n * Set to `false` if the host compiles Frankie's classes itself instead of\n * loading `@frankieai/react/styles.css` — e.g. a Tailwind v4 host that\n * `@source`s this package — so it isn't given a second, independently-themed\n * copy of the stylesheet when its own build never sets the styles-ready\n * sentinel. Defaults to `true`: a host that simply forgot the import, or\n * whose CSS chunk hasn't landed yet, still gets a correctly-styled widget.\n */\n injectStyles?: boolean;\n}\n\ninterface FrankieContextValue {\n client: FrankieClient;\n config?: PublicWidgetConfig;\n configError?: string;\n loading: boolean;\n context?: PageContext;\n theme?: WidgetTheme;\n injectStyles: boolean;\n /** The host page's colors, sampled once for \"inherit my site's theme\" (null: not inheriting, or unreadable). */\n hostTheme: Partial<WidgetTheme> | null;\n /**\n * True once the chat knows its colors: the config (or its error) has arrived\n * and, when inheriting, the host page has been sampled. Until then the chat\n * stays invisible, so a visitor in OS dark mode never sees a black flash on\n * a light site before the real theme lands.\n */\n themeReady: boolean;\n}\n\n/** Layout effects warn on the server; there's nothing to paint there anyway. */\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** How long the chat may stay hidden waiting for its theme before it shows anyway. */\nconst THEME_WAIT_MS = 5_000;\n\nconst FrankieContext = createContext<FrankieContextValue | null>(null);\n\nexport function FrankieProvider({\n children,\n context,\n theme,\n config: providedConfig,\n injectStyles = true,\n ...clientOptions\n}: FrankieProviderProps) {\n const tokenCallback = useRef(clientOptions.getUserToken);\n tokenCallback.current = clientOptions.getUserToken;\n const client = useMemo(\n () => new FrankieClient({ ...clientOptions, ...(clientOptions.getUserToken ? { getUserToken: () => tokenCallback.current?.() } : {}) }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientOptions.baseUrl, clientOptions.publishableKey, clientOptions.storageNamespace, clientOptions.conversationId, clientOptions.persist, clientOptions.fetchImpl, clientOptions.api?.baseUrl, clientOptions.api?.credentials, Boolean(clientOptions.getUserToken)],\n );\n\n const [config, setConfig] = useState<PublicWidgetConfig | undefined>(providedConfig);\n const [configError, setConfigError] = useState<string | undefined>(undefined);\n const [loading, setLoading] = useState(!providedConfig);\n\n // The widget config is the workspace's, not the conversation's: fetch it\n // once per workspace. Fetching it again whenever the client was rebuilt for\n // another conversation flipped `loading` back on, and the panel replaced the\n // transcript with \"Starting Frankie…\" for a moment on every history switch.\n const configKey = `${clientOptions.baseUrl ?? \"\"}|${clientOptions.publishableKey}`;\n const clientRef = useRef(client);\n clientRef.current = client;\n useEffect(() => {\n if (providedConfig) return;\n const abort = new AbortController();\n setLoading(true);\n clientRef.current\n .getConfig(abort.signal)\n .then((next) => {\n setConfig(next);\n setConfigError(undefined);\n })\n .catch((error: unknown) => {\n if (abort.signal.aborted) return;\n setConfigError(error instanceof Error ? error.message : \"Could not load the assistant.\");\n })\n .finally(() => {\n if (!abort.signal.aborted) setLoading(false);\n });\n return () => abort.abort();\n }, [configKey, providedConfig]);\n\n // \"Inherit my site's theme\": sample the host page once, here, and share it.\n // Each chat component sampling on its own mount read the page at different\n // moments — the panel, opening later, found Frankie's own (fallback-colored)\n // send button and inherited itself while the launcher had the site's color.\n // Sampling reads the live DOM, so it runs post-mount and never on the server.\n const inheriting = (theme?.source ?? config?.theme?.source) === \"inherit\";\n const [hostTheme, setHostTheme] = useState<Partial<WidgetTheme> | null>(null);\n const [hostSampled, setHostSampled] = useState(false);\n // Before paint, so the first frame already has the site's colors.\n useIsomorphicLayoutEffect(() => {\n if (!inheriting) { setHostTheme(null); return; }\n // Keep the previous object when nothing changed, so a re-sample that finds\n // the same colors doesn't re-render every chat component.\n const sample = () => setHostTheme((previous) => {\n const next = inferHostTheme();\n return JSON.stringify(previous) === JSON.stringify(next) ? previous : next;\n });\n sample();\n setHostSampled(true);\n // Follow the host when it switches between its own light and dark themes.\n return watchHostTheme(sample);\n }, [inheriting]);\n\n // The chat stays invisible until its colors are known — but only for so\n // long. A config request that neither answers nor fails (a hung connection,\n // a server that accepts and stalls) has no client-side timeout, and before\n // this bound it left the launcher and the chat page invisible with no\n // message for as long as the browser waited. After the wait the chat shows\n // with the colors it has; a config that arrives later still applies.\n const themeKnown = Boolean(config || configError) && (!inheriting || hostSampled);\n const [waitedForTheme, setWaitedForTheme] = useState(false);\n useEffect(() => {\n if (themeKnown) return;\n const timer = setTimeout(() => setWaitedForTheme(true), THEME_WAIT_MS);\n return () => clearTimeout(timer);\n }, [themeKnown]);\n\n const value = useMemo<FrankieContextValue>(\n () => ({\n client,\n ...(config ? { config } : {}),\n ...(configError ? { configError } : {}),\n loading,\n ...(context ? { context } : {}),\n ...(theme ? { theme } : {}),\n injectStyles,\n hostTheme,\n themeReady: themeKnown || waitedForTheme,\n }),\n [client, config, configError, loading, context, theme, injectStyles, hostTheme, themeKnown, waitedForTheme],\n );\n\n return <FrankieContext.Provider value={value}><ConversationState key={client.namespace + \":\" + (client.conversationId ?? \"\")}>{children}</ConversationState></FrankieContext.Provider>;\n}\n\nfunction ConversationState({ children }: { children: ReactNode }) {\n const chat = useFrankieChatState();\n return <FrankieChatContext.Provider value={chat}>{children}</FrankieChatContext.Provider>;\n}\n\nexport function useFrankie(): FrankieContextValue {\n const value = useContext(FrankieContext);\n if (!value) throw new Error(\"Frankie components must be rendered inside <FrankieProvider>.\");\n return value;\n}\n\nexport function useFrankieTheme(): WidgetTheme | undefined {\n const { config, theme, hostTheme: inferred } = useFrankie();\n const merged = useMemo<WidgetTheme | undefined>(() => ({ ...config?.theme, ...theme }), [config?.theme, theme]);\n const inheriting = merged?.source === \"inherit\";\n return useMemo(() => {\n if (!inheriting || !inferred) return merged;\n // Detection succeeded: the host's own colors replace the configured\n // palette, which stays as the fallback already folded into `merged`\n // wherever a specific inferred value is missing.\n return { ...merged, ...inferred };\n }, [merged, inheriting, inferred]);\n}\n","/**\n * The stable identity a host-signed user token carries, used only to decide\n * whether a cached chat session still belongs to the same person.\n *\n * The client never verifies the token — it has no secret, and the server\n * re-verifies on every `/v1/session` anyway. It only needs a key that stays\n * constant while the same user's token is re-minted. A JWT's `sub` is that\n * key: hosts mint with `setIssuedAt()`, so the raw string changes every\n * second even though nothing about the user did.\n *\n * Anything that isn't a readable JWT falls back to the raw token, which is\n * exactly the previous behaviour — an opaque token that changes still resets\n * the session, so this can only ever reuse a session, never leak one.\n */\nexport function sessionIdentity(token: string | undefined): string | undefined {\n if (!token) return undefined;\n return jwtSubject(token) ?? token;\n}\n\nfunction jwtSubject(token: string): string | undefined {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[1]) return undefined;\n try {\n const payload = JSON.parse(base64UrlDecode(parts[1])) as { sub?: unknown };\n return typeof payload.sub === \"string\" && payload.sub ? payload.sub : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction base64UrlDecode(segment: string): string {\n const base64 = segment.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = base64 + \"=\".repeat((4 - (base64.length % 4)) % 4);\n if (typeof atob === \"function\") {\n const bytes = atob(padded);\n return decodeURIComponent(Array.from(bytes, (c) => \"%\" + c.charCodeAt(0).toString(16).padStart(2, \"0\")).join(\"\"));\n }\n return Buffer.from(padded, \"base64\").toString(\"utf8\");\n}\n","import type {\n ActionDecision,\n ChatAttachment,\n ChatRequestBody,\n CreateSessionResponse,\n FrankieServerEvent,\n HostToolRequest,\n HostToolResult,\n PublicWidgetConfig,\n} from \"@frankieai/core\";\nimport { sessionIdentity } from \"./identity.js\";\n\nexport type PageContext = Record<string, string | number | boolean | null>;\n\n/**\n * How the widget reaches the host's own API for client-side tool execution.\n *\n * When a workspace marks an API source `execution: \"client\"`, Frankie asks the\n * browser to make the call itself — from the user's existing session — instead\n * of calling the API from Frankie's servers. This says where that API lives and\n * how the browser authenticates to it: normally the app's own cookies\n * (`credentials: \"include\"` on a same-origin or CORS-allowed API), or a bearer\n * token the app already holds, added via `headers`. Frankie sends no credential\n * of its own; the host API authorizes the call exactly as it would any request\n * from the host's frontend.\n */\nexport interface HostApiClientOptions {\n /** Base URL that operation paths resolve against, e.g. \"/api\" or \"https://api.acme.com\". */\n baseUrl: string;\n /** fetch credentials mode. \"include\" sends the app's cookies. Defaults to \"same-origin\". */\n credentials?: RequestCredentials;\n /** Extra headers for every host call (e.g. an app access token). Awaited per call. */\n headers?: () => Promise<Record<string, string>> | Record<string, string>;\n}\n\n/**\n * The hosted Frankie data plane. Embedding needs only a publishable key, so\n * `baseUrl` defaults here — override it only when self-hosting the server.\n */\nexport const FRANKIE_CLOUD_URL = \"https://frankie-control-665333762719.us-central1.run.app\";\n\nexport interface FrankieClientOptions {\n /** Where the Frankie server is mounted. Defaults to the hosted cloud. */\n baseUrl?: string;\n /** Safe to ship to the browser; the origin allowlist is what authorises it. */\n publishableKey: string;\n /**\n * Fetches a short-lived JWT for the signed-in user from the host's own\n * backend. Called before each turn; return undefined after sign-out.\n * Omit only for tenants that allow anonymous chat.\n */\n getUserToken?: () => Promise<string | undefined> | string | undefined;\n fetchImpl?: typeof fetch;\n /**\n * Reaches the host's own API for `execution: \"client\"` tool calls (see\n * `HostApiClientOptions`). Defaults to the site the user is on, with the\n * page's own cookies (`credentials: \"same-origin\"`): set it when your API\n * lives on another origin or signs requests with a token.\n */\n api?: HostApiClientOptions;\n /**\n * Remember the session (and, in the hook, the transcript) in localStorage so a\n * page reload resumes the same anonymous conversation. Authenticated chat\n * never writes tokens or transcripts to localStorage; it uses server history.\n */\n persist?: boolean;\n /** Override localStorage namespace for session and transcript. */\n storageNamespace?: string;\n /** Stable id for a server-backed conversation. */\n conversationId?: string;\n}\n\nexport interface ChatConversation {\n id: string;\n title: string;\n createdAt: number;\n updatedAt: number;\n}\n\nexport class FrankieApiError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly code: string,\n ) {\n super(message);\n }\n}\n\nexport class FrankieClient {\n private readonly baseUrl: string;\n private readonly doFetch: typeof fetch;\n private session?: CreateSessionResponse;\n /** Identity (JWT `sub`, else the raw token) the cached session was issued for. */\n private sessionUserKey?: string;\n /** The host token fetched for the latest turn; sent with it so tools get a live one. */\n private latestUserToken?: string;\n\n constructor(private readonly options: FrankieClientOptions) {\n this.baseUrl = (options.baseUrl ?? FRANKIE_CLOUD_URL).replace(/\\/+$/, \"\");\n this.doFetch = options.fetchImpl ?? globalThis.fetch.bind(globalThis);\n this.session = this.readStored();\n if (options.getUserToken && typeof window !== \"undefined\") {\n try { for (const suffix of [\"session\", \"messages\"]) window.localStorage.removeItem(`${this.namespace}.${suffix}`); } catch { /* storage may be unavailable */ }\n }\n }\n\n get sessionId(): string | undefined {\n return this.session?.sessionId;\n }\n\n get conversationId(): string | undefined {\n return this.options.conversationId;\n }\n\n /** Whether this client persists across reloads (browser only, opt-out via `persist:false`). */\n get persist(): boolean {\n // Authenticated history comes from the identity-checked server. Do not leave\n // a previous user's bearer token or transcript in shared browser storage.\n return !this.options.getUserToken && this.options.persist !== false && typeof window !== \"undefined\";\n }\n\n /** localStorage namespace for this tenant/conversation, shared with the chat hook. */\n get namespace(): string {\n return this.options.storageNamespace ?? `frankie.${this.options.publishableKey}`;\n }\n\n private readStored(): CreateSessionResponse | undefined {\n if (!this.persist) return undefined;\n try {\n const raw = window.localStorage.getItem(`${this.namespace}.session`);\n if (!raw) return undefined;\n const session = JSON.parse(raw) as CreateSessionResponse;\n return new Date(session.expiresAt).getTime() > Date.now() + 30_000 ? session : undefined;\n } catch {\n return undefined;\n }\n }\n\n private writeStored(): void {\n if (!this.persist) return;\n try {\n if (this.session) window.localStorage.setItem(`${this.namespace}.session`, JSON.stringify(this.session));\n else window.localStorage.removeItem(`${this.namespace}.session`);\n } catch {\n /* storage may be unavailable (private mode); the chat still works in memory */\n }\n }\n\n async getConfig(signal?: AbortSignal): Promise<PublicWidgetConfig> {\n const response = await this.doFetch(`${this.baseUrl}/v1/config`, {\n headers: { \"x-frankie-key\": this.options.publishableKey },\n ...(signal ? { signal } : {}),\n });\n return (await this.unwrap(response)) as PublicWidgetConfig;\n }\n\n async ensureSession(context?: PageContext): Promise<CreateSessionResponse> {\n // The host's token endpoint knows why it failed (not signed in, secret\n // missing, lookup not wired); a thrown Error carries that reason to the user.\n let userToken: string | undefined;\n try {\n userToken = await this.options.getUserToken?.();\n } catch (error) {\n this.resetSession();\n const reason = error instanceof Error && error.message.trim() ? error.message.trim() : undefined;\n throw new FrankieApiError(\n reason ?? \"Your signed-in Frankie session could not be verified. Refresh the page and sign in again.\",\n 401,\n \"user_token_unavailable\",\n );\n }\n if (this.options.getUserToken && !userToken) {\n this.resetSession();\n throw new FrankieApiError(\n \"Your signed-in Frankie session could not be verified. Refresh the page and sign in again.\",\n 401,\n \"user_token_unavailable\",\n );\n }\n // Reset only when the *person* changed, not when the same person's token\n // was re-minted. Hosts mint a fresh JWT (with `iat`) before every turn, so\n // comparing raw bytes threw the session away on every message and started\n // a new, empty transcript each time — the agent never saw the last turn.\n // Sign-out is handled above; a different user resets here as before.\n this.latestUserToken = userToken;\n const userKey = sessionIdentity(userToken);\n if (userKey !== this.sessionUserKey) this.resetSession();\n if (this.session && new Date(this.session.expiresAt).getTime() > Date.now() + 30_000) return this.session;\n const response = await this.doFetch(`${this.baseUrl}/v1/session`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"x-frankie-key\": this.options.publishableKey },\n body: JSON.stringify({\n ...(userToken ? { userToken } : {}),\n ...(context ? { context } : {}),\n ...(this.options.conversationId ? { conversationId: this.options.conversationId } : {}),\n }),\n });\n this.session = (await this.unwrap(response)) as CreateSessionResponse;\n this.sessionUserKey = userKey;\n this.writeStored();\n return this.session;\n }\n\n async listConversations(): Promise<ChatConversation[]> {\n const userToken = await this.requireUserToken();\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations`, {\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${userToken}` },\n });\n const body = (await this.unwrap(response)) as { conversations?: ChatConversation[] };\n return Array.isArray(body.conversations) ? body.conversations : [];\n }\n\n /** Whether a conversation can be reloaded from the server (a signed-in user and a conversation id). */\n get canResume(): boolean {\n return Boolean(this.options.conversationId && this.options.getUserToken);\n }\n\n /**\n * Ask the server to stop the running turn. Closing the stream no longer\n * stops it (a closed tab shouldn't lose the reply), so Stop says so\n * explicitly. Best effort: the stream is closed right after either way.\n */\n async stopTurn(): Promise<void> {\n if (!this.session) return;\n await this.doFetch(`${this.baseUrl}/v1/chat/stop`, {\n method: \"POST\",\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${this.session.sessionToken}` },\n }).catch(() => undefined);\n }\n\n async resumeConversation(id = this.options.conversationId): Promise<{ messages: unknown[]; title: string; busy: boolean } | undefined> {\n if (!id || !this.options.getUserToken) return undefined;\n const userToken = await this.requireUserToken();\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations/${encodeURIComponent(id)}/resume`, {\n method: \"POST\",\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${userToken}` },\n });\n if (response.status === 404) return undefined;\n const body = (await this.unwrap(response)) as { session: CreateSessionResponse; messages?: unknown[]; title?: string; busy?: boolean };\n this.session = body.session;\n this.sessionUserKey = sessionIdentity(userToken);\n this.writeStored();\n return { messages: Array.isArray(body.messages) ? body.messages : [], title: body.title ?? \"New chat\", busy: body.busy === true };\n }\n\n async syncConversation(messages: unknown[], title?: string): Promise<void> {\n if (!this.session || !this.options.conversationId) return;\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations/${encodeURIComponent(this.options.conversationId)}`, {\n method: \"PUT\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-frankie-key\": this.options.publishableKey,\n authorization: `Bearer ${this.session.sessionToken}`,\n },\n body: JSON.stringify({ messages, ...(title ? { title } : {}) }),\n });\n await this.unwrap(response);\n }\n\n async deleteConversation(id: string): Promise<void> {\n const userToken = await this.requireUserToken();\n const response = await this.doFetch(`${this.baseUrl}/v1/conversations/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n headers: { \"x-frankie-key\": this.options.publishableKey, authorization: `Bearer ${userToken}` },\n });\n if (response.status !== 404) await this.unwrap(response);\n }\n\n private async requireUserToken(): Promise<string> {\n const token = await this.options.getUserToken?.();\n if (!token) throw new FrankieApiError(\"Sign in to access your chat history.\", 401, \"unauthorized\");\n return token;\n }\n\n resetSession(): void {\n this.session = undefined;\n this.writeStored();\n }\n\n async send(\n input: {\n message: string;\n context?: PageContext;\n model?: string;\n attachments?: ChatAttachment[];\n /** The user's answer to the action the last reply paused for approval. */\n confirm?: ActionDecision;\n signal?: AbortSignal;\n },\n onEvent: (event: FrankieServerEvent) => void,\n ): Promise<void> {\n const session = await this.ensureSession(input.context);\n const body: ChatRequestBody = {\n sessionId: session.sessionId,\n message: input.message,\n ...(input.context ? { context: input.context } : {}),\n ...(input.model ? { model: input.model } : {}),\n ...(input.attachments?.length ? { attachments: input.attachments } : {}),\n ...(this.latestUserToken ? { userToken: this.latestUserToken } : {}),\n // This widget shows the approval card, so the server may pause writes for it.\n confirmActions: true,\n ...(input.confirm ? { confirm: input.confirm } : {}),\n };\n\n const response = await this.doFetch(`${this.baseUrl}/v1/chat`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-frankie-key\": this.options.publishableKey,\n authorization: `Bearer ${session.sessionToken}`,\n },\n body: JSON.stringify(body),\n ...(input.signal ? { signal: input.signal } : {}),\n });\n\n if (!response.ok || !response.body) {\n if (response.status === 401 || response.status === 404) this.resetSession();\n await this.unwrap(response);\n return;\n }\n\n for await (const event of readEventStream(response.body)) {\n // A client-side tool call is handled here, not surfaced to the UI: run it\n // against the host API on the user's own session and POST the outcome\n // back. It runs detached so reading the stream never blocks on a host call\n // (the server's agent loop is what waits, via the tool-result endpoint).\n if (event.type === \"tool_call_request\") {\n void this.runClientTool(session, event.callId, event.request);\n continue;\n }\n onEvent(event);\n }\n }\n\n /**\n * Execute one host API call in the browser and report the outcome back to the\n * server. Never throws: a transport failure or a missing `api` client becomes\n * a result the model can see and explain, exactly like a non-2xx status.\n */\n private async runClientTool(\n session: CreateSessionResponse,\n callId: string,\n request: HostToolRequest,\n ): Promise<void> {\n const result = await this.callHostApi(request);\n await this.postToolResult(session, callId, result).catch(() => undefined);\n }\n\n private async callHostApi(request: HostToolRequest): Promise<HostToolResult> {\n const api = this.options.api ?? (typeof window !== \"undefined\" ? { baseUrl: window.location.origin } : undefined);\n if (!api) {\n return {\n status: 0,\n error:\n \"This assistant tried to call your API from the browser, but no host API client is configured \" +\n \"in the Frankie SDK (the `api` option).\",\n };\n }\n try {\n const extra = api.headers ? await api.headers() : {};\n const response = await this.doFetch(joinHostUrl(api.baseUrl, request.path), {\n method: request.method,\n credentials: api.credentials ?? \"same-origin\",\n headers: {\n accept: \"application/json, text/plain;q=0.9, */*;q=0.8\",\n ...(request.body !== undefined ? { \"content-type\": \"application/json\" } : {}),\n ...extra,\n },\n ...(request.body !== undefined ? { body: JSON.stringify(request.body) } : {}),\n });\n const body = await response.text().catch(() => \"\");\n return { status: response.status, body };\n } catch (error) {\n return { status: 0, error: error instanceof Error ? error.message : String(error) };\n }\n }\n\n private async postToolResult(\n session: CreateSessionResponse,\n callId: string,\n result: HostToolResult,\n ): Promise<void> {\n await this.doFetch(`${this.baseUrl}/v1/chat/tool-result`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n \"x-frankie-key\": this.options.publishableKey,\n authorization: `Bearer ${session.sessionToken}`,\n },\n body: JSON.stringify({ callId, result }),\n });\n }\n\n private async unwrap(response: Response): Promise<unknown> {\n if (response.ok) return response.json();\n let code = \"internal\";\n let message = `Request failed with status ${response.status}.`;\n try {\n const body = (await response.json()) as { error?: { code?: string; message?: string } };\n code = body.error?.code ?? code;\n message = body.error?.message ?? message;\n } catch {\n /* keep the default message */\n }\n throw new FrankieApiError(message, response.status, code);\n }\n}\n\n/**\n * Join the host's configured base URL with a server-supplied relative path. The\n * server always sends a relative path (params interpolated, no scheme or host),\n * and this refuses anything that looks absolute or protocol-relative — so a tool\n * call can never be steered to a different origin than the one the host set.\n */\nexport function joinHostUrl(baseUrl: string, path: string): string {\n if (/^[a-z][a-z0-9+.-]*:/i.test(path) || path.startsWith(\"//\")) {\n throw new Error(\"A client tool path must be relative to the configured host API base URL.\");\n }\n return `${baseUrl.replace(/\\/+$/, \"\")}/${path.replace(/^\\/+/, \"\")}`;\n}\n\nexport async function* readEventStream(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<FrankieServerEvent> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n let split = buffer.indexOf(\"\\n\\n\");\n while (split !== -1) {\n const frame = buffer.slice(0, split);\n buffer = buffer.slice(split + 2);\n const event = parseFrame(frame);\n if (event) yield event;\n split = buffer.indexOf(\"\\n\\n\");\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nfunction parseFrame(frame: string): FrankieServerEvent | null {\n const data = frame\n .split(\"\\n\")\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).replace(/^ /, \"\"))\n .join(\"\\n\");\n if (!data) return null;\n try {\n return JSON.parse(data) as FrankieServerEvent;\n } catch {\n return null;\n }\n}\n","import type { WidgetTheme } from \"@frankieai/core\";\n\ntype Rgba = [number, number, number, number];\n\n/** Computed colors always come back as `rgb()`/`rgba()`, never a hex or name. */\nfunction parseRgb(value: string): Rgba | null {\n const match = value.match(/rgba?\\(\\s*([\\d.]+)[,\\s]+([\\d.]+)[,\\s]+([\\d.]+)(?:\\s*[,/]\\s*([\\d.]+%?))?\\s*\\)/i);\n if (!match) return null;\n const alpha = match[4] === undefined ? 1 : match[4].endsWith(\"%\") ? Number(match[4].slice(0, -1)) / 100 : Number(match[4]);\n return [Number(match[1]), Number(match[2]), Number(match[3]), alpha];\n}\n\nfunction toCss([r, g, b, a]: Rgba): string {\n return a >= 1 ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\nfunction sameColor(a: Rgba, b: Rgba): boolean {\n return Math.abs(a[0] - b[0]) < 12 && Math.abs(a[1] - b[1]) < 12 && Math.abs(a[2] - b[2]) < 12;\n}\n\n/** Relative luminance (WCAG), 0 = black, 1 = white. */\nfunction luminance([r, g, b]: Rgba): number {\n const channel = (c: number) => {\n const v = c / 255;\n return v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);\n}\n\n/** Whether a color has a hue of its own: a grey or near-black link is text, not a brand color. */\nfunction colorful([r, g, b]: Rgba): boolean {\n const max = Math.max(r, g, b), min = Math.min(r, g, b);\n return max - min >= 40;\n}\n\nfunction contrastRatio(a: Rgba, b: Rgba): number {\n const la = luminance(a), lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/**\n * Elements likely to carry a site's own accent color, checked in order.\n * `data-frankie-accent` is an explicit opt-in hook a host can add to any\n * element (e.g. their own primary button) to make detection reliable instead\n * of guessed; everything after it is a heuristic guess at a \"branded\"\n * element, weakest last.\n */\nconst ACCENT_SELECTORS = [\"button[type=submit]\", \"a[href]\", \"button\", \"[class*=primary]\", \"[class*=btn]\"];\n\n/**\n * Brand-color custom properties, in the same order the dashboard's\n * server-side preview reads them (`inferTheme` in @frankieai/core), so the\n * live widget and the Appearance preview agree.\n */\nconst PRIMARY_VARS = [\"--primary\", \"--color-primary\", \"--brand\", \"--color-brand\", \"--accent\", \"--color-accent\"];\n\n/**\n * Frankie's own DOM is never \"the host's\" color: once the chat is open, its\n * send button (a `button[type=submit]` filled with the fallback accent) would\n * otherwise be the first match and the widget would inherit itself.\n */\nconst OWN_DOM = \".frankie-root, [data-frankie-ignore]\";\n\nfunction firstHostElement(selector: string): Element | null {\n for (const element of document.querySelectorAll(selector)) if (!element.closest(OWN_DOM)) return element;\n return null;\n}\n\n/** Resolve any CSS color (hex, hsl, oklch, shadcn's bare \"221 83% 53%\") to RGBA, or null. */\nfunction resolveColor(raw: string): Rgba | null {\n const value = raw.trim();\n if (!value) return null;\n const candidate = /^[\\d.]+(?:deg)?\\s+[\\d.]+%\\s+[\\d.]+%(?:\\s*\\/\\s*[\\d.]+%?)?$/.test(value) ? `hsl(${value})` : value;\n const probe = document.createElement(\"span\");\n probe.style.color = candidate;\n if (!probe.style.color) return null; // not a color\n probe.style.display = \"none\";\n probe.setAttribute(\"data-frankie-ignore\", \"\");\n document.body.appendChild(probe);\n const computed = getComputedStyle(probe).color;\n probe.remove();\n const rgb = parseRgb(computed);\n if (rgb) return rgb;\n // Newer browsers keep oklch()/lab() as-is in computed styles: rasterize one pixel instead.\n try {\n const context = document.createElement(\"canvas\").getContext(\"2d\", { willReadFrequently: true });\n if (!context) return null;\n context.fillStyle = computed;\n context.fillRect(0, 0, 1, 1);\n const [r, g, b, a] = context.getImageData(0, 0, 1, 1).data;\n return a ? [r!, g!, b!, a / 255] : null;\n } catch {\n return null;\n }\n}\n\n/** The page's declared brand color (`--primary` and friends on :root or body), if any. */\nfunction declaredAccent(background: Rgba): Rgba | null {\n for (const element of [document.documentElement, document.body]) {\n const style = getComputedStyle(element);\n if (typeof style.getPropertyValue !== \"function\") continue;\n for (const name of PRIMARY_VARS) {\n const color = resolveColor(style.getPropertyValue(name));\n if (color && color[3] > 0.5 && !sameColor(color, background)) return color;\n }\n }\n return null;\n}\n\n/** A color custom property declared on <html> or <body> (shadcn's `--background`, Tailwind v4's `--color-background`). */\nfunction declaredColor(names: string[]): Rgba | null {\n for (const element of [document.documentElement, document.body]) {\n const style = getComputedStyle(element);\n if (typeof style.getPropertyValue !== \"function\") continue;\n for (const name of names) {\n const color = resolveColor(style.getPropertyValue(name));\n if (color && color[3] >= 0.5) return color;\n }\n }\n return null;\n}\n\n/** An element's own background, when it's opaque enough to be a page surface. */\nfunction solidBackground(element: Element | null | undefined): Rgba | null {\n if (!element || element.closest(OWN_DOM)) return null;\n const color = parseRgb(getComputedStyle(element).backgroundColor ?? \"\");\n return color && color[3] >= 0.5 ? color : null;\n}\n\n/**\n * The page's real background and text. Many sites leave <body> transparent\n * and paint <html>, a top-level wrapper (`<div class=\"min-h-screen bg-…\">`)\n * or only declare `--background`, so each is tried in turn; <body>'s own\n * text color stays the default unless the background came from elsewhere\n * and the page declares `--foreground`.\n */\nfunction pageColors(bodyStyle: CSSStyleDeclaration): { background: Rgba; foreground: Rgba } | null {\n const bodyText = parseRgb(bodyStyle.color);\n const fromBody = parseRgb(bodyStyle.backgroundColor);\n if (fromBody && fromBody[3] >= 0.5) return bodyText ? { background: fromBody, foreground: bodyText } : null;\n const wrapper = document.body.firstElementChild;\n const background = solidBackground(document.documentElement) ?? solidBackground(wrapper) ?? solidBackground(wrapper?.firstElementChild) ?? declaredColor([\"--background\", \"--color-background\"]);\n const foreground = declaredColor([\"--foreground\", \"--color-foreground\"]) ?? bodyText;\n return background && foreground ? { background, foreground } : null;\n}\n\n/** `a` moved `amount` of the way toward `b`: a surface or hairline in the page's own palette. */\nfunction mix(a: Rgba, b: Rgba, amount: number): Rgba {\n return [0, 1, 2].map((i) => Math.round(a[i]! + (b[i]! - a[i]!) * amount)).concat(1) as Rgba;\n}\n\n/** The page's corner radius in rem, from a shadcn-style `--radius` (rem or px), if it declares one. */\nfunction declaredRadius(): string | undefined {\n const raw = getComputedStyle(document.documentElement).getPropertyValue?.(\"--radius\").trim();\n const match = raw?.match(/^([\\d.]+)(rem|px)$/);\n if (!match) return undefined;\n const rem = match[2] === \"px\" ? Number(match[1]) / 16 : Number(match[1]);\n return rem >= 0 && rem <= 2 ? String(Math.round(rem * 1000) / 1000) : undefined;\n}\n\n/**\n * Best-effort read of the host page's own background/text/accent colors, for\n * a workspace that chose \"inherit my site's theme\" over a manually configured\n * palette. Conservative: returns `null` — never a guess — when nothing\n * sampled is reliable (no DOM to read, no opaque surface on <body>, <html>,\n * the top wrapper or a declared `--background`, illegible contrast), so the\n * caller falls back to the configured theme rather than shipping a widget\n * that looks broken. With no distinct accent it still returns the page's\n * surfaces and leaves the configured accent in place.\n *\n * Never attempts full brand-color extraction (logos, images, gradients) —\n * only colors real elements are already rendering with, via\n * `getComputedStyle`. The provider re-runs it when the host switches its own\n * theme (see `watchHostTheme`).\n */\nexport function inferHostTheme(): Partial<WidgetTheme> | null {\n if (typeof document === \"undefined\" || typeof getComputedStyle !== \"function\") return null;\n try {\n const bodyStyle = getComputedStyle(document.body);\n const colors = pageColors(bodyStyle);\n if (!colors) return null; // no opaque surface anywhere: nothing real to read\n const { background, foreground } = colors;\n if (contrastRatio(background, foreground) < 2) return null; // too low-contrast to trust as a real reading\n\n // An explicit opt-in wins, then the page's declared brand variable, then\n // a guess from rendered elements (never Frankie's own).\n let accent: Rgba | null = null;\n const marked = firstHostElement(\"[data-frankie-accent]\");\n if (marked) {\n const style = getComputedStyle(marked);\n const surface = parseRgb(style.backgroundColor);\n accent = surface && surface[3] > 0.5 && !sameColor(surface, background) ? surface : parseRgb(style.color);\n }\n accent ??= declaredAccent(background);\n for (const selector of accent ? [] : ACCENT_SELECTORS) {\n const element = firstHostElement(selector);\n if (!element) continue;\n const style = getComputedStyle(element);\n const surface = parseRgb(style.backgroundColor);\n if (surface && surface[3] > 0.5 && !sameColor(surface, background)) { accent = surface; break; }\n const text = parseRgb(style.color);\n if (text && text[3] > 0.5 && !sameColor(text, foreground) && colorful(text)) { accent = text; break; }\n }\n\n // The page's light/dark decides the widget's, not the visitor's OS: a dark\n // site on a light-mode machine must not get light borders and buttons.\n // Muted surfaces and hairlines come from the page's own two colors so\n // nothing in the widget keeps the default palette.\n const font = bodyStyle.fontFamily?.trim();\n const radius = declaredRadius();\n // No accent distinct from the page: the page's surfaces still apply, and\n // the configured accent (the fallback) stays for buttons and links.\n return {\n mode: luminance(background) < 0.4 ? \"dark\" : \"light\",\n background: toCss(background),\n foreground: toCss(foreground),\n ...(accent ? {\n primary: toCss(accent),\n // Whichever of near-black or near-white reads better on the accent.\n primaryForeground: contrastRatio(accent, [10, 10, 10, 1]) > contrastRatio(accent, [250, 250, 250, 1]) ? \"#0a0a0a\" : \"#fafafa\",\n } : {}),\n muted: toCss(mix(background, foreground, 0.07)),\n border: toCss(mix(background, foreground, 0.14)),\n ...(font ? { fontFamily: font } : {}),\n ...(radius ? { radius } : {}),\n };\n } catch {\n return null; // any DOM access failure (unusual host environment): never guess\n }\n}\n\n/**\n * Call `onChange` when the host page may have switched its theme: a class,\n * `data-*` or inline style change on <html> or <body> (how sites toggle light\n * and dark), or the OS color scheme flipping. Coalesced to one call per\n * frame. Watches only those two elements' own attributes, never the subtree,\n * so the widget's own rendering can't trigger it. Returns a cleanup.\n */\nexport function watchHostTheme(onChange: () => void): () => void {\n if (typeof document === \"undefined\" || typeof MutationObserver === \"undefined\") return () => {};\n let frame = 0;\n const schedule = () => {\n if (frame) return;\n frame = (typeof requestAnimationFrame === \"function\" ? requestAnimationFrame : (fn: FrameRequestCallback) => setTimeout(fn, 16) as unknown as number)(() => {\n frame = 0;\n onChange();\n });\n };\n const observer = new MutationObserver(schedule);\n for (const element of [document.documentElement, document.body]) {\n if (element) observer.observe(element, { attributes: true, attributeFilter: [\"class\", \"style\", \"data-theme\", \"data-mode\", \"data-color-scheme\", \"data-color-mode\"] });\n }\n const media = typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(\"(prefers-color-scheme: dark)\") : undefined;\n media?.addEventListener?.(\"change\", schedule);\n return () => {\n observer.disconnect();\n media?.removeEventListener?.(\"change\", schedule);\n if (frame && typeof cancelAnimationFrame === \"function\") cancelAnimationFrame(frame);\n };\n}\n","\"use client\";\n\nimport { createContext, useContext, useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ActionDecision, ApiToolInventory, ChatAttachment, LoadedSkill } from \"@frankieai/core\";\nimport { FrankieApiError } from \"../lib/client.js\";\nimport {\n applyEvent,\n newAssistantMessage,\n newUserMessage,\n type AssistantChatMessage,\n type ChatMessage,\n} from \"../lib/messages.js\";\nimport { useFrankie } from \"./FrankieProvider.jsx\";\n\n/** Extra inputs a turn can carry: attached files and a chosen model. */\nexport interface SendOptions {\n attachments?: ChatAttachment[];\n model?: string;\n /** Answer the action the last reply paused for approval. */\n confirm?: ActionDecision;\n}\n\nexport interface UseFrankieChatResult {\n loadedSkills: LoadedSkill[] | null;\n /** Which OpenAPI sources are live for this turn, and a content fingerprint of each. */\n activeApis: ApiToolInventory[] | null;\n restoring: boolean;\n messages: ChatMessage[];\n /** True from the moment a message is sent until the turn ends. */\n busy: boolean;\n /** Set when the turn failed outright (network, auth, rate limit). */\n error?: string;\n send: (text: string, opts?: SendOptions) => Promise<void>;\n /**\n * Approve or cancel the action a reply paused (`message.action`). Approving\n * runs exactly what the card shows; either way the assistant then replies.\n */\n answerAction: (toolUseId: string, approved: boolean) => Promise<void>;\n /** Abort the in-flight turn. The partial answer stays in the transcript. */\n stop: () => void;\n /** Clear the transcript and start a brand-new server session. */\n reset: () => void;\n}\n\nlet counter = 0;\nconst nextId = (prefix: string) => `${prefix}_${Date.now().toString(36)}_${(counter += 1)}`;\n\nexport const FrankieChatContext = createContext<UseFrankieChatResult | null>(null);\nexport function useFrankieChat(): UseFrankieChatResult {\n const value = useContext(FrankieChatContext);\n if (!value) throw new Error(\"useFrankieChat requires FrankieProvider.\");\n return value;\n}\n\nexport function useFrankieChatState(): UseFrankieChatResult {\n const { client, context } = useFrankie();\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | undefined>(undefined);\n const [restored, setRestored] = useState(false);\n const [loadedSkills, setLoadedSkills] = useState<LoadedSkill[] | null>(null);\n const [activeApis, setActiveApis] = useState<ApiToolInventory[] | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const mounted = useRef(true);\n // State updates can be batched while a stream is arriving. Keep the latest\n // transcript synchronously so the final server sync cannot save a stale copy.\n const messagesRef = useRef<ChatMessage[]>([]);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n abortRef.current?.abort();\n };\n }, []);\n\n const storageKey = `${client.namespace}.messages`;\n\n // A reply the server is still writing after this browser lost the stream (a\n // closed tab, a phone that backgrounded the page): show it as working and\n // check back until it lands, then show the server's finished copy.\n const followingRef = useRef(false);\n const followRunningTurn = useCallback(async () => {\n if (followingRef.current || !client.canResume) return;\n followingRef.current = true;\n setBusy(true);\n try {\n const deadline = Date.now() + 3 * 60_000;\n while (mounted.current && Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 2500));\n const remote = await client.resumeConversation().catch(() => undefined);\n if (!remote || remote.busy) continue;\n if (remote.messages.length) {\n const finished = remote.messages as ChatMessage[];\n messagesRef.current = finished;\n if (mounted.current) setMessages(finished);\n }\n if (mounted.current) setError(undefined);\n return;\n }\n } finally {\n followingRef.current = false;\n if (mounted.current) setBusy(false);\n }\n }, [client]);\n\n // Restore local storage immediately, then hydrate the authoritative server\n // transcript. The server path makes the same signed-in user's chats available\n // on another browser or device.\n useEffect(() => {\n let active = true;\n const restore = async () => {\n if (client.persist) {\n try {\n const raw = window.localStorage.getItem(storageKey);\n const stored = raw ? (JSON.parse(raw) as ChatMessage[]) : [];\n if (Array.isArray(stored) && stored.length && active) {\n messagesRef.current = stored;\n setMessages(stored);\n }\n } catch {\n /* storage unavailable; server history can still restore */\n }\n }\n try {\n const remote = await client.resumeConversation();\n if (active && remote?.messages.length) {\n const restoredMessages = remote.busy ? stillWorking(remote.messages as ChatMessage[]) : (remote.messages as ChatMessage[]);\n messagesRef.current = restoredMessages;\n setMessages(restoredMessages);\n }\n if (active && remote?.busy) void followRunningTurn();\n } catch (caught) {\n if (active) {\n const message =\n caught instanceof FrankieApiError\n ? caught.message\n : caught instanceof Error && caught.message.startsWith(\"Frankie\")\n ? caught.message\n : \"Could not restore this conversation. Check your sign-in and connection, then try opening it again.\";\n setError(message);\n }\n } finally {\n if (active) setRestored(true);\n }\n };\n void restore();\n return () => { active = false; };\n }, [client, storageKey, followRunningTurn]);\n\n // Persist once a turn settles — never mid-stream — so the saved copy is clean.\n // Wait until the initial restore has completed so an empty initial state never\n // deletes an existing transcript.\n useEffect(() => {\n if (!client.persist || !restored || busy) return;\n try {\n if (messages.length) window.localStorage.setItem(storageKey, JSON.stringify(messages));\n else window.localStorage.removeItem(storageKey);\n } catch {\n /* ignore */\n }\n if (messages.length) void client.syncConversation(messages, conversationTitle(messages)).catch(() => undefined);\n }, [messages, busy, client, storageKey, restored]);\n\n const patchAssistant = useCallback((id: string, update: (m: AssistantChatMessage) => AssistantChatMessage) => {\n const next = messagesRef.current.map((message) =>\n message.id === id && message.role === \"assistant\" ? update(message) : message,\n );\n messagesRef.current = next;\n setMessages(next);\n }, []);\n\n const send = useCallback(\n async (text: string, opts?: SendOptions) => {\n const trimmed = text.trim();\n const attachments = opts?.attachments ?? [];\n if ((!trimmed && attachments.length === 0) || abortRef.current || !restored) return;\n // The server needs a message; an attachment-only turn gets a stand-in.\n const outgoing = trimmed || \"Please take a look at the attached file(s).\";\n\n const assistantId = nextId(\"a\");\n setError(undefined);\n setLoadedSkills(null);\n setActiveApis(null);\n setBusy(true);\n const next = [\n ...messagesRef.current,\n newUserMessage(\n nextId(\"u\"),\n trimmed,\n attachments.map((file) => ({ name: file.name, mimeType: file.mimeType })),\n ),\n newAssistantMessage(assistantId),\n ];\n messagesRef.current = next;\n setMessages(next);\n\n const abort = new AbortController();\n abortRef.current = abort;\n\n // Models stream text in uneven bursts — a few characters, then a lull,\n // then a whole paragraph at once — which reads as a stutter no matter how\n // efficiently React renders it. So we don't paint what arrives; we buffer\n // it and *reveal* it at a steady cadence, a slice per animation frame, so\n // the answer types out smoothly regardless of how lumpy the network is.\n // The slice is a fraction of what's waiting, so a big burst drains fast\n // (and never lags far behind) while a trickle still advances a few chars a\n // frame. Thinking isn't rendered, so it commits in full without pacing.\n // Any structural event, the stream's end, or an abort flushes the whole\n // remaining buffer at once, so ordering holds and no token is dropped.\n let bufferedText = \"\";\n let pendingThinking = \"\";\n let frame: number | null = null;\n const raf = typeof requestAnimationFrame === \"function\" ? requestAnimationFrame : (cb: FrameRequestCallback) => setTimeout(() => cb(performance.now()), 16) as unknown as number;\n const cancelRaf = typeof cancelAnimationFrame === \"function\" ? cancelAnimationFrame : (clearTimeout as (id: number) => void);\n const revealTick = () => {\n frame = null;\n let addText = \"\";\n if (bufferedText) {\n // Reveal ~1/6 of what's waiting (at least a few chars) so the pace\n // eases with the backlog: snappy on a burst, gentle on a trickle.\n const step = Math.max(3, Math.ceil(bufferedText.length / 6));\n addText = bufferedText.slice(0, step);\n bufferedText = bufferedText.slice(step);\n }\n const addThinking = pendingThinking;\n pendingThinking = \"\";\n if (addText || addThinking) {\n patchAssistant(assistantId, (message) => ({ ...message, text: message.text + addText, thinking: message.thinking + addThinking }));\n }\n if (bufferedText) scheduleReveal();\n };\n const scheduleReveal = () => { if (frame === null) frame = raf(revealTick); };\n const flushDeltas = () => {\n if (frame !== null) { cancelRaf(frame); frame = null; }\n if (!bufferedText && !pendingThinking) return;\n const addText = bufferedText;\n const addThinking = pendingThinking;\n bufferedText = \"\";\n pendingThinking = \"\";\n patchAssistant(assistantId, (message) => ({ ...message, text: message.text + addText, thinking: message.thinking + addThinking }));\n };\n\n let lostStream = false;\n try {\n let receivedText = false;\n let receivedToolActivity = false;\n await client.send(\n {\n message: outgoing,\n ...(context ? { context } : {}),\n ...(opts?.model ? { model: opts.model } : {}),\n ...(attachments.length ? { attachments } : {}),\n ...(opts?.confirm ? { confirm: opts.confirm } : {}),\n signal: abort.signal,\n },\n (event) => {\n if (event.type === \"skills_loaded\") { setLoadedSkills(event.skills); return; }\n if (event.type === \"tools_active\") { setActiveApis(event.apis); return; }\n if (event.type === \"ping\" || event.type === \"session\") return;\n if (event.type === \"text_delta\") { receivedText = true; bufferedText += event.text; scheduleReveal(); return; }\n if (event.type === \"thinking_delta\") { pendingThinking += event.text; scheduleReveal(); return; }\n if (event.type === \"tool_start\" || event.type === \"tool_end\") receivedToolActivity = true;\n // A structural event: land the buffered text before it so the tool\n // row or terminal status sits in the right place in the transcript.\n flushDeltas();\n patchAssistant(assistantId, (message) => applyEvent(message, event));\n },\n );\n // Commit anything still buffered when the stream ends, so the closing\n // words appear at once instead of trickling after the reply is done.\n flushDeltas();\n // A terminated stream with no text previously looked like Frankie did\n // nothing. Preserve visible tool activity, but never leave an empty\n // assistant bubble for the user to interpret.\n if (!receivedText) {\n patchAssistant(assistantId, (message) => ({\n ...message,\n status: \"done\",\n text: receivedToolActivity\n ? \"Completed. Open Agent activity for the details.\"\n : \"Frankie didn’t return an answer. Please try again.\",\n }));\n }\n } catch (caught) {\n // Land any tokens buffered when the stream broke off, so a stopped or\n // failed turn still shows everything that had arrived.\n flushDeltas();\n if (abort.signal.aborted) {\n patchAssistant(assistantId, (message) => ({\n ...message,\n status: \"done\",\n text: message.text || \"Stopped.\",\n }));\n } else if (!(caught instanceof FrankieApiError) && client.canResume) {\n // The connection dropped, not the turn: the server keeps going and\n // saves the reply. Keep this message working and pick it up below.\n lostStream = true;\n } else {\n const message =\n caught instanceof FrankieApiError\n ? caught.message\n : caught instanceof Error && caught.message.startsWith(\"Frankie\")\n ? caught.message\n : \"Could not reach the assistant. Check your connection and try again.\";\n if (mounted.current) setError(message);\n patchAssistant(assistantId, (current) => ({ ...current, status: \"error\", error: message }));\n }\n } finally {\n // Ensure no frame is left pending and every buffered token is committed\n // before the turn settles and the transcript is persisted.\n flushDeltas();\n // A turn that ended without a `turn_end` event still has to settle,\n // unless the server is still writing it (followed below).\n if (!lostStream) {\n patchAssistant(assistantId, (message) =>\n message.status === \"streaming\" ? { ...message, status: \"done\" } : message,\n );\n }\n // Persist before send() resolves. This closes the previous gap where a\n // route change or tab close could leave a server conversation with no\n // rendered messages even though the agent transcript existed.\n const transcript = messagesRef.current;\n if (transcript.length) {\n await client.syncConversation(transcript, conversationTitle(transcript)).catch(() => undefined);\n }\n if (mounted.current) setBusy(false);\n abortRef.current = null;\n if (lostStream) void followRunningTurn();\n }\n },\n [busy, client, context, patchAssistant, restored, followRunningTurn],\n );\n\n const answerAction = useCallback(\n async (toolUseId: string, approved: boolean) => {\n if (abortRef.current) return;\n const holder = messagesRef.current.find(\n (message): message is AssistantChatMessage =>\n message.role === \"assistant\" && message.action?.toolUseId === toolUseId && message.action.state === \"pending\",\n );\n if (!holder) return;\n patchAssistant(holder.id, (message) =>\n message.action ? { ...message, action: { ...message.action, state: approved ? \"approved\" : \"cancelled\" } } : message,\n );\n await send(approved ? \"Go ahead\" : \"Cancel\", { confirm: { toolUseId, approved } });\n },\n [patchAssistant, send],\n );\n\n // Closing the stream no longer stops a turn (a closed tab keeps its reply),\n // so Stop tells the server first, then closes it.\n const stop = useCallback(() => {\n if (!abortRef.current) return;\n void client.stopTurn();\n abortRef.current.abort();\n }, [client]);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n client.resetSession();\n messagesRef.current = [];\n setMessages([]);\n setError(undefined);\n setBusy(false);\n setRestored(true);\n setLoadedSkills(null);\n setActiveApis(null);\n try {\n if (client.persist) window.localStorage.removeItem(`${client.namespace}.messages`);\n } catch {\n /* ignore */\n }\n }, [client]);\n\n return { messages, busy, loadedSkills, activeApis, restoring: !restored, ...(error ? { error } : {}), send, answerAction, stop, reset };\n}\n\n\nfunction conversationTitle(messages: ChatMessage[]): string | undefined {\n const first = messages.find((message) => message.role === \"user\" && message.text.trim());\n if (!first) return undefined;\n const title = first.text.trim().replace(/\\s+/g, \" \");\n return title.length > 42 ? `${title.slice(0, 42)}…` : title;\n}\n\n/** Show the last reply as still being written: reopen a broken one, or add an empty one. */\nfunction stillWorking(messages: ChatMessage[]): ChatMessage[] {\n const last = messages[messages.length - 1];\n if (last?.role === \"assistant\") return [...messages.slice(0, -1), { ...last, status: \"streaming\", error: undefined }];\n return [...messages, { id: \"pending_\" + Date.now(), role: \"assistant\", text: \"\", thinking: \"\", tools: [], status: \"streaming\", createdAt: Date.now() }];\n}\n","import type { ActionPreview, FrankieServerEvent } from \"@frankieai/core\";\n\nexport interface ToolActivity {\n id: string;\n name: string;\n label: string;\n status: \"running\" | \"done\" | \"failed\" | \"denied\";\n detail?: string;\n}\n\n/** A file shown on a user message (name/type only — the bytes aren't kept in the transcript). */\nexport interface MessageAttachment {\n name: string;\n mimeType: string;\n}\n\nexport interface UserChatMessage {\n id: string;\n role: \"user\";\n text: string;\n attachments?: MessageAttachment[];\n createdAt: number;\n}\n\n/**\n * An action that changes data, paused until the user approves it. `pending`\n * shows Approve and Cancel; the other states record the answer.\n */\nexport interface ActionRequest {\n toolUseId: string;\n name: string;\n label: string;\n preview: ActionPreview;\n state: \"pending\" | \"approved\" | \"cancelled\";\n}\n\nexport interface AssistantChatMessage {\n id: string;\n role: \"assistant\";\n text: string;\n thinking: string;\n tools: ToolActivity[];\n status: \"streaming\" | \"done\" | \"error\";\n error?: string;\n createdAt: number;\n /** An action this reply is waiting on the user to approve. */\n action?: ActionRequest;\n}\n\nexport type ChatMessage = UserChatMessage | AssistantChatMessage;\n\n/**\n * Fold one server event into the assistant message being built.\n *\n * Kept pure and separate from React so the streaming logic is testable without\n * a DOM, and so a host writing its own UI can reuse it.\n */\nexport function applyEvent(message: AssistantChatMessage, event: FrankieServerEvent): AssistantChatMessage {\n switch (event.type) {\n case \"text_delta\":\n return { ...message, text: message.text + event.text };\n\n case \"thinking_delta\":\n return { ...message, thinking: message.thinking + event.text };\n\n case \"tool_start\":\n return {\n ...message,\n tools: [\n ...message.tools,\n { id: event.toolUseId, name: event.name, label: event.label, status: \"running\" },\n ],\n };\n\n case \"tool_end\":\n return {\n ...message,\n tools: message.tools.map((tool) =>\n tool.id === event.toolUseId\n ? {\n ...tool,\n status: event.ok ? \"done\" : \"failed\",\n ...(event.preview ? { detail: event.preview } : {}),\n }\n : tool,\n ),\n };\n\n case \"action_confirm\":\n return {\n ...message,\n action: { toolUseId: event.toolUseId, name: event.name, label: event.label, preview: event.preview, state: \"pending\" },\n };\n\n case \"tool_denied\":\n return {\n ...message,\n tools: upsertTool(message.tools, {\n id: event.toolUseId,\n name: event.name,\n label: event.name,\n status: \"denied\",\n detail: event.reason,\n }),\n };\n\n case \"turn_end\":\n return {\n ...message,\n status: message.status === \"error\" ? \"error\" : \"done\",\n // A turn that produced nothing still needs to say something.\n text: message.text || fallbackText(event.stopReason),\n };\n\n case \"error\":\n return { ...message, status: \"error\", error: event.message };\n\n default:\n return message;\n }\n}\n\nfunction upsertTool(tools: ToolActivity[], next: ToolActivity): ToolActivity[] {\n return tools.some((tool) => tool.id === next.id)\n ? tools.map((tool) => (tool.id === next.id ? { ...tool, ...next } : tool))\n : [...tools, next];\n}\n\nfunction fallbackText(stopReason: string): string {\n switch (stopReason) {\n case \"max_turns\":\n return \"I got stuck working on that. Could you narrow it down a little?\";\n case \"aborted\":\n return \"Stopped.\";\n case \"error\":\n return \"Something went wrong before I could answer. Try again?\";\n default:\n return \"\";\n }\n}\n\nexport function newAssistantMessage(id: string): AssistantChatMessage {\n return { id, role: \"assistant\", text: \"\", thinking: \"\", tools: [], status: \"streaming\", createdAt: Date.now() };\n}\n\nexport function newUserMessage(id: string, text: string, attachments?: MessageAttachment[]): UserChatMessage {\n return { id, role: \"user\", text, ...(attachments?.length ? { attachments } : {}), createdAt: Date.now() };\n}\n","\"use client\";\n\nimport { MessageCircle, X } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useStylesReady } from \"../../lib/styles-ready.js\";\nimport { bottomObstruction } from \"../../lib/bottom-bars.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { Button } from \"../ui/button.js\";\nimport { ChatPanel, type ChatPanelProps } from \"./ChatPanel.js\";\n\nexport interface FrankieWidgetProps extends ChatPanelProps {\n position?: \"bottom-right\" | \"bottom-left\";\n /** Distance from the viewport edges, in pixels. */\n offset?: number;\n defaultOpen?: boolean;\n /** Host overlays should sit above the assistant. Override for your layer system. */\n zIndex?: number;\n /** Controlled mode: pass both to drive open state from the host app. */\n open?: boolean;\n onOpenChange?: (open: boolean) => void;\n /** Replace the floating launcher button. */\n renderLauncher?: (props: { open: boolean; toggle: () => void; label: string }) => React.ReactNode;\n /**\n * `false` hides the floating button, for apps with their own entry point:\n * give any element `data-frankie-open` (a nav tab, a \"Help\" link) and a\n * click on it opens the chat. Defaults to `true`.\n */\n launcher?: boolean;\n /**\n * Sit above fixed bars along the bottom of the page (a mobile tab bar, a\n * cookie banner) instead of underneath them. Defaults to `true`.\n */\n avoidBottomBars?: boolean;\n /**\n * Close the open panel when the user clicks or taps anywhere outside the\n * widget, like a popover. Defaults to `true`. Clicks on elements marked\n * `data-frankie-open` (or `data-frankie-keep-open`) never close it.\n */\n closeOnOutsideClick?: boolean;\n}\n\n/**\n * The drop-in embed: a floating launcher plus the chat panel.\n *\n * Renders `position: fixed` at the document level, so it does not care what\n * layout the host page uses.\n */\nexport function FrankieWidget({\n position = \"bottom-right\",\n offset = 24,\n defaultOpen = false,\n zIndex = 20,\n open: controlledOpen,\n onOpenChange,\n renderLauncher,\n launcher = true,\n avoidBottomBars = true,\n closeOnOutsideClick = true,\n ...panelProps\n}: FrankieWidgetProps) {\n const { config, injectStyles, themeReady } = useFrankie();\n const theme = useFrankieTheme();\n const rootRef = useRef<HTMLDivElement>(null);\n // Never paint the panel as raw HTML while the host's CSS chunk is still in flight.\n const stylesReady = useStylesReady(rootRef, undefined, injectStyles);\n const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);\n const open = controlledOpen ?? uncontrolledOpen;\n\n const setOpen = (next: boolean) => {\n if (controlledOpen === undefined) setUncontrolledOpen(next);\n onOpenChange?.(next);\n };\n\n // Escape closes the panel, matching every other overlay on the host's page.\n useEffect(() => {\n if (!open) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setOpen(false);\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n // A click or tap outside the widget closes it. Pointerdown, not click, so a\n // drag that starts inside (selecting text in a reply) and ends outside\n // doesn't count, and the host's own control still gets its click.\n useEffect(() => {\n if (!open || !closeOnOutsideClick) return;\n const onPointerDown = (event: PointerEvent) => {\n const target = event.target;\n if (!(target instanceof Node) || !target.isConnected || rootRef.current?.contains(target)) return;\n if (target instanceof Element && target.closest(\"[data-frankie-open], [data-frankie-keep-open]\")) return;\n setOpen(false);\n };\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n return () => document.removeEventListener(\"pointerdown\", onPointerDown, true);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, closeOnOutsideClick]);\n\n // Any element marked `data-frankie-open` opens the chat, so a host's own\n // \"Ask\" tab or help link can be the entry point.\n useEffect(() => {\n const onClick = (event: MouseEvent) => {\n const target = event.target instanceof Element ? event.target.closest(\"[data-frankie-open]\") : null;\n if (!target || rootRef.current?.contains(target)) return;\n if (target instanceof HTMLAnchorElement && !target.getAttribute(\"href\")?.startsWith(\"#\")) event.preventDefault();\n setOpen(true);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Lift above a fixed bar along the bottom (a mobile tab bar) rather than\n // hiding beneath it. Measured after paint, on resize, and as the page\n // changes (throttled), since apps often mount their nav after the widget.\n const [lift, setLift] = useState(0);\n useEffect(() => {\n if (!avoidBottomBars || typeof window === \"undefined\") { setLift(0); return; }\n let frame = 0;\n let last = 0;\n const measure = () => {\n frame = 0;\n last = Date.now();\n setLift(bottomObstruction(rootRef.current, position, offset));\n };\n const schedule = () => {\n if (frame) return;\n const wait = Math.max(0, 400 - (Date.now() - last));\n frame = window.setTimeout(() => requestAnimationFrame(measure), wait) as unknown as number;\n };\n schedule();\n window.addEventListener(\"resize\", schedule);\n // The chat's own updates (a reply streaming in) never move the host's bars.\n const observer = typeof MutationObserver === \"function\"\n ? new MutationObserver((records) => { if (records.some((record) => !rootRef.current?.contains(record.target))) schedule(); })\n : undefined;\n observer?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [\"class\", \"style\"] });\n return () => {\n window.clearTimeout(frame);\n window.removeEventListener(\"resize\", schedule);\n observer?.disconnect();\n };\n }, [avoidBottomBars, position, offset]);\n\n const label = config?.branding.launcherLabel ?? \"Need a hand?\";\n const mode = useResolvedMode(theme?.mode);\n const side = position === \"bottom-right\" ? { right: offset } : { left: offset };\n\n return (\n <div\n ref={rootRef}\n className={cn(\"frankie-root frankie-widget\", mode === \"dark\" && \"dark\")}\n // `opacity` rather than `visibility` or unmounting: the sentinel has to stay\n // measurable on this element, and the two alternatives both drop the widget\n // out of the accessibility tree for the wait.\n style={{ ...themeToCssVars(theme), position: \"fixed\", bottom: lift ? lift + Math.min(offset, 12) : offset, ...side, zIndex, pointerEvents: \"none\", [\"--frankie-lift\" as string]: `${lift}px`, ...(stylesReady && themeReady ? null : { opacity: 0 as const }) }}\n data-frankie-mode={mode}\n data-frankie-styles={stylesReady ? \"ready\" : \"pending\"}\n >\n <div className={cn(\"flex flex-col gap-3\", position === \"bottom-left\" ? \"items-start\" : \"items-end\")}>\n {/* No clipping wrapper: `.frankie-widget-panel` caps its own height, and an\n `overflow: hidden` box here cut the panel's shadow off (all that showed was\n faint square corners). */}\n {open ? <div style={{ pointerEvents: \"auto\" }}><ChatPanel {...panelProps} className={cn(\"frankie-widget-panel\", panelProps.className)} onClose={() => setOpen(false)} autoFocus /></div> : null}\n {launcher || renderLauncher ? <div style={{ pointerEvents: \"auto\" }}>\n {renderLauncher ? (\n renderLauncher({ open, toggle: () => setOpen(!open), label })\n ) : (\n <Button\n size=\"icon\"\n className=\"size-12 rounded-full shadow-lg\"\n onClick={() => setOpen(!open)}\n aria-expanded={open}\n aria-label={open ? \"Close the assistant\" : label}\n title={label}\n >\n {open ? <X className=\"size-5\" /> : <MessageCircle className=\"size-5\" />}\n </Button>\n )}</div> : null}\n </div>\n </div>\n );\n}\n","import { useEffect, useLayoutEffect, useState, type RefObject } from \"react\";\nimport { ensureFrankieStyles } from \"./ensure-frankie-styles.js\";\n\n/** `useLayoutEffect` warns during SSR, where there is nothing to measure anyway. */\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** The property `frankie.css` sets on `.frankie-root`, and the value it sets. */\nconst SENTINEL = \"--frankie-styles-ready\";\n\n/**\n * Whether this widget's stylesheet has actually been applied to `node`.\n *\n * The widget is mounted inside someone else's application and bundler. A host\n * that imports `@frankieai/react/styles.css` from a client component ships it\n * in that component's chunk, and on a client-side navigation React can render\n * the panel before the CSS arrives — the panel then paints as raw HTML: native\n * `<details>` markers, no pills, browser-default type. A full reload hides this\n * because the stylesheet is in `<head>` before first paint.\n *\n * Probing a custom property costs nothing and asks the only question that\n * matters — are our rules in effect for this element — rather than guessing at\n * `<link>` elements the host may have inlined, bundled or renamed.\n *\n * Callers hide the root with `opacity` for the wait rather than `visibility` or\n * unmounting: the sentinel must stay measurable on that element, and both of the\n * alternatives would also drop the widget out of the accessibility tree.\n *\n * The wait is deliberately bounded. After `timeoutMs`, with no sentinel found,\n * this injects the stylesheet itself (`ensureFrankieStyles`) as a fallback for\n * a host that never loaded `styles.css` — the far more common way this shows\n * up in practice than the one case it must not clobber: a Tailwind v4 host\n * told it may skip our stylesheet and compile the same classes itself, which\n * also never sets the sentinel. That host opts out with `injectStyles={false}`\n * so it doesn't get a second, independently-themed copy of the stylesheet.\n */\nexport function useStylesReady(ref: RefObject<HTMLElement | null>, timeoutMs = 600, injectFallback = true): boolean {\n const [ready, setReady] = useState(false);\n\n useIsomorphicLayoutEffect(() => {\n if (ready) return;\n const node = ref.current;\n if (!node || typeof window === \"undefined\" || typeof window.getComputedStyle !== \"function\") {\n setReady(true);\n return;\n }\n\n const applied = () => window.getComputedStyle(node).getPropertyValue(SENTINEL).trim() === \"1\";\n // The common case — stylesheet already in `<head>` — resolves here, inside a\n // layout effect, so nothing unstyled is ever painted and nothing flickers.\n if (applied()) {\n setReady(true);\n return;\n }\n\n const deadline = Date.now() + timeoutMs;\n let frame = 0;\n const poll = () => {\n if (applied()) {\n setReady(true);\n return;\n }\n if (Date.now() >= deadline) {\n // Inline `<style>` insertion applies synchronously, so re-check right\n // after — but set `ready` regardless, matching the pre-existing\n // \"never hide the widget forever\" guarantee.\n if (injectFallback) ensureFrankieStyles();\n setReady(true);\n return;\n }\n frame = window.requestAnimationFrame(poll);\n };\n // `requestAnimationFrame` is missing in some test environments; fall back to\n // rendering rather than hiding the widget forever.\n if (typeof window.requestAnimationFrame !== \"function\") {\n setReady(true);\n return;\n }\n frame = window.requestAnimationFrame(poll);\n return () => window.cancelAnimationFrame(frame);\n }, [ref, ready, timeoutMs, injectFallback]);\n\n return ready;\n}\n","// Generated by scripts/embed-css.mjs from dist/frankie.css — do not edit by hand.\nexport const FRANKIE_CSS_CONTENT: string = \"/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\\n@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-ease:initial}}}@layer theme{.frankie-root{--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--leading-tight:1.25;--radius-md:.375rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--color-background:var(--frankie-background);--color-foreground:var(--frankie-foreground);--color-primary:var(--frankie-primary)}}@layer components{.frankie-page-shell{background:var(--frankie-background);height:100%;min-height:0;color:var(--frankie-foreground);flex:1;display:flex}.frankie-page-main{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.frankie-history-desktop{border-right:1px solid var(--frankie-border);background:var(--frankie-secondary);flex-direction:column;flex-shrink:0;gap:16px;width:260px;padding:20px 14px;transition:width .16s,padding .16s;display:flex;overflow:auto}.frankie-history-toggle{border:1px solid var(--frankie-border);background:var(--frankie-background);width:32px;height:32px;color:var(--frankie-muted-foreground);cursor:pointer;border-radius:8px;flex-shrink:0;justify-content:center;align-self:flex-end;align-items:center;display:flex}.frankie-history-toggle:hover{color:var(--frankie-foreground)}.frankie-history-collapsed{width:56px;padding:20px 8px;overflow:visible}.frankie-history-collapsed .frankie-history-toggle{align-self:center}.frankie-history-heading{justify-content:space-between;align-items:center;font-size:14px;display:flex}.frankie-new-chat{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;border-radius:10px;justify-content:center;align-items:center;gap:8px;min-height:44px;font-weight:600;display:flex}.frankie-history-search>span{color:var(--frankie-muted-foreground);margin-bottom:6px;font-size:12px;display:block}.frankie-history-search input{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:8px;width:100%;min-height:40px;padding:8px 10px}.frankie-history-list{flex-direction:column;flex:1;gap:4px;display:flex}.frankie-history-list button{text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:8px;flex-direction:column;align-items:flex-start;gap:5px;min-height:54px;padding:10px;display:flex}.frankie-history-list button span{text-overflow:ellipsis;white-space:nowrap;width:100%;font-size:13px;overflow:hidden}.frankie-history-list small{color:var(--frankie-muted-foreground);font-size:11px}.frankie-history-list button:hover,.frankie-history-list button[aria-current]{background:var(--frankie-background);border-color:var(--frankie-border)}.frankie-history-desktop>p,.frankie-history-dialog p{color:var(--frankie-muted-foreground);font-size:13px}.frankie-header-caption{color:var(--frankie-muted-foreground);font-size:12px}.frankie-page-header button{cursor:pointer;justify-content:center;align-items:center;gap:6px;min-width:40px;min-height:40px;display:inline-flex}.frankie-chat-settings{position:relative}.frankie-chat-settings summary{cursor:pointer;border-radius:8px;place-items:center;width:40px;height:40px;list-style:none;display:grid}.frankie-chat-settings summary::-webkit-details-marker{display:none}.frankie-chat-settings>div{z-index:5;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:12px;gap:12px;width:min(280px,80vw);padding:16px;font-size:13px;display:grid;position:absolute;top:46px;right:0;box-shadow:0 10px 30px #0002}.frankie-chat-settings p{color:var(--frankie-muted-foreground);margin:0}.frankie-loaded-skills{width:100%;max-width:760px;color:var(--frankie-muted-foreground);font-size:12px}.frankie-loaded-skills summary{cursor:pointer;padding:6px 0}.frankie-loaded-skills ul{margin:0;padding:4px 0;list-style:none}.frankie-loaded-skills li{gap:8px;padding:4px 0;display:flex}.frankie-loaded-skills small{opacity:.7}.frankie-history-dialog{background:var(--frankie-secondary);width:min(320px,86vw);max-width:none;height:100dvh;max-height:none;color:var(--frankie-foreground);border:0;margin:0;padding:0}.frankie-history-dialog::backdrop{background:#0008}.frankie-history-dialog>aside{height:100%;padding:calc(20px + env(safe-area-inset-top)) 16px calc(20px + env(safe-area-inset-bottom));flex-direction:column;gap:16px;display:flex;overflow:auto}.frankie-mobile-only{display:none!important}.frankie-widget-panel{max-height:calc(100dvh - 100px - var(--frankie-lift,0px))}.frankie-root button:focus-visible,.frankie-root summary:focus-visible,.frankie-root input:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:3px}.frankie-root button:disabled{opacity:.5;cursor:not-allowed}@media (max-width:760px){.frankie-history-desktop{display:none}.frankie-mobile-only{display:inline-flex!important}.frankie-header-caption{display:none}.frankie-page-header{padding:6px 10px}.frankie-history-heading button{background:0 0;border:0;justify-content:center;align-items:center;min-width:44px;min-height:44px}.frankie-page-prompts{gap:8px}.frankie-widget-panel{width:calc(100vw - 32px)}}.frankie-root{--frankie-styles-ready:1;--frankie-background:#fff;--frankie-foreground:#0a0a0a;--frankie-primary:#171717;--frankie-primary-foreground:#fafafa;--frankie-secondary:#f5f5f5;--frankie-secondary-foreground:#171717;--frankie-muted:#f5f5f5;--frankie-muted-foreground:#737373;--frankie-border:#e5e5e5;--frankie-radius:.625rem;--frankie-font:ui-sans-serif, system-ui, -apple-system, \\\"Segoe UI\\\", Roboto, \\\"Helvetica Neue\\\", Arial, sans-serif;font-family:var(--frankie-font);color:var(--frankie-foreground);-webkit-font-smoothing:antialiased;line-height:1.5}.frankie-root.dark{--frankie-background:#0a0a0a;--frankie-foreground:#fafafa;--frankie-primary:#fafafa;--frankie-primary-foreground:#171717;--frankie-secondary:#262626;--frankie-secondary-foreground:#fafafa;--frankie-muted:#262626;--frankie-muted-foreground:#a3a3a3;--frankie-border:#262626}.frankie-root,.frankie-root *,.frankie-root :before,.frankie-root :after{box-sizing:border-box}.frankie-root button,.frankie-root textarea,.frankie-root input{font:inherit;color:inherit;margin:0}.frankie-root button{background-color:#0000;border:0 solid}.frankie-root textarea{background-color:var(--frankie-background)}.frankie-focus:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:2px}.frankie-scroll{scrollbar-width:thin;scrollbar-color:var(--frankie-border) transparent}.frankie-prose>*+*{margin-top:.6rem}.frankie-prose p{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;line-height:1.6}.frankie-prose>.frankie-h{letter-spacing:-.01em;margin-top:1.15rem;line-height:1.3}.frankie-prose>.frankie-h:first-child{margin-top:0}.frankie-prose h3.frankie-h{font-size:1.06em;font-weight:700}.frankie-prose h4.frankie-h{font-size:1em;font-weight:600}.frankie-prose h5.frankie-h{color:var(--frankie-muted-foreground);font-size:.92em;font-weight:600}.frankie-prose strong{font-weight:600}.frankie-prose ul,.frankie-prose ol{flex-direction:column;gap:.35rem;margin:0;padding-left:1.4rem;display:flex}.frankie-prose li{padding-left:.15rem;line-height:1.55}.frankie-prose li::marker{color:var(--frankie-muted-foreground)}.frankie-prose ul{list-style:outside}.frankie-prose ol{list-style:decimal}.frankie-prose a{color:var(--frankie-primary);text-underline-offset:2px;text-decoration:underline}.frankie-prose code{background-color:var(--frankie-muted);border-radius:.25rem;padding:.1em .3em;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;font-size:.85em}.frankie-prose pre{border-radius:var(--frankie-radius);background-color:var(--frankie-muted);margin:0;padding:.625rem .75rem;overflow-x:auto}.frankie-prose pre code{background:0 0;padding:0}.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, var(--frankie-primary), transparent 65%), var(--frankie-background);flex-direction:column;min-width:0;display:flex;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, color-mix(in srgb, var(--frankie-primary) 8%, transparent), transparent 65%), var(--frankie-background)}}.frankie-chat-page{color:var(--frankie-foreground)}.frankie-page-header{border-bottom:1px solid var(--frankie-border);background:var(--frankie-background);flex:0 0 56px;justify-content:space-between;align-items:center;gap:16px;min-height:56px;padding:0 24px;display:flex}@supports (color:color-mix(in lab, red, red)){.frankie-page-header{background:color-mix(in srgb, var(--frankie-background) 92%, transparent)}}.frankie-page-header{-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.frankie-page-header>div{align-items:center;gap:10px;display:flex}.frankie-page-header strong{font-size:.925rem;font-weight:650}.frankie-page-status{background:var(--frankie-primary);width:10px;height:10px;box-shadow:0 0 0 4px var(--frankie-primary);border-radius:999px}@supports (color:color-mix(in lab, red, red)){.frankie-page-status{box-shadow:0 0 0 4px color-mix(in srgb, var(--frankie-primary) 16%, transparent)}}.frankie-page-header button{border:1px solid var(--frankie-border);border-radius:calc(var(--frankie-radius) * .9);background:var(--frankie-secondary);cursor:pointer;min-height:36px;padding:0 13px}.frankie-page-header button:hover{border-color:var(--frankie-primary)}.frankie-page-canvas{flex:1;min-width:0;min-height:0;display:flex}.frankie-page-canvas>.frankie-inline{width:100%;max-width:56rem;margin:0 auto}.frankie-page-canvas .frankie-scroll{padding:2rem clamp(1rem,4vw,4rem) 1.25rem}.frankie-page-canvas .frankie-root>.border-t{background:linear-gradient(180deg, transparent, var(--frankie-background) 30%);border-top:0;padding:.75rem clamp(1rem,4vw,4rem) 1.5rem}.frankie-page-empty{text-align:center;flex-direction:column;flex-shrink:0;justify-content:safe center;align-items:center;gap:2rem;width:min(100%,44rem);min-height:100%;margin:auto;padding:clamp(2rem,7vh,4.5rem) 1rem 2rem;display:flex;container-type:inline-size}.frankie-page-welcome h1{letter-spacing:-.02em;margin:0;font-size:clamp(1.55rem,3.4vw,2rem);font-weight:700;line-height:1.15}.frankie-page-welcome p{max-width:34rem;color:var(--frankie-muted-foreground);margin:.6rem auto 0;font-size:1rem}.frankie-page-suggest{flex-direction:column;align-items:center;gap:1rem;width:100%;display:flex}.frankie-page-suggest-label{color:var(--frankie-muted-foreground);letter-spacing:.12em;text-transform:uppercase;margin:0;font-size:.72rem;font-weight:600}.frankie-page-prompts{grid-template-columns:1fr;gap:.75rem;width:100%;display:grid}@container (min-width:30rem){.frankie-page-prompts{grid-template-columns:repeat(2,minmax(0,1fr))}}.frankie-page-prompts button{border:1px solid var(--frankie-border);background:var(--frankie-background);text-align:left;cursor:pointer;border-radius:14px;align-items:flex-start;gap:.85rem;min-height:64px;padding:.9rem 1rem;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-page-prompts button:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 45%, var(--frankie-border))}}.frankie-page-prompts button:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 4%, var(--frankie-background))}}.frankie-page-prompts button:hover:not(:disabled){transform:translateY(-1px)}.frankie-prompt-icon{background:var(--frankie-muted);width:34px;height:34px;color:var(--frankie-foreground);border-radius:9px;flex:none;place-items:center;display:grid}.frankie-prompt-text{flex-direction:column;gap:2px;min-width:0;display:flex}.frankie-prompt-text strong{font-size:.9rem;font-weight:600;line-height:1.3}.frankie-prompt-text small{color:var(--frankie-muted-foreground);font-size:.8rem;line-height:1.35;overflow:hidden}.frankie-page-suggest-hint{color:var(--frankie-muted-foreground);margin:0;font-size:.85rem}.frankie-widget-panel .frankie-page-empty{gap:1rem;padding:1rem .25rem}.frankie-widget-panel .frankie-page-welcome h1{font-size:1.25rem}.frankie-widget-panel .frankie-page-welcome p{margin-top:.35rem;font-size:.88rem;line-height:1.45}.frankie-widget-panel .frankie-page-suggest{gap:.55rem}.frankie-widget-panel .frankie-page-suggest-label{font-size:.66rem}.frankie-widget-panel .frankie-page-prompts{flex-wrap:wrap;justify-content:center;gap:.4rem;display:flex}.frankie-widget-panel .frankie-page-prompts button{border-radius:999px;align-items:center;gap:.4rem;max-width:100%;min-height:34px;padding:.3rem .8rem .3rem .45rem}.frankie-widget-panel .frankie-prompt-icon{border-radius:999px;width:22px;height:22px}.frankie-widget-panel .frankie-prompt-icon svg{width:13px;height:13px}.frankie-widget-panel .frankie-prompt-text strong{white-space:nowrap;text-overflow:ellipsis;font-size:.82rem;font-weight:550;overflow:hidden}.frankie-widget-panel .frankie-prompt-text small,.frankie-widget-panel .frankie-page-suggest-hint{display:none}@media (max-width:640px){.frankie-page-header{flex-basis:52px;min-height:52px;padding:0 12px}.frankie-page-canvas .frankie-scroll{padding:1.25rem .75rem .5rem}.frankie-page-canvas .frankie-root>.border-t{padding:.65rem .75rem calc(.65rem + env(safe-area-inset-bottom))}}}@layer utilities;.frankie-root .\\\\@container{container-type:inline-size}.frankie-root .invisible{visibility:hidden}.frankie-root .visible{visibility:visible}.frankie-root .absolute{position:absolute}.frankie-root .fixed{position:fixed}.frankie-root .relative{position:relative}.frankie-root .static{position:static}.frankie-root .sticky{position:sticky}.frankie-root .isolate{isolation:isolate}.frankie-root .container{width:100%}@media (min-width:40rem){.frankie-root .container{max-width:40rem}}@media (min-width:48rem){.frankie-root .container{max-width:48rem}}@media (min-width:64rem){.frankie-root .container{max-width:64rem}}@media (min-width:80rem){.frankie-root .container{max-width:80rem}}@media (min-width:96rem){.frankie-root .container{max-width:96rem}}.frankie-root .mt-0{margin-top:0}.frankie-root .mt-0\\\\.5{margin-top:calc(var(--spacing) * .5)}.frankie-root .mt-1{margin-top:var(--spacing)}.frankie-root .mt-2{margin-top:calc(var(--spacing) * 2)}.frankie-root .block{display:block}.frankie-root .contents{display:contents}.frankie-root .flex{display:flex}.frankie-root .grid{display:grid}.frankie-root .hidden{display:none}.frankie-root .inline{display:inline}.frankie-root .inline-flex{display:inline-flex}.frankie-root .table{display:table}.frankie-root .size-1{width:var(--spacing);height:var(--spacing)}.frankie-root .size-1\\\\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.frankie-root .size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.frankie-root .size-3\\\\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.frankie-root .size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.frankie-root .size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.frankie-root .size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.frankie-root .size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.frankie-root .size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.frankie-root .size-full{width:100%;height:100%}.frankie-root .h-8{height:calc(var(--spacing) * 8)}.frankie-root .h-9{height:calc(var(--spacing) * 9)}.frankie-root .h-10{height:calc(var(--spacing) * 10)}.frankie-root .h-\\\\[32rem\\\\]{height:32rem}.frankie-root .h-full{height:100%}.frankie-root .h-px{height:1px}.frankie-root .min-h-7{min-height:calc(var(--spacing) * 7)}.frankie-root .min-h-\\\\[2\\\\.25rem\\\\]{min-height:2.25rem}.frankie-root .min-h-screen{min-height:100vh}.frankie-root .w-\\\\[22rem\\\\]{width:22rem}.frankie-root .w-full{width:100%}.frankie-root .max-w-\\\\[12rem\\\\]{max-width:12rem}.frankie-root .max-w-\\\\[14rem\\\\]{max-width:14rem}.frankie-root .max-w-\\\\[85\\\\%\\\\]{max-width:85%}.frankie-root .max-w-\\\\[calc\\\\(100vw-2rem\\\\)\\\\]{max-width:calc(100vw - 2rem)}.frankie-root .min-w-0{min-width:0}.frankie-root .flex-1{flex:1}.frankie-root .shrink-0{flex-shrink:0}.frankie-root .grow{flex-grow:1}.frankie-root .animate-pulse{animation:var(--animate-pulse)}.frankie-root .animate-spin{animation:var(--animate-spin)}.frankie-root .cursor-pointer{cursor:pointer}.frankie-root .resize{resize:both}.frankie-root .resize-none{resize:none}.frankie-root .list-none{list-style-type:none}.frankie-root .flex-col{flex-direction:column}.frankie-root .flex-wrap{flex-wrap:wrap}.frankie-root .items-center{align-items:center}.frankie-root .items-end{align-items:flex-end}.frankie-root .items-start{align-items:flex-start}.frankie-root .justify-center{justify-content:center}.frankie-root .justify-end{justify-content:flex-end}.frankie-root .gap-1{gap:var(--spacing)}.frankie-root .gap-1\\\\.5{gap:calc(var(--spacing) * 1.5)}.frankie-root .gap-2{gap:calc(var(--spacing) * 2)}.frankie-root .gap-3{gap:calc(var(--spacing) * 3)}.frankie-root .gap-4{gap:calc(var(--spacing) * 4)}.frankie-root .truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.frankie-root .overflow-hidden{overflow:hidden}.frankie-root .overflow-y-auto{overflow-y:auto}.frankie-root .overscroll-contain{overscroll-behavior:contain}.frankie-root .rounded{border-radius:.25rem}.frankie-root .rounded-\\\\[calc\\\\(var\\\\(--frankie-radius\\\\)\\\\+4px\\\\)\\\\]{border-radius:calc(var(--frankie-radius) + 4px)}.frankie-root .rounded-\\\\[calc\\\\(var\\\\(--frankie-radius\\\\)-2px\\\\)\\\\]{border-radius:calc(var(--frankie-radius) - 2px)}.frankie-root .rounded-\\\\[var\\\\(--frankie-radius\\\\)\\\\]{border-radius:var(--frankie-radius)}.frankie-root .rounded-full{border-radius:3.40282e38px}.frankie-root .rounded-md{border-radius:var(--radius-md)}.frankie-root .rounded-none{border-radius:0}.frankie-root .border{border-style:var(--tw-border-style);border-width:1px}.frankie-root .border-0{border-style:var(--tw-border-style);border-width:0}.frankie-root .border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.frankie-root .border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.frankie-root .border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.frankie-root .border-border{border-color:var(--frankie-border)}.frankie-root .border-transparent{border-color:#0000}.frankie-root .bg-background{background-color:var(--frankie-background)}.frankie-root .bg-border{background-color:var(--frankie-border)}.frankie-root .bg-emerald-500{background-color:var(--color-emerald-500)}.frankie-root .bg-emerald-500\\\\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-emerald-500\\\\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.frankie-root .bg-muted{background-color:var(--frankie-muted)}.frankie-root .bg-muted-foreground{background-color:var(--frankie-muted-foreground)}.frankie-root .bg-primary{background-color:var(--frankie-primary)}.frankie-root .bg-red-500{background-color:var(--color-red-500)}.frankie-root .bg-red-500\\\\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-red-500\\\\/10{background-color:color-mix(in oklab, var(--color-red-500) 10%, transparent)}}.frankie-root .bg-secondary{background-color:var(--frankie-secondary)}.frankie-root .object-cover{object-fit:cover}.frankie-root .p-3{padding:calc(var(--spacing) * 3)}.frankie-root .p-4{padding:calc(var(--spacing) * 4)}.frankie-root .px-1{padding-inline:var(--spacing)}.frankie-root .px-2{padding-inline:calc(var(--spacing) * 2)}.frankie-root .px-3{padding-inline:calc(var(--spacing) * 3)}.frankie-root .px-4{padding-inline:calc(var(--spacing) * 4)}.frankie-root .px-6{padding-inline:calc(var(--spacing) * 6)}.frankie-root .py-0{padding-block:0}.frankie-root .py-0\\\\.5{padding-block:calc(var(--spacing) * .5)}.frankie-root .py-1{padding-block:var(--spacing)}.frankie-root .py-2{padding-block:calc(var(--spacing) * 2)}.frankie-root .py-3{padding-block:calc(var(--spacing) * 3)}.frankie-root .pl-2{padding-left:calc(var(--spacing) * 2)}.frankie-root .text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.frankie-root .text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.frankie-root .text-\\\\[0\\\\.625rem\\\\]{font-size:.625rem}.frankie-root .text-\\\\[0\\\\.6875rem\\\\]{font-size:.6875rem}.frankie-root .text-\\\\[11px\\\\]{font-size:11px}.frankie-root .leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.frankie-root .font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.frankie-root .font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.frankie-root .whitespace-nowrap{white-space:nowrap}.frankie-root .text-emerald-600{color:var(--color-emerald-600)}.frankie-root .text-foreground{color:var(--frankie-foreground)}.frankie-root .text-muted-foreground{color:var(--frankie-muted-foreground)}.frankie-root .text-primary{color:var(--frankie-primary)}.frankie-root .text-primary-foreground{color:var(--frankie-primary-foreground)}.frankie-root .text-red-600{color:var(--color-red-600)}.frankie-root .text-secondary-foreground{color:var(--frankie-secondary-foreground)}.frankie-root .italic{font-style:italic}.frankie-root .underline-offset-4{text-underline-offset:4px}.frankie-root .opacity-60{opacity:.6}.frankie-root .opacity-70{opacity:.7}.frankie-root .shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .outline{outline-style:var(--tw-outline-style);outline-width:1px}.frankie-root .transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.frankie-root .select-all{-webkit-user-select:all;user-select:all}.frankie-root .placeholder\\\\:text-muted-foreground::placeholder{color:var(--frankie-muted-foreground)}@media (hover:hover){.frankie-root .hover\\\\:border-border:hover{border-color:var(--frankie-border)}.frankie-root .hover\\\\:bg-muted:hover{background-color:var(--frankie-muted)}.frankie-root .hover\\\\:text-foreground:hover{color:var(--frankie-foreground)}.frankie-root .hover\\\\:underline:hover{text-decoration-line:underline}.frankie-root .hover\\\\:opacity-90:hover{opacity:.9}.frankie-root .hover\\\\:opacity-100:hover{opacity:1}}.frankie-root .focus-visible\\\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .focus-visible\\\\:ring-primary:focus-visible{--tw-ring-color:var(--frankie-primary)}.frankie-root .focus-visible\\\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.frankie-root .disabled\\\\:pointer-events-none:disabled{pointer-events:none}.frankie-root .disabled\\\\:cursor-not-allowed:disabled{cursor:not-allowed}.frankie-root .disabled\\\\:opacity-50:disabled{opacity:.5}.frankie-root .dark\\\\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.frankie-root .dark\\\\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.frankie-root .\\\\[\\\\&_svg\\\\]\\\\:pointer-events-none svg{pointer-events:none}.frankie-root .\\\\[\\\\&_svg\\\\]\\\\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .\\\\[\\\\&_svg\\\\]\\\\:shrink-0 svg{flex-shrink:0}.frankie-root .\\\\[\\\\&\\\\:\\\\:-webkit-details-marker\\\\]\\\\:hidden::-webkit-details-marker{display:none}.frankie-root.frankie-chat-panel{min-height:0;position:relative}.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px var(--frankie-primary), 0 12px 28px -12px #0f172a47, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px color-mix(in srgb, var(--frankie-primary) 22%, #0f172a6b), 0 12px 28px -12px #0f172a47, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 8%, transparent)}}.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 12%, transparent)}}.frankie-root .frankie-agent-context-wrap{flex:none;justify-content:center;padding:12px 10px;display:flex}.frankie-root .frankie-agent-context{max-width:100%;color:var(--frankie-foreground);font-size:13px;position:relative}.frankie-root .frankie-agent-context>summary{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;white-space:nowrap;border-radius:999px;justify-content:center;align-items:center;gap:10px;min-height:44px;padding:7px 13px;list-style:none;display:flex;box-shadow:0 1px 3px #0000000d}.frankie-root .frankie-agent-context>summary::-webkit-details-marker{display:none}.frankie-root .frankie-agent-context>summary>svg{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-context-count{background:var(--frankie-muted);color:var(--frankie-muted-foreground);border-radius:999px;padding:2px 10px;font-size:12px}.frankie-root .frankie-context-status{color:var(--frankie-muted-foreground);align-items:center;gap:6px;font-size:12px;display:flex}.frankie-root .frankie-context-status i{background:currentColor;border-radius:50%;width:7px;height:7px}.frankie-root .frankie-context-status[data-active=true]{color:#00976b}.frankie-root .frankie-context-detail{z-index:10;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:16px;width:340px;max-width:calc(100vw - 32px);padding:18px;position:absolute;top:calc(100% + 10px);left:50%;transform:translate(-50%);box-shadow:0 12px 32px #00000016}.frankie-root .frankie-context-detail p{color:var(--frankie-muted-foreground);margin:10px 0 0;font-size:12px;line-height:1.6}.frankie-root .frankie-context-detail ul{max-height:220px;margin:12px 0;padding:0;list-style:none;overflow:auto}.frankie-root .frankie-context-detail li{border-bottom:1px solid var(--frankie-border);overflow-wrap:anywhere;justify-content:space-between;gap:12px;padding:8px 0;display:flex}.frankie-root .frankie-context-detail small{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.frankie-root.frankie-chat-panel-fill{--frankie-canvas:var(--frankie-background)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill{--frankie-canvas:color-mix(in srgb,var(--frankie-background) 60%,var(--frankie-muted))}}.frankie-root.frankie-chat-panel-fill{background:var(--frankie-canvas)}.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:44px 16px 26px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:14px max(24px,50% - 480px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-message-list{gap:38px}.frankie-root.frankie-chat-panel-fill .frankie-user-bubble{background:var(--frankie-muted);color:var(--frankie-foreground);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:24px;padding:12px 20px;font-size:15px}.frankie-root.frankie-chat-panel-fill .frankie-assistant-content{flex:1;max-width:100%;font-size:15px;line-height:1.7}.frankie-root.frankie-chat-panel-fill .frankie-assistant-message{gap:14px}.frankie-root .frankie-thinking{color:var(--frankie-muted-foreground);gap:5px}.frankie-root .frankie-thinking-label{margin-right:6px;font-size:14px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{background:0 0;border:0;flex:none;padding:16px max(24px,50% - 500px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-composer{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:28px;padding:14px;box-shadow:0 2px 4px #0000000a}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:color-mix(in srgb,var(--frankie-foreground) 32%,var(--frankie-border))}}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px color-mix(in srgb,var(--frankie-primary) 8%,transparent)}}.frankie-root.frankie-chat-panel-fill .frankie-composer-controls{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.frankie-root.frankie-chat-panel-fill .frankie-composer textarea{resize:none;width:100%;min-height:32px;max-height:140px;color:var(--frankie-foreground);box-shadow:none;background:0 0;border:0;border-radius:0;outline:none;flex:0 0 100%;order:-1;margin-bottom:4px;padding:2px 6px;font-size:16px}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Send],.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Stop]{background:var(--frankie-primary);width:42px;height:42px;color:var(--frankie-primary-foreground);border:0;border-radius:50%;margin-left:auto}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Attach\\\\ files]{border-radius:50%;width:42px;height:42px}.frankie-root .frankie-composer-actions{align-items:center;gap:6px;min-width:0;display:flex}.frankie-root .frankie-composer-meta{align-items:center;gap:8px;margin-top:6px;padding:0 2px;display:flex}.frankie-root .frankie-model-picker{align-items:center;min-width:0;display:inline-flex}.frankie-root .frankie-model-select{appearance:none;max-width:12rem;color:var(--frankie-muted-foreground);font:inherit;cursor:pointer;text-overflow:ellipsis;background-color:#0000;background-image:url(\\\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b8b8b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>\\\");background-position:right 6px center;background-repeat:no-repeat;background-size:11px;border:0;border-radius:8px;padding:5px 22px 5px 8px;font-size:12px;font-weight:500;line-height:1;transition:background-color .12s,color .12s}.frankie-root .frankie-model-select:hover{background-color:var(--frankie-muted);color:var(--frankie-foreground)}.frankie-root .frankie-model-select:disabled{opacity:.5;cursor:not-allowed}.frankie-root .frankie-model-select option{color:var(--frankie-foreground);background:var(--frankie-background);font-weight:400}.frankie-root .frankie-history-desktop{background:var(--frankie-background);width:290px;padding:20px 12px}.frankie-root .frankie-history-collapsed{width:56px;padding:20px 8px}.frankie-root .frankie-new-chat{background:0 0;border:0;justify-content:flex-start;padding:8px 12px;font-size:15px}.frankie-root .frankie-history-heading{color:var(--frankie-muted-foreground);padding:0 12px;font-size:12px;font-weight:400}.frankie-root .frankie-history-list button{min-height:44px;padding:10px 12px}.frankie-root .frankie-history-list button span{font-size:14px}.frankie-root .frankie-history-list button[aria-current],.frankie-root .frankie-history-list button:hover{background:var(--frankie-muted);border-color:#0000}.frankie-root .frankie-history-list small{display:none}.frankie-root .frankie-history-group{flex-direction:column;gap:2px;display:flex}.frankie-root .frankie-history-group+.frankie-history-group{margin-top:12px}.frankie-root .frankie-history-group-label{letter-spacing:.04em;color:var(--frankie-muted-foreground);margin:0;padding:4px 12px;font-size:11px;font-weight:600}.frankie-root .frankie-page-header{background:var(--frankie-background);height:56px;min-height:56px}.frankie-root .frankie-page-header>div{min-width:0}.frankie-root .frankie-thread-title{text-overflow:ellipsis;white-space:nowrap;max-width:55vw;font-weight:500;overflow:hidden}.frankie-root .frankie-page-header button{background:0 0;border-color:#0000}.frankie-root .frankie-page-canvas>.frankie-inline{max-width:none}@media (max-width:760px){.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:20px 8px 14px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:12px 16px 16px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{padding:10px 12px calc(12px + env(safe-area-inset-bottom))}.frankie-root .frankie-agent-context>summary{gap:6px;padding:7px 9px;font-size:12px}.frankie-root .frankie-context-count{padding:2px 7px;font-size:11px}.frankie-root .frankie-context-status{gap:4px;font-size:11px}.frankie-root .frankie-agent-context>summary>svg{width:14px}.frankie-root .frankie-thread-title{max-width:42vw}}.frankie-root .frankie-agent-context>summary>svg:first-child{box-sizing:content-box;background:var(--frankie-muted);border-radius:50%;padding:4px}.frankie-root.frankie-chat-panel-fill .frankie-agent-context>summary{font-size:14px}.frankie-root .frankie-agent-context>summary>span:first-of-type{border-right:1px solid var(--frankie-border);padding-right:10px}@media (max-width:760px){.frankie-root .frankie-page-header button>span{display:none}}.frankie-root .frankie-choices{flex-direction:column;gap:8px;margin-top:12px;display:flex}.frankie-root .frankie-choice{text-align:left;cursor:pointer;border:1px solid var(--frankie-border);border-radius:var(--frankie-radius);background:var(--frankie-background);width:100%;color:var(--frankie-foreground);font:inherit;align-items:center;gap:12px;padding:11px 14px;font-size:14px;line-height:1.4;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-root .frankie-choice:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 55%, var(--frankie-border))}}.frankie-root .frankie-choice:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 6%, var(--frankie-background))}}.frankie-root .frankie-choice:active:not(:disabled){transform:scale(.99)}.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px var(--frankie-primary);outline:none}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, var(--frankie-primary) 40%, transparent)}}.frankie-root .frankie-choice:disabled{opacity:.6;cursor:default}.frankie-root .frankie-choice-index{border:1px solid var(--frankie-border);background:var(--frankie-muted);width:24px;height:24px;color:var(--frankie-muted-foreground);border-radius:50%;flex:none;place-items:center;font-size:12px;font-weight:600;transition:background-color .15s,color .15s,border-color .15s;display:grid}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-primary);border-color:var(--frankie-primary);color:var(--frankie-primary-foreground)}.frankie-root .frankie-choice-label{flex:1;min-width:0}.frankie-root .frankie-choice-go{color:var(--frankie-muted-foreground);opacity:0;flex:none;transition:opacity .15s,transform .15s;transform:translate(-4px)}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-go{opacity:1;color:var(--frankie-primary);transform:none}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-choice,.frankie-root .frankie-choice-go,.frankie-root .frankie-choice-index{transition:none}}.frankie-root .frankie-choice-other{color:var(--frankie-muted-foreground);border-style:dashed}.frankie-root .frankie-choice-other .frankie-choice-index{background:0 0}.frankie-root .frankie-choice-other:hover:not(:disabled){color:var(--frankie-foreground)}.frankie-root .frankie-choice-other:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-muted);border-color:var(--frankie-border);color:var(--frankie-foreground)}.frankie-root .frankie-choice-hint{color:var(--frankie-muted-foreground);flex:none;font-size:12px}.frankie-root .frankie-skeleton{flex-direction:column;gap:8px;max-width:min(420px,100%);padding:4px 0 6px;display:flex}.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, var(--frankie-muted-foreground) 25%, var(--frankie-muted-foreground) 37%, var(--frankie-muted-foreground) 63%);border-radius:6px;height:12px}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 25%, color-mix(in srgb, var(--frankie-muted-foreground) 19%, transparent) 37%, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 63%)}}.frankie-root .frankie-skeleton-line{background-size:400% 100%;animation:2.2s ease-in-out infinite frankie-shimmer}.frankie-root .frankie-skeleton.is-delayed{animation:.25s .35s both frankie-skeleton-in}@keyframes frankie-skeleton-in{0%{opacity:0}to{opacity:1}}@keyframes frankie-shimmer{0%{background-position:100%}to{background-position:0}}@keyframes frankie-skeleton-pulse{0%,to{opacity:.45}50%{opacity:.85}}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-skeleton-line{background:var(--frankie-muted-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:color-mix(in srgb, var(--frankie-muted-foreground) 16%, transparent)}}.frankie-root .frankie-skeleton-line{animation:1.6s ease-in-out infinite frankie-skeleton-pulse}}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden])){flex-wrap:wrap;align-items:center;row-gap:8px}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden]))>textarea{flex:1 0 100%;order:-1;width:100%}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden]))>.frankie-composer-actions{flex:auto}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden]))>button{margin-left:auto}.frankie-root .frankie-action{border:1px solid var(--frankie-border);border-radius:var(--frankie-radius);background:var(--frankie-background);color:var(--frankie-foreground);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;font-size:14px;line-height:1.4;display:flex}.frankie-root .frankie-action.is-pending{border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action.is-pending{border-color:color-mix(in srgb, var(--frankie-primary) 45%, var(--frankie-border))}}.frankie-root .frankie-action.is-pending{background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action.is-pending{background:color-mix(in srgb, var(--frankie-primary) 5%, var(--frankie-background))}}.frankie-root .frankie-action-head{align-items:flex-start;gap:10px;display:flex}.frankie-root .frankie-action-icon{color:var(--frankie-primary);flex-shrink:0;margin-top:2px}.frankie-root .frankie-action-title{flex-direction:column;gap:2px;min-width:0;display:flex}.frankie-root .frankie-action-title strong{overflow-wrap:anywhere;font-weight:600}.frankie-root .frankie-action-title span{color:var(--frankie-muted-foreground);font-size:12px}.frankie-root .frankie-action-route{border-radius:calc(var(--frankie-radius) * .6);background:var(--frankie-muted);overflow-wrap:anywhere;padding:6px 8px;font-size:12px;display:block}.frankie-root .frankie-action-route span{font-weight:600}.frankie-root .frankie-action-fields{gap:6px;margin:0;display:grid}.frankie-root .frankie-action-fields>div{grid-template-columns:minmax(0,35%) minmax(0,1fr);gap:10px;display:grid}.frankie-root .frankie-action-fields dt{color:var(--frankie-muted-foreground);overflow-wrap:anywhere;font-size:12px}.frankie-root .frankie-action-fields dd{white-space:pre-wrap;overflow-wrap:anywhere;max-height:8.5em;margin:0;font-size:13px;overflow:auto}.frankie-root .frankie-action-buttons{flex-wrap:wrap;gap:8px;display:flex}.frankie-root .frankie-action-buttons button{cursor:pointer;font:inherit;border-radius:var(--frankie-radius);border:1px solid var(--frankie-border);align-items:center;gap:6px;padding:7px 14px;font-size:14px;font-weight:500;transition:background-color .15s,border-color .15s;display:inline-flex}.frankie-root .frankie-action-approve{background:var(--frankie-primary);color:var(--frankie-primary-foreground);border-color:var(--frankie-primary)}.frankie-root .frankie-action-approve:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action-approve:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 88%, black)}}.frankie-root .frankie-action-cancel{background:var(--frankie-background);color:var(--frankie-foreground)}.frankie-root .frankie-action-cancel:hover:not(:disabled){background:var(--frankie-muted)}.frankie-root .frankie-action-buttons button:disabled{opacity:.55;cursor:not-allowed}.frankie-root .frankie-action-buttons button:focus-visible{box-shadow:0 0 0 2px var(--frankie-primary);outline:none}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action-buttons button:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, var(--frankie-primary) 40%, transparent)}}@property --tw-border-style{syntax:\\\"*\\\";inherits:false;initial-value:solid}@property --tw-leading{syntax:\\\"*\\\";inherits:false}@property --tw-font-weight{syntax:\\\"*\\\";inherits:false}@property --tw-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:\\\"*\\\";inherits:false}@property --tw-shadow-alpha{syntax:\\\"<percentage>\\\";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:\\\"*\\\";inherits:false}@property --tw-inset-shadow-alpha{syntax:\\\"<percentage>\\\";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:\\\"*\\\";inherits:false}@property --tw-ring-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:\\\"*\\\";inherits:false}@property --tw-inset-ring-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:\\\"*\\\";inherits:false}@property --tw-ring-offset-width{syntax:\\\"<length>\\\";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:\\\"*\\\";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:\\\"*\\\";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:\\\"*\\\";inherits:false;initial-value:solid}@property --tw-ease{syntax:\\\"*\\\";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}\";\n","import { FRANKIE_CSS_CONTENT } from \"../generated/css-content.js\";\n\nconst STYLE_ELEMENT_ID = \"frankie-fallback-styles\";\nlet attempted = false;\n\n/**\n * Last resort for a host that never loaded `@frankieai/react/styles.css` —\n * forgot the import, or its CSS chunk hasn't landed by the time\n * `useStylesReady`'s bounded wait gives up. Injects the same stylesheet the\n * package ships as `./styles.css`, so the widget still renders correctly\n * instead of as raw, unstyled HTML.\n *\n * Only called when a host hasn't opted out via `injectStyles={false}` — the\n * documented escape hatch for a host that compiles Frankie's classes itself\n * (e.g. a Tailwind v4 host `@source`-ing this package) and would not want a\n * second, independently-themed copy of the stylesheet appended.\n */\nexport function ensureFrankieStyles(): void {\n if (attempted || typeof document === \"undefined\") return;\n attempted = true;\n if (document.getElementById(STYLE_ELEMENT_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ELEMENT_ID;\n style.textContent = FRANKIE_CSS_CONTENT;\n document.head.appendChild(style);\n}\n\n/** Test-only: clears the injected-once guard so each test starts clean. */\nexport function __resetFrankieStylesForTests(): void {\n attempted = false;\n document.getElementById(STYLE_ELEMENT_ID)?.remove();\n}\n","/**\n * How far a fixed or sticky bar along the bottom of the viewport (a mobile tab\n * bar, a cookie banner, a sticky \"Buy\" bar) rises over the widget's corner, in\n * pixels. The launcher sits above it instead of hiding beneath it.\n *\n * It samples the corner where the launcher sits, ignoring the widget itself,\n * and counts only elements anchored to the bottom edge and shorter than 40% of\n * the viewport, so a full-height sidebar or a modal never pushes it away.\n */\nexport function bottomObstruction(root: Element | null, position: \"bottom-right\" | \"bottom-left\", offset: number): number {\n if (typeof document === \"undefined\" || typeof window === \"undefined\" || typeof document.elementsFromPoint !== \"function\") return 0;\n const width = window.innerWidth;\n const height = window.innerHeight;\n if (!width || !height) return 0;\n const xs = position === \"bottom-right\" ? [width - offset - 24, width - 8] : [offset + 24, 8];\n const ys = [height - offset - 24, height - 4];\n let lift = 0;\n const seen = new Set<Element>();\n for (const x of xs) for (const y of ys) {\n for (const hit of document.elementsFromPoint(x, y)) {\n if (root?.contains(hit)) continue;\n const bar = anchoredAncestor(hit);\n if (!bar || seen.has(bar)) continue;\n seen.add(bar);\n const rect = bar.getBoundingClientRect();\n const anchoredToBottom = rect.bottom >= height - 4;\n const short = rect.height > 0 && rect.height < height * 0.4;\n if (anchoredToBottom && short) lift = Math.max(lift, height - rect.top);\n }\n }\n return Math.round(lift);\n}\n\n/** The nearest fixed or sticky element at or above `element` (a few levels up at most). */\nfunction anchoredAncestor(element: Element): Element | null {\n let node: Element | null = element;\n for (let depth = 0; node && depth < 8 && node !== document.body && node !== document.documentElement; depth += 1) {\n const position = getComputedStyle(node).position;\n if (position === \"fixed\" || position === \"sticky\") return node;\n node = node.parentElement;\n }\n return null;\n}\n","import type { WidgetTheme } from \"@frankieai/core\";\nimport { useSyncExternalStore, type CSSProperties } from \"react\";\n\n/**\n * Map a theme to the CSS custom properties the widget's styles read.\n *\n * Anything not set falls through to the defaults in `frankie.css`, which are\n * themselves shadcn/ui token names — so a host that already runs shadcn can\n * drop the stylesheet and the widget inherits their design system instead.\n */\nexport function themeToCssVars(theme: WidgetTheme | undefined): CSSProperties {\n if (!theme) return {};\n const vars: Record<string, string> = {};\n if (theme.primary) vars[\"--frankie-primary\"] = theme.primary;\n if (theme.primaryForeground) vars[\"--frankie-primary-foreground\"] = theme.primaryForeground;\n if (theme.background) vars[\"--frankie-background\"] = theme.background;\n if (theme.foreground) vars[\"--frankie-foreground\"] = theme.foreground;\n if (theme.muted) vars[\"--frankie-muted\"] = theme.muted;\n if (theme.border) vars[\"--frankie-border\"] = theme.border;\n if (theme.radius) vars[\"--frankie-radius\"] = `${theme.radius}rem`;\n if (theme.fontFamily) vars[\"--frankie-font\"] = theme.fontFamily;\n return vars as CSSProperties;\n}\n\n/** Resolve `mode: \"system\"` against the browser, so the widget can hard-set a class. */\nexport function resolveMode(mode: WidgetTheme[\"mode\"]): \"light\" | \"dark\" {\n if (mode === \"light\" || mode === \"dark\") return mode;\n if (typeof window === \"undefined\" || !window.matchMedia) return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\nfunction subscribeToScheme(onChange: () => void): () => void {\n const media = typeof window !== \"undefined\" && window.matchMedia ? window.matchMedia(DARK_QUERY) : undefined;\n media?.addEventListener?.(\"change\", onChange);\n return () => media?.removeEventListener?.(\"change\", onChange);\n}\nconst prefersDark = () => typeof window !== \"undefined\" && Boolean(window.matchMedia?.(DARK_QUERY).matches);\n\n/**\n * `resolveMode` as a hook. Server-rendered pages can't know the visitor's\n * scheme, and a hydrated attribute that differs from the server's is never\n * patched, so a plain `resolveMode` call could leave \"system\" stuck on light.\n * This renders light on the server and during hydration, then the visitor's\n * scheme, and follows it when they switch.\n */\nexport function useResolvedMode(mode: WidgetTheme[\"mode\"]): \"light\" | \"dark\" {\n const dark = useSyncExternalStore(subscribeToScheme, prefersDark, () => false);\n if (mode === \"light\" || mode === \"dark\") return mode;\n return dark ? \"dark\" : \"light\";\n}\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/** shadcn/ui's class helper: conditional classes with Tailwind conflict resolution. */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { ButtonHTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport const buttonVariants = cva(\n \"frankie-focus inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-[var(--frankie-radius)] text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:opacity-90\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-muted\",\n outline: \"border border-border bg-background hover:bg-muted\",\n ghost: \"hover:bg-muted\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-[calc(var(--frankie-radius)-2px)] px-3 text-xs\",\n lg: \"h-10 px-6\",\n icon: \"size-9\",\n },\n },\n defaultVariants: { variant: \"default\", size: \"default\" },\n },\n);\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nexport function Button({ className, variant, size, asChild = false, ...props }: ButtonProps) {\n const Comp = asChild ? Slot : \"button\";\n return <Comp className={cn(buttonVariants({ variant, size }), className)} {...props} />;\n}\n","\"use client\";\n\nimport { RotateCcw, X } from \"lucide-react\";\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\nimport type { ChatMessage, ToolActivity } from \"../../lib/messages.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { Avatar } from \"../ui/avatar.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from \"../ui/card.js\";\nimport { ScrollArea } from \"../ui/scroll-area.js\";\nimport { AgentContext } from \"./AgentContext.js\";\nimport { ChatEmptyState } from \"./ChatEmptyState.js\";\nimport { Composer } from \"./Composer.js\";\nimport { MessageBubble } from \"./MessageBubble.js\";\n\nexport interface ChatPanelProps {\n className?: string;\n /** Replace the header entirely, or pass `false` to drop it. */\n header?: ReactNode | false;\n /** Rendered under the composer — good for a \"powered by\" or a privacy note. */\n footer?: ReactNode;\n /** Shown before the first message, in place of the default greeting block. */\n emptyState?: ReactNode;\n /** Take over message rendering completely. */\n renderMessage?: (message: ChatMessage) => ReactNode;\n /** Take over the tool-activity rows. */\n renderTool?: (tool: ToolActivity) => ReactNode;\n placeholder?: string;\n /** Shows a close button in the header when provided. */\n onClose?: () => void;\n autoFocus?: boolean;\n /** Fill the parent instead of the fixed floating size — for full-page use. */\n fill?: boolean;\n}\n\nexport function ChatPanel({\n className,\n header,\n footer,\n emptyState,\n renderMessage,\n renderTool,\n placeholder,\n onClose,\n autoFocus,\n fill,\n}: ChatPanelProps) {\n const { config, configError, loading } = useFrankie();\n const theme = useFrankieTheme();\n const { messages, busy, restoring, error, send, answerAction, stop, reset } = useFrankieChat();\n const scrollRef = useRef<HTMLDivElement>(null);\n // Tracks whether the reader was at the bottom just before this render, not\n // where the bottom ends up after it. Measuring post-render (against the\n // already-grown scrollHeight) breaks as soon as one update — a sent message\n // plus the reply's opening burst of text, say — adds more than the near-\n // bottom threshold in a single paint: the check then reads \"scrolled away\"\n // even though the reader never touched the scrollbar, and every later\n // delta stops following. A live scroll listener captures the real, current\n // position instead of re-deriving a stale one from updated content.\n const atBottomRef = useRef(true);\n\n const features = config?.features;\n const pickerModels = features?.modelPicker.enabled ? features.modelPicker.models : [];\n const [model, setModel] = useState<string | undefined>(undefined);\n // A \"Something else\" choice focuses the composer through this handle.\n const composerFocus = useRef<(() => void) | null>(null);\n const focusComposer = () => composerFocus.current?.();\n\n useEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n const onScroll = () => {\n atBottomRef.current = node.scrollHeight - node.scrollTop - node.clientHeight < 160;\n };\n node.addEventListener(\"scroll\", onScroll, { passive: true });\n return () => node.removeEventListener(\"scroll\", onScroll);\n }, []);\n\n // Follow the stream, but only while the reader is already near the bottom.\n // With no messages yet the welcome screen starts at its top: scrolling it to\n // the bottom cut its heading off in the small floating panel.\n useEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n if (messages.length === 0) { node.scrollTop = 0; return; }\n if (!atBottomRef.current) return;\n node.scrollTop = node.scrollHeight;\n }, [messages]);\n\n const name = config?.branding.name ?? \"Assistant\";\n const mode = useResolvedMode(theme?.mode);\n\n return (\n <Card\n className={cn(\n \"frankie-root frankie-chat-panel\",\n fill && \"frankie-chat-panel-fill\",\n fill ? \"h-full w-full rounded-none border-0 shadow-none\" : \"h-[32rem] w-[22rem] max-w-[calc(100vw-2rem)]\",\n mode === \"dark\" && \"dark\",\n className,\n )}\n style={themeToCssVars(theme)}\n data-frankie-mode={mode}\n >\n {header === false ? null : header !== undefined ? (\n header\n ) : (\n <CardHeader>\n <Avatar {...(config?.branding.avatarUrl ? { src: config.branding.avatarUrl } : {})} name={name} />\n <div className=\"min-w-0 flex-1\">\n <CardTitle>{name}</CardTitle>\n {config?.branding.tagline ? <CardDescription>{config.branding.tagline}</CardDescription> : null}\n </div>\n <Button size=\"icon\" variant=\"ghost\" onClick={reset} aria-label=\"Start a new conversation\" title=\"New conversation\">\n <RotateCcw />\n </Button>\n {onClose ? (\n <Button size=\"icon\" variant=\"ghost\" onClick={onClose} aria-label=\"Close\">\n <X />\n </Button>\n ) : null}\n </CardHeader>\n )}\n\n {config?.agentContext === false ? null : <AgentContext />}\n <CardContent className=\"frankie-conversation-content\">\n <ScrollArea ref={scrollRef} className=\"frankie-transcript flex h-full flex-col gap-4 p-4\" aria-live=\"polite\">\n {configError ? (\n <div className=\"frankie-config-error\" role=\"alert\">\n <strong>Frankie couldn’t start</strong>\n <p>{configError}</p>\n <p>Refresh this page after checking the workspace’s allowed origin and sign-in settings.</p>\n </div>\n ) : loading ? (\n <p className=\"text-sm text-muted-foreground\">Starting Frankie…</p>\n ) : messages.length === 0 ? (\n (emptyState ?? <ChatEmptyState />)\n ) : (\n <div className=\"frankie-message-list flex flex-col gap-4\">\n {messages.map((message, index) =>\n renderMessage ? (\n <div key={message.id}>{renderMessage(message)}</div>\n ) : (\n <MessageBubble\n key={message.id}\n message={message}\n assistantName={name}\n {...(config?.branding.avatarUrl ? { avatarUrl: config.branding.avatarUrl } : {})}\n showToolActivity={config?.showToolActivity ?? true}\n {...(renderTool ? { renderTool } : {})}\n onChoose={send}\n onRequestInput={focusComposer}\n active={index === messages.length - 1}\n busy={busy}\n onAction={(toolUseId, approved) => void answerAction(toolUseId, approved)}\n />\n ),\n )}\n </div>\n )}\n </ScrollArea>\n </CardContent>\n\n <CardFooter className=\"frankie-composer-footer flex flex-col gap-2\">\n {error ? <p className=\"w-full text-xs text-red-600 dark:text-red-400\">{error}</p> : null}\n <div className=\"w-full\">\n <Composer\n onSend={send}\n onStop={stop}\n busy={busy}\n focusHandleRef={composerFocus}\n disabled={loading || restoring || Boolean(configError)}\n {...(placeholder ? { placeholder } : {})}\n {...(autoFocus ? { autoFocus } : {})}\n {...(features?.attachments.enabled ? { attachments: features.attachments } : {})}\n {...(pickerModels.length\n ? { modelPicker: { models: pickerModels, value: model ?? pickerModels[0]?.id, onChange: setModel } }\n : {})}\n />\n </div>\n {footer}\n </CardFooter>\n </Card>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils.js\";\n\nexport interface AvatarProps {\n src?: string;\n name: string;\n className?: string;\n}\n\n/** Image avatar with an initials fallback — no image request when `src` is absent. */\nexport function Avatar({ src, name, className }: AvatarProps) {\n const initials = name\n .split(/\\s+/)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase() ?? \"\")\n .join(\"\");\n\n return (\n <span\n className={cn(\n \"inline-flex size-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary text-[0.625rem] font-semibold text-primary-foreground\",\n className,\n )}\n aria-hidden=\"true\"\n >\n {src ? <img src={src} alt=\"\" className=\"size-full object-cover\" /> : initials}\n </span>\n );\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\n \"flex flex-col rounded-[calc(var(--frankie-radius)+4px)] border border-border bg-background text-foreground shadow-lg\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport function CardHeader({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"flex items-center gap-3 border-b border-border px-4 py-3\", className)} {...props} />;\n}\n\nexport function CardTitle({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"text-sm font-semibold leading-tight\", className)} {...props} />;\n}\n\nexport function CardDescription({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"text-xs text-muted-foreground\", className)} {...props} />;\n}\n\nexport function CardContent({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"flex-1 overflow-hidden\", className)} {...props} />;\n}\n\nexport function CardFooter({ className, ...props }: HTMLAttributes<HTMLDivElement>) {\n return <div className={cn(\"border-t border-border p-3\", className)} {...props} />;\n}\n","\"use client\";\n\nimport { forwardRef, type HTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A plain overflow container rather than Radix's ScrollArea: the widget lives\n * inside someone else's page, and a native scroller inherits their scrollbar\n * behaviour instead of fighting it.\n */\nexport const ScrollArea = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(\n function ScrollArea({ className, ...props }, ref) {\n return <div ref={ref} className={cn(\"frankie-scroll overflow-y-auto overscroll-contain\", className)} {...props} />;\n },\n);\n","\"use client\";\nimport { ArrowUpRight, Sparkles } from \"lucide-react\";\nimport { useFrankie } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\n\n/** Reports server-confirmed skills for the current turn; never implies drafts are active. */\nexport function AgentContext() {\n const { loading, configError } = useFrankie();\n const { loadedSkills, activeApis, busy, restoring, error } = useFrankieChat();\n const status = configError || error ? \"Unavailable\" : loading || restoring ? \"Connecting\" : busy && loadedSkills === null ? \"Checking\" : loadedSkills?.length ? \"Active\" : loadedSkills ? \"Tools only\" : \"Ready\";\n return <div className=\"frankie-agent-context-wrap\">\n <details className=\"frankie-agent-context\">\n <summary aria-label=\"Agent context\"><Sparkles size={17} aria-hidden=\"true\" /><span>Agent context</span><span className=\"frankie-context-count\">{loadedSkills === null ? \"Not checked\" : `${loadedSkills.length} ${loadedSkills.length === 1 ? \"skill\" : \"skills\"}`}</span><span className=\"frankie-context-status\" data-active={status === \"Active\"}><i aria-hidden=\"true\" />{status}</span><ArrowUpRight size={15} aria-hidden=\"true\" /></summary>\n <div className=\"frankie-context-detail\">\n <strong>{loadedSkills === null ? \"Skills are checked when you send a message\" : loadedSkills.length ? `${loadedSkills.length} ${loadedSkills.length === 1 ? \"skill\" : \"skills\"} loaded` : \"No skills loaded for this turn\"}</strong>\n {loadedSkills?.length ? <ul>{loadedSkills.map(skill => <li key={skill.id}><span>{skill.name}</span><small>v{skill.revision}</small></li>)}</ul> : <p>Frankie can use the tools approved for this workspace. Drafts and skills without available tools are not loaded.</p>}\n <p>Published skills and updates are checked on every message. Only approved, available tools can run.</p>\n {activeApis?.length ? <ul className=\"frankie-active-apis\">{activeApis.map(api => <li key={api.name}><span>{api.name}</span><small>spec {api.specHash} · {api.operationCount} {api.operationCount === 1 ? \"operation\" : \"operations\"}</small></li>)}</ul> : null}\n </div>\n </details>\n <span className=\"frankie-sr-only\" role=\"status\">{loadedSkills === null ? status : `${loadedSkills.length} ${loadedSkills.length === 1 ? \"skill\" : \"skills\"} loaded. ${status}.`}</span>\n </div>;\n}\n","\"use client\";\n\nimport { BarChart3, BookOpen, Compass, FileText, Image as ImageIcon, LayoutGrid, Lightbulb, MessageSquare, Mic, Music, Sparkles, Wand2, Zap, type LucideIcon } from \"lucide-react\";\nimport { useFrankie } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\n\n/** Named icons a starter card may choose (mirrors core's STARTER_PROMPT_ICONS). */\nconst NAMED_ICONS: Record<string, LucideIcon> = {\n sparkles: Sparkles, wand: Wand2, book: BookOpen, chat: MessageSquare, chart: BarChart3, bolt: Zap,\n music: Music, mic: Mic, image: ImageIcon, compass: Compass, lightbulb: Lightbulb, file: FileText,\n};\n\n/** Rotating icon set for cards without an explicit icon, so each reads distinctly. */\nconst PROMPT_ICONS = [Sparkles, Wand2, BarChart3, Zap, MessageSquare, LayoutGrid];\n\n/** A prompt may pack a short description after an em/en dash or a hyphen (\"Do X — the details\"); split it so the card can show a title and subtitle. Falls back to title-only. */\nfunction splitPrompt(prompt: string): { title: string; description?: string } {\n const match = prompt.match(/^(.+?)\\s+[—–]\\s+(.+)$/) ?? prompt.match(/^(.+?)\\s+-\\s+(.+)$/);\n if (match) return { title: match[1]!.trim(), description: match[2]!.trim() };\n return { title: prompt.trim() };\n}\n\n/**\n * The empty state shown before the first message on every chat surface — the\n * floating widget, the inline panel, and the full page. A \"Welcome to {name}\"\n * line plus a grid of suggestion cards (title + optional subtitle). The prompts\n * are workspace-driven: an authored `suggestedPrompts` list, or ones the server\n * derived from the workspace's published skills. Clicking a card sends only the\n * title. The card grid collapses to one column in the narrow widget via a\n * container query (see `.frankie-page-prompts`).\n */\nexport function ChatEmptyState() {\n const { config } = useFrankie();\n const { send, busy, restoring } = useFrankieChat();\n const name = config?.branding.name ?? \"Frankie\";\n // Explicit starter cards (configured in the dashboard) win; each sends its own\n // message on click. Otherwise fall back to the string suggestions (authored or\n // skill-derived), where the card title is also the message that's sent.\n const starters = config?.starterPrompts?.length\n ? config.starterPrompts.slice(0, 6).map(card => ({ title: card.title, description: card.description, message: card.message, icon: card.icon }))\n : (config?.suggestedPrompts ?? []).slice(0, 6).map(prompt => {\n const { title, description } = splitPrompt(prompt);\n return { title, description, message: title, icon: undefined as string | undefined };\n });\n return <section className=\"frankie-page-empty\" aria-label=\"Start a conversation\">\n <div className=\"frankie-page-welcome\">\n <h1>Welcome to {name}</h1>\n <p>{config?.greeting ?? `Ask me anything about your ${name} workspace`}</p>\n </div>\n {starters.length ? <div className=\"frankie-page-suggest\">\n <p className=\"frankie-page-suggest-label\">Try asking me to</p>\n <div className=\"frankie-page-prompts\">{starters.map((card, index) => {\n const Icon = (card.icon && NAMED_ICONS[card.icon]) || PROMPT_ICONS[index % PROMPT_ICONS.length]!;\n return <button key={card.title + index} type=\"button\" disabled={busy || restoring} onClick={() => void send(card.message)} title={card.description || undefined}>\n <span className=\"frankie-prompt-icon\" aria-hidden><Icon size={18} /></span>\n <span className=\"frankie-prompt-text\"><strong>{card.title}</strong>{card.description ? <small>{card.description}</small> : null}</span>\n </button>;\n })}</div>\n <p className=\"frankie-page-suggest-hint\">Click a suggestion or type your own question below</p>\n </div> : null}\n </section>;\n}\n","\"use client\";\n\nimport { ArrowUp, Paperclip, Plus, Square, X } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState, type KeyboardEvent, type MutableRefObject } from \"react\";\nimport type { ChatAttachment } from \"@frankieai/core\";\nimport { Button } from \"../ui/button.js\";\nimport { Textarea } from \"../ui/textarea.js\";\n\nexport interface ComposerAttachmentsConfig {\n enabled: boolean;\n /** Accepted MIME patterns, e.g. \"image/*\". */\n accept: string[];\n maxCount: number;\n maxMb: number;\n}\n\nexport interface ComposerModelPicker {\n models: { id: string; label: string }[];\n value?: string;\n onChange: (id: string) => void;\n}\n\nexport interface ComposerProps {\n onSend: (text: string, opts?: { attachments?: ChatAttachment[]; model?: string }) => void;\n onStop: () => void;\n busy: boolean;\n placeholder?: string;\n disabled?: boolean;\n autoFocus?: boolean;\n /** Enables the attach button when provided and `enabled`. */\n attachments?: ComposerAttachmentsConfig;\n /** Enables a model dropdown when provided with at least one model. */\n modelPicker?: ComposerModelPicker;\n /** Populated with a function that focuses the input, so a \"type your own\" choice can jump here. */\n focusHandleRef?: MutableRefObject<(() => void) | null>;\n}\n\ninterface PickedFile extends ChatAttachment {\n id: string;\n}\n\nconst MAX_ROWS_PX = 140;\n\nexport function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker, focusHandleRef }: ComposerProps) {\n const [value, setValue] = useState(\"\");\n const [files, setFiles] = useState<PickedFile[]>([]);\n const [fileError, setFileError] = useState<string | undefined>(undefined);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n // Publish a focus handle so a \"Something else\" choice elsewhere in the panel\n // can move the caret here without reaching into the DOM.\n useEffect(() => {\n if (!focusHandleRef) return;\n focusHandleRef.current = () => textareaRef.current?.focus();\n return () => { focusHandleRef.current = null; };\n }, [focusHandleRef]);\n\n const canAttach = Boolean(attachments?.enabled);\n const models = modelPicker?.models ?? [];\n\n // Grow with the content up to a cap, then scroll.\n useEffect(() => {\n const node = textareaRef.current;\n if (!node) return;\n node.style.height = \"auto\";\n node.style.height = `${Math.min(node.scrollHeight, MAX_ROWS_PX)}px`;\n }, [value]);\n\n const addFiles = useCallback(\n async (list: FileList | null) => {\n if (!list || !attachments) return;\n setFileError(undefined);\n const room = attachments.maxCount - files.length;\n const chosen = Array.from(list).slice(0, Math.max(0, room));\n if (Array.from(list).length > room) setFileError(`You can attach up to ${attachments.maxCount} file(s).`);\n\n const next: PickedFile[] = [];\n for (const file of chosen) {\n if (!mimeAccepted(file.type || \"application/octet-stream\", attachments.accept)) {\n setFileError(`Files of type ${file.type || \"unknown\"} aren't allowed.`);\n continue;\n }\n if (file.size > attachments.maxMb * 1024 * 1024) {\n setFileError(`Each file must be under ${attachments.maxMb} MB.`);\n continue;\n }\n try {\n next.push({\n id: `${file.name}-${file.size}-${Math.random().toString(36).slice(2, 7)}`,\n name: file.name,\n mimeType: file.type || \"application/octet-stream\",\n data: await readAsBase64(file),\n });\n } catch {\n setFileError(\"Couldn't read that file.\");\n }\n }\n if (next.length) setFiles((current) => [...current, ...next]);\n },\n [attachments, files.length],\n );\n\n const removeFile = (id: string) => setFiles((current) => current.filter((file) => file.id !== id));\n\n const submit = useCallback(() => {\n const text = value.trim();\n if ((!text && files.length === 0) || busy || disabled) return;\n setValue(\"\");\n const attached = files.map(({ name, mimeType, data }) => ({ name, mimeType, data }));\n setFiles([]);\n setFileError(undefined);\n onSend(text, {\n ...(attached.length ? { attachments: attached } : {}),\n ...(modelPicker?.value ? { model: modelPicker.value } : {}),\n });\n }, [busy, disabled, files, modelPicker?.value, onSend, value]);\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === \"Enter\" && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n submit();\n }\n };\n\n const nothingToSend = value.trim().length === 0 && files.length === 0;\n\n return (\n <form\n className=\"frankie-composer flex flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\n {files.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {files.map((file) => (\n <span\n key={file.id}\n className=\"inline-flex max-w-[14rem] items-center gap-1.5 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs\"\n >\n <Paperclip className=\"size-3 shrink-0 opacity-70\" />\n <span className=\"truncate\">{file.name}</span>\n <button\n type=\"button\"\n onClick={() => removeFile(file.id)}\n aria-label={`Remove ${file.name}`}\n className=\"frankie-focus shrink-0 opacity-60 hover:opacity-100\"\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n </div>\n ) : null}\n\n {fileError ? <p className=\"text-xs text-red-600 dark:text-red-400\">{fileError}</p> : null}\n\n <div className=\"frankie-composer-controls flex items-end gap-2\">\n <Textarea\n ref={textareaRef}\n rows={1}\n value={value}\n disabled={disabled}\n autoFocus={autoFocus}\n onChange={(event) => setValue(event.target.value)}\n onKeyDown={onKeyDown}\n placeholder={placeholder ?? \"Ask anything…\"}\n aria-label=\"Message\"\n />\n\n {/* One toolbar row: attach + model picker on the left, send on the right —\n instead of stacking the model picker on its own line below. */}\n <div className=\"frankie-composer-actions\">\n {canAttach ? (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n hidden\n accept={attachments?.accept.join(\",\")}\n onChange={(event) => {\n void addFiles(event.target.files);\n event.target.value = \"\";\n }}\n />\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"outline\"\n disabled={disabled || files.length >= (attachments?.maxCount ?? 0)}\n onClick={() => fileInputRef.current?.click()}\n aria-label=\"Attach files\"\n title=\"Attach files\"\n >\n <Plus />\n </Button>\n </>\n ) : null}\n\n {models.length > 0 ? (\n <label className=\"frankie-model-picker\" title=\"Model\">\n <span className=\"frankie-sr-only\">Model</span>\n <select\n value={modelPicker?.value ?? models[0]?.id}\n onChange={(event) => modelPicker?.onChange(event.target.value)}\n disabled={disabled}\n aria-label=\"Model\"\n className=\"frankie-model-select\"\n >\n {models.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label}\n </option>\n ))}\n </select>\n </label>\n ) : null}\n </div>\n\n {busy ? (\n <Button type=\"button\" size=\"icon\" variant=\"outline\" onClick={onStop} aria-label=\"Stop\">\n <Square />\n </Button>\n ) : (\n <Button type=\"submit\" size=\"icon\" disabled={disabled || nothingToSend} aria-label=\"Send\">\n <ArrowUp />\n </Button>\n )}\n </div>\n </form>\n );\n}\n\nfunction mimeAccepted(mimeType: string, accept: string[]): boolean {\n return accept.some((pattern) =>\n pattern.endsWith(\"/*\") ? mimeType.startsWith(pattern.slice(0, -1)) : pattern === mimeType,\n );\n}\n\n/** Read a File to a base64 string (no data-URL prefix), for the wire protocol. */\nfunction readAsBase64(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n const result = String(reader.result);\n const comma = result.indexOf(\",\");\n resolve(comma >= 0 ? result.slice(comma + 1) : result);\n };\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(file);\n });\n}\n\n","\"use client\";\n\nimport { forwardRef, type TextareaHTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/** `forwardRef` rather than React 19's ref-as-prop, so React 18 hosts work too. */\nexport const Textarea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>(\n function Textarea({ className, ...props }, ref) {\n return (\n <textarea\n ref={ref}\n className={cn(\n \"frankie-focus flex min-h-[2.25rem] w-full resize-none rounded-[var(--frankie-radius)] border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n );\n },\n);\n","\"use client\";\n\nimport { Check, Copy, Paperclip } from \"lucide-react\";\nimport \"./message-actions.css\";\nimport { memo, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport { extractChoices } from \"../../lib/choices.js\";\nimport type { ChatMessage, ToolActivity } from \"../../lib/messages.js\";\nimport { Avatar } from \"../ui/avatar.js\";\nimport { ActionConfirm } from \"./ActionConfirm.js\";\nimport { Choices } from \"./Choices.js\";\nimport { Markdown } from \"./Markdown.js\";\nimport { ToolActivityList } from \"./ToolActivityList.js\";\n\nexport interface MessageBubbleProps {\n message: ChatMessage;\n assistantName: string;\n avatarUrl?: string;\n showToolActivity: boolean;\n renderTool?: (tool: ToolActivity) => ReactNode;\n /** Send a picked choice as the next message. Omitted when the host renders its own. */\n onChoose?: (choice: string) => void;\n /** Focus the composer so the user can type their own answer instead of picking. */\n onRequestInput?: () => void;\n /** True only for the newest message, so stale choices from earlier turns aren't clickable. */\n active?: boolean;\n /** A turn is in flight, so choices should be disabled. */\n busy?: boolean;\n /** Approve or cancel an action this reply paused. */\n onAction?: (toolUseId: string, approved: boolean) => void;\n}\n\n// Memoized: during a stream only the message whose object identity changed (the\n// one being written) re-renders. Every earlier bubble keeps its parsed markdown,\n// which is what keeps a long transcript from re-laying-out on every token.\nexport const MessageBubble = memo(function MessageBubble({\n message,\n assistantName,\n avatarUrl,\n showToolActivity,\n renderTool,\n onChoose,\n onRequestInput,\n active,\n busy,\n onAction,\n}: MessageBubbleProps) {\n if (message.role === \"user\") {\n return (\n <div className=\"frankie-user-message flex flex-col items-end gap-1\">\n {message.attachments?.length ? (\n <div className=\"flex max-w-[85%] flex-wrap justify-end gap-1\">\n {message.attachments.map((file, index) => (\n <span\n key={`${file.name}-${index}`}\n className=\"inline-flex max-w-[12rem] items-center gap-1 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs\"\n >\n <Paperclip className=\"size-3 shrink-0 opacity-70\" />\n <span className=\"truncate\">{file.name}</span>\n </span>\n ))}\n </div>\n ) : null}\n {message.text ? (\n <div className=\"flex max-w-[85%] items-end gap-1\">\n <div className=\"frankie-user-bubble rounded-[var(--frankie-radius)] bg-primary px-3 py-2 text-sm text-primary-foreground\">{message.text}</div>\n <MessageCopyButton text={message.text} />\n </div>\n ) : null}\n </div>\n );\n }\n\n // Lift any offered options out of the prose. While the reply is still\n // streaming, a half-written block is hidden (options stays null) so the raw\n // fence never flashes. Options become clickable only on the newest, settled\n // message so answering an earlier turn's question can't jump the transcript.\n const { body, options } = extractChoices(message.text);\n const showChoices = Boolean(active && onChoose && options && message.status === \"done\");\n // A running tool row is its own progress indicator; otherwise, while the turn\n // is still streaming with no visible text yet, the model is thinking (before\n // its first words, or in the pause after a tool result while it composes the\n // answer). Show a skeleton there so those gaps never look frozen.\n // Only before the first action: once tools have run, the \"Working\" list is\n // the progress indicator, and a skeleton toggling in the gaps between calls\n // reads as flashing.\n const waiting = message.status === \"streaming\" && body.length === 0 && message.tools.length === 0;\n\n return (\n <div className=\"frankie-assistant-message flex gap-2\">\n <div className=\"frankie-assistant-actions mt-0.5 flex shrink-0 flex-col items-center gap-1\">\n <Avatar {...(avatarUrl ? { src: avatarUrl } : {})} name={assistantName} />\n {body ? <MessageCopyButton text={body} /> : null}\n </div>\n <div className=\"frankie-assistant-content min-w-0 max-w-[85%]\">\n {message.status === \"error\" ? (\n <p className=\"rounded-[var(--frankie-radius)] bg-red-500/10 px-3 py-2 text-sm text-red-600 dark:text-red-400\">\n {message.error}\n </p>\n ) : body ? (\n <Markdown text={body} className=\"text-sm\" />\n ) : null}\n {message.action ? (\n <ActionConfirm\n action={message.action}\n live={Boolean(active)}\n disabled={busy || message.status === \"streaming\"}\n {...(onAction ? { onAnswer: onAction } : {})}\n />\n ) : null}\n {showChoices ? <Choices options={options ?? []} onPick={onChoose!} {...(onRequestInput ? { onSomethingElse: onRequestInput } : {})} disabled={busy} /> : null}\n {showToolActivity ? (\n <ToolActivityList tools={message.tools} {...(renderTool ? { renderTool } : {})} />\n ) : null}\n {waiting ? <Thinking delayed /> : null}\n </div>\n </div>\n );\n});\n\nfunction MessageCopyButton({ text }: { text: string }) {\n const [copied, setCopied] = useState(false);\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(text);\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1500);\n } catch { /* Clipboard access can be disabled by an embedding host. */ }\n };\n return <button type=\"button\" onClick={() => void copy()} className=\"frankie-message-copy inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-transparent text-muted-foreground transition hover:border-border hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\" aria-label={copied ? \"Copied\" : \"Copy message\"} title={copied ? \"Copied\" : \"Copy message\"}>{copied ? <Check className=\"size-3.5\" aria-hidden=\"true\" /> : <Copy className=\"size-3.5\" aria-hidden=\"true\" />}</button>;\n}\n\nexport function TypingDots() {\n return (\n <span className=\"frankie-thinking inline-flex items-center gap-1 py-2\" role=\"status\" aria-label=\"Thinking\">\n <span className=\"frankie-thinking-label\">Thinking</span>\n {[0, 1, 2].map((index) => (\n <span\n key={index}\n className={cn(\"size-1.5 animate-pulse rounded-full bg-muted-foreground\")}\n style={{ animationDelay: `${index * 150}ms` }}\n />\n ))}\n </span>\n );\n}\n\n/**\n * A shimmering skeleton shown while the reply is still being produced — before\n * the first words, or during the pause after a tool result while the model\n * composes its answer — so those gaps read as \"loading\" rather than frozen.\n */\nexport function Thinking({ delayed = false }: { delayed?: boolean }) {\n return (\n <div className={\"frankie-skeleton\" + (delayed ? \" is-delayed\" : \"\")} role=\"status\" aria-label=\"Thinking\">\n <span className=\"frankie-skeleton-line\" style={{ width: \"62%\" }} />\n <span className=\"frankie-skeleton-line\" style={{ width: \"92%\" }} />\n <span className=\"frankie-skeleton-line\" style={{ width: \"48%\" }} />\n </div>\n );\n}\n\n","/**\n * Interactive choice blocks.\n *\n * The agent can end a reply with a fenced block to offer the user a small set\n * of discrete answers:\n *\n * ```frankie:choices\n * British\n * Indian\n * Australian\n * ```\n *\n * The SDK lifts that block out of the prose and renders it as clickable options\n * (the composer stays open, so the user can always type their own answer). The\n * block is a plain fenced code block, so a host that doesn't understand it just\n * shows a harmless code listing instead — never broken markup.\n */\nexport interface ExtractedChoices {\n /** The reply text with the choices block removed. */\n body: string;\n /** The offered options, or null when there is no complete block. */\n options: string[] | null;\n}\n\nconst CLOSED = /```frankie:choices[ \\t]*\\r?\\n([\\s\\S]*?)```/;\nconst OPEN = /```frankie:choices[ \\t]*\\r?\\n?[\\s\\S]*$/;\n\nexport function extractChoices(text: string): ExtractedChoices {\n const closed = CLOSED.exec(text);\n if (closed) {\n const options = parseOptionLines(closed[1] ?? \"\");\n const body = (text.slice(0, closed.index) + text.slice(closed.index + closed[0].length))\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trimEnd();\n return { body, options: options.length ? options : null };\n }\n // An unterminated block means the reply is still streaming the options in.\n // Hide the half-written fence so the user never sees the raw ``` marker.\n const open = OPEN.exec(text);\n if (open) return { body: text.slice(0, open.index).trimEnd(), options: null };\n return { body: text, options: null };\n}\n\nfunction parseOptionLines(block: string): string[] {\n return block\n .split(\"\\n\")\n .map((line) => line.trim().replace(/^([-*+]|\\d+[.)])\\s+/, \"\").trim())\n .filter(Boolean);\n}\n","\"use client\";\n\nimport { Check, ShieldAlert, X } from \"lucide-react\";\nimport type { ActionRequest } from \"../../lib/messages.js\";\n\n/**\n * An action that changes data, shown before it runs. Nothing happens until\n * the user presses Approve, and then exactly what is shown here runs — the\n * assistant can't change the inputs in between.\n */\nexport function ActionConfirm({\n action,\n onAnswer,\n disabled,\n live,\n}: {\n action: ActionRequest;\n onAnswer?: (toolUseId: string, approved: boolean) => void;\n disabled?: boolean;\n /** False once the conversation has moved on without an answer. */\n live: boolean;\n}) {\n const pending = action.state === \"pending\";\n const fields = inputFields(action.preview.input);\n const status = pending\n ? live ? undefined : \"Not run\"\n : action.state === \"approved\" ? \"Approved\" : \"Cancelled\";\n return (\n <div className={\"frankie-action\" + (pending && live ? \" is-pending\" : \"\")} role=\"group\" aria-label={`Approve: ${action.label}`}>\n <div className=\"frankie-action-head\">\n <ShieldAlert className=\"frankie-action-icon size-4\" aria-hidden />\n <div className=\"frankie-action-title\">\n <strong>{action.label}</strong>\n <span>{pending && live ? \"Needs your approval before it runs\" : status}</span>\n </div>\n </div>\n {action.preview.method && action.preview.path ? (\n <code className=\"frankie-action-route\">\n <span>{action.preview.method}</span> {action.preview.path}\n </code>\n ) : null}\n {fields.length ? (\n <dl className=\"frankie-action-fields\">\n {fields.map(([key, value]) => (\n <div key={key}>\n <dt>{key}</dt>\n <dd>{value}</dd>\n </div>\n ))}\n </dl>\n ) : null}\n {pending && live && onAnswer ? (\n <div className=\"frankie-action-buttons\">\n <button type=\"button\" className=\"frankie-action-approve\" disabled={disabled} onClick={() => onAnswer(action.toolUseId, true)}>\n <Check className=\"size-4\" aria-hidden /> Approve\n </button>\n <button type=\"button\" className=\"frankie-action-cancel\" disabled={disabled} onClick={() => onAnswer(action.toolUseId, false)}>\n <X className=\"size-4\" aria-hidden /> Cancel\n </button>\n </div>\n ) : null}\n </div>\n );\n}\n\n/** Top-level inputs (and a JSON body's fields) as label/value rows; nested values as JSON. */\nfunction inputFields(input: unknown): Array<[string, string]> {\n if (input === undefined || input === null) return [];\n if (typeof input !== \"object\" || Array.isArray(input)) return [[\"input\", short(input)]];\n const rows: Array<[string, string]> = [];\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n if (value === undefined) continue;\n if (key === \"body\" && value && typeof value === \"object\" && !Array.isArray(value)) {\n for (const [field, inner] of Object.entries(value as Record<string, unknown>)) rows.push([field, short(inner)]);\n continue;\n }\n rows.push([key, short(value)]);\n }\n return rows;\n}\n\n/** The whole value, never cut short: what's approved is exactly what's shown (long ones scroll). */\nfunction short(value: unknown): string {\n return typeof value === \"string\" ? value : JSON.stringify(value, null, 1);\n}\n","\"use client\";\n\nimport { ArrowRight, PencilLine } from \"lucide-react\";\n\n/**\n * Clickable answer options the agent offered via a `frankie:choices` block.\n * Picking one sends it as the next message. A trailing \"Something else\" row\n * doesn't send — it moves the caret to the composer so the user can type their\n * own answer, while the offered options stay put in case they change their mind.\n */\nexport function Choices({\n options,\n onPick,\n onSomethingElse,\n disabled,\n}: {\n options: string[];\n onPick: (choice: string) => void;\n onSomethingElse?: () => void;\n disabled?: boolean;\n}) {\n if (options.length === 0) return null;\n return (\n <div className=\"frankie-choices\" role=\"group\" aria-label=\"Choose an option\">\n {options.map((option, index) => (\n <button\n key={`${index}-${option}`}\n type=\"button\"\n className=\"frankie-choice\"\n disabled={disabled}\n onClick={() => onPick(option)}\n >\n <span className=\"frankie-choice-index\" aria-hidden>{index + 1}</span>\n <span className=\"frankie-choice-label\">{option}</span>\n <ArrowRight className=\"frankie-choice-go size-4\" aria-hidden />\n </button>\n ))}\n {onSomethingElse ? (\n <button\n type=\"button\"\n className=\"frankie-choice frankie-choice-other\"\n disabled={disabled}\n onClick={onSomethingElse}\n >\n <span className=\"frankie-choice-index\" aria-hidden><PencilLine className=\"size-3.5\" /></span>\n <span className=\"frankie-choice-label\">Something else…</span>\n <span className=\"frankie-choice-hint\" aria-hidden>Type your own</span>\n </button>\n ) : null}\n </div>\n );\n}\n","\"use client\";\n\nimport { Fragment, memo, useMemo, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A deliberately small Markdown subset — headings, paragraphs, lists, fenced\n * code, bold, italic, inline code and links.\n *\n * Everything is rendered as React elements; nothing goes through\n * `dangerouslySetInnerHTML`. Model output lands in someone else's production\n * page, so it is never trusted as markup.\n *\n * Memoized on `text`: while a reply streams, the parent re-renders every frame,\n * but the block tree is only recomputed when the text actually grows — the\n * settled messages above never re-parse at all.\n */\nexport const Markdown = memo(function Markdown({ text, className }: { text: string; className?: string }) {\n const blocks = useMemo(() => renderBlocks(text), [text]);\n return <div className={cn(\"frankie-prose\", className)}>{blocks}</div>;\n});\n\nfunction renderBlocks(text: string): ReactNode[] {\n const lines = text.split(\"\\n\");\n const blocks: ReactNode[] = [];\n let paragraph: string[] = [];\n let list: { ordered: boolean; items: string[] } | null = null;\n let code: { lang: string; lines: string[] } | null = null;\n\n const flushParagraph = () => {\n if (paragraph.length === 0) return;\n blocks.push(<p key={`p${blocks.length}`}>{renderInline(paragraph.join(\" \"))}</p>);\n paragraph = [];\n };\n const flushList = () => {\n if (!list) return;\n const items = list.items.map((item, index) => <li key={index}>{renderInline(item)}</li>);\n blocks.push(list.ordered ? <ol key={`l${blocks.length}`}>{items}</ol> : <ul key={`l${blocks.length}`}>{items}</ul>);\n list = null;\n };\n\n for (const line of lines) {\n const fence = /^```(\\w*)\\s*$/.exec(line);\n if (fence) {\n if (code) {\n blocks.push(\n <pre key={`c${blocks.length}`}>\n <code>{code.lines.join(\"\\n\")}</code>\n </pre>,\n );\n code = null;\n } else {\n flushParagraph();\n flushList();\n code = { lang: fence[1] ?? \"\", lines: [] };\n }\n continue;\n }\n if (code) {\n code.lines.push(line);\n continue;\n }\n\n const bullet = /^\\s*[-*+]\\s+(.*)$/.exec(line);\n const ordered = /^\\s*\\d+[.)]\\s+(.*)$/.exec(line);\n if (bullet ?? ordered) {\n flushParagraph();\n const isOrdered = Boolean(ordered);\n if (!list || list.ordered !== isOrdered) {\n flushList();\n list = { ordered: isOrdered, items: [] };\n }\n list.items.push((ordered?.[1] ?? bullet?.[1]) ?? \"\");\n continue;\n }\n\n if (line.trim() === \"\") {\n flushParagraph();\n flushList();\n continue;\n }\n\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line);\n if (heading) {\n flushParagraph();\n flushList();\n const level = (heading[1] ?? \"#\").length;\n const Tag = level <= 1 ? \"h3\" : level === 2 ? \"h4\" : \"h5\";\n blocks.push(<Tag key={`h${blocks.length}`} className=\"frankie-h\">{renderInline(heading[2] ?? \"\")}</Tag>);\n continue;\n }\n\n flushList();\n paragraph.push(line.trim());\n }\n\n if (code) {\n blocks.push(\n <pre key={`c${blocks.length}`}>\n <code>{code.lines.join(\"\\n\")}</code>\n </pre>,\n );\n }\n flushParagraph();\n flushList();\n return blocks;\n}\n\n// `*italic*` needs a non-space, non-asterisk character right inside each\n// asterisk, so \"2 * 3\" and a \"***\" divider stay text.\nconst INLINE = /(\\*\\*[^*]+\\*\\*|\\*(?=[^\\s*])[^*\\n]*?[^\\s*]\\*(?!\\*)|`[^`]+`|\\[[^\\]]+\\]\\((?:https?:\\/\\/|\\/)[^)\\s]+\\)|\\bhttps?:\\/\\/[^\\s<>()]+)/g;\n\nfunction renderInline(text: string): ReactNode[] {\n const nodes: ReactNode[] = [];\n let lastIndex = 0;\n\n for (const match of text.matchAll(INLINE)) {\n const token = match[0];\n const start = match.index;\n if (start > lastIndex) nodes.push(text.slice(lastIndex, start));\n nodes.push(<Fragment key={start}>{renderToken(token)}</Fragment>);\n lastIndex = start + token.length;\n }\n\n if (lastIndex < text.length) nodes.push(text.slice(lastIndex));\n return nodes;\n}\n\nfunction renderToken(token: string): ReactNode {\n if (token.startsWith(\"**\")) return <strong>{token.slice(2, -2)}</strong>;\n if (token.startsWith(\"*\")) return <em>{token.slice(1, -1)}</em>;\n if (token.startsWith(\"`\")) return <code>{token.slice(1, -1)}</code>;\n\n const link = /^\\[([^\\]]+)\\]\\(([^)]+)\\)$/.exec(token);\n const href = link ? (link[2] ?? \"\") : token;\n const label = link ? (link[1] ?? \"\") : token;\n\n // Only http(s) and same-site paths become links; anything else stays text,\n // so a model can never emit a `javascript:` href into the host page.\n // `//host` and `/\\host` are other sites in disguise: only real same-site paths count as relative.\n if (!/^(https?:\\/\\/|\\/(?![\\/\\\\]))/.test(href)) return token;\n return (\n <a href={href} target=\"_blank\" rel=\"noopener noreferrer nofollow\">\n {label}\n </a>\n );\n}\n","\"use client\";\n\nimport { Check, ChevronDown, Loader2, ShieldAlert, Sparkles, Wrench, X } from \"lucide-react\";\nimport { SKILL_TOOL_NAME } from \"@frankieai/core\";\nimport { Badge } from \"../ui/badge.js\";\nimport type { ToolActivity } from \"../../lib/messages.js\";\n\nexport interface ToolActivityListProps {\n tools: ToolActivity[];\n /** Replace the default row rendering entirely. */\n renderTool?: (tool: ToolActivity) => React.ReactNode;\n}\n\n/** The \"what the assistant is doing\" strip under a streaming answer. */\nexport function ToolActivityList({ tools, renderTool }: ToolActivityListProps) {\n if (tools.length === 0) return null;\n const running = tools.some((tool) => tool.status === \"running\");\n return (\n <details className=\"frankie-tool-activity mt-2\" open={running}>\n <summary className=\"inline-flex min-h-7 cursor-pointer list-none items-center gap-1.5 rounded-md px-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden\">\n <ChevronDown className=\"frankie-tool-chevron size-3.5 transition-transform\" aria-hidden=\"true\" />\n {running ? <Loader2 className=\"size-3.5 animate-spin\" aria-hidden=\"true\" /> : <Wrench className=\"size-3.5\" aria-hidden=\"true\" />}\n <span>{running ? \"Working\" : \"Activity\"}</span>\n <span className=\"text-[11px]\">· {tools.length} tool call{tools.length === 1 ? \"\" : \"s\"}</span>\n </summary>\n <ul className=\"mt-1 flex flex-col gap-1 border-l border-border pl-2\">{tools.map((tool) => <li key={tool.id}>{renderTool ? renderTool(tool) : <ToolActivityRow tool={tool} />}</li>)}</ul>\n </details>\n );\n}\n\nexport function ToolActivityRow({ tool }: { tool: ToolActivity }) {\n // A skill-activation call carries its own icon so \"Using skill: X\" reads as a\n // skill being applied, not just another tool; the spinner still wins while it\n // runs so its status stays legible.\n const isSkill = tool.name === SKILL_TOOL_NAME;\n return (\n <Badge variant={badgeVariant(tool.status)} title={tool.detail ?? tool.name}>\n {isSkill && tool.status !== \"running\" ? <Sparkles className=\"size-3\" aria-hidden=\"true\" /> : <ToolIcon status={tool.status} />}\n <span className=\"truncate\">{tool.label}</span>\n </Badge>\n );\n}\n\nfunction ToolIcon({ status }: { status: ToolActivity[\"status\"] }) {\n switch (status) {\n case \"running\":\n return <Loader2 className=\"size-3 animate-spin\" aria-hidden=\"true\" />;\n case \"done\":\n return <Check className=\"size-3\" aria-hidden=\"true\" />;\n case \"denied\":\n return <ShieldAlert className=\"size-3\" aria-hidden=\"true\" />;\n default:\n return <X className=\"size-3\" aria-hidden=\"true\" />;\n }\n}\n\nfunction badgeVariant(status: ToolActivity[\"status\"]): \"default\" | \"success\" | \"danger\" | \"outline\" {\n if (status === \"done\") return \"success\";\n if (status === \"failed\") return \"danger\";\n if (status === \"denied\") return \"outline\";\n return \"default\";\n}\n","\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\nconst badgeVariants = cva(\n \"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[0.6875rem] font-medium\",\n {\n variants: {\n variant: {\n default: \"border-transparent bg-muted text-muted-foreground\",\n success: \"border-transparent bg-emerald-500/10 text-emerald-600 dark:text-emerald-400\",\n danger: \"border-transparent bg-red-500/10 text-red-600 dark:text-red-400\",\n outline: \"border-border text-muted-foreground\",\n },\n },\n defaultVariants: { variant: \"default\" },\n },\n);\n\nexport function Badge({\n className,\n variant,\n ...props\n}: HTMLAttributes<HTMLSpanElement> & VariantProps<typeof badgeVariants>) {\n return <span className={cn(badgeVariants({ variant }), className)} {...props} />;\n}\n","\"use client\";\n\nimport { useRef, type CSSProperties } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useStylesReady } from \"../../lib/styles-ready.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { ChatPanel, type ChatPanelProps } from \"./ChatPanel.js\";\n\nexport interface FrankieInlineProps extends ChatPanelProps {\n /** Extra styles on the filling wrapper — e.g. a `maxWidth` to centre the column. */\n style?: CSSProperties;\n}\n\n/**\n * Full-page (or any-container) chat: the panel fills its parent instead of\n * floating in a corner. Drop it into a route or a sized element — it brings its\n * own theme root, so it styles correctly wherever it lands.\n *\n * <div style={{ height: \"100dvh\" }}>\n * <FrankieProvider …><FrankieInline /></FrankieProvider>\n * </div>\n */\nexport function FrankieInline({ className, style, ...panelProps }: FrankieInlineProps) {\n const { injectStyles, themeReady } = useFrankie();\n const theme = useFrankieTheme();\n const mode = useResolvedMode(theme?.mode);\n const rootRef = useRef<HTMLDivElement>(null);\n const stylesReady = useStylesReady(rootRef, undefined, injectStyles);\n return (\n <div\n ref={rootRef}\n className={cn(\"frankie-root frankie-inline\", mode === \"dark\" && \"dark\", className)}\n style={{ ...themeToCssVars(theme), display: \"flex\", height: \"100%\", width: \"100%\", ...style, ...(stylesReady && themeReady ? null : { opacity: 0 as const }) }}\n data-frankie-mode={mode}\n data-frankie-styles={stylesReady ? \"ready\" : \"pending\"}\n >\n <ChatPanel {...panelProps} fill />\n </div>\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState, type CSSProperties } from \"react\";\nimport { ChevronsLeft, ChevronsRight, History, Menu, Plus, Settings, X } from \"lucide-react\";\nimport type { FrankieProviderProps } from \"../../hooks/FrankieProvider.jsx\";\nimport { FrankieProvider, useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { useFrankieChat } from \"../../hooks/useFrankieChat.js\";\nimport { useViewportFill } from \"../../lib/viewport-fill.js\";\nimport { useStylesReady } from \"../../lib/styles-ready.js\";\nimport { themeToCssVars, useResolvedMode } from \"../../lib/theme.js\";\nimport { FrankieApiError, type ChatConversation } from \"../../lib/client.js\";\nimport { FrankieInline } from \"./FrankieInline.js\";\nimport type { ChatPanelProps } from \"./ChatPanel.js\";\n\nexport interface FrankieChatPageProps extends Omit<FrankieProviderProps, \"children\">,\n Pick<ChatPanelProps, \"header\" | \"footer\" | \"emptyState\" | \"renderMessage\" | \"renderTool\" | \"placeholder\" | \"autoFocus\"> {\n className?: string;\n style?: CSSProperties;\n /** Include the built-in conversation sidebar. Defaults to true. */\n showHistory?: boolean;\n /** Optional host navigation, e.g. a Back to dashboard link. */\n navigation?: React.ReactNode;\n /**\n * Grow to fill the screen from where the chat starts down to the bottom\n * edge, or to the top of a fixed bottom bar. Defaults to true; set false\n * when a sized parent should decide the height.\n */\n fillViewport?: boolean;\n}\n\n/** Buckets conversations into relative-date groups (\"Today\", \"Yesterday\", …) like the reference sidebar. Order is preserved within each group; empty groups are dropped. */\nfunction groupByDate(items: ChatConversation[]): { label: string; items: ChatConversation[] }[] {\n const now = new Date();\n const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();\n const day = 86400000;\n const buckets: { label: string; items: ChatConversation[] }[] = [\n { label: \"Today\", items: [] }, { label: \"Yesterday\", items: [] }, { label: \"Previous 7 days\", items: [] }, { label: \"Older\", items: [] },\n ];\n for (const item of items) {\n const at = new Date(item.updatedAt).getTime();\n const bucket = at >= startOfToday ? 0 : at >= startOfToday - day ? 1 : at >= startOfToday - 7 * day ? 2 : 3;\n buckets[bucket]!.items.push(item);\n }\n return buckets.filter(bucket => bucket.items.length);\n}\n\n/**\n * The sidebar's state. It is owned by `FrankieChatPage`, above the provider,\n * because everything inside the provider remounts when the conversation\n * changes (that is how each chat gets fresh state). Keeping the list, its\n * loading/error state and the search box here is what stops the sidebar from\n * emptying and re-fetching every time a conversation is clicked.\n */\ninterface HistoryState {\n conversations: ChatConversation[];\n setConversations: (items: ChatConversation[]) => void;\n historyError: string;\n setHistoryError: (message: string) => void;\n historyLoading: boolean;\n setHistoryLoading: (loading: boolean) => void;\n query: string;\n setQuery: (query: string) => void;\n}\n\nfunction useHistoryState(): HistoryState {\n const [conversations, setConversations] = useState<ChatConversation[]>([]);\n const [historyError, setHistoryError] = useState(\"\");\n const [historyLoading, setHistoryLoading] = useState(false);\n const [query, setQuery] = useState(\"\");\n return { conversations, setConversations, historyError, setHistoryError, historyLoading, setHistoryLoading, query, setQuery };\n}\n\nfunction PageChrome({ showHistory, navigation, currentId, onSelect, onNew, history: state, ...panel }: Pick<FrankieChatPageProps, \"header\" | \"footer\" | \"emptyState\" | \"renderMessage\" | \"renderTool\" | \"placeholder\" | \"autoFocus\" | \"navigation\"> & { showHistory: boolean; currentId: string; onSelect: (id: string) => void; onNew: () => void; history: HistoryState }) {\n const { client, config, themeReady } = useFrankie();\n const { busy, messages } = useFrankieChat();\n const theme = useFrankieTheme();\n const mode = useResolvedMode(theme?.mode);\n const { conversations, setConversations, historyError, setHistoryError, historyLoading, setHistoryLoading, query, setQuery } = state;\n const [drawerOpen, setDrawerOpen] = useState(false);\n const [collapsed, setCollapsed] = useState(() => {\n try { return window.localStorage.getItem(\"frankie.history-collapsed\") === \"1\"; }\n catch { return false; }\n });\n const dialog = useRef<HTMLDialogElement>(null);\n const menu = useRef<HTMLButtonElement>(null);\n const requestVersion = useRef(0);\n const refresh = useCallback(async () => {\n if (!showHistory || !config || config.allowAnonymous) { setHistoryError(\"\"); return; }\n const version = ++requestVersion.current;\n setHistoryLoading(true); setHistoryError(\"\");\n try { const items = await client.listConversations(); if (version === requestVersion.current) setConversations(items); }\n catch (caught) {\n if (version !== requestVersion.current) { /* superseded by a newer request */ }\n else {\n const message =\n caught instanceof FrankieApiError ? caught.message\n : caught instanceof Error && caught.message.startsWith(\"Frankie\") ? caught.message\n : \"History couldn’t load. Check your sign-in and try again.\";\n setHistoryError(message);\n }\n }\n finally { if (version === requestVersion.current) setHistoryLoading(false); }\n }, [client, showHistory, config?.allowAnonymous, setConversations, setHistoryError, setHistoryLoading]);\n useEffect(() => { if (!busy) void refresh(); return () => { requestVersion.current++; }; }, [refresh, busy]);\n useEffect(() => {\n try { window.localStorage.setItem(\"frankie.history-collapsed\", collapsed ? \"1\" : \"0\"); }\n catch { /* storage unavailable */ }\n }, [collapsed]);\n useEffect(() => {\n const node = dialog.current;\n if (drawerOpen) { node?.showModal?.(); }\n else { node?.close?.(); }\n }, [drawerOpen]);\n const close = () => { setDrawerOpen(false); menu.current?.focus(); };\n const select = (id: string) => { if (!busy) { onSelect(id); close(); } };\n const history = <>\n <div className=\"frankie-history-heading\"><strong>Conversations</strong><button type=\"button\" className=\"frankie-mobile-only\" aria-label=\"Close conversations\" onClick={close}><X size={18} /></button></div>\n <button type=\"button\" className=\"frankie-new-chat\" disabled={busy} onClick={() => { onNew(); close(); }}><Plus size={17} />New chat</button>\n <label className=\"frankie-history-search\"><span>Search conversations</span><input type=\"search\" placeholder=\"Search chats…\" value={query} onChange={event => setQuery(event.target.value)} /></label>\n <nav className=\"frankie-history-list\" aria-label=\"Conversations\">{groupByDate(conversations.filter(item => item.title.toLowerCase().includes(query.toLowerCase()))).map(group => <div className=\"frankie-history-group\" key={group.label}><p className=\"frankie-history-group-label\">{group.label}</p>{group.items.map(item => <button type=\"button\" key={item.id} aria-current={item.id === currentId ? \"page\" : undefined} disabled={busy} onClick={() => select(item.id)}><span>{item.title}</span><small>{new Date(item.updatedAt).toLocaleDateString()}</small></button>)}</div>)}</nav>\n {historyLoading && !conversations.length ? <p role=\"status\">Loading history…</p> : historyError ? <p role=\"status\">{historyError}<button type=\"button\" onClick={() => void refresh()}>Try again</button></p> : !conversations.length && !historyLoading ? <p>{config?.allowAnonymous ? \"Sign in to sync conversations across devices.\" : \"Your conversations will appear here.\"}</p> : null}\n {navigation ? <div className=\"frankie-host-navigation\">{navigation}</div> : null}\n </>;\n // Hidden until the theme is known, so the page never flashes the OS's dark mode first.\n return <div className={\"frankie-page-shell\" + (mode === \"dark\" ? \" dark\" : \"\")} style={{ ...themeToCssVars(theme), ...(themeReady ? null : { opacity: 0 }) }}>\n {showHistory ? <aside className={\"frankie-history-desktop\" + (collapsed ? \" frankie-history-collapsed\" : \"\")}>\n <button type=\"button\" className=\"frankie-history-toggle\" aria-label={collapsed ? \"Expand conversations\" : \"Collapse conversations\"} aria-expanded={!collapsed} onClick={() => setCollapsed(value => !value)}>{collapsed ? <ChevronsRight size={16} /> : <ChevronsLeft size={16} />}</button>\n {collapsed ? null : history}\n </aside> : null}\n <div className=\"frankie-page-main\">\n <header className=\"frankie-page-header\"><div>{showHistory ? <button type=\"button\" className=\"frankie-mobile-only\" ref={menu} aria-label=\"Open conversations\" aria-haspopup=\"dialog\" aria-expanded={drawerOpen} onClick={() => setDrawerOpen(true)}><Menu size={20} /></button> : null}<strong className=\"frankie-thread-title\">{conversations.find(item => item.id === currentId)?.title ?? messages.find(message => message.role === \"user\")?.text ?? \"New chat\"}</strong></div><div>\n <button type=\"button\" disabled={busy} onClick={onNew} aria-label=\"Start a new conversation\"><Plus size={16} /><span>New chat</span></button>\n <details className=\"frankie-chat-settings\"><summary aria-label=\"Chat settings\"><Settings size={19} /></summary><div><strong>Chat settings</strong><p>History syncs automatically for signed-in users.</p><button type=\"button\" onClick={() => void refresh()} disabled={historyLoading || !showHistory}><History size={16} />Refresh history</button><small>Skills are checked at the start of each message.</small></div></details>\n </div></header>\n <div className=\"frankie-page-canvas\"><FrankieInline {...panel} header={panel.header ?? false} placeholder={panel.placeholder ?? `Ask anything about your ${config?.branding.name ?? \"Frankie\"} workspace…`} emptyState={panel.emptyState} /></div>\n </div>\n {showHistory ? <dialog ref={dialog} className=\"frankie-history-dialog frankie-root\" aria-label=\"Chat conversations\" style={themeToCssVars(theme)} onCancel={close} onClick={event => { if (event.target === event.currentTarget) close(); }}><aside>{history}</aside></dialog> : null}\n </div>;\n}\n\n/** Reusable product chat with one shared conversation state and scoped styling. */\nexport function FrankieChatPage({ className, style, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus, showHistory = true, navigation, fillViewport = true, ...provider }: FrankieChatPageProps) {\n const [id, setId] = useState(() => provider.conversationId ?? crypto.randomUUID());\n const onNew = () => setId(crypto.randomUUID());\n const history = useHistoryState();\n const pageRef = useRef<HTMLDivElement>(null);\n const stylesReady = useStylesReady(pageRef, undefined, provider.injectStyles);\n // Fill the screen down to the host's bottom bar even when no parent sets a\n // height (a route inside an app layout), instead of stopping at 36rem.\n const fill = useViewportFill(pageRef, fillViewport);\n return <div ref={pageRef} className={[\"frankie-root\", \"frankie-chat-page\", className].filter(Boolean).join(\" \")} style={{ height: \"100%\", minHeight: fill > 360 ? `${fill}px` : \"min(36rem, 100dvh)\", width: \"100%\", ...style, ...(stylesReady ? null : { opacity: 0 as const }) }} data-frankie-styles={stylesReady ? \"ready\" : \"pending\"}>\n <FrankieProvider {...provider} conversationId={id} storageNamespace={(provider.storageNamespace ?? \"frankie.\" + provider.publishableKey) + \".\" + id}>\n <PageChrome showHistory={showHistory} navigation={navigation} currentId={id} onSelect={setId} onNew={onNew} history={history} header={header} footer={footer} emptyState={emptyState} renderMessage={renderMessage} renderTool={renderTool} placeholder={placeholder} autoFocus={autoFocus} />\n </FrankieProvider>\n </div>;\n}\n","import { useEffect, useState, type RefObject } from \"react\";\nimport { bottomObstruction } from \"./bottom-bars.js\";\n\n/**\n * The height that fills the viewport from where `ref` starts down to the\n * bottom edge, less any fixed bar along the bottom (a mobile tab bar). A\n * full-page chat inside a host layout with no sized parent otherwise stops at\n * its minimum height and leaves a gap above the host's navigation.\n * Re-measured on resize and, throttled, as the page changes.\n */\nexport function useViewportFill(ref: RefObject<HTMLElement | null>, enabled: boolean): number {\n const [fill, setFill] = useState(0);\n useEffect(() => {\n if (!enabled || typeof window === \"undefined\") { setFill(0); return; }\n let timer = 0;\n let last = 0;\n const measure = () => {\n timer = 0;\n last = Date.now();\n const node = ref.current;\n if (!node) return;\n const top = node.getBoundingClientRect().top + window.scrollY;\n const lift = bottomObstruction(node, \"bottom-right\", 0);\n setFill(Math.max(0, Math.round(window.innerHeight - top - lift)));\n };\n const schedule = () => {\n if (timer) return;\n timer = window.setTimeout(() => requestAnimationFrame(measure), Math.max(0, 300 - (Date.now() - last)));\n };\n measure();\n window.addEventListener(\"resize\", schedule);\n const observer = typeof MutationObserver === \"function\"\n ? new MutationObserver((records) => { if (records.some((record) => !ref.current?.contains(record.target))) schedule(); })\n : undefined;\n observer?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: [\"class\", \"style\"] });\n return () => { window.clearTimeout(timer); window.removeEventListener(\"resize\", schedule); observer?.disconnect(); };\n }, [ref, enabled]);\n return fill;\n}\n","\"use client\";\n\nimport { Button } from \"../ui/button.js\";\n\nexport function SuggestedPrompts({\n prompts,\n onPick,\n disabled,\n}: {\n prompts: string[];\n onPick: (prompt: string) => void;\n disabled?: boolean;\n}) {\n if (prompts.length === 0) return null;\n return (\n <div className=\"flex flex-wrap gap-2\">\n {prompts.map((prompt) => (\n <Button\n key={prompt}\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={disabled}\n onClick={() => onPick(prompt)}\n >\n {prompt}\n </Button>\n ))}\n </div>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils.js\";\n\nexport function Separator({ className }: { className?: string }) {\n return <div role=\"separator\" className={cn(\"h-px w-full bg-border\", className)} />;\n}\n"],"mappings":"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","names":["createContext","useContext","useEffect","useRef","useState","useEffect","createContext","useRef","useState","useContext","X","useEffect","useRef","useState","useEffect","useLayoutEffect","useState","useIsomorphicLayoutEffect","useEffect","useLayoutEffect","useState","short","jsx","X","useEffect","useRef","useState","jsx","jsx","jsx","ScrollArea","jsx","Sparkles","jsx","jsxs","Sparkles","useCallback","useEffect","useRef","useState","forwardRef","jsx","forwardRef","Textarea","jsx","jsxs","useState","useRef","useEffect","useCallback","Check","Paperclip","memo","useState","X","jsx","jsxs","jsx","jsxs","Fragment","useMemo","jsx","Markdown","useMemo","Fragment","Check","ShieldAlert","Sparkles","X","cva","jsx","cva","jsx","jsxs","Sparkles","Check","ShieldAlert","X","jsx","jsxs","memo","MessageBubble","Paperclip","useState","Check","jsx","jsxs","useRef","useState","useEffect","X","jsx","jsxs","useRef","useState","useEffect","X","useRef","jsx","useRef","useCallback","useEffect","useRef","useState","Plus","X","useEffect","useState","useState","useEffect","Fragment","jsx","jsxs","useState","useRef","useCallback","useEffect","X","Plus","jsx","jsx"]}