cencori 1.4.0 → 1.4.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/README.md +25 -0
- package/dist/index.d.mts +149 -1
- package/dist/index.d.ts +149 -1
- package/dist/index.js +330 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +328 -0
- package/dist/index.mjs.map +1 -1
- package/dist/memory/index.d.mts +160 -1
- package/dist/memory/index.d.ts +160 -1
- package/dist/memory/index.js +121 -0
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/index.mjs +121 -0
- package/dist/memory/index.mjs.map +1 -1
- package/dist/react/index.d.mts +250 -1
- package/dist/react/index.d.ts +250 -1
- package/dist/react/index.js +729 -51
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +729 -50
- package/dist/react/index.mjs.map +1 -1
- package/dist/voice/index.d.mts +130 -0
- package/dist/voice/index.d.ts +130 -0
- package/dist/voice/index.js +141 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/index.mjs +116 -0
- package/dist/voice/index.mjs.map +1 -0
- package/package.json +6 -1
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/index.ts","../../src/react/vision/vision-format-banner.tsx","../../src/react/vision/vision-uploader.tsx","../../src/react/vision/downscale.ts"],"sourcesContent":["/**\n * cencori/react — importable UI components for AI apps\n *\n * @example\n * import { VisionUploader } from 'cencori/react';\n *\n * <VisionUploader\n * endpoint=\"https://api.cencori.com/api/ai/vision\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}\n * task=\"describe\"\n * onResult={(result) => console.log(result)}\n * />\n */\n\nexport { VisionFormatBanner } from './vision/vision-format-banner';\nexport type { VisionFormatBannerProps, VisionProvider } from './vision/vision-format-banner';\n\nexport { VisionUploader } from './vision/vision-uploader';\nexport type { VisionUploaderProps, VisionTask } from './vision/vision-uploader';\n","/**\n * VisionFormatBanner\n *\n * Drop-in banner that documents the supported image formats and size limits\n * for the Cencori Vision API. Zero external state — safe to embed anywhere,\n * including inside a customer's own product.\n *\n * Deps: react, lucide-react, tailwindcss.\n */\n\n'use client';\n\nimport { ImageIcon, Info } from 'lucide-react';\nimport type { ReactNode } from 'react';\n\nexport type VisionProvider = 'openai' | 'anthropic' | 'google';\n\nexport interface VisionFormatBannerProps {\n /** Restrict displayed limits to a specific provider. Omit to show universal (all-provider) limits. */\n provider?: VisionProvider;\n /** Tone. */\n variant?: 'info' | 'muted';\n /** Extra classes. */\n className?: string;\n /** Optional title override. */\n title?: ReactNode;\n}\n\nconst UNIVERSAL_FORMATS = ['JPEG', 'PNG', 'WEBP', 'GIF'];\n\nconst PROVIDER_LIMITS: Record<VisionProvider, { formats: string[]; maxMB: number; notes: string }> = {\n openai: { formats: ['JPEG', 'PNG', 'WEBP', 'GIF'], maxMB: 20, notes: 'Non-animated GIFs only.' },\n anthropic: { formats: ['JPEG', 'PNG', 'WEBP', 'GIF'], maxMB: 5, notes: 'Max 8000×8000 dimensions.' },\n google: { formats: ['JPEG', 'PNG', 'WEBP', 'GIF', 'HEIC', 'HEIF'], maxMB: 20, notes: 'HEIC/HEIF supported.' },\n};\n\nfunction joinClasses(...parts: Array<string | undefined | false>): string {\n return parts.filter(Boolean).join(' ');\n}\n\nexport function VisionFormatBanner({\n provider,\n variant = 'info',\n className,\n title,\n}: VisionFormatBannerProps) {\n const info = provider ? PROVIDER_LIMITS[provider] : { formats: UNIVERSAL_FORMATS, maxMB: 5, notes: 'These formats work across every supported provider.' };\n const heading = title ?? (provider ? `Supported for ${providerLabel(provider)}` : 'Supported image formats');\n\n const base = variant === 'info'\n ? 'bg-blue-50 border-blue-200 text-blue-900 dark:bg-blue-950/40 dark:border-blue-900 dark:text-blue-100'\n : 'bg-neutral-50 border-neutral-200 text-neutral-800 dark:bg-neutral-900 dark:border-neutral-800 dark:text-neutral-100';\n\n return (\n <div\n role=\"note\"\n className={joinClasses(\n 'flex gap-3 rounded-lg border p-4 text-sm',\n base,\n className\n )}\n >\n <div className=\"flex-shrink-0 pt-0.5\">\n <Info className=\"h-5 w-5 opacity-70\" aria-hidden />\n </div>\n <div className=\"flex flex-col gap-2\">\n <p className=\"font-medium\">{heading}</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {info.formats.map(f => (\n <span\n key={f}\n className=\"inline-flex items-center gap-1 rounded-md border border-current/20 bg-white/50 px-2 py-0.5 text-xs font-medium dark:bg-black/20\"\n >\n <ImageIcon className=\"h-3 w-3 opacity-70\" aria-hidden />\n {f}\n </span>\n ))}\n </div>\n <p className=\"text-xs opacity-80\">\n Up to <strong>{info.maxMB}MB</strong> per image. {info.notes}\n </p>\n </div>\n </div>\n );\n}\n\nfunction providerLabel(p: VisionProvider): string {\n if (p === 'openai') return 'OpenAI';\n if (p === 'anthropic') return 'Anthropic';\n return 'Google';\n}\n","/**\n * VisionUploader\n *\n * Drop-in image uploader for the Cencori Vision API. Handles drag-drop,\n * client-side format + size validation, and calls the endpoint via\n * multipart/form-data. Renders clear error messages and a supported-formats\n * banner. Zero external state — safe to embed in a customer's own product.\n *\n * Usage:\n * <VisionUploader\n * endpoint=\"https://api.cencori.com/api/ai/vision\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}\n * task=\"describe\"\n * onResult={(result) => console.log(result)}\n * />\n *\n * Deps: react, lucide-react, tailwindcss.\n */\n\n'use client';\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport { AlertCircle, Loader2, Upload, X } from 'lucide-react';\nimport { VisionFormatBanner, type VisionProvider } from './vision-format-banner';\nimport { canDownscale, downscaleImage } from './downscale';\n\n// ── Public types ────────────────────────────────────────────────\n\nexport type VisionTask = 'analyze' | 'describe' | 'ocr' | 'classify';\n\nexport interface VisionUploaderProps {\n /** Base Vision endpoint. Defaults to same-origin `/api/ai/vision`. */\n endpoint?: string;\n /** Cencori API key. Sent as `Authorization: Bearer`. */\n apiKey?: string;\n /** Which task to run: analyze (default), describe, ocr, or classify. */\n task?: VisionTask;\n /** Provider to target — informs client-side validation. Defaults to universal (all providers). */\n provider?: VisionProvider;\n /** Optional model override. */\n model?: string;\n /** Custom prompt (only meaningful for task=\"analyze\"). */\n prompt?: string;\n /** Called on success. */\n onResult?: (result: unknown) => void;\n /** Called on error (before the built-in banner renders it). */\n onError?: (error: { code: string; message: string }) => void;\n /** Hide the supported-formats banner. Default: shown. */\n hideBanner?: boolean;\n /**\n * Auto-compress images that exceed the provider's size limit. JPEG/PNG/WEBP only.\n * Default: true.\n */\n autoCompress?: boolean;\n /** Additional request headers to forward on upload (in addition to Authorization). */\n headers?: Record<string, string>;\n /** Extra classes. */\n className?: string;\n}\n\n// ── Client-side validation rules (mirror the server) ────────────\n\nconst UNIVERSAL_FORMATS = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];\n\nconst PROVIDER_RULES: Record<VisionProvider, { formats: string[]; maxBytes: number }> = {\n openai: { formats: UNIVERSAL_FORMATS, maxBytes: 20 * 1024 * 1024 },\n anthropic: { formats: UNIVERSAL_FORMATS, maxBytes: 5 * 1024 * 1024 },\n google: { formats: [...UNIVERSAL_FORMATS, 'image/heic', 'image/heif'], maxBytes: 20 * 1024 * 1024 },\n};\n\nfunction rulesFor(provider?: VisionProvider) {\n if (provider) return PROVIDER_RULES[provider];\n return { formats: UNIVERSAL_FORMATS, maxBytes: 5 * 1024 * 1024 };\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n return `${(bytes / 1024).toFixed(0)}KB`;\n}\n\nfunction friendlyFormats(mimes: string[]): string {\n return mimes.map(m => m.replace('image/', '').toUpperCase()).join(', ');\n}\n\nfunction joinClasses(...parts: Array<string | undefined | false>): string {\n return parts.filter(Boolean).join(' ');\n}\n\n// ── Component ───────────────────────────────────────────────────\n\nexport function VisionUploader({\n endpoint = '/api/ai/vision',\n apiKey,\n task = 'analyze',\n provider,\n model,\n prompt,\n onResult,\n onError,\n hideBanner = false,\n autoCompress = true,\n headers,\n className,\n}: VisionUploaderProps) {\n const [file, setFile] = useState<File | null>(null);\n const [preview, setPreview] = useState<string | null>(null);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState<unknown>(null);\n const [dragging, setDragging] = useState(false);\n const [notice, setNotice] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const rules = useMemo(() => rulesFor(provider), [provider]);\n const targetUrl = task === 'analyze' ? endpoint : `${endpoint.replace(/\\/$/, '')}/${task}`;\n\n const acceptFile = useCallback(async (f: File) => {\n setResult(null);\n setNotice(null);\n let effective = f;\n\n // Wrong format: fail fast, no attempt at compression\n const mime = (f.type || '').toLowerCase();\n if (!rules.formats.includes(mime)) {\n const err = {\n code: 'unsupported_format',\n message: `\"${f.name}\" is ${mime || 'an unknown type'}. Please upload one of: ${friendlyFormats(rules.formats)}.`,\n };\n setError(err);\n setFile(null);\n setPreview(null);\n onError?.(err);\n return;\n }\n\n // Too big: try auto-compression if allowed and format is downscalable\n if (f.size > rules.maxBytes) {\n if (autoCompress && canDownscale(mime)) {\n try {\n const result = await downscaleImage(f, rules.maxBytes);\n effective = result.file;\n setNotice(\n `Compressed from ${formatBytes(result.originalBytes)} to ${formatBytes(result.finalBytes)} to fit the ${provider ?? 'universal'} limit.`\n );\n } catch (compressErr) {\n const err = {\n code: 'image_too_large',\n message: compressErr instanceof Error\n ? compressErr.message\n : `\"${f.name}\" is ${formatBytes(f.size)} — the limit is ${formatBytes(rules.maxBytes)}${provider ? ` for ${provider}` : ''}.`,\n };\n setError(err);\n setFile(null);\n setPreview(null);\n onError?.(err);\n return;\n }\n } else {\n const err = {\n code: 'image_too_large',\n message: `\"${f.name}\" is ${formatBytes(f.size)} — the limit is ${formatBytes(rules.maxBytes)}${provider ? ` for ${provider}` : ''}.`,\n };\n setError(err);\n setFile(null);\n setPreview(null);\n onError?.(err);\n return;\n }\n }\n\n setError(null);\n setFile(effective);\n setPreview(URL.createObjectURL(effective));\n }, [rules, provider, autoCompress, onError]);\n\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n setDragging(false);\n const f = e.dataTransfer.files?.[0];\n if (f) acceptFile(f);\n }, [acceptFile]);\n\n const handleSubmit = useCallback(async () => {\n if (!file) return;\n setLoading(true);\n setError(null);\n setResult(null);\n try {\n const form = new FormData();\n form.append('file', file);\n if (prompt && task === 'analyze') form.append('prompt', prompt);\n if (model) form.append('model', model);\n\n const requestHeaders: Record<string, string> = { ...(headers ?? {}) };\n if (apiKey) requestHeaders.Authorization = `Bearer ${apiKey}`;\n\n const res = await fetch(targetUrl, {\n method: 'POST',\n headers: Object.keys(requestHeaders).length ? requestHeaders : undefined,\n body: form,\n });\n const data = await res.json();\n if (!res.ok) {\n const err = {\n code: (data && typeof data === 'object' && 'error' in data ? String(data.error) : 'request_failed'),\n message: (data && typeof data === 'object' && 'message' in data ? String(data.message) : `Request failed with status ${res.status}`),\n };\n setError(err);\n onError?.(err);\n return;\n }\n setResult(data);\n onResult?.(data);\n } catch (err) {\n const e = { code: 'network_error', message: err instanceof Error ? err.message : 'Network error' };\n setError(e);\n onError?.(e);\n } finally {\n setLoading(false);\n }\n }, [file, prompt, model, task, targetUrl, apiKey, headers, onResult, onError]);\n\n const clear = useCallback(() => {\n setFile(null);\n setPreview(null);\n setResult(null);\n setError(null);\n setNotice(null);\n if (inputRef.current) inputRef.current.value = '';\n }, []);\n\n return (\n <div className={joinClasses('flex flex-col gap-3', className)}>\n {!hideBanner && <VisionFormatBanner provider={provider} />}\n\n <div\n onDragOver={e => { e.preventDefault(); setDragging(true); }}\n onDragLeave={() => setDragging(false)}\n onDrop={handleDrop}\n onClick={() => inputRef.current?.click()}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Upload image\"\n className={joinClasses(\n 'flex cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed p-8 text-center transition',\n dragging\n ? 'border-blue-500 bg-blue-50 dark:bg-blue-950/30'\n : 'border-neutral-300 hover:border-neutral-400 dark:border-neutral-700 dark:hover:border-neutral-600',\n )}\n >\n {preview ? (\n <div className=\"relative\">\n <img src={preview} alt=\"Preview\" className=\"max-h-64 rounded-lg object-contain\" />\n <button\n type=\"button\"\n onClick={e => { e.stopPropagation(); clear(); }}\n className=\"absolute -right-2 -top-2 rounded-full bg-white p-1 shadow ring-1 ring-neutral-200 hover:bg-neutral-100 dark:bg-neutral-900 dark:ring-neutral-700\"\n aria-label=\"Remove image\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <>\n <Upload className=\"h-8 w-8 text-neutral-400\" aria-hidden />\n <p className=\"text-sm font-medium\">Drop an image or click to upload</p>\n <p className=\"text-xs text-neutral-500\">{friendlyFormats(rules.formats)} · up to {formatBytes(rules.maxBytes)}</p>\n </>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept={rules.formats.join(',')}\n className=\"hidden\"\n onChange={e => {\n const f = e.target.files?.[0];\n if (f) acceptFile(f);\n }}\n />\n </div>\n\n {error && (\n <div role=\"alert\" className=\"flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-900 dark:border-red-900 dark:bg-red-950/40 dark:text-red-100\">\n <AlertCircle className=\"mt-0.5 h-4 w-4 flex-shrink-0\" aria-hidden />\n <div>\n <p className=\"font-medium\">Can't process this image</p>\n <p className=\"text-xs opacity-90\">{error.message}</p>\n </div>\n </div>\n )}\n\n {notice && !error && (\n <p className=\"text-xs text-neutral-500 dark:text-neutral-400\">{notice}</p>\n )}\n\n {file && !loading && !result && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n className=\"inline-flex items-center justify-center rounded-lg bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 dark:bg-white dark:text-neutral-900 dark:hover:bg-neutral-100\"\n >\n Analyze image\n </button>\n )}\n\n {loading && (\n <div className=\"inline-flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400\">\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden />\n Analyzing…\n </div>\n )}\n\n {result !== null && (\n <div className=\"rounded-lg border border-neutral-200 bg-neutral-50 p-3 text-sm dark:border-neutral-800 dark:bg-neutral-900\">\n <p className=\"mb-1 text-xs font-medium uppercase text-neutral-500\">Result</p>\n <pre className=\"overflow-auto whitespace-pre-wrap break-words text-xs\">\n {typeof result === 'string' ? result : JSON.stringify(result, null, 2)}\n </pre>\n </div>\n )}\n </div>\n );\n}\n","/**\n * Client-side image downscaling.\n *\n * Iteratively reduces JPEG/PNG/WEBP dimensions until the image fits under\n * `maxBytes`. Uses <canvas>, so it must run in the browser. GIF, HEIC, and\n * HEIF are not downscaled — the caller should reject those with a format error.\n */\n\nconst DOWNSCALABLE_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']);\n\nexport function canDownscale(mimeType: string): boolean {\n return DOWNSCALABLE_TYPES.has(mimeType.toLowerCase());\n}\n\nexport interface DownscaleResult {\n file: File;\n originalBytes: number;\n finalBytes: number;\n steps: number;\n}\n\nexport async function downscaleImage(\n file: File,\n maxBytes: number,\n options: { minDimension?: number; qualityFloor?: number } = {}\n): Promise<DownscaleResult> {\n const originalBytes = file.size;\n if (originalBytes <= maxBytes) {\n return { file, originalBytes, finalBytes: originalBytes, steps: 0 };\n }\n if (!canDownscale(file.type)) {\n throw new Error(`Cannot auto-compress ${file.type}. Please upload a smaller image.`);\n }\n\n const bitmap = await createImageBitmap(file);\n let { width, height } = bitmap;\n const minDim = options.minDimension ?? 320;\n let quality = 0.9;\n const qFloor = options.qualityFloor ?? 0.5;\n // Re-encode to JPEG unless PNG has transparency we should preserve. Simpler: always JPEG for compression.\n const outputType = 'image/jpeg';\n\n let steps = 0;\n let bytes = originalBytes;\n let outFile = file;\n\n while (bytes > maxBytes && steps < 8) {\n steps += 1;\n // Prefer reducing dimensions first (bigger win), then quality\n if (Math.min(width, height) > minDim && steps <= 4) {\n width = Math.floor(width * 0.75);\n height = Math.floor(height * 0.75);\n } else if (quality > qFloor) {\n quality = Math.max(qFloor, quality - 0.15);\n } else {\n break;\n }\n\n const canvas = typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(width, height)\n : Object.assign(document.createElement('canvas'), { width, height });\n\n const ctx = (canvas as HTMLCanvasElement | OffscreenCanvas).getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable');\n (ctx as CanvasRenderingContext2D).drawImage(bitmap, 0, 0, width, height);\n\n const blob = canvas instanceof OffscreenCanvas\n ? await canvas.convertToBlob({ type: outputType, quality })\n : await new Promise<Blob>((resolve, reject) => {\n (canvas as HTMLCanvasElement).toBlob(\n (b) => b ? resolve(b) : reject(new Error('toBlob returned null')),\n outputType,\n quality,\n );\n });\n\n bytes = blob.size;\n outFile = new File([blob], renameToJpeg(file.name), { type: outputType, lastModified: Date.now() });\n }\n\n bitmap.close?.();\n\n if (bytes > maxBytes) {\n throw new Error(`Could not compress image below the ${(maxBytes / (1024 * 1024)).toFixed(1)}MB limit.`);\n }\n\n return { file: outFile, originalBytes, finalBytes: bytes, steps };\n}\n\nfunction renameToJpeg(name: string): string {\n const dot = name.lastIndexOf('.');\n if (dot === -1) return `${name}.jpg`;\n return `${name.slice(0, dot)}.jpg`;\n}\n"],"mappings":"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","names":["import_lucide_react","import_jsx_runtime","UNIVERSAL_FORMATS","joinClasses","result"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts","../../src/react/chat/chat.tsx","../../src/react/memory/use-memory.ts","../../src/react/vision/vision-format-banner.tsx","../../src/react/vision/vision-uploader.tsx","../../src/react/vision/downscale.ts","../../src/react/voice/voice-recorder.tsx","../../src/react/voice/use-voice-recorder.ts","../../src/react/voice/speak-button.tsx"],"sourcesContent":["/**\n * cencori/react — importable UI components for AI apps\n *\n * @example Memory-aware chat in two lines\n * import { Chat } from 'cencori/react';\n *\n * <Chat\n * model=\"gpt-4o\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}\n * memory={{ userId: session.user.id }}\n * />\n *\n * @example Vision uploader\n * import { VisionUploader } from 'cencori/react';\n *\n * <VisionUploader endpoint=\"https://api.cencori.com/api/ai/vision\" task=\"describe\" />\n */\n\n// Memory-aware chat + memory hook\nexport { Chat } from './chat/chat';\nexport type { ChatProps, ChatMessage, ChatMemoryProp } from './chat/chat';\n\nexport { useMemory } from './memory/use-memory';\nexport type {\n UseMemoryOptions,\n UseMemoryResult,\n UserMemory,\n MemoryScope,\n} from './memory/use-memory';\n\n// Vision\nexport { VisionFormatBanner } from './vision/vision-format-banner';\nexport type { VisionFormatBannerProps, VisionProvider } from './vision/vision-format-banner';\n\nexport { VisionUploader } from './vision/vision-uploader';\nexport type { VisionUploaderProps, VisionTask } from './vision/vision-uploader';\n\n// Voice — record + transcribe, and text-to-speech playback\nexport { VoiceRecorder } from './voice/voice-recorder';\nexport type { VoiceRecorderProps, VoiceTranscript } from './voice/voice-recorder';\n\nexport { SpeakButton } from './voice/speak-button';\nexport type { SpeakButtonProps } from './voice/speak-button';\n\nexport { useVoiceRecorder } from './voice/use-voice-recorder';\nexport type { UseVoiceRecorderResult, RecorderState } from './voice/use-voice-recorder';\n","/**\n * Chat\n *\n * Drop-in chat UI wired to the Cencori gateway. Pass `memory={{ userId }}`\n * and the conversation becomes stateful across sessions — the gateway\n * retrieves what it knows about the user before each reply and persists new\n * facts after. This is the two-line integration from the memory roadmap:\n *\n * <Chat\n * model=\"gpt-4o\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY!}\n * memory={{ userId: session.user.id }}\n * />\n *\n * Streams by default (SSE). Zero external state — safe to embed in a\n * customer's own product.\n *\n * Deps: react, lucide-react, tailwindcss.\n */\n\n'use client';\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type FormEvent,\n type ReactNode,\n} from 'react';\nimport { Brain, Loader2, Send } from 'lucide-react';\n\n// ── Public types ────────────────────────────────────────────────\n\n/** Mirrors `ChatMemoryOptions` from the core SDK. */\nexport interface ChatMemoryProp {\n userId?: string;\n sessionId?: string;\n scope?: 'session' | 'user';\n retrieve?: boolean;\n write?: boolean;\n topK?: number;\n threshold?: number;\n namespace?: string;\n extract?: {\n model?: string;\n prompt?: string;\n minImportance?: number;\n };\n}\n\nexport interface ChatMessage {\n role: 'system' | 'user' | 'assistant';\n content: string;\n}\n\nexport interface ChatProps {\n /** Model to route to (e.g. \"gpt-4o\", \"claude-sonnet-4-5\"). */\n model: string;\n /** Cencori API key. Sent as `CENCORI_API_KEY`. Use a publishable/test key on the client. */\n apiKey?: string;\n /**\n * Turn on gateway memory. `{ userId }` is enough — retrieval + writeback\n * default to on. Omit to run a stateless chat.\n */\n memory?: ChatMemoryProp;\n /** System prompt prepended to every request. */\n system?: string;\n /** Seed the transcript (not sent as system — shown as prior turns). */\n initialMessages?: ChatMessage[];\n /** Sampling temperature. */\n temperature?: number;\n /** Max tokens for each reply. */\n maxTokens?: number;\n /** Stream tokens as they arrive. Default: true. */\n stream?: boolean;\n /**\n * Base URL of the Cencori gateway. Defaults to `https://cencori.com`.\n * Override to point at a self-hosted gateway or a same-origin proxy.\n */\n baseUrl?: string;\n /** Placeholder for the input. */\n placeholder?: string;\n /** Called after each reply with how many memories the gateway injected. */\n onMemoryRetrieved?: (count: number) => void;\n /** Called on any request error (before the inline banner renders it). */\n onError?: (error: Error) => void;\n /** Custom empty-state node shown before the first message. */\n emptyState?: ReactNode;\n /** Extra classes on the root element. */\n className?: string;\n /** Hide the \"recalled N memories\" indicator. Default: shown when memory is on. */\n hideMemoryIndicator?: boolean;\n}\n\n// ── Component ───────────────────────────────────────────────────\n\nconst DEFAULT_BASE_URL = 'https://cencori.com';\n\nexport function Chat({\n model,\n apiKey,\n memory,\n system,\n initialMessages,\n temperature,\n maxTokens,\n stream = true,\n baseUrl = DEFAULT_BASE_URL,\n placeholder = 'Send a message…',\n onMemoryRetrieved,\n onError,\n emptyState,\n className,\n hideMemoryIndicator = false,\n}: ChatProps) {\n const [messages, setMessages] = useState<ChatMessage[]>(initialMessages ?? []);\n const [input, setInput] = useState('');\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [recalled, setRecalled] = useState<number | null>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n\n const endpoint = useMemo(\n () => `${baseUrl.replace(/\\/$/, '')}/api/v1/chat/completions`,\n [baseUrl]\n );\n\n useEffect(() => {\n scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight, behavior: 'smooth' });\n }, [messages, busy]);\n\n const send = useCallback(\n async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed || busy) return;\n\n setError(null);\n setBusy(true);\n\n const userMsg: ChatMessage = { role: 'user', content: trimmed };\n const history = [...messages, userMsg];\n setMessages(history);\n setInput('');\n\n const outbound: ChatMessage[] = system\n ? [{ role: 'system', content: system }, ...history]\n : history;\n\n const body = {\n model,\n messages: outbound,\n memory,\n temperature,\n max_tokens: maxTokens,\n stream,\n };\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (apiKey) headers['CENCORI_API_KEY'] = apiKey;\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!res.ok || (stream && !res.body)) {\n const data = (await res.json().catch(() => ({}))) as {\n error?: { message?: string } | string;\n };\n const msg =\n typeof data.error === 'object' ? data.error?.message : data.error;\n throw new Error(msg || `Cencori API error (${res.status})`);\n }\n\n if (memory && !hideMemoryIndicator) {\n const count = Number(res.headers.get('X-Cencori-Memory-Retrieved') || 0);\n setRecalled(count);\n onMemoryRetrieved?.(count);\n }\n\n if (stream && res.body) {\n // Placeholder assistant message we append tokens into.\n setMessages((prev) => [...prev, { role: 'assistant', content: '' }]);\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const t = line.trim();\n if (!t.startsWith('data: ')) continue;\n const payload = t.slice(6);\n if (payload === '[DONE]') break;\n try {\n const parsed = JSON.parse(payload) as {\n choices?: Array<{ delta?: { content?: string } }>;\n };\n const delta = parsed.choices?.[0]?.delta?.content ?? '';\n if (!delta) continue;\n setMessages((prev) => {\n const next = [...prev];\n const last = next[next.length - 1];\n if (last?.role === 'assistant') {\n next[next.length - 1] = {\n ...last,\n content: last.content + delta,\n };\n }\n return next;\n });\n } catch {\n // skip malformed chunk\n }\n }\n }\n } else {\n const data = (await res.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n const reply = data.choices?.[0]?.message?.content ?? '';\n setMessages((prev) => [...prev, { role: 'assistant', content: reply }]);\n }\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e.message);\n onError?.(e);\n // Roll the optimistic user turn back so they can retry.\n setMessages((prev) =>\n prev.filter((m, i) => !(i === prev.length - 1 && m.role === 'user' && m.content === trimmed))\n );\n setInput(trimmed);\n } finally {\n setBusy(false);\n }\n },\n [\n apiKey, busy, endpoint, hideMemoryIndicator, maxTokens, memory, messages,\n model, onError, onMemoryRetrieved, stream, system, temperature,\n ]\n );\n\n const onSubmit = (e: FormEvent) => {\n e.preventDefault();\n void send(input);\n };\n\n const visible = messages.filter((m) => m.role !== 'system');\n\n return (\n <div\n className={`flex flex-col h-full min-h-[24rem] rounded-xl border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-950 ${className ?? ''}`}\n >\n <div ref={scrollRef} className=\"flex-1 overflow-y-auto p-4 space-y-3\">\n {visible.length === 0 && (\n <div className=\"h-full flex items-center justify-center text-sm text-neutral-400\">\n {emptyState ?? 'Start the conversation.'}\n </div>\n )}\n {visible.map((m, i) => (\n <div\n key={i}\n className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}\n >\n <div\n className={`max-w-[85%] whitespace-pre-wrap rounded-2xl px-3.5 py-2 text-sm ${\n m.role === 'user'\n ? 'bg-neutral-900 text-white dark:bg-white dark:text-neutral-900'\n : 'bg-neutral-100 text-neutral-900 dark:bg-neutral-900 dark:text-neutral-100'\n }`}\n >\n {m.content || (busy && i === visible.length - 1 ? '…' : '')}\n </div>\n </div>\n ))}\n </div>\n\n {memory && !hideMemoryIndicator && recalled !== null && (\n <div className=\"flex items-center gap-1.5 px-4 py-1.5 text-xs text-neutral-500 border-t border-neutral-100 dark:border-neutral-900\">\n <Brain className=\"h-3.5 w-3.5\" />\n {recalled > 0\n ? `Recalled ${recalled} ${recalled === 1 ? 'memory' : 'memories'} about this user`\n : 'No prior memories yet — learning'}\n </div>\n )}\n\n {error && (\n <div className=\"px-4 py-2 text-xs text-red-600 dark:text-red-400\">{error}</div>\n )}\n\n <form\n onSubmit={onSubmit}\n className=\"flex items-center gap-2 border-t border-neutral-200 p-3 dark:border-neutral-800\"\n >\n <input\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder={placeholder}\n disabled={busy}\n className=\"flex-1 rounded-lg bg-neutral-100 px-3 py-2 text-sm outline-none placeholder:text-neutral-400 disabled:opacity-60 dark:bg-neutral-900\"\n />\n <button\n type=\"submit\"\n disabled={busy || !input.trim()}\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-lg bg-neutral-900 text-white transition disabled:opacity-40 dark:bg-white dark:text-neutral-900\"\n aria-label=\"Send\"\n >\n {busy ? <Loader2 className=\"h-4 w-4 animate-spin\" /> : <Send className=\"h-4 w-4\" />}\n </button>\n </form>\n </div>\n );\n}\n","/**\n * useMemory\n *\n * Client hook over the scoped memory API (`/v1/memory/*`) for building custom\n * \"what we remember about you\" UI — GDPR panels, preference editors, profile\n * pages. Mirrors the roadmap sketch:\n *\n * const { memories, write, forget, search, exportAll } = useMemory({ userId });\n *\n * Reads on mount and after every mutation. Zero external state.\n *\n * Deps: react.\n */\n\n'use client';\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\n// ── Public types ────────────────────────────────────────────────\n\nexport type MemoryScope = 'session' | 'user';\n\nexport interface UseMemoryOptions {\n /** End-user id — scope defaults to `user`. */\n userId?: string;\n /** Session id — pass with `scope: 'session'`. */\n sessionId?: string;\n /** Which scope to read/write. Default: `user`. */\n scope?: MemoryScope;\n /** Optional sub-scope partition (e.g. per-project). */\n namespace?: string;\n /** Cencori API key. Sent as `CENCORI_API_KEY`. */\n apiKey?: string;\n /** Gateway base URL. Default: `https://cencori.com`. */\n baseUrl?: string;\n /** How many memories to list. Default: 50. */\n limit?: number;\n /** Skip the initial list on mount. Default: false. */\n manual?: boolean;\n}\n\nexport interface UserMemory {\n id: string;\n content: string;\n namespace?: string | null;\n importance: number;\n createdAt: string | null;\n}\n\nexport interface UseMemoryResult {\n /** The user's memories, most recent first. */\n memories: UserMemory[];\n /** True during the initial load or a refresh. */\n loading: boolean;\n /** Last error message, if any. */\n error: string | null;\n /** Re-fetch the list. */\n refresh: () => Promise<void>;\n /** Write a new memory for this user. */\n write: (content: string, opts?: { importance?: number; metadata?: Record<string, unknown> }) => Promise<void>;\n /** Semantic search over this user's memories (does not mutate the list). */\n search: (query: string, topK?: number) => Promise<UserMemory[]>;\n /** Hard-delete a memory by id. */\n forget: (id: string) => Promise<void>;\n /** Download every memory as a JSON file (client-side GDPR export). */\n exportAll: () => Promise<void>;\n}\n\nconst DEFAULT_BASE_URL = 'https://cencori.com';\n\nexport function useMemory(options: UseMemoryOptions): UseMemoryResult {\n const {\n userId,\n sessionId,\n scope = 'user',\n namespace,\n apiKey,\n baseUrl = DEFAULT_BASE_URL,\n limit = 50,\n manual = false,\n } = options;\n\n const [memories, setMemories] = useState<UserMemory[]>([]);\n const [loading, setLoading] = useState(!manual);\n const [error, setError] = useState<string | null>(null);\n\n const base = useMemo(() => baseUrl.replace(/\\/$/, ''), [baseUrl]);\n\n const headers = useMemo(() => {\n const h: Record<string, string> = { 'Content-Type': 'application/json' };\n if (apiKey) h['CENCORI_API_KEY'] = apiKey;\n return h;\n }, [apiKey]);\n\n const scopeBody = useCallback(\n () => ({ userId, sessionId, scope, namespace }),\n [userId, sessionId, scope, namespace]\n );\n\n const request = useCallback(\n async <T,>(path: string, init: RequestInit): Promise<T> => {\n const res = await fetch(`${base}${path}`, { ...init, headers });\n if (!res.ok) {\n const data = (await res.json().catch(() => ({}))) as { message?: string; error?: { message?: string } };\n throw new Error(data.error?.message || data.message || `Request failed (${res.status})`);\n }\n return res.json() as Promise<T>;\n },\n [base, headers]\n );\n\n const refresh = useCallback(async () => {\n if (!userId && !sessionId) return;\n setLoading(true);\n setError(null);\n try {\n const params = new URLSearchParams();\n if (userId) params.set('userId', userId);\n if (sessionId) params.set('sessionId', sessionId);\n params.set('scope', scope);\n if (namespace) params.set('namespace', namespace);\n params.set('limit', String(limit));\n const data = await request<{ memories: UserMemory[] }>(\n `/v1/memory/list?${params.toString()}`,\n { method: 'GET' }\n );\n setMemories(data.memories ?? []);\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n } finally {\n setLoading(false);\n }\n }, [userId, sessionId, scope, namespace, limit, request]);\n\n useEffect(() => {\n if (!manual) void refresh();\n }, [manual, refresh]);\n\n const write = useCallback<UseMemoryResult['write']>(\n async (content, opts) => {\n setError(null);\n try {\n await request('/v1/memory/write', {\n method: 'POST',\n body: JSON.stringify({ ...scopeBody(), content, ...opts }),\n });\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n throw err;\n }\n },\n [request, scopeBody, refresh]\n );\n\n const search = useCallback<UseMemoryResult['search']>(\n async (query, topK = 5) => {\n const data = await request<{ results: UserMemory[] }>('/v1/memory/search', {\n method: 'POST',\n body: JSON.stringify({ ...scopeBody(), query, topK }),\n });\n return data.results ?? [];\n },\n [request, scopeBody]\n );\n\n const forget = useCallback<UseMemoryResult['forget']>(\n async (id) => {\n setError(null);\n // Optimistic removal, rolled back on failure via refresh.\n setMemories((prev) => prev.filter((m) => m.id !== id));\n try {\n await request(`/v1/memory/${id}`, { method: 'DELETE' });\n } catch (err) {\n setError(err instanceof Error ? err.message : String(err));\n await refresh();\n throw err;\n }\n },\n [request, refresh]\n );\n\n const exportAll = useCallback<UseMemoryResult['exportAll']>(async () => {\n // No server export endpoint in Phase 1 — assemble from the list client-side.\n const params = new URLSearchParams();\n if (userId) params.set('userId', userId);\n if (sessionId) params.set('sessionId', sessionId);\n params.set('scope', scope);\n if (namespace) params.set('namespace', namespace);\n params.set('limit', '1000');\n const data = await request<{ memories: UserMemory[] }>(\n `/v1/memory/list?${params.toString()}`,\n { method: 'GET' }\n );\n const blob = new Blob([JSON.stringify(data.memories ?? [], null, 2)], {\n type: 'application/json',\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `cencori-memory-${userId ?? sessionId ?? 'export'}.json`;\n a.click();\n URL.revokeObjectURL(url);\n }, [request, userId, sessionId, scope, namespace]);\n\n return { memories, loading, error, refresh, write, search, forget, exportAll };\n}\n","/**\n * VisionFormatBanner\n *\n * Drop-in banner that documents the supported image formats and size limits\n * for the Cencori Vision API. Zero external state — safe to embed anywhere,\n * including inside a customer's own product.\n *\n * Deps: react, lucide-react, tailwindcss.\n */\n\n'use client';\n\nimport { ImageIcon, Info } from 'lucide-react';\nimport type { ReactNode } from 'react';\n\nexport type VisionProvider = 'openai' | 'anthropic' | 'google';\n\nexport interface VisionFormatBannerProps {\n /** Restrict displayed limits to a specific provider. Omit to show universal (all-provider) limits. */\n provider?: VisionProvider;\n /** Tone. */\n variant?: 'info' | 'muted';\n /** Extra classes. */\n className?: string;\n /** Optional title override. */\n title?: ReactNode;\n}\n\nconst UNIVERSAL_FORMATS = ['JPEG', 'PNG', 'WEBP', 'GIF'];\n\nconst PROVIDER_LIMITS: Record<VisionProvider, { formats: string[]; maxMB: number; notes: string }> = {\n openai: { formats: ['JPEG', 'PNG', 'WEBP', 'GIF'], maxMB: 20, notes: 'Non-animated GIFs only.' },\n anthropic: { formats: ['JPEG', 'PNG', 'WEBP', 'GIF'], maxMB: 5, notes: 'Max 8000×8000 dimensions.' },\n google: { formats: ['JPEG', 'PNG', 'WEBP', 'GIF', 'HEIC', 'HEIF'], maxMB: 20, notes: 'HEIC/HEIF supported.' },\n};\n\nfunction joinClasses(...parts: Array<string | undefined | false>): string {\n return parts.filter(Boolean).join(' ');\n}\n\nexport function VisionFormatBanner({\n provider,\n variant = 'info',\n className,\n title,\n}: VisionFormatBannerProps) {\n const info = provider ? PROVIDER_LIMITS[provider] : { formats: UNIVERSAL_FORMATS, maxMB: 5, notes: 'These formats work across every supported provider.' };\n const heading = title ?? (provider ? `Supported for ${providerLabel(provider)}` : 'Supported image formats');\n\n const base = variant === 'info'\n ? 'bg-blue-50 border-blue-200 text-blue-900 dark:bg-blue-950/40 dark:border-blue-900 dark:text-blue-100'\n : 'bg-neutral-50 border-neutral-200 text-neutral-800 dark:bg-neutral-900 dark:border-neutral-800 dark:text-neutral-100';\n\n return (\n <div\n role=\"note\"\n className={joinClasses(\n 'flex gap-3 rounded-lg border p-4 text-sm',\n base,\n className\n )}\n >\n <div className=\"flex-shrink-0 pt-0.5\">\n <Info className=\"h-5 w-5 opacity-70\" aria-hidden />\n </div>\n <div className=\"flex flex-col gap-2\">\n <p className=\"font-medium\">{heading}</p>\n <div className=\"flex flex-wrap gap-1.5\">\n {info.formats.map(f => (\n <span\n key={f}\n className=\"inline-flex items-center gap-1 rounded-md border border-current/20 bg-white/50 px-2 py-0.5 text-xs font-medium dark:bg-black/20\"\n >\n <ImageIcon className=\"h-3 w-3 opacity-70\" aria-hidden />\n {f}\n </span>\n ))}\n </div>\n <p className=\"text-xs opacity-80\">\n Up to <strong>{info.maxMB}MB</strong> per image. {info.notes}\n </p>\n </div>\n </div>\n );\n}\n\nfunction providerLabel(p: VisionProvider): string {\n if (p === 'openai') return 'OpenAI';\n if (p === 'anthropic') return 'Anthropic';\n return 'Google';\n}\n","/**\n * VisionUploader\n *\n * Drop-in image uploader for the Cencori Vision API. Handles drag-drop,\n * client-side format + size validation, and calls the endpoint via\n * multipart/form-data. Renders clear error messages and a supported-formats\n * banner. Zero external state — safe to embed in a customer's own product.\n *\n * Usage:\n * <VisionUploader\n * endpoint=\"https://api.cencori.com/api/ai/vision\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}\n * task=\"describe\"\n * onResult={(result) => console.log(result)}\n * />\n *\n * Deps: react, lucide-react, tailwindcss.\n */\n\n'use client';\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport { AlertCircle, Loader2, Upload, X } from 'lucide-react';\nimport { VisionFormatBanner, type VisionProvider } from './vision-format-banner';\nimport { canDownscale, downscaleImage } from './downscale';\n\n// ── Public types ────────────────────────────────────────────────\n\nexport type VisionTask = 'analyze' | 'describe' | 'ocr' | 'classify';\n\nexport interface VisionUploaderProps {\n /** Base Vision endpoint. Defaults to same-origin `/api/ai/vision`. */\n endpoint?: string;\n /** Cencori API key. Sent as `Authorization: Bearer`. */\n apiKey?: string;\n /** Which task to run: analyze (default), describe, ocr, or classify. */\n task?: VisionTask;\n /** Provider to target — informs client-side validation. Defaults to universal (all providers). */\n provider?: VisionProvider;\n /** Optional model override. */\n model?: string;\n /** Custom prompt (only meaningful for task=\"analyze\"). */\n prompt?: string;\n /** Called on success. */\n onResult?: (result: unknown) => void;\n /** Called on error (before the built-in banner renders it). */\n onError?: (error: { code: string; message: string }) => void;\n /** Hide the supported-formats banner. Default: shown. */\n hideBanner?: boolean;\n /**\n * Auto-compress images that exceed the provider's size limit. JPEG/PNG/WEBP only.\n * Default: true.\n */\n autoCompress?: boolean;\n /** Additional request headers to forward on upload (in addition to Authorization). */\n headers?: Record<string, string>;\n /** Extra classes. */\n className?: string;\n}\n\n// ── Client-side validation rules (mirror the server) ────────────\n\nconst UNIVERSAL_FORMATS = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];\n\nconst PROVIDER_RULES: Record<VisionProvider, { formats: string[]; maxBytes: number }> = {\n openai: { formats: UNIVERSAL_FORMATS, maxBytes: 20 * 1024 * 1024 },\n anthropic: { formats: UNIVERSAL_FORMATS, maxBytes: 5 * 1024 * 1024 },\n google: { formats: [...UNIVERSAL_FORMATS, 'image/heic', 'image/heif'], maxBytes: 20 * 1024 * 1024 },\n};\n\nfunction rulesFor(provider?: VisionProvider) {\n if (provider) return PROVIDER_RULES[provider];\n return { formats: UNIVERSAL_FORMATS, maxBytes: 5 * 1024 * 1024 };\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n return `${(bytes / 1024).toFixed(0)}KB`;\n}\n\nfunction friendlyFormats(mimes: string[]): string {\n return mimes.map(m => m.replace('image/', '').toUpperCase()).join(', ');\n}\n\nfunction joinClasses(...parts: Array<string | undefined | false>): string {\n return parts.filter(Boolean).join(' ');\n}\n\n// ── Component ───────────────────────────────────────────────────\n\nexport function VisionUploader({\n endpoint = '/api/ai/vision',\n apiKey,\n task = 'analyze',\n provider,\n model,\n prompt,\n onResult,\n onError,\n hideBanner = false,\n autoCompress = true,\n headers,\n className,\n}: VisionUploaderProps) {\n const [file, setFile] = useState<File | null>(null);\n const [preview, setPreview] = useState<string | null>(null);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n const [loading, setLoading] = useState(false);\n const [result, setResult] = useState<unknown>(null);\n const [dragging, setDragging] = useState(false);\n const [notice, setNotice] = useState<string | null>(null);\n const inputRef = useRef<HTMLInputElement | null>(null);\n\n const rules = useMemo(() => rulesFor(provider), [provider]);\n const targetUrl = task === 'analyze' ? endpoint : `${endpoint.replace(/\\/$/, '')}/${task}`;\n\n const acceptFile = useCallback(async (f: File) => {\n setResult(null);\n setNotice(null);\n let effective = f;\n\n // Wrong format: fail fast, no attempt at compression\n const mime = (f.type || '').toLowerCase();\n if (!rules.formats.includes(mime)) {\n const err = {\n code: 'unsupported_format',\n message: `\"${f.name}\" is ${mime || 'an unknown type'}. Please upload one of: ${friendlyFormats(rules.formats)}.`,\n };\n setError(err);\n setFile(null);\n setPreview(null);\n onError?.(err);\n return;\n }\n\n // Too big: try auto-compression if allowed and format is downscalable\n if (f.size > rules.maxBytes) {\n if (autoCompress && canDownscale(mime)) {\n try {\n const result = await downscaleImage(f, rules.maxBytes);\n effective = result.file;\n setNotice(\n `Compressed from ${formatBytes(result.originalBytes)} to ${formatBytes(result.finalBytes)} to fit the ${provider ?? 'universal'} limit.`\n );\n } catch (compressErr) {\n const err = {\n code: 'image_too_large',\n message: compressErr instanceof Error\n ? compressErr.message\n : `\"${f.name}\" is ${formatBytes(f.size)} — the limit is ${formatBytes(rules.maxBytes)}${provider ? ` for ${provider}` : ''}.`,\n };\n setError(err);\n setFile(null);\n setPreview(null);\n onError?.(err);\n return;\n }\n } else {\n const err = {\n code: 'image_too_large',\n message: `\"${f.name}\" is ${formatBytes(f.size)} — the limit is ${formatBytes(rules.maxBytes)}${provider ? ` for ${provider}` : ''}.`,\n };\n setError(err);\n setFile(null);\n setPreview(null);\n onError?.(err);\n return;\n }\n }\n\n setError(null);\n setFile(effective);\n setPreview(URL.createObjectURL(effective));\n }, [rules, provider, autoCompress, onError]);\n\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n setDragging(false);\n const f = e.dataTransfer.files?.[0];\n if (f) acceptFile(f);\n }, [acceptFile]);\n\n const handleSubmit = useCallback(async () => {\n if (!file) return;\n setLoading(true);\n setError(null);\n setResult(null);\n try {\n const form = new FormData();\n form.append('file', file);\n if (prompt && task === 'analyze') form.append('prompt', prompt);\n if (model) form.append('model', model);\n\n const requestHeaders: Record<string, string> = { ...(headers ?? {}) };\n if (apiKey) requestHeaders.Authorization = `Bearer ${apiKey}`;\n\n const res = await fetch(targetUrl, {\n method: 'POST',\n headers: Object.keys(requestHeaders).length ? requestHeaders : undefined,\n body: form,\n });\n const data = await res.json();\n if (!res.ok) {\n const err = {\n code: (data && typeof data === 'object' && 'error' in data ? String(data.error) : 'request_failed'),\n message: (data && typeof data === 'object' && 'message' in data ? String(data.message) : `Request failed with status ${res.status}`),\n };\n setError(err);\n onError?.(err);\n return;\n }\n setResult(data);\n onResult?.(data);\n } catch (err) {\n const e = { code: 'network_error', message: err instanceof Error ? err.message : 'Network error' };\n setError(e);\n onError?.(e);\n } finally {\n setLoading(false);\n }\n }, [file, prompt, model, task, targetUrl, apiKey, headers, onResult, onError]);\n\n const clear = useCallback(() => {\n setFile(null);\n setPreview(null);\n setResult(null);\n setError(null);\n setNotice(null);\n if (inputRef.current) inputRef.current.value = '';\n }, []);\n\n return (\n <div className={joinClasses('flex flex-col gap-3', className)}>\n {!hideBanner && <VisionFormatBanner provider={provider} />}\n\n <div\n onDragOver={e => { e.preventDefault(); setDragging(true); }}\n onDragLeave={() => setDragging(false)}\n onDrop={handleDrop}\n onClick={() => inputRef.current?.click()}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Upload image\"\n className={joinClasses(\n 'flex cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed p-8 text-center transition',\n dragging\n ? 'border-blue-500 bg-blue-50 dark:bg-blue-950/30'\n : 'border-neutral-300 hover:border-neutral-400 dark:border-neutral-700 dark:hover:border-neutral-600',\n )}\n >\n {preview ? (\n <div className=\"relative\">\n <img src={preview} alt=\"Preview\" className=\"max-h-64 rounded-lg object-contain\" />\n <button\n type=\"button\"\n onClick={e => { e.stopPropagation(); clear(); }}\n className=\"absolute -right-2 -top-2 rounded-full bg-white p-1 shadow ring-1 ring-neutral-200 hover:bg-neutral-100 dark:bg-neutral-900 dark:ring-neutral-700\"\n aria-label=\"Remove image\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <>\n <Upload className=\"h-8 w-8 text-neutral-400\" aria-hidden />\n <p className=\"text-sm font-medium\">Drop an image or click to upload</p>\n <p className=\"text-xs text-neutral-500\">{friendlyFormats(rules.formats)} · up to {formatBytes(rules.maxBytes)}</p>\n </>\n )}\n <input\n ref={inputRef}\n type=\"file\"\n accept={rules.formats.join(',')}\n className=\"hidden\"\n onChange={e => {\n const f = e.target.files?.[0];\n if (f) acceptFile(f);\n }}\n />\n </div>\n\n {error && (\n <div role=\"alert\" className=\"flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-900 dark:border-red-900 dark:bg-red-950/40 dark:text-red-100\">\n <AlertCircle className=\"mt-0.5 h-4 w-4 flex-shrink-0\" aria-hidden />\n <div>\n <p className=\"font-medium\">Can't process this image</p>\n <p className=\"text-xs opacity-90\">{error.message}</p>\n </div>\n </div>\n )}\n\n {notice && !error && (\n <p className=\"text-xs text-neutral-500 dark:text-neutral-400\">{notice}</p>\n )}\n\n {file && !loading && !result && (\n <button\n type=\"button\"\n onClick={handleSubmit}\n className=\"inline-flex items-center justify-center rounded-lg bg-neutral-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-neutral-800 dark:bg-white dark:text-neutral-900 dark:hover:bg-neutral-100\"\n >\n Analyze image\n </button>\n )}\n\n {loading && (\n <div className=\"inline-flex items-center gap-2 text-sm text-neutral-600 dark:text-neutral-400\">\n <Loader2 className=\"h-4 w-4 animate-spin\" aria-hidden />\n Analyzing…\n </div>\n )}\n\n {result !== null && (\n <div className=\"rounded-lg border border-neutral-200 bg-neutral-50 p-3 text-sm dark:border-neutral-800 dark:bg-neutral-900\">\n <p className=\"mb-1 text-xs font-medium uppercase text-neutral-500\">Result</p>\n <pre className=\"overflow-auto whitespace-pre-wrap break-words text-xs\">\n {typeof result === 'string' ? result : JSON.stringify(result, null, 2)}\n </pre>\n </div>\n )}\n </div>\n );\n}\n","/**\n * Client-side image downscaling.\n *\n * Iteratively reduces JPEG/PNG/WEBP dimensions until the image fits under\n * `maxBytes`. Uses <canvas>, so it must run in the browser. GIF, HEIC, and\n * HEIF are not downscaled — the caller should reject those with a format error.\n */\n\nconst DOWNSCALABLE_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']);\n\nexport function canDownscale(mimeType: string): boolean {\n return DOWNSCALABLE_TYPES.has(mimeType.toLowerCase());\n}\n\nexport interface DownscaleResult {\n file: File;\n originalBytes: number;\n finalBytes: number;\n steps: number;\n}\n\nexport async function downscaleImage(\n file: File,\n maxBytes: number,\n options: { minDimension?: number; qualityFloor?: number } = {}\n): Promise<DownscaleResult> {\n const originalBytes = file.size;\n if (originalBytes <= maxBytes) {\n return { file, originalBytes, finalBytes: originalBytes, steps: 0 };\n }\n if (!canDownscale(file.type)) {\n throw new Error(`Cannot auto-compress ${file.type}. Please upload a smaller image.`);\n }\n\n const bitmap = await createImageBitmap(file);\n let { width, height } = bitmap;\n const minDim = options.minDimension ?? 320;\n let quality = 0.9;\n const qFloor = options.qualityFloor ?? 0.5;\n // Re-encode to JPEG unless PNG has transparency we should preserve. Simpler: always JPEG for compression.\n const outputType = 'image/jpeg';\n\n let steps = 0;\n let bytes = originalBytes;\n let outFile = file;\n\n while (bytes > maxBytes && steps < 8) {\n steps += 1;\n // Prefer reducing dimensions first (bigger win), then quality\n if (Math.min(width, height) > minDim && steps <= 4) {\n width = Math.floor(width * 0.75);\n height = Math.floor(height * 0.75);\n } else if (quality > qFloor) {\n quality = Math.max(qFloor, quality - 0.15);\n } else {\n break;\n }\n\n const canvas = typeof OffscreenCanvas !== 'undefined'\n ? new OffscreenCanvas(width, height)\n : Object.assign(document.createElement('canvas'), { width, height });\n\n const ctx = (canvas as HTMLCanvasElement | OffscreenCanvas).getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable');\n (ctx as CanvasRenderingContext2D).drawImage(bitmap, 0, 0, width, height);\n\n const blob = canvas instanceof OffscreenCanvas\n ? await canvas.convertToBlob({ type: outputType, quality })\n : await new Promise<Blob>((resolve, reject) => {\n (canvas as HTMLCanvasElement).toBlob(\n (b) => b ? resolve(b) : reject(new Error('toBlob returned null')),\n outputType,\n quality,\n );\n });\n\n bytes = blob.size;\n outFile = new File([blob], renameToJpeg(file.name), { type: outputType, lastModified: Date.now() });\n }\n\n bitmap.close?.();\n\n if (bytes > maxBytes) {\n throw new Error(`Could not compress image below the ${(maxBytes / (1024 * 1024)).toFixed(1)}MB limit.`);\n }\n\n return { file: outFile, originalBytes, finalBytes: bytes, steps };\n}\n\nfunction renameToJpeg(name: string): string {\n const dot = name.lastIndexOf('.');\n if (dot === -1) return `${name}.jpg`;\n return `${name.slice(0, dot)}.jpg`;\n}\n","/**\n * VoiceRecorder\n *\n * Drop-in microphone recorder + transcriber for the Cencori Voice API. Records\n * from the mic, uploads to the transcriptions endpoint, and returns the text.\n * Zero external state — safe to embed in a customer's own product.\n *\n * Usage:\n * <VoiceRecorder\n * endpoint=\"https://api.cencori.com/api/ai/audio/transcriptions\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}\n * model=\"nova-3\"\n * onTranscript={(text) => console.log(text)}\n * />\n *\n * Deps: react, lucide-react, tailwindcss.\n */\n\n'use client';\n\nimport { useCallback, useState } from 'react';\nimport { AlertCircle, Loader2, Mic, Square } from 'lucide-react';\nimport { useVoiceRecorder } from './use-voice-recorder';\n\nexport interface VoiceTranscript {\n text: string;\n language?: string;\n duration?: number;\n provider?: string;\n model?: string;\n segments?: Array<{ start: number; end: number; text: string; speaker?: string }>;\n}\n\nexport interface VoiceRecorderProps {\n /** Transcriptions endpoint. Defaults to same-origin `/api/ai/audio/transcriptions`. */\n endpoint?: string;\n /** Cencori API key. Sent as `Authorization: Bearer`. */\n apiKey?: string;\n /** Transcription model — provider is inferred from it. Default: `whisper-1`. */\n model?: string;\n /** Language hint (e.g. `en`, `yo`, `ha`, `ig`). */\n language?: string;\n /** Request speaker labels (use a diarization-capable model). */\n diarize?: boolean;\n /** Called with the final transcript text. */\n onTranscript?: (text: string, full: VoiceTranscript) => void;\n /** Called on error (before the built-in banner renders it). */\n onError?: (error: { code: string; message: string }) => void;\n /** Additional request headers to forward (in addition to Authorization). */\n headers?: Record<string, string>;\n /** Extra classes on the root element. */\n className?: string;\n}\n\nfunction joinClasses(...parts: Array<string | undefined | false>): string {\n return parts.filter(Boolean).join(' ');\n}\n\nfunction formatClock(total: number): string {\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n\nexport function VoiceRecorder({\n endpoint = '/api/ai/audio/transcriptions',\n apiKey,\n model = 'whisper-1',\n language,\n diarize = false,\n onTranscript,\n onError,\n headers,\n className,\n}: VoiceRecorderProps) {\n const rec = useVoiceRecorder();\n const [uploading, setUploading] = useState(false);\n const [transcript, setTranscript] = useState<VoiceTranscript | null>(null);\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n\n const transcribe = useCallback(async (blob: Blob) => {\n setUploading(true);\n setError(null);\n try {\n const form = new FormData();\n const ext = (blob.type.split('/')[1] || 'webm').split(';')[0];\n form.append('file', blob, `recording.${ext}`);\n form.append('model', model);\n form.append('response_format', diarize ? 'verbose_json' : 'json');\n if (language) form.append('language', language);\n if (diarize) form.append('diarize', 'true');\n\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}), ...headers },\n body: form,\n });\n const data = await res.json().catch(() => ({}));\n if (!res.ok) {\n const err = {\n code: typeof data.error === 'string' ? data.error : 'request_failed',\n message: typeof data.message === 'string' ? data.message : `Transcription failed (${res.status})`,\n };\n setError(err);\n onError?.(err);\n return;\n }\n const full = data as VoiceTranscript;\n setTranscript(full);\n onTranscript?.(full.text ?? '', full);\n } catch (e) {\n const err = { code: 'network_error', message: e instanceof Error ? e.message : 'Network error' };\n setError(err);\n onError?.(err);\n } finally {\n setUploading(false);\n }\n }, [endpoint, apiKey, model, language, diarize, headers, onTranscript, onError]);\n\n const toggle = useCallback(async () => {\n if (rec.state === 'recording') {\n const blob = await rec.stop();\n if (blob) await transcribe(blob);\n } else {\n setTranscript(null);\n await rec.start();\n }\n }, [rec, transcribe]);\n\n const recording = rec.state === 'recording';\n const busy = rec.state === 'requesting' || uploading;\n const activeError = error || (rec.error ? { code: 'mic_error', message: rec.error } : null);\n\n return (\n <div className={joinClasses('cencori-voice-recorder', className)} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>\n <button\n type=\"button\"\n onClick={toggle}\n disabled={busy}\n aria-label={recording ? 'Stop recording' : 'Start recording'}\n className={joinClasses('cencori-voice-btn', recording && 'is-recording')}\n style={{\n display: 'inline-flex', alignItems: 'center', justifyContent: 'center',\n width: 56, height: 56, borderRadius: '9999px', border: 'none', cursor: busy ? 'default' : 'pointer',\n background: recording ? '#dc2626' : '#111827', color: '#fff',\n opacity: busy ? 0.6 : 1, transition: 'background 0.15s',\n }}\n >\n {busy ? <Loader2 size={22} className=\"animate-spin\" /> : recording ? <Square size={20} /> : <Mic size={22} />}\n </button>\n <div style={{ display: 'flex', flexDirection: 'column' }}>\n <span style={{ fontVariantNumeric: 'tabular-nums', fontWeight: 600 }}>\n {recording ? formatClock(rec.seconds) : uploading ? 'Transcribing…' : 'Tap to record'}\n </span>\n {recording && (\n <span style={{ fontSize: 12, color: '#dc2626', display: 'inline-flex', alignItems: 'center', gap: 6 }}>\n <span style={{ width: 8, height: 8, borderRadius: '9999px', background: '#dc2626', display: 'inline-block' }} />\n recording\n </span>\n )}\n </div>\n </div>\n\n {activeError && (\n <div role=\"alert\" style={{ display: 'flex', gap: 8, alignItems: 'flex-start', color: '#b91c1c', fontSize: 13 }}>\n <AlertCircle size={16} style={{ flexShrink: 0, marginTop: 2 }} />\n <span>{activeError.message}</span>\n </div>\n )}\n\n {transcript && (\n <div className=\"cencori-voice-result\" style={{ fontSize: 14, lineHeight: 1.5 }}>\n {transcript.segments && transcript.segments.some((s) => s.speaker)\n ? transcript.segments.map((s, i) => (\n <p key={i} style={{ margin: '4px 0' }}>\n {s.speaker && <strong style={{ marginRight: 6 }}>{s.speaker}:</strong>}\n {s.text}\n </p>\n ))\n : <p style={{ margin: 0 }}>{transcript.text}</p>}\n </div>\n )}\n </div>\n );\n}\n","/**\n * useVoiceRecorder\n *\n * Minimal microphone-recording hook built on the MediaRecorder API. Handles\n * permission, start/stop, elapsed time, and returns the recorded audio as a\n * Blob. No external state — safe to use inside a customer's own components.\n *\n * Usage:\n * const rec = useVoiceRecorder();\n * rec.start(); // prompts for mic permission\n * const blob = await rec.stop(); // resolves with the recorded audio\n */\n\n'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nexport type RecorderState = 'idle' | 'requesting' | 'recording' | 'stopped' | 'error';\n\nexport interface UseVoiceRecorderResult {\n state: RecorderState;\n /** Seconds elapsed in the current/last recording. */\n seconds: number;\n /** MediaRecorder mime type in use once recording starts. */\n mimeType: string | null;\n error: string | null;\n /** The most recent recording, available after stop(). */\n blob: Blob | null;\n start: () => Promise<void>;\n /** Stop and resolve with the recorded Blob (also set on `blob`). */\n stop: () => Promise<Blob | null>;\n /** Discard the current recording and reset to idle. */\n reset: () => void;\n}\n\n/** Pick the first mime type the browser can actually record. */\nfunction pickMimeType(): string | undefined {\n if (typeof MediaRecorder === 'undefined') return undefined;\n const candidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4', 'audio/ogg'];\n return candidates.find((t) => MediaRecorder.isTypeSupported(t));\n}\n\nexport function useVoiceRecorder(): UseVoiceRecorderResult {\n const [state, setState] = useState<RecorderState>('idle');\n const [seconds, setSeconds] = useState(0);\n const [mimeType, setMimeType] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [blob, setBlob] = useState<Blob | null>(null);\n\n const recorderRef = useRef<MediaRecorder | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const chunksRef = useRef<Blob[]>([]);\n const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n const cleanupStream = useCallback(() => {\n if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; }\n streamRef.current?.getTracks().forEach((t) => t.stop());\n streamRef.current = null;\n }, []);\n\n // Stop everything if the component unmounts mid-recording.\n useEffect(() => cleanupStream, [cleanupStream]);\n\n const start = useCallback(async () => {\n setError(null);\n setBlob(null);\n setSeconds(0);\n if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {\n setState('error');\n setError('Microphone recording is not supported in this browser.');\n return;\n }\n try {\n setState('requesting');\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n streamRef.current = stream;\n const type = pickMimeType();\n const recorder = new MediaRecorder(stream, type ? { mimeType: type } : undefined);\n chunksRef.current = [];\n recorder.ondataavailable = (e) => { if (e.data.size > 0) chunksRef.current.push(e.data); };\n recorder.start();\n recorderRef.current = recorder;\n setMimeType(recorder.mimeType || type || 'audio/webm');\n setState('recording');\n timerRef.current = setInterval(() => setSeconds((s) => s + 1), 1000);\n } catch (e) {\n cleanupStream();\n setState('error');\n setError(e instanceof Error ? e.message : 'Could not access the microphone.');\n }\n }, [cleanupStream]);\n\n const stop = useCallback((): Promise<Blob | null> => {\n const recorder = recorderRef.current;\n if (!recorder || recorder.state === 'inactive') {\n return Promise.resolve(null);\n }\n return new Promise((resolve) => {\n recorder.onstop = () => {\n const type = recorder.mimeType || 'audio/webm';\n const recorded = new Blob(chunksRef.current, { type });\n cleanupStream();\n recorderRef.current = null;\n setBlob(recorded);\n setState('stopped');\n resolve(recorded);\n };\n recorder.stop();\n });\n }, [cleanupStream]);\n\n const reset = useCallback(() => {\n cleanupStream();\n recorderRef.current = null;\n chunksRef.current = [];\n setBlob(null);\n setSeconds(0);\n setError(null);\n setState('idle');\n }, [cleanupStream]);\n\n return { state, seconds, mimeType, error, blob, start, stop, reset };\n}\n","/**\n * SpeakButton\n *\n * Drop-in text-to-speech button for the Cencori Voice API. Synthesizes the\n * given text and plays it back, with loading/playing/error states. Zero\n * external state — safe to embed in a customer's own product.\n *\n * Usage:\n * <SpeakButton\n * endpoint=\"https://api.cencori.com/api/ai/audio/speech\"\n * apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}\n * text=\"Hello from Cencori.\"\n * model=\"aura-asteria-en\"\n * />\n *\n * Deps: react, lucide-react.\n */\n\n'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { AlertCircle, Loader2, Pause, Volume2 } from 'lucide-react';\n\nexport interface SpeakButtonProps {\n /** Text to synthesize. */\n text: string;\n /** Speech endpoint. Defaults to same-origin `/api/ai/audio/speech`. */\n endpoint?: string;\n /** Cencori API key. Sent as `Authorization: Bearer`. */\n apiKey?: string;\n /** TTS model — provider is inferred from it. Default: `tts-1`. */\n model?: string;\n /** Voice id/name. Provider-specific. */\n voice?: string;\n /** Language code (Spitch: `en`, `yo`, `ha`, `ig`, `am`). */\n language?: string;\n /** Button label. Default: \"Listen\". Pass null to render icon-only. */\n label?: string | null;\n /** Additional request headers to forward (in addition to Authorization). */\n headers?: Record<string, string>;\n onError?: (error: { code: string; message: string }) => void;\n className?: string;\n}\n\nfunction joinClasses(...parts: Array<string | undefined | false>): string {\n return parts.filter(Boolean).join(' ');\n}\n\nexport function SpeakButton({\n text,\n endpoint = '/api/ai/audio/speech',\n apiKey,\n model = 'tts-1',\n voice,\n language,\n label = 'Listen',\n headers,\n onError,\n className,\n}: SpeakButtonProps) {\n const [state, setState] = useState<'idle' | 'loading' | 'playing'>('idle');\n const [error, setError] = useState<{ code: string; message: string } | null>(null);\n const audioRef = useRef<HTMLAudioElement | null>(null);\n const urlRef = useRef<string | null>(null);\n\n const cleanup = useCallback(() => {\n if (audioRef.current) { audioRef.current.pause(); audioRef.current = null; }\n if (urlRef.current) { URL.revokeObjectURL(urlRef.current); urlRef.current = null; }\n }, []);\n\n // Revoke the object URL / stop audio on unmount.\n useEffect(() => cleanup, [cleanup]);\n\n const play = useCallback(async () => {\n // Toggle off if already playing.\n if (state === 'playing') { cleanup(); setState('idle'); return; }\n if (!text.trim()) return;\n\n setState('loading');\n setError(null);\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),\n ...headers,\n },\n body: JSON.stringify({ input: text, model, voice, language }),\n });\n if (!res.ok) {\n const data = await res.json().catch(() => ({}));\n const err = {\n code: typeof data.error === 'string' ? data.error : 'request_failed',\n message: typeof data.message === 'string' ? data.message : `Speech failed (${res.status})`,\n };\n setError(err);\n onError?.(err);\n setState('idle');\n return;\n }\n const blob = await res.blob();\n cleanup();\n const url = URL.createObjectURL(blob);\n urlRef.current = url;\n const audio = new Audio(url);\n audioRef.current = audio;\n audio.onended = () => { setState('idle'); cleanup(); };\n audio.onerror = () => { setState('idle'); cleanup(); };\n await audio.play();\n setState('playing');\n } catch (e) {\n const err = { code: 'network_error', message: e instanceof Error ? e.message : 'Network error' };\n setError(err);\n onError?.(err);\n setState('idle');\n }\n }, [state, text, endpoint, apiKey, model, voice, language, headers, onError, cleanup]);\n\n return (\n <span style={{ display: 'inline-flex', flexDirection: 'column', gap: 4 }}>\n <button\n type=\"button\"\n onClick={play}\n disabled={state === 'loading' || !text.trim()}\n aria-label={label ?? (state === 'playing' ? 'Pause' : 'Listen')}\n className={joinClasses('cencori-speak-btn', className)}\n style={{\n display: 'inline-flex', alignItems: 'center', gap: 6,\n padding: label ? '6px 12px' : 8, borderRadius: 8, border: '1px solid #e5e7eb',\n background: '#fff', color: '#111827', cursor: state === 'loading' ? 'default' : 'pointer',\n fontSize: 14, fontWeight: 500,\n }}\n >\n {state === 'loading'\n ? <Loader2 size={16} className=\"animate-spin\" />\n : state === 'playing'\n ? <Pause size={16} />\n : <Volume2 size={16} />}\n {label && <span>{label}</span>}\n </button>\n {error && (\n <span role=\"alert\" style={{ display: 'inline-flex', gap: 4, alignItems: 'center', color: '#b91c1c', fontSize: 12 }}>\n <AlertCircle size={13} />{error.message}\n </span>\n )}\n </span>\n );\n}\n"],"mappings":"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","names":["import_react","DEFAULT_BASE_URL","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","UNIVERSAL_FORMATS","joinClasses","result","import_react","import_lucide_react","import_react","import_jsx_runtime","joinClasses","import_react","import_lucide_react","import_jsx_runtime","joinClasses"]}
|