@frankieai/react 0.1.0 → 0.1.1

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/client.ts","../src/hooks/useFrankieChat.ts","../src/lib/messages.ts","../src/components/chat/FrankieWidget.tsx","../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/Composer.tsx","../src/components/ui/textarea.tsx","../src/components/chat/Markdown.tsx","../src/components/chat/ToolActivityList.tsx","../src/components/ui/badge.tsx","../src/components/chat/MessageBubble.tsx","../src/components/chat/SuggestedPrompts.tsx","../src/components/chat/FrankieInline.tsx","../src/components/ui/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport type { PublicWidgetConfig, WidgetTheme } from \"@frankieai/core\";\nimport { FrankieClient, type FrankieClientOptions, type PageContext } from \"../lib/client.js\";\n\nexport interface FrankieProviderProps extends FrankieClientOptions {\n children: ReactNode;\n /**\n * Page context sent with every turn — current route, plan tier, whatever the\n * agent should know. Treated as data by the server, never as instructions.\n */\n context?: PageContext;\n /** Overrides for the server-side theme, so a host can restyle without a redeploy. */\n theme?: WidgetTheme;\n /** Skip the `/v1/config` fetch by supplying the config yourself. */\n config?: PublicWidgetConfig;\n}\n\ninterface FrankieContextValue {\n client: FrankieClient;\n config?: PublicWidgetConfig;\n configError?: string;\n loading: boolean;\n context?: PageContext;\n theme?: WidgetTheme;\n}\n\nconst FrankieContext = createContext<FrankieContextValue | null>(null);\n\nexport function FrankieProvider({\n children,\n context,\n theme,\n config: providedConfig,\n ...clientOptions\n}: FrankieProviderProps) {\n const client = useMemo(\n () => new FrankieClient(clientOptions),\n // The client holds session state; rebuild it only when the endpoint changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientOptions.baseUrl, clientOptions.publishableKey],\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 useEffect(() => {\n if (providedConfig) return;\n const abort = new AbortController();\n setLoading(true);\n client\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 }, [client, providedConfig]);\n\n const value = useMemo<FrankieContextValue>(\n () => ({\n client,\n ...(config ? { config } : {}),\n ...(configError ? { configError } : {}),\n loading,\n ...(context ? { context } : {}),\n ...(theme ? { theme } : {}),\n }),\n [client, config, configError, loading, context, theme],\n );\n\n return <FrankieContext.Provider value={value}>{children}</FrankieContext.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\n/** The theme actually in effect: provider override wins over server config. */\nexport function useFrankieTheme(): WidgetTheme | undefined {\n const { config, theme } = useFrankie();\n return useMemo(() => ({ ...config?.theme, ...theme }), [config?.theme, theme]);\n}\n","import type {\n ChatRequestBody,\n CreateSessionResponse,\n FrankieServerEvent,\n PublicWidgetConfig,\n} from \"@frankieai/core\";\n\nexport type PageContext = Record<string, string | number | boolean | null>;\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-data-gq5o5syq2q-uc.a.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 once per session, and again when a session expires.\n * Omit only for tenants that allow anonymous chat.\n */\n getUserToken?: () => Promise<string | undefined> | string | undefined;\n fetchImpl?: typeof fetch;\n /**\n * Remember the session (and, in the hook, the transcript) in localStorage so a\n * page reload resumes the same conversation. On by default in the browser;\n * set false for an ephemeral chat that starts fresh every load.\n */\n persist?: boolean;\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\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 }\n\n get sessionId(): string | undefined {\n return this.session?.sessionId;\n }\n\n /** Whether this client persists across reloads (browser only, opt-out via `persist:false`). */\n get persist(): boolean {\n return this.options.persist !== false && typeof window !== \"undefined\";\n }\n\n /** localStorage namespace for this tenant, shared with the chat hook. */\n get namespace(): string {\n return `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 // A stored session past its expiry is useless; drop it.\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 /** Opens (or reuses) a chat session. */\n async ensureSession(context?: PageContext): Promise<CreateSessionResponse> {\n if (this.session && new Date(this.session.expiresAt).getTime() > Date.now() + 30_000) {\n return this.session;\n }\n const userToken = await this.options.getUserToken?.();\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({ ...(userToken ? { userToken } : {}), ...(context ? { context } : {}) }),\n });\n this.session = (await this.unwrap(response)) as CreateSessionResponse;\n this.writeStored();\n return this.session;\n }\n\n /** Forget the current session so the next send starts a fresh conversation. */\n resetSession(): void {\n this.session = undefined;\n this.writeStored();\n }\n\n /**\n * Sends a message and invokes `onEvent` for each server event as it arrives.\n * Resolves when the turn ends.\n */\n async send(\n input: { message: string; context?: PageContext; signal?: AbortSignal },\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 };\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 // A dropped session is recoverable: clear it so the next send re-auths.\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 onEvent(event);\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/** Parse the server's `data:`-only SSE stream into typed events. */\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","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\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\nexport interface UseFrankieChatResult {\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) => 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 function useFrankieChat(): 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 abortRef = useRef<AbortController | null>(null);\n const mounted = useRef(true);\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 // Restore the transcript on mount, so a page reload keeps the conversation.\n // The client resumes the matching server session, so context is kept too.\n useEffect(() => {\n if (!client.persist) return;\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) setMessages(stored);\n } catch {\n /* storage unavailable; start empty */\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storageKey]);\n\n // Persist once a turn settles — never mid-stream — so the saved copy is clean.\n useEffect(() => {\n if (!client.persist || 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 }, [messages, busy, client, storageKey]);\n\n const patchAssistant = useCallback((id: string, update: (m: AssistantChatMessage) => AssistantChatMessage) => {\n setMessages((current) =>\n current.map((message) =>\n message.id === id && message.role === \"assistant\" ? update(message) : message,\n ),\n );\n }, []);\n\n const send = useCallback(\n async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed || busy) return;\n\n const assistantId = nextId(\"a\");\n setError(undefined);\n setBusy(true);\n setMessages((current) => [\n ...current,\n newUserMessage(nextId(\"u\"), trimmed),\n newAssistantMessage(assistantId),\n ]);\n\n const abort = new AbortController();\n abortRef.current = abort;\n\n try {\n await client.send({ message: trimmed, ...(context ? { context } : {}), signal: abort.signal }, (event) => {\n if (event.type === \"ping\" || event.type === \"session\") return;\n patchAssistant(assistantId, (message) => applyEvent(message, event));\n });\n } catch (caught) {\n if (abort.signal.aborted) {\n patchAssistant(assistantId, (message) => ({\n ...message,\n status: \"done\",\n text: message.text || \"Stopped.\",\n }));\n } else {\n const message =\n caught instanceof FrankieApiError\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 if (mounted.current) {\n setBusy(false);\n // A turn that ended without a `turn_end` event still has to settle.\n patchAssistant(assistantId, (message) =>\n message.status === \"streaming\" ? { ...message, status: \"done\" } : message,\n );\n }\n abortRef.current = null;\n }\n },\n [busy, client, context, patchAssistant],\n );\n\n const stop = useCallback(() => abortRef.current?.abort(), []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n client.resetSession();\n setMessages([]);\n setError(undefined);\n setBusy(false);\n try {\n if (client.persist) window.localStorage.removeItem(`${client.namespace}.messages`);\n } catch {\n /* ignore */\n }\n }, [client]);\n\n return { messages, busy, ...(error ? { error } : {}), send, stop, reset };\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\nexport interface UserChatMessage {\n id: string;\n role: \"user\";\n text: string;\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): UserChatMessage {\n return { id, role: \"user\", text, createdAt: Date.now() };\n}\n","\"use client\";\n\nimport { MessageCircle, X } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { resolveMode, themeToCssVars } 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 /** 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\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 open: controlledOpen,\n onOpenChange,\n renderLauncher,\n ...panelProps\n}: FrankieWidgetProps) {\n const { config } = useFrankie();\n const theme = useFrankieTheme();\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 const label = config?.branding.launcherLabel ?? \"Need a hand?\";\n const mode = resolveMode(theme?.mode);\n const side = position === \"bottom-right\" ? { right: offset } : { left: offset };\n\n return (\n <div\n className={cn(\"frankie-root frankie-widget\", mode === \"dark\" && \"dark\")}\n style={{ ...themeToCssVars(theme), position: \"fixed\", bottom: offset, ...side, zIndex: 2147483000 }}\n data-frankie-mode={mode}\n >\n <div className={cn(\"flex flex-col gap-3\", position === \"bottom-left\" ? \"items-start\" : \"items-end\")}>\n {open ? <ChatPanel {...panelProps} onClose={() => setOpen(false)} autoFocus /> : null}\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 )}\n </div>\n </div>\n );\n}\n","import type { WidgetTheme } from \"@frankieai/core\";\nimport 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","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, 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 { resolveMode, themeToCssVars } 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 { Composer } from \"./Composer.js\";\nimport { MessageBubble } from \"./MessageBubble.js\";\nimport { SuggestedPrompts } from \"./SuggestedPrompts.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, error, send, stop, reset } = useFrankieChat();\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Follow the stream, but only while the reader is already near the bottom.\n useEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight;\n if (distanceFromBottom < 160) node.scrollTop = node.scrollHeight;\n }, [messages]);\n\n const name = config?.branding.name ?? \"Assistant\";\n const mode = resolveMode(theme?.mode);\n\n return (\n <Card\n className={cn(\n \"frankie-root\",\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 <CardContent>\n <ScrollArea ref={scrollRef} className=\"flex h-full flex-col gap-4 p-4\" aria-live=\"polite\">\n {configError ? (\n <p className=\"text-sm text-muted-foreground\">{configError}</p>\n ) : loading ? (\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n ) : messages.length === 0 ? (\n (emptyState ?? (\n <div className=\"flex flex-col gap-4\">\n <p className=\"text-sm text-muted-foreground\">{config?.greeting}</p>\n <SuggestedPrompts prompts={config?.suggestedPrompts ?? []} onPick={send} disabled={busy} />\n </div>\n ))\n ) : (\n <div className=\"flex flex-col gap-4\">\n {messages.map((message) =>\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 />\n ),\n )}\n </div>\n )}\n </ScrollArea>\n </CardContent>\n\n <CardFooter className=\"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 disabled={Boolean(configError)}\n {...(placeholder ? { placeholder } : {})}\n {...(autoFocus ? { autoFocus } : {})}\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\";\n\nimport { ArrowUp, Square } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState, type KeyboardEvent } from \"react\";\nimport { Button } from \"../ui/button.js\";\nimport { Textarea } from \"../ui/textarea.js\";\n\nexport interface ComposerProps {\n onSend: (text: string) => void;\n onStop: () => void;\n busy: boolean;\n placeholder?: string;\n disabled?: boolean;\n autoFocus?: boolean;\n}\n\nconst MAX_ROWS_PX = 140;\n\nexport function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus }: ComposerProps) {\n const [value, setValue] = useState(\"\");\n const textareaRef = useRef<HTMLTextAreaElement>(null);\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 submit = useCallback(() => {\n const text = value.trim();\n if (!text || busy || disabled) return;\n setValue(\"\");\n onSend(text);\n }, [busy, disabled, onSend, value]);\n\n const onKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n };\n\n return (\n <form\n className=\"flex items-end gap-2\"\n onSubmit={(event) => {\n event.preventDefault();\n submit();\n }}\n >\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 {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 || value.trim().length === 0} aria-label=\"Send\">\n <ArrowUp />\n </Button>\n )}\n </form>\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 { Fragment, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A deliberately small Markdown subset — paragraphs, lists, fenced code, bold,\n * 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 */\nexport function Markdown({ text, className }: { text: string; className?: string }) {\n return <div className={cn(\"frankie-prose\", className)}>{renderBlocks(text)}</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 blocks.push(<p key={`h${blocks.length}`} className=\"font-semibold\">{renderInline(heading[1] ?? \"\")}</p>);\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\nconst INLINE = /(\\*\\*[^*]+\\*\\*|`[^`]+`|\\[[^\\]]+\\]\\((?: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 <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, Loader2, ShieldAlert, X } from \"lucide-react\";\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 return (\n <ul className=\"mt-2 flex flex-col gap-1\">\n {tools.map((tool) => (\n <li key={tool.id}>{renderTool ? renderTool(tool) : <ToolActivityRow tool={tool} />}</li>\n ))}\n </ul>\n );\n}\n\nexport function ToolActivityRow({ tool }: { tool: ToolActivity }) {\n return (\n <Badge variant={badgeVariant(tool.status)} title={tool.detail ?? tool.name}>\n <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 type { ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport type { ChatMessage, ToolActivity } from \"../../lib/messages.js\";\nimport { Avatar } from \"../ui/avatar.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}\n\nexport function MessageBubble({\n message,\n assistantName,\n avatarUrl,\n showToolActivity,\n renderTool,\n}: MessageBubbleProps) {\n if (message.role === \"user\") {\n return (\n <div className=\"flex justify-end\">\n <div className=\"max-w-[85%] rounded-[var(--frankie-radius)] bg-primary px-3 py-2 text-sm text-primary-foreground\">\n {message.text}\n </div>\n </div>\n );\n }\n\n const empty = message.text.length === 0 && message.tools.length === 0;\n\n return (\n <div className=\"flex gap-2\">\n <Avatar {...(avatarUrl ? { src: avatarUrl } : {})} name={assistantName} className=\"mt-0.5\" />\n <div className=\"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 ) : empty && message.status === \"streaming\" ? (\n <TypingDots />\n ) : (\n <Markdown text={message.text} className=\"text-sm\" />\n )}\n {showToolActivity ? (\n <ToolActivityList tools={message.tools} {...(renderTool ? { renderTool } : {})} />\n ) : null}\n </div>\n </div>\n );\n}\n\nexport function TypingDots() {\n return (\n <span className=\"inline-flex items-center gap-1 py-2\" role=\"status\" aria-label=\"Thinking\">\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","\"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 type { CSSProperties } from \"react\";\nimport { useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { resolveMode, themeToCssVars } 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 theme = useFrankieTheme();\n const mode = resolveMode(theme?.mode);\n return (\n <div\n className={cn(\"frankie-root frankie-inline\", mode === \"dark\" && \"dark\", className)}\n style={{ ...themeToCssVars(theme), display: \"flex\", height: \"100%\", width: \"100%\", ...style }}\n data-frankie-mode={mode}\n >\n <ChatPanel {...panelProps} fill />\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":["useEffect","useState","useState","useEffect","X","useEffect","useState","jsx","X","useEffect","useRef","jsx","jsx","jsx","ScrollArea","useCallback","useEffect","useRef","useState","forwardRef","jsx","forwardRef","Textarea","jsx","useState","useRef","useEffect","useCallback","jsx","cva","jsx","cva","jsx","jsxs","jsx","jsxs","jsx","jsx","jsxs","useRef","useEffect","X","jsx","jsxs","useState","useEffect","X","jsx","jsx"]}
1
+ {"version":3,"sources":["../src/hooks/FrankieProvider.tsx","../src/lib/client.ts","../src/hooks/useFrankieChat.ts","../src/lib/messages.ts","../src/components/chat/FrankieWidget.tsx","../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/Composer.tsx","../src/components/ui/textarea.tsx","../src/components/chat/MessageBubble.tsx","../src/components/chat/Markdown.tsx","../src/components/chat/ToolActivityList.tsx","../src/components/ui/badge.tsx","../src/components/chat/SuggestedPrompts.tsx","../src/components/chat/FrankieInline.tsx","../src/components/ui/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from \"react\";\nimport type { PublicWidgetConfig, WidgetTheme } from \"@frankieai/core\";\nimport { FrankieClient, type FrankieClientOptions, type PageContext } from \"../lib/client.js\";\n\nexport interface FrankieProviderProps extends FrankieClientOptions {\n children: ReactNode;\n /**\n * Page context sent with every turn — current route, plan tier, whatever the\n * agent should know. Treated as data by the server, never as instructions.\n */\n context?: PageContext;\n /** Overrides for the server-side theme, so a host can restyle without a redeploy. */\n theme?: WidgetTheme;\n /** Skip the `/v1/config` fetch by supplying the config yourself. */\n config?: PublicWidgetConfig;\n}\n\ninterface FrankieContextValue {\n client: FrankieClient;\n config?: PublicWidgetConfig;\n configError?: string;\n loading: boolean;\n context?: PageContext;\n theme?: WidgetTheme;\n}\n\nconst FrankieContext = createContext<FrankieContextValue | null>(null);\n\nexport function FrankieProvider({\n children,\n context,\n theme,\n config: providedConfig,\n ...clientOptions\n}: FrankieProviderProps) {\n const client = useMemo(\n () => new FrankieClient(clientOptions),\n // The client holds session state; rebuild it only when the endpoint changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientOptions.baseUrl, clientOptions.publishableKey],\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 useEffect(() => {\n if (providedConfig) return;\n const abort = new AbortController();\n setLoading(true);\n client\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 }, [client, providedConfig]);\n\n const value = useMemo<FrankieContextValue>(\n () => ({\n client,\n ...(config ? { config } : {}),\n ...(configError ? { configError } : {}),\n loading,\n ...(context ? { context } : {}),\n ...(theme ? { theme } : {}),\n }),\n [client, config, configError, loading, context, theme],\n );\n\n return <FrankieContext.Provider value={value}>{children}</FrankieContext.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\n/** The theme actually in effect: provider override wins over server config. */\nexport function useFrankieTheme(): WidgetTheme | undefined {\n const { config, theme } = useFrankie();\n return useMemo(() => ({ ...config?.theme, ...theme }), [config?.theme, theme]);\n}\n","import type {\n ChatAttachment,\n ChatRequestBody,\n CreateSessionResponse,\n FrankieServerEvent,\n PublicWidgetConfig,\n} from \"@frankieai/core\";\n\nexport type PageContext = Record<string, string | number | boolean | null>;\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-data-gq5o5syq2q-uc.a.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 once per session, and again when a session expires.\n * Omit only for tenants that allow anonymous chat.\n */\n getUserToken?: () => Promise<string | undefined> | string | undefined;\n fetchImpl?: typeof fetch;\n /**\n * Remember the session (and, in the hook, the transcript) in localStorage so a\n * page reload resumes the same conversation. On by default in the browser;\n * set false for an ephemeral chat that starts fresh every load.\n */\n persist?: boolean;\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\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 }\n\n get sessionId(): string | undefined {\n return this.session?.sessionId;\n }\n\n /** Whether this client persists across reloads (browser only, opt-out via `persist:false`). */\n get persist(): boolean {\n return this.options.persist !== false && typeof window !== \"undefined\";\n }\n\n /** localStorage namespace for this tenant, shared with the chat hook. */\n get namespace(): string {\n return `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 // A stored session past its expiry is useless; drop it.\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 /** Opens (or reuses) a chat session. */\n async ensureSession(context?: PageContext): Promise<CreateSessionResponse> {\n if (this.session && new Date(this.session.expiresAt).getTime() > Date.now() + 30_000) {\n return this.session;\n }\n const userToken = await this.options.getUserToken?.();\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({ ...(userToken ? { userToken } : {}), ...(context ? { context } : {}) }),\n });\n this.session = (await this.unwrap(response)) as CreateSessionResponse;\n this.writeStored();\n return this.session;\n }\n\n /** Forget the current session so the next send starts a fresh conversation. */\n resetSession(): void {\n this.session = undefined;\n this.writeStored();\n }\n\n /**\n * Sends a message and invokes `onEvent` for each server event as it arrives.\n * Resolves when the turn ends.\n */\n async send(\n input: {\n message: string;\n context?: PageContext;\n /** Model id chosen by the user, honoured only if the workspace allows it. */\n model?: string;\n /** Files attached to this turn. */\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 // A dropped session is recoverable: clear it so the next send re-auths.\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 onEvent(event);\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/** Parse the server's `data:`-only SSE stream into typed events. */\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","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ChatAttachment } 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 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 function useFrankieChat(): 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 abortRef = useRef<AbortController | null>(null);\n const mounted = useRef(true);\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 // Restore the transcript on mount, so a page reload keeps the conversation.\n // The client resumes the matching server session, so context is kept too.\n useEffect(() => {\n if (!client.persist) return;\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) setMessages(stored);\n } catch {\n /* storage unavailable; start empty */\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [storageKey]);\n\n // Persist once a turn settles — never mid-stream — so the saved copy is clean.\n useEffect(() => {\n if (!client.persist || 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 }, [messages, busy, client, storageKey]);\n\n const patchAssistant = useCallback((id: string, update: (m: AssistantChatMessage) => AssistantChatMessage) => {\n setMessages((current) =>\n current.map((message) =>\n message.id === id && message.role === \"assistant\" ? update(message) : message,\n ),\n );\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) || busy) 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 setBusy(true);\n setMessages((current) => [\n ...current,\n newUserMessage(\n nextId(\"u\"),\n trimmed,\n attachments.map((file) => ({ name: file.name, mimeType: file.mimeType })),\n ),\n newAssistantMessage(assistantId),\n ]);\n\n const abort = new AbortController();\n abortRef.current = abort;\n\n try {\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 === \"ping\" || event.type === \"session\") return;\n patchAssistant(assistantId, (message) => applyEvent(message, event));\n },\n );\n } catch (caught) {\n if (abort.signal.aborted) {\n patchAssistant(assistantId, (message) => ({\n ...message,\n status: \"done\",\n text: message.text || \"Stopped.\",\n }));\n } else {\n const message =\n caught instanceof FrankieApiError\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 if (mounted.current) {\n setBusy(false);\n // A turn that ended without a `turn_end` event still has to settle.\n patchAssistant(assistantId, (message) =>\n message.status === \"streaming\" ? { ...message, status: \"done\" } : message,\n );\n }\n abortRef.current = null;\n }\n },\n [busy, client, context, patchAssistant],\n );\n\n const stop = useCallback(() => abortRef.current?.abort(), []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n client.resetSession();\n setMessages([]);\n setError(undefined);\n setBusy(false);\n try {\n if (client.persist) window.localStorage.removeItem(`${client.namespace}.messages`);\n } catch {\n /* ignore */\n }\n }, [client]);\n\n return { messages, busy, ...(error ? { error } : {}), send, stop, reset };\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, useState } from \"react\";\nimport { useFrankie, useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { resolveMode, themeToCssVars } 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 /** 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\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 open: controlledOpen,\n onOpenChange,\n renderLauncher,\n ...panelProps\n}: FrankieWidgetProps) {\n const { config } = useFrankie();\n const theme = useFrankieTheme();\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 const label = config?.branding.launcherLabel ?? \"Need a hand?\";\n const mode = resolveMode(theme?.mode);\n const side = position === \"bottom-right\" ? { right: offset } : { left: offset };\n\n return (\n <div\n className={cn(\"frankie-root frankie-widget\", mode === \"dark\" && \"dark\")}\n style={{ ...themeToCssVars(theme), position: \"fixed\", bottom: offset, ...side, zIndex: 2147483000 }}\n data-frankie-mode={mode}\n >\n <div className={cn(\"flex flex-col gap-3\", position === \"bottom-left\" ? \"items-start\" : \"items-end\")}>\n {open ? <ChatPanel {...panelProps} onClose={() => setOpen(false)} autoFocus /> : null}\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 )}\n </div>\n </div>\n );\n}\n","import type { WidgetTheme } from \"@frankieai/core\";\nimport 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","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 { resolveMode, themeToCssVars } 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 { Composer } from \"./Composer.js\";\nimport { MessageBubble } from \"./MessageBubble.js\";\nimport { SuggestedPrompts } from \"./SuggestedPrompts.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, error, send, stop, reset } = useFrankieChat();\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const features = config?.features;\n const pickerModels = features?.modelPicker.enabled ? features.modelPicker.models : [];\n const [model, setModel] = useState<string | undefined>(undefined);\n\n // Follow the stream, but only while the reader is already near the bottom.\n useEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight;\n if (distanceFromBottom < 160) node.scrollTop = node.scrollHeight;\n }, [messages]);\n\n const name = config?.branding.name ?? \"Assistant\";\n const mode = resolveMode(theme?.mode);\n\n return (\n <Card\n className={cn(\n \"frankie-root\",\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 <CardContent>\n <ScrollArea ref={scrollRef} className=\"flex h-full flex-col gap-4 p-4\" aria-live=\"polite\">\n {configError ? (\n <p className=\"text-sm text-muted-foreground\">{configError}</p>\n ) : loading ? (\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n ) : messages.length === 0 ? (\n (emptyState ?? (\n <div className=\"flex flex-col gap-4\">\n <p className=\"text-sm text-muted-foreground\">{config?.greeting}</p>\n <SuggestedPrompts prompts={config?.suggestedPrompts ?? []} onPick={send} disabled={busy} />\n </div>\n ))\n ) : (\n <div className=\"flex flex-col gap-4\">\n {messages.map((message) =>\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 />\n ),\n )}\n </div>\n )}\n </ScrollArea>\n </CardContent>\n\n <CardFooter className=\"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 disabled={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\";\n\nimport { ArrowUp, Paperclip, Square, X } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState, type KeyboardEvent } 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}\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 }: 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 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) {\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=\"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=\"flex items-end gap-2\">\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 <Paperclip />\n </Button>\n </>\n ) : null}\n\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 {models.length > 0 ? (\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-focus h-9 max-w-[8rem] rounded-[var(--frankie-radius)] border border-border bg-background px-2 text-sm\"\n >\n {models.map((model) => (\n <option key={model.id} value={model.id}>\n {model.label}\n </option>\n ))}\n </select>\n ) : null}\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","\"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 { Paperclip } from \"lucide-react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport type { ChatMessage, ToolActivity } from \"../../lib/messages.js\";\nimport { Avatar } from \"../ui/avatar.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}\n\nexport function MessageBubble({\n message,\n assistantName,\n avatarUrl,\n showToolActivity,\n renderTool,\n}: MessageBubbleProps) {\n if (message.role === \"user\") {\n return (\n <div className=\"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=\"max-w-[85%] rounded-[var(--frankie-radius)] bg-primary px-3 py-2 text-sm text-primary-foreground\">\n {message.text}\n </div>\n ) : null}\n </div>\n );\n }\n\n const empty = message.text.length === 0 && message.tools.length === 0;\n\n return (\n <div className=\"flex gap-2\">\n <Avatar {...(avatarUrl ? { src: avatarUrl } : {})} name={assistantName} className=\"mt-0.5\" />\n <div className=\"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 ) : empty && message.status === \"streaming\" ? (\n <TypingDots />\n ) : (\n <Markdown text={message.text} className=\"text-sm\" />\n )}\n {showToolActivity ? (\n <ToolActivityList tools={message.tools} {...(renderTool ? { renderTool } : {})} />\n ) : null}\n </div>\n </div>\n );\n}\n\nexport function TypingDots() {\n return (\n <span className=\"inline-flex items-center gap-1 py-2\" role=\"status\" aria-label=\"Thinking\">\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","\"use client\";\n\nimport { Fragment, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\n\n/**\n * A deliberately small Markdown subset — paragraphs, lists, fenced code, bold,\n * 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 */\nexport function Markdown({ text, className }: { text: string; className?: string }) {\n return <div className={cn(\"frankie-prose\", className)}>{renderBlocks(text)}</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 blocks.push(<p key={`h${blocks.length}`} className=\"font-semibold\">{renderInline(heading[1] ?? \"\")}</p>);\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\nconst INLINE = /(\\*\\*[^*]+\\*\\*|`[^`]+`|\\[[^\\]]+\\]\\((?: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 <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, Loader2, ShieldAlert, X } from \"lucide-react\";\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 return (\n <ul className=\"mt-2 flex flex-col gap-1\">\n {tools.map((tool) => (\n <li key={tool.id}>{renderTool ? renderTool(tool) : <ToolActivityRow tool={tool} />}</li>\n ))}\n </ul>\n );\n}\n\nexport function ToolActivityRow({ tool }: { tool: ToolActivity }) {\n return (\n <Badge variant={badgeVariant(tool.status)} title={tool.detail ?? tool.name}>\n <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 { 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 type { CSSProperties } from \"react\";\nimport { useFrankieTheme } from \"../../hooks/FrankieProvider.jsx\";\nimport { resolveMode, themeToCssVars } 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 theme = useFrankieTheme();\n const mode = resolveMode(theme?.mode);\n return (\n <div\n className={cn(\"frankie-root frankie-inline\", mode === \"dark\" && \"dark\", className)}\n style={{ ...themeToCssVars(theme), display: \"flex\", height: \"100%\", width: \"100%\", ...style }}\n data-frankie-mode={mode}\n >\n <ChatPanel {...panelProps} fill />\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":["useEffect","useState","useState","useEffect","X","useEffect","useState","jsx","X","useEffect","useRef","useState","jsx","jsx","jsx","ScrollArea","useCallback","useEffect","useRef","useState","forwardRef","jsx","forwardRef","Textarea","jsx","useState","useRef","useEffect","useCallback","Paperclip","Fragment","jsx","Fragment","X","cva","jsx","cva","jsx","jsxs","X","jsx","jsxs","Paperclip","jsx","jsx","jsxs","useRef","useState","useEffect","X","jsx","jsxs","useState","useEffect","X","jsx","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frankieai/react",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Embeddable, themeable chat widget for the Frankie onboarding agent — shadcn/ui components, works with or without Tailwind in the host app.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "clsx": "^2.1.1",
32
32
  "lucide-react": "^0.460.0",
33
33
  "tailwind-merge": "^2.5.0",
34
- "@frankieai/core": "0.1.0"
34
+ "@frankieai/core": "0.1.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@tailwindcss/cli": "^4.0.0",
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { RotateCcw, X } from "lucide-react";
4
- import { useEffect, useRef, type ReactNode } from "react";
4
+ import { useEffect, useRef, useState, type ReactNode } from "react";
5
5
  import { useFrankie, useFrankieTheme } from "../../hooks/FrankieProvider.jsx";
6
6
  import { useFrankieChat } from "../../hooks/useFrankieChat.js";
7
7
  import type { ChatMessage, ToolActivity } from "../../lib/messages.js";
@@ -52,6 +52,10 @@ export function ChatPanel({
52
52
  const { messages, busy, error, send, stop, reset } = useFrankieChat();
53
53
  const scrollRef = useRef<HTMLDivElement>(null);
54
54
 
55
+ const features = config?.features;
56
+ const pickerModels = features?.modelPicker.enabled ? features.modelPicker.models : [];
57
+ const [model, setModel] = useState<string | undefined>(undefined);
58
+
55
59
  // Follow the stream, but only while the reader is already near the bottom.
56
60
  useEffect(() => {
57
61
  const node = scrollRef.current;
@@ -138,6 +142,10 @@ export function ChatPanel({
138
142
  disabled={Boolean(configError)}
139
143
  {...(placeholder ? { placeholder } : {})}
140
144
  {...(autoFocus ? { autoFocus } : {})}
145
+ {...(features?.attachments.enabled ? { attachments: features.attachments } : {})}
146
+ {...(pickerModels.length
147
+ ? { modelPicker: { models: pickerModels, value: model ?? pickerModels[0]?.id, onChange: setModel } }
148
+ : {})}
141
149
  />
142
150
  </div>
143
151
  {footer}