best-email-builder 0.7.0 → 0.9.0

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +127 -735
  3. package/dist/ai-server.js +3 -3
  4. package/dist/ai.js +129 -59
  5. package/dist/ai.js.map +1 -1
  6. package/dist/builder.js +8 -8
  7. package/dist/{chunk-C6WHIBGH.js → chunk-4IXP5YVN.js} +3 -3
  8. package/dist/{chunk-C6WHIBGH.js.map → chunk-4IXP5YVN.js.map} +1 -1
  9. package/dist/chunk-62HBUBBI.js +3866 -0
  10. package/dist/chunk-62HBUBBI.js.map +1 -0
  11. package/dist/{chunk-QH5OI43L.js → chunk-AS2EPMA6.js} +8 -8
  12. package/dist/{chunk-W424KPCR.js → chunk-AXIM6E7T.js} +2 -2
  13. package/dist/{chunk-QRUMXCGW.js → chunk-D4JBPAJ5.js} +3 -3
  14. package/dist/{chunk-3RPWH5PC.js → chunk-FFSSAMHH.js} +4 -4
  15. package/dist/{chunk-4U7L2NYQ.js → chunk-G4U6PSF6.js} +2 -2
  16. package/dist/{chunk-UMVBT7NW.js → chunk-GWXY3MWQ.js} +4 -4
  17. package/dist/{chunk-CNJZLZRH.js → chunk-LUM7J5FF.js} +2 -2
  18. package/dist/{chunk-SAIW46IC.js → chunk-P7ABBLX6.js} +2 -2
  19. package/dist/{chunk-CTZ56YJG.js → chunk-VJKF6SR4.js} +889 -5
  20. package/dist/chunk-VJKF6SR4.js.map +1 -0
  21. package/dist/index.js +10 -10
  22. package/dist/library.js +5 -5
  23. package/dist/server.js +5 -5
  24. package/dist/types/ai/ConnectAi.d.ts +3 -1
  25. package/dist/types/ai/ModelPicker.d.ts +7 -3
  26. package/dist/types/ai/i18n/de.d.ts +2 -0
  27. package/dist/types/ai/i18n/es.d.ts +2 -0
  28. package/dist/types/ai/i18n/pt.d.ts +2 -0
  29. package/dist/types/ai/i18n/ru.d.ts +2 -0
  30. package/dist/types/ai/i18n/zh.d.ts +2 -0
  31. package/dist/types/ai/keys.d.ts +15 -3
  32. package/dist/types/core/constants.d.ts +1 -1
  33. package/dist/types/engine/NativeAdapter.d.ts +1 -1
  34. package/dist/types/i18n/de.d.ts +2 -0
  35. package/dist/types/i18n/es.d.ts +2 -0
  36. package/dist/types/i18n/pt.d.ts +2 -0
  37. package/dist/types/i18n/ru.d.ts +2 -0
  38. package/dist/types/i18n/zh.d.ts +2 -0
  39. package/package.json +2 -2
  40. package/dist/chunk-CTZ56YJG.js.map +0 -1
  41. package/dist/chunk-JI36FNWE.js +0 -1361
  42. package/dist/chunk-JI36FNWE.js.map +0 -1
  43. /package/dist/{chunk-QH5OI43L.js.map → chunk-AS2EPMA6.js.map} +0 -0
  44. /package/dist/{chunk-W424KPCR.js.map → chunk-AXIM6E7T.js.map} +0 -0
  45. /package/dist/{chunk-QRUMXCGW.js.map → chunk-D4JBPAJ5.js.map} +0 -0
  46. /package/dist/{chunk-3RPWH5PC.js.map → chunk-FFSSAMHH.js.map} +0 -0
  47. /package/dist/{chunk-4U7L2NYQ.js.map → chunk-G4U6PSF6.js.map} +0 -0
  48. /package/dist/{chunk-UMVBT7NW.js.map → chunk-GWXY3MWQ.js.map} +0 -0
  49. /package/dist/{chunk-CNJZLZRH.js.map → chunk-LUM7J5FF.js.map} +0 -0
  50. /package/dist/{chunk-SAIW46IC.js.map → chunk-P7ABBLX6.js.map} +0 -0
package/dist/ai.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/ai/keys.js","../src/ai/ConnectAi.jsx","../src/ai/ModelPicker.jsx","../src/ai/GenerateButton.jsx","../src/ai/chip.js","../src/ai/RewriteText.jsx","../src/ai/SubjectIdeas.jsx","../src/ai/createAi.js"],"sourcesContent":["// @ts-check\n/**\n * THE USER'S KEY — when the AI runs in the page with `askKey: true`, each\n * user connects their own provider key, once.\n *\n * The first AI action asks for it (the Connect dialog, `ConnectAi.jsx`); it is\n * kept in this browser (localStorage, one entry per provider) and sent only to\n * the provider. A key the provider refuses is forgotten, and asked again.\n * Given several providers, the user chooses one in the same dialog; the\n * choice is kept too, and so is the model chosen for each provider.\n *\n * ai.key.get(id?) the key in use, or the one kept for that provider ('' when none)\n * ai.key.set(key) connect a key without the dialog\n * ai.key.forget() disconnect: the next AI action asks again\n * ai.key.request() ask the user now → the key, or '' when they cancel\n * ai.key.choose(id) use another of the providers (its kept key, if any)\n * ai.key.model(id?) the model chosen for that provider (its own by default)\n * ai.key.chooseModel(model, id?) write with another of its models\n * ai.key.listModels(id?, { apiKey, signal }) the models it offers (providers.js)\n * ai.key.subscribe(fn) called on every change; returns the unsubscribe\n * ai.key.provider the provider in use: name, model, where to get a key, what is free\n * ai.key.providers all those the user can choose from\n */\n\nimport { listProviderModels } from './providers.js';\n\n/** @typedef {import('./providers.js').AiProvider} AiProvider */\n/** @typedef {import('./providers.js').ModelChoice} ModelChoice */\n\nconst STORAGE_PREFIX = 'best-email-builder:ai-key:';\nconst CHOICE_SLOT = 'best-email-builder:ai-provider';\nconst MODEL_PREFIX = 'best-email-builder:ai-model:';\n\n/** localStorage when there is one that works (not in private modes that throw, not on the server). */\nfunction browserStorage() {\n try {\n const storage = globalThis.localStorage;\n return storage && typeof storage.getItem === 'function' ? storage : null;\n } catch {\n return null;\n }\n}\n\n/**\n * @typedef {Readonly<{id: string, name: string, model: string|null, keyUrl: string|null, keyHint: string, free: string|null, publicModels: boolean}>} ProviderChoice\n * @typedef {{\n * provider: ProviderChoice,\n * providers: ReadonlyArray<ProviderChoice>,\n * get: (id?: string) => string,\n * set: (key: string) => void,\n * forget: () => void,\n * request: () => Promise<string>,\n * choose: (id: string) => void,\n * model: (id?: string) => string,\n * chooseModel: (model: string, id?: string) => void,\n * listModels: (id?: string, options?: {apiKey?: string, signal?: AbortSignal}) => Promise<ModelChoice[]>,\n * subscribe: (listener: () => void) => () => void,\n * state: () => Readonly<{asking: boolean, refused: boolean, connected: boolean}>,\n * answer: (key: string|null) => void,\n * refuse: () => void,\n * }} AiKey\n * `state`, `answer` and `refuse` serve the Connect dialog and the module:\n * whether the user is being asked, whether the last key was refused.\n * `listModels`: with the key given, else the one kept (none is needed for\n * a provider whose list is public, OpenRouter's).\n */\n\n/** @param {AiProvider} provider @returns {ProviderChoice} */\nconst choiceOf = (provider) =>\n Object.freeze({\n id: provider.id,\n name: provider.name,\n model: provider.model ?? null,\n keyUrl: provider.keyUrl,\n keyHint: provider.keyHint,\n free: provider.free,\n publicModels: Boolean(provider.publicModels),\n });\n\n/**\n * @param {AiProvider | AiProvider[]} providerOrList one provider, or those the user chooses from\n * @param {{fetch?: typeof fetch}} [options] the fetch that lists the models\n * @returns {AiKey}\n */\nexport function createKeyStore(providerOrList, { fetch: fetchImpl } = {}) {\n const originals = Array.isArray(providerOrList) ? providerOrList : [providerOrList];\n const providers = Object.freeze(originals.map(choiceOf));\n if (!providers.length) throw new TypeError('createKeyStore: at least one provider');\n const known = (/** @type {unknown} */ id) => providers.some((provider) => provider.id === id);\n /** Kept here too: a browser that does not store still works for the page's life. */\n const memory = new Map();\n let refused = false;\n /** @type {{promise: Promise<string>, resolve: (key: string) => void}|null} */\n let pending = null;\n /** @type {Set<() => void>} */\n const listeners = new Set();\n /** @type {ReturnType<AiKey['state']>} */\n let snapshot;\n\n /** @param {string} slot */\n const read = (slot) => {\n try {\n return browserStorage()?.getItem(slot) || '';\n } catch {\n return '';\n }\n };\n /** @param {string} slot @param {string} value */\n const write = (slot, value) => {\n try {\n const storage = browserStorage();\n if (value) storage?.setItem(slot, value);\n else storage?.removeItem(slot);\n } catch {\n // Full, or blocked: the key stays in memory.\n }\n };\n /** @param {string} id */\n const keyOf = (id) => (memory.has(id) ? memory.get(id) : read(STORAGE_PREFIX + id));\n /** @param {string} id @param {string} value */\n const keep = (id, value) => {\n memory.set(id, value);\n write(STORAGE_PREFIX + id, value);\n };\n // The provider chosen last time; else the first one with a key; else the first.\n const kept = read(CHOICE_SLOT);\n let current = known(kept) ? kept : (providers.find((provider) => keyOf(provider.id)) || providers[0]).id;\n /** @param {string} [id] */\n const get = (id) => keyOf(id === undefined ? current : String(id));\n /** @param {string} [id] */\n const providerId = (id) => {\n if (id === undefined) return current;\n if (!known(id)) throw new TypeError(`ai.key: no provider \"${id}\" — one of ${providers.map((provider) => provider.id).join(', ')}`);\n return id;\n };\n /** The models listed, by provider and key: a dialog opened again shows them at once. */\n const lists = new Map();\n const changed = () => {\n snapshot = Object.freeze({ asking: Boolean(pending), refused, connected: Boolean(get()) });\n for (const listener of [...listeners]) listener();\n };\n snapshot = Object.freeze({ asking: false, refused: false, connected: Boolean(get()) });\n\n /** @param {string} key */\n const answer = (key) => {\n const value = String(key || '').trim();\n if (value) {\n keep(current, value);\n refused = false;\n }\n const waiting = pending;\n pending = null;\n changed();\n waiting?.resolve(value);\n };\n\n return Object.freeze({\n get provider() {\n return /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === current));\n },\n providers,\n get,\n set: (/** @type {string} */ key) => {\n if (typeof key !== 'string' || !key.trim()) throw new TypeError('ai.key.set: a non-empty key');\n answer(key);\n },\n forget: () => {\n keep(current, '');\n changed();\n },\n choose: (/** @type {string} */ id) => {\n if (!known(id)) throw new TypeError(`ai.key.choose: one of ${providers.map((provider) => provider.id).join(', ')}`);\n if (id === current) return;\n current = id;\n refused = false;\n write(CHOICE_SLOT, id);\n changed();\n },\n model: (/** @type {string|undefined} */ id) => {\n const choice = /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === providerId(id)));\n const kept = memory.get(MODEL_PREFIX + choice.id) ?? read(MODEL_PREFIX + choice.id);\n return kept || choice.model || '';\n },\n chooseModel: (/** @type {string} */ model, /** @type {string|undefined} */ id) => {\n if (typeof model !== 'string' || !model.trim()) throw new TypeError('ai.key.chooseModel: a model id');\n const choice = /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === providerId(id)));\n // The provider's own model is kept as no choice: a new default reaches the user.\n const value = model.trim() === choice.model ? '' : model.trim();\n memory.set(MODEL_PREFIX + choice.id, value);\n write(MODEL_PREFIX + choice.id, value);\n changed();\n },\n listModels: async (/** @type {string|undefined} */ id, { apiKey, signal } = {}) => {\n const at = providers.findIndex((provider) => provider.id === providerId(id));\n const key = String(apiKey ?? keyOf(providers[at].id)).trim();\n const slot = `${providers[at].id}\\n${key}`;\n if (!lists.has(slot)) lists.set(slot, await listProviderModels({ provider: originals[at], apiKey: key, fetch: fetchImpl, signal }));\n return lists.get(slot);\n },\n request: () => {\n if (!pending) {\n /** @type {(key: string) => void} */\n let resolve = () => {};\n const promise = new Promise((done) => (resolve = done));\n pending = { promise, resolve };\n changed();\n }\n return pending.promise;\n },\n subscribe: (/** @type {() => void} */ listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n state: () => snapshot,\n answer: (/** @type {string|null} */ key) => answer(key || ''),\n refuse: () => {\n keep(current, '');\n refused = true;\n changed();\n },\n });\n}\n","import { useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport { ArrowUpRight, Eye, EyeOff, Gift, KeyRound, ShieldCheck, TriangleAlert } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { cn } from '../ui/primitives/cn.js';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { Input, Label } from '../ui/primitives/fields.jsx';\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/primitives/overlays.jsx';\nimport ModelPicker from './ModelPicker.jsx';\n\n/** A key this long is complete enough to ask the provider for its models. */\nconst LISTABLE_KEY = 20;\n\n/**\n * CONNECT — the dialog that asks the user for their provider key, when the AI\n * runs in the page with `createAi({ provider, askKey: true })`.\n *\n * It opens by itself when an AI action needs a key and there is none, or the\n * provider refused the last one; the action goes on once the key is given.\n * Cancel ends the action (\"Connect an AI provider first\"). The key is kept in\n * this browser and sent only to the provider (see keys.js).\n *\n * Given several providers (`createAi({ provider: [...] })`), the user chooses\n * one at the top; a provider whose key this browser already keeps needs no\n * new one. Under the key, the model: the provider's own, or another of its\n * list (ModelPicker.jsx).\n */\nexport default function ConnectAi({ aiKey }) {\n const { t } = useUi();\n const state = useSyncExternalStore(aiKey.subscribe, aiKey.state, aiKey.state);\n const [value, setValue] = useState('');\n const [shown, setShown] = useState(false);\n const [chosenId, setChosenId] = useState(aiKey.provider.id);\n /** The models chosen in the dialog, by provider, until Connect. */\n const [picks, setPicks] = useState(/** @type {Record<string, string>} */ ({}));\n const [modelsOpen, setModelsOpen] = useState(false);\n const inputRef = useRef(/** @type {HTMLInputElement|null} */ (null));\n const choicesRef = useRef(/** @type {HTMLDivElement|null} */ (null));\n const inputId = useId();\n const hintId = useId();\n const choices = aiKey.providers;\n const chosen = choices.find((provider) => provider.id === chosenId) || aiKey.provider;\n const { name, keyUrl, keyHint, free } = chosen;\n\n // Each time it opens: the provider in use, an empty field, the key hidden.\n useEffect(() => {\n if (!state.asking) return;\n setChosenId(aiKey.provider.id);\n setValue('');\n setShown(false);\n setPicks({});\n setModelsOpen(false);\n }, [state.asking, aiKey]);\n\n const key = value.trim();\n // The key this browser keeps for the provider chosen (none for one refused).\n const saved = aiKey.get(chosen.id);\n // A key from elsewhere (another provider's) is the usual mistake: said, not blocked.\n const unusual = Boolean(key && keyHint && !key.startsWith(keyHint));\n const refused = state.refused && chosen.id === aiKey.provider.id;\n /** @param {string} id */\n const modelFor = (id) => picks[id] ?? aiKey.model(id);\n\n const submit = (/** @type {React.FormEvent} */ event) => {\n event.preventDefault();\n const answer = key || saved;\n if (!answer) return;\n if (chosen.id !== aiKey.provider.id) aiKey.choose(chosen.id);\n if (picks[chosen.id] && picks[chosen.id] !== aiKey.model(chosen.id)) aiKey.chooseModel(picks[chosen.id], chosen.id);\n aiKey.answer(answer);\n };\n\n /** @param {string} id */\n const choose = (id) => {\n setChosenId(id);\n setValue('');\n setModelsOpen(false);\n };\n\n /** Arrows move through the providers and choose, as in any radio group. */\n const onChoicesKeyDown = (/** @type {React.KeyboardEvent} */ event) => {\n const at = choices.findIndex((provider) => provider.id === chosen.id);\n const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];\n const to = step ? (at + step + choices.length) % choices.length : event.key === 'Home' ? 0 : event.key === 'End' ? choices.length - 1 : -1;\n if (to === -1) return;\n event.preventDefault();\n choose(choices[to].id);\n /** @type {HTMLElement|undefined} */ (choicesRef.current?.querySelectorAll('[role=\"radio\"]')[to])?.focus();\n };\n\n return (\n <Dialog open={state.asking} onOpenChange={(open) => !open && aiKey.answer(null)}>\n <DialogContent\n className={cn('eb:max-h-[calc(100dvh-2rem)] eb:gap-0 eb:overflow-y-auto eb:p-0', choices.length > 1 ? 'eb:sm:max-w-xl' : 'eb:sm:max-w-md')}\n data-testid=\"eb-ai-connect\"\n onOpenAutoFocus={(event) => {\n event.preventDefault();\n inputRef.current?.focus();\n }}\n // Esc closes the list of models first, then the dialog. From inside\n // the list, the list closes itself (and gives the focus back to its field).\n onEscapeKeyDown={(event) => {\n if (!modelsOpen) return;\n event.preventDefault();\n if (!(event.target instanceof Element && event.target.closest('[data-eb-models]'))) setModelsOpen(false);\n }}\n >\n {/* min-w-0: the long names of the list of models do not widen the dialog. */}\n <form onSubmit={submit} className=\"eb:min-w-0\">\n <div className=\"eb:flex eb:flex-col eb:gap-5 eb:px-6 eb:pt-6 eb:pb-5\">\n <DialogHeader className=\"eb:flex-row eb:items-start eb:gap-3.5 eb:pr-6\">\n <span className=\"eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-full eb:bg-primary/10 eb:text-primary\">\n <KeyRound className=\"eb:size-5\" aria-hidden=\"true\" />\n </span>\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\">\n <DialogTitle>{t('Connect {provider}', { provider: name })}</DialogTitle>\n <DialogDescription className=\"eb:text-[13px] eb:leading-relaxed eb:text-foreground-lighter\">\n {t('The AI writes with your own {provider} key. Connect it once: this browser remembers it.', { provider: name })}\n </DialogDescription>\n </div>\n </DialogHeader>\n\n {choices.length > 1 && (\n <div\n ref={choicesRef}\n role=\"radiogroup\"\n aria-label={t('AI provider')}\n className=\"eb:grid eb:grid-cols-2 eb:gap-1.5 eb:sm:grid-cols-3\"\n onKeyDown={onChoicesKeyDown}\n data-testid=\"eb-ai-connect-providers\"\n >\n {choices.map((provider) => {\n const checked = provider.id === chosen.id;\n return (\n <button\n key={provider.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n tabIndex={checked ? 0 : -1}\n onClick={() => choose(provider.id)}\n className={cn(\n 'eb:flex eb:min-w-0 eb:flex-col eb:gap-0.5 eb:rounded-md eb:border eb:px-2.5 eb:py-2 eb:text-left eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n checked ? 'eb:border-primary eb:bg-primary/10' : 'eb:border-border eb:hover:bg-muted'\n )}\n >\n <span className=\"eb:flex eb:items-center eb:gap-1.5 eb:text-[13px] eb:leading-tight eb:font-medium eb:text-foreground\">\n <span className=\"eb:truncate\">{provider.name}</span>\n {provider.free && <Gift className=\"eb:size-3 eb:shrink-0 eb:text-success\" aria-label={t(provider.free)} />}\n {aiKey.get(provider.id) && (\n <span className=\"eb:ml-auto eb:size-1.5 eb:shrink-0 eb:rounded-full eb:bg-success\" title={t('Key saved')} aria-label={t('Key saved')} />\n )}\n </span>\n {modelFor(provider.id) && (\n <span className=\"eb:truncate eb:font-mono eb:text-[10.5px] eb:text-foreground-muted\" title={modelFor(provider.id)}>\n {modelFor(provider.id)}\n </span>\n )}\n </button>\n );\n })}\n </div>\n )}\n\n {refused && (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-2 eb:rounded-md eb:border eb:border-destructive/30 eb:bg-destructive/10 eb:px-3 eb:py-2 eb:text-[12.5px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {t('{provider} refused the last key. Paste another one.', { provider: name })}\n </p>\n )}\n\n {(keyUrl || free) && (\n <div className=\"eb:flex eb:flex-wrap eb:items-center eb:gap-x-3 eb:gap-y-2\">\n {keyUrl && (\n <Button asChild variant=\"outline\" size=\"sm-compact\">\n <a\n href={keyUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n // A new tab, always — even where a link's target is ignored (webviews).\n onClick={(event) => {\n event.preventDefault();\n window.open(keyUrl, '_blank', 'noopener,noreferrer');\n }}\n data-testid=\"eb-ai-connect-get-key\"\n >\n {t('Get a key')}\n <ArrowUpRight aria-hidden=\"true\" />\n </a>\n </Button>\n )}\n {free && (\n <span className=\"eb:inline-flex eb:items-center eb:gap-1.5 eb:rounded-full eb:bg-success/10 eb:px-2.5 eb:py-1 eb:text-[11.5px] eb:font-medium eb:text-success\">\n <Gift className=\"eb:size-3.5\" aria-hidden=\"true\" />\n {t(free)}\n </span>\n )}\n </div>\n )}\n\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\">\n <Label htmlFor={inputId}>{t('API key')}</Label>\n <div className=\"eb:relative\">\n <Input\n ref={inputRef}\n id={inputId}\n type={shown ? 'text' : 'password'}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n placeholder={keyHint ? `${keyHint}…` : t('Paste the key')}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n aria-describedby={hintId}\n className=\"eb:h-9 eb:pr-10 eb:font-mono eb:text-[12.5px]\"\n data-testid=\"eb-ai-connect-key\"\n />\n <button\n type=\"button\"\n className=\"eb:absolute eb:top-1/2 eb:right-1 eb:flex eb:size-7 eb:-translate-y-1/2 eb:items-center eb:justify-center eb:rounded eb:text-foreground-muted eb:transition-colors eb:outline-none eb:hover:bg-muted eb:hover:text-foreground eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50\"\n onClick={() => setShown((current) => !current)}\n aria-label={shown ? t('Hide the key') : t('Show the key')}\n aria-pressed={shown}\n >\n {shown ? <EyeOff className=\"eb:size-4\" aria-hidden=\"true\" /> : <Eye className=\"eb:size-4\" aria-hidden=\"true\" />}\n </button>\n </div>\n <p id={hintId} className=\"eb:min-h-4 eb:text-[11.5px] eb:text-foreground-muted\">\n {unusual ? (\n <span className=\"eb:text-warning\">{t('{provider} keys usually start with {prefix}.', { provider: name, prefix: keyHint })}</span>\n ) : saved && !key ? (\n t('A key for {provider} is saved in this browser: Connect uses it, or paste another one.', { provider: name })\n ) : keyHint ? (\n t('{provider} keys start with {prefix}.', { provider: name, prefix: keyHint })\n ) : null}\n </p>\n </div>\n\n <ModelPicker\n aiKey={aiKey}\n provider={chosen}\n apiKey={key.length >= LISTABLE_KEY ? key : saved}\n delay={key.length >= LISTABLE_KEY ? 350 : 0}\n value={modelFor(chosen.id)}\n onChange={(model) => setPicks((current) => ({ ...current, [chosen.id]: model }))}\n open={modelsOpen}\n onOpenChange={setModelsOpen}\n />\n </div>\n\n <div className=\"eb:flex eb:items-start eb:gap-2 eb:border-t eb:border-border eb:bg-surface-75 eb:px-6 eb:py-3 eb:text-[11.5px] eb:leading-relaxed eb:text-foreground-lighter\">\n <ShieldCheck className=\"eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-foreground-muted\" aria-hidden=\"true\" />\n <span>{t('The key stays in this browser and is sent only to {provider}.', { provider: name })}</span>\n </div>\n\n <DialogFooter className=\"eb:border-t eb:border-border eb:px-6 eb:py-4\">\n <Button type=\"button\" variant=\"ghost\" onClick={() => aiKey.answer(null)}>\n {t('Cancel')}\n </Button>\n <Button type=\"submit\" disabled={!key && !saved} data-testid=\"eb-ai-connect-submit\">\n {t('Connect')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n","import { useEffect, useId, useMemo, useRef, useState } from 'react';\nimport { Check, ChevronDown, Gift, LoaderCircle, Search } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { cn } from '../ui/primitives/cn.js';\n\n/**\n * MODEL — the model of the provider chosen, in the Connect dialog: a field\n * that opens the provider's list of models in place (not over the dialog, so\n * the list scrolls like the rest of it).\n *\n * The list comes from the provider with the key (OpenRouter's needs none):\n * the default model first, then the free ones, then the newest. Type to\n * filter, ↑/↓ to move, Enter to choose, Esc to close. An id the list does not\n * have can be used as it is (\"Use “…”\"); without the list, the default model\n * still works.\n *\n * @param {{\n * aiKey: import('./keys.js').AiKey,\n * provider: import('./keys.js').ProviderChoice,\n * apiKey: string,\n * delay?: number,\n * value: string,\n * onChange: (model: string) => void,\n * open: boolean,\n * onOpenChange: (open: boolean) => void,\n * }} props\n * `apiKey`: the key the list is asked with ('' when there is none yet);\n * `delay`: how long to wait after it changes (a key being pasted).\n */\nexport default function ModelPicker({ aiKey, provider, apiKey, delay = 0, value, onChange, open, onOpenChange }) {\n const { t } = useUi();\n const [list, setList] = useState(/** @type {{status: 'loading'|'ready'|'needs-key'|'refused'|'failed', models: import('./providers.js').ModelChoice[]}} */ ({ status: 'loading', models: [] }));\n const [query, setQuery] = useState('');\n const [freeOnly, setFreeOnly] = useState(false);\n const [active, setActive] = useState(0);\n const triggerRef = useRef(/** @type {HTMLButtonElement|null} */ (null));\n const listRef = useRef(/** @type {HTMLDivElement|null} */ (null));\n const labelId = useId();\n const valueId = useId();\n const listId = useId();\n const hintId = useId();\n\n // The list of the provider, asked again when the key changes.\n useEffect(() => {\n if (!apiKey && !provider.publicModels) {\n setList({ status: 'needs-key', models: [] });\n return undefined;\n }\n const controller = new AbortController();\n setList({ status: 'loading', models: [] });\n const timer = setTimeout(() => {\n aiKey.listModels(provider.id, { apiKey, signal: controller.signal }).then(\n (models) => !controller.signal.aborted && setList({ status: 'ready', models }),\n (error) => !controller.signal.aborted && setList({ status: error?.code === 'KEY_REFUSED' ? 'refused' : 'failed', models: [] })\n );\n }, delay);\n return () => {\n clearTimeout(timer);\n controller.abort();\n };\n }, [aiKey, provider.id, provider.publicModels, apiKey, delay]);\n\n // Each time it opens: no search, the model in use under the keys.\n useEffect(() => {\n if (!open) return;\n setQuery('');\n setFreeOnly(false);\n }, [open]);\n\n // The default model first, then the one in use, then the list.\n const models = useMemo(() => {\n const listed = new Map(list.models.map((model) => [model.id, model]));\n const seen = new Set();\n /** @type {import('./providers.js').ModelChoice[]} */\n const all = [];\n const add = (/** @type {import('./providers.js').ModelChoice} */ model) => {\n if (model.id && !seen.has(model.id)) seen.add(model.id) && all.push(model);\n };\n for (const id of [provider.model, value]) if (id) add(listed.get(id) || { id, name: id, free: false });\n list.models.forEach(add);\n return all;\n }, [list.models, provider.model, value]);\n const hasFree = list.models.some((model) => model.free);\n const typed = query.trim();\n const matches = useMemo(() => {\n const words = typed.toLowerCase().split(/\\s+/).filter(Boolean);\n return models.filter((model) => (!freeOnly || model.free) && words.every((word) => `${model.id} ${model.name}`.toLowerCase().includes(word)));\n }, [models, typed, freeOnly]);\n // An id of one's own: a model the list does not have (or not yet).\n const custom = typed && !/\\s/.test(typed) && !models.some((model) => model.id === typed) ? typed : '';\n const count = matches.length + (custom ? 1 : 0);\n const current = models.find((model) => model.id === value);\n\n useEffect(() => setActive(Math.max(0, matches.findIndex((model) => model.id === value))), [matches, value]);\n useEffect(() => {\n if (open) listRef.current?.querySelector('[data-active=\"true\"]')?.scrollIntoView({ block: 'nearest' });\n }, [active, open]);\n\n const close = () => {\n triggerRef.current?.focus();\n onOpenChange(false);\n };\n /** @param {string} id */\n const pick = (id) => {\n onChange(id);\n close();\n };\n\n const onKeyDown = (/** @type {React.KeyboardEvent} */ event) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActive((at) => (count ? (at + (event.key === 'ArrowDown' ? 1 : -1) + count) % count : 0));\n } else if (event.key === 'Enter') {\n // The model, not the form: Connect is the button's.\n event.preventDefault();\n const id = active < matches.length ? matches[active]?.id : custom;\n if (id) pick(id);\n }\n };\n\n const optionId = (/** @type {number} */ at) => `${listId}-${at}`;\n const badges = (/** @type {import('./providers.js').ModelChoice} */ model) => (\n <>\n {model.free && (\n <span className=\"eb:inline-flex eb:shrink-0 eb:items-center eb:gap-1 eb:rounded-full eb:bg-success/10 eb:px-1.5 eb:py-px eb:text-[10.5px] eb:font-medium eb:text-success\">\n <Gift className=\"eb:size-3\" aria-hidden=\"true\" />\n {t('Free')}\n </span>\n )}\n {model.id === provider.model && (\n <span className=\"eb:shrink-0 eb:rounded-full eb:bg-muted eb:px-1.5 eb:py-px eb:text-[10.5px] eb:font-medium eb:text-foreground-lighter\">{t('Default')}</span>\n )}\n </>\n );\n\n const status = {\n loading: (\n <>\n <LoaderCircle className=\"eb:size-3 eb:shrink-0 eb:animate-spin\" aria-hidden=\"true\" />\n {t('Loading the models of {provider}…', { provider: provider.name })}\n </>\n ),\n 'needs-key': t('Paste the key to see all the models of {provider}.', { provider: provider.name }),\n refused: <span className=\"eb:text-warning\">{t('{provider} refused this key: its models could not be listed.', { provider: provider.name })}</span>,\n failed: t('The list of models could not be loaded. The default model works, or type the id of another.'),\n ready: list.models.length\n ? hasFree\n ? t('{count} models, the free ones first.', { count: list.models.length })\n : t('{count} models.', { count: list.models.length })\n : t('{provider} lists no models here: type the id of the one you want.', { provider: provider.name }),\n }[list.status];\n\n return (\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\" data-testid=\"eb-ai-connect-model-field\">\n <span id={labelId} className=\"eb:text-[12.5px] eb:leading-none eb:font-medium eb:text-foreground\">\n {t('Model')}\n </span>\n <button\n ref={triggerRef}\n type=\"button\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-labelledby={`${labelId} ${valueId}`}\n aria-describedby={hintId}\n onClick={() => onOpenChange(!open)}\n className={cn(\n 'eb:flex eb:h-9 eb:w-full eb:min-w-0 eb:items-center eb:gap-2 eb:rounded-md eb:border eb:border-input eb:bg-transparent eb:px-3 eb:text-left eb:shadow-xs eb:transition-[color,box-shadow] eb:outline-none eb:focus-visible:border-ring eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n open && 'eb:border-ring'\n )}\n data-testid=\"eb-ai-connect-model\"\n >\n <span id={valueId} className=\"eb:min-w-0 eb:truncate eb:font-mono eb:text-[12.5px] eb:text-foreground\">{value || t('Choose a model')}</span>\n {current && badges(current)}\n <ChevronDown className={cn('eb:ml-auto eb:size-4 eb:shrink-0 eb:text-foreground-muted eb:transition-transform', open && 'eb:rotate-180')} aria-hidden=\"true\" />\n </button>\n\n {open && (\n <div\n className=\"eb:flex eb:flex-col eb:overflow-hidden eb:rounded-md eb:border eb:border-border eb:bg-background\"\n onKeyDown={(event) => event.key === 'Escape' && close()}\n data-eb-models=\"\"\n data-testid=\"eb-ai-connect-models\"\n >\n <div className=\"eb:flex eb:items-center eb:gap-2 eb:border-b eb:border-border eb:p-2\">\n <div className=\"eb:relative eb:min-w-0 eb:flex-1\">\n <Search className=\"eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted\" aria-hidden=\"true\" />\n <input\n autoFocus\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={count ? optionId(active) : undefined}\n aria-label={t('Search the models')}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={onKeyDown}\n placeholder={t('Search the models…')}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n className=\"eb:h-8 eb:w-full eb:rounded-md eb:bg-surface-75 eb:pr-2 eb:pl-7 eb:text-[13px] eb:text-foreground eb:outline-none eb:placeholder:text-foreground-muted eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40\"\n data-testid=\"eb-ai-connect-model-search\"\n />\n </div>\n {hasFree && (\n <button\n type=\"button\"\n aria-pressed={freeOnly}\n onClick={() => setFreeOnly((on) => !on)}\n className={cn(\n 'eb:inline-flex eb:h-8 eb:shrink-0 eb:items-center eb:gap-1.5 eb:rounded-md eb:border eb:px-2.5 eb:text-[12px] eb:font-medium eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n freeOnly ? 'eb:border-success/40 eb:bg-success/10 eb:text-success' : 'eb:border-border eb:text-foreground-lighter eb:hover:bg-muted'\n )}\n >\n <Gift className=\"eb:size-3.5\" aria-hidden=\"true\" />\n {t('Free only')}\n </button>\n )}\n </div>\n <div ref={listRef} id={listId} role=\"listbox\" aria-labelledby={labelId} className=\"eb:max-h-56 eb:overflow-y-auto eb:p-1\">\n {matches.map((model, at) => (\n <button\n key={model.id}\n id={optionId(at)}\n type=\"button\"\n role=\"option\"\n tabIndex={-1}\n aria-selected={model.id === value}\n data-active={at === active}\n data-model={model.id}\n onClick={() => pick(model.id)}\n onMouseMove={() => at !== active && setActive(at)}\n className={cn(\n 'eb:flex eb:w-full eb:min-w-0 eb:items-start eb:gap-2 eb:rounded-md eb:px-2 eb:py-1.5 eb:text-left',\n at === active ? 'eb:bg-accent eb:text-accent-foreground' : ''\n )}\n >\n <Check className={cn('eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-primary', model.id !== value && 'eb:invisible')} aria-hidden=\"true\" />\n <span className=\"eb:flex eb:min-w-0 eb:flex-1 eb:flex-col eb:gap-0.5\">\n <span className=\"eb:flex eb:min-w-0 eb:items-center eb:gap-1.5\">\n <span className=\"eb:truncate eb:text-[13px] eb:text-foreground\">{model.name}</span>\n {badges(model)}\n </span>\n {model.name !== model.id && <span className=\"eb:truncate eb:font-mono eb:text-[10.5px] eb:text-foreground-muted\">{model.id}</span>}\n </span>\n </button>\n ))}\n {custom && (\n <button\n id={optionId(matches.length)}\n type=\"button\"\n role=\"option\"\n tabIndex={-1}\n aria-selected={false}\n data-active={active === matches.length}\n onClick={() => pick(custom)}\n onMouseMove={() => active !== matches.length && setActive(matches.length)}\n className={cn(\n 'eb:flex eb:w-full eb:min-w-0 eb:items-center eb:gap-2 eb:rounded-md eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px] eb:text-primary',\n active === matches.length ? 'eb:bg-accent' : ''\n )}\n data-testid=\"eb-ai-connect-model-custom\"\n >\n <span className=\"eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n <span className=\"eb:min-w-0 eb:truncate\">{t('Use “{model}”', { model: custom })}</span>\n </button>\n )}\n {!count && <p className=\"eb:px-2 eb:py-3 eb:text-center eb:text-[12px] eb:text-foreground-muted\">{t('No model matches.')}</p>}\n </div>\n </div>\n )}\n\n <p id={hintId} role=\"status\" className=\"eb:flex eb:min-h-4 eb:items-center eb:gap-1.5 eb:text-[11.5px] eb:text-foreground-muted\" data-testid=\"eb-ai-connect-models-status\">\n {status}\n </p>\n </div>\n );\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { Check, Loader2, Search, Sparkles, TriangleAlert } from 'lucide-react';\nimport { SEARCH_KEYS, SearchKeyHint, useSearchShortcut } from '../ui/primitives/searchShortcut.jsx';\nimport { useUi } from '../ui/context.jsx';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { Input, Textarea } from '../ui/primitives/fields.jsx';\nimport { SectionLabel } from '../ui/primitives/misc.jsx';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/primitives/overlays.jsx';\nimport { chipClassName } from './chip.js';\nimport { MAX_DETAILS_LENGTH } from './generator.js';\nimport { purposesByCategory } from './purposes.js';\nimport { brandTemplate, hasBrand } from '../features/brand/brand.js';\n\n/**\n * GENERATE — toolbar button and dialog: an email written from scratch.\n *\n * One decision for the user: what the email is for (a badge). Details are\n * optional; the language is the interface's, the sender and the variables\n * are the builder's own. The dialog says up front that the email in the\n * editor will be replaced — no confirmation after a minute of waiting.\n *\n * The result REPLACES the email in the editor, subject and preview text\n * included (they belong to the new email); the name is filled when empty.\n * A saved template counts as modified: the next save keeps it.\n */\n\n/** Lowercase, without accents: \"Été\" matches \"ete\". @param {string} text */\nconst fold = (text) =>\n text\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n\nexport default function GenerateButton({ context, generate: generateEmail }) {\n const { editor, ready, isDirty, markDirty, template, setMeta, variables, sender, brand } = context;\n const { t, locale, notify } = useUi();\n const [open, setOpen] = useState(false);\n const [purpose, setPurpose] = useState(/** @type {string|null} */ (null));\n const [details, setDetails] = useState('');\n const [query, setQuery] = useState('');\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState('');\n const [seconds, setSeconds] = useState(0);\n const [dirty, setDirty] = useState(false);\n const abortRef = useRef(/** @type {AbortController|null} */ (null));\n const searchRef = useRef(/** @type {HTMLInputElement|null} */ (null));\n useSearchShortcut(searchRef);\n\n useEffect(() => () => abortRef.current?.abort(), []);\n useEffect(() => {\n if (!busy) return undefined;\n setSeconds(0);\n const timer = setInterval(() => setSeconds((s) => s + 1), 1000);\n return () => clearInterval(timer);\n }, [busy]);\n\n const groups = useMemo(() => {\n const words = fold(query.trim()).split(/\\s+/).filter(Boolean);\n return purposesByCategory()\n .map((group) => ({\n ...group,\n purposes: group.purposes.filter((entry) => {\n const label = fold(`${t(entry.label)} ${entry.label} ${t(group.label)}`);\n return words.every((word) => label.includes(word));\n }),\n }))\n .filter((group) => group.purposes.length > 0);\n }, [query, t]);\n\n const openDialog = () => {\n setError('');\n setDirty(isDirty());\n setOpen(true);\n };\n\n const close = (next) => {\n if (next) return;\n abortRef.current?.abort();\n setOpen(false);\n };\n\n const generate = async () => {\n if (busy || (!purpose && !details.trim())) return;\n setError('');\n setBusy(true);\n const controller = new AbortController();\n abortRef.current = controller;\n try {\n const email = await generateEmail(\n {\n purpose,\n details: details.trim(),\n language: String(locale || 'en').slice(0, 2),\n sender: sender || {},\n ...(hasBrand(brand) ? { brand: { primary: brand.primary, heading: brand.heading, font: brand.font } } : {}),\n variables: (variables || []).map((variable) => ({ key: variable.key, label: variable.label })),\n },\n { signal: controller.signal }\n );\n if (controller.signal.aborted) return;\n // The brand's logo where the model wrote the name (an uploaded logo\n // is not an https address the model may be given).\n editor.loadMjml(hasBrand(brand) ? brandTemplate(email.mjml, brand, { sender, t }) : email.mjml);\n setMeta((meta) => ({\n ...meta,\n name: meta.name || email.name,\n subject: email.subject || meta.subject,\n preheader: email.preheader || meta.preheader,\n }));\n if (template.id) markDirty();\n setOpen(false);\n notify('success', t('Your email is ready. Edit anything, then save.'));\n } catch (err) {\n if (!controller.signal.aborted) setError(err?.message ? t(err.message) : t('The email could not be generated.'));\n } finally {\n if (abortRef.current === controller) abortRef.current = null;\n setBusy(false);\n }\n };\n\n const cancel = () => {\n abortRef.current?.abort();\n setBusy(false);\n };\n\n const canGenerate = Boolean(purpose || details.trim());\n\n return (\n <>\n <Button variant=\"outline\" size=\"sm-compact\" disabled={!ready} onClick={openDialog}>\n <Sparkles />\n {t('Generate')}\n </Button>\n <Dialog open={open} onOpenChange={close}>\n <DialogContent\n className=\"eb:flex eb:max-h-[calc(100dvh-2rem)] eb:flex-col eb:gap-0 eb:p-0 eb:sm:max-w-2xl\"\n data-testid=\"eb-generate\"\n onOpenAutoFocus={(event) => {\n // The search field rather than the first badge.\n event.preventDefault();\n searchRef.current?.focus();\n }}\n >\n <DialogHeader className=\"eb:gap-1.5 eb:px-6 eb:pt-6 eb:pb-3\">\n <DialogTitle>{t('Generate an email')}</DialogTitle>\n <DialogDescription>{t('Pick what it is for. AI writes a complete email that opens here, ready to edit.')}</DialogDescription>\n </DialogHeader>\n\n <div className=\"eb:px-6 eb:pb-3\">\n <div className=\"eb:relative\">\n <Search className=\"eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted\" aria-hidden=\"true\" />\n <Input\n ref={searchRef}\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder={t('Search: sale, password, birthday…')}\n aria-label={t('Search')}\n aria-keyshortcuts={SEARCH_KEYS}\n className=\"eb:pr-16 eb:pl-8 eb:[&::-webkit-search-cancel-button]:hidden\"\n disabled={busy}\n />\n <SearchKeyHint className=\"eb:absolute eb:top-1/2 eb:right-2 eb:-translate-y-1/2\" />\n </div>\n </div>\n\n {/* The scroll box is a div: a fieldset does not clip its overflow\n as a flex item, and the badges would run under the footer. */}\n <div className=\"eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:border-y eb:border-border eb:px-6 eb:py-4\" data-testid=\"eb-generate-purposes\">\n <fieldset disabled={busy} className=\"eb:m-0 eb:min-w-0 eb:border-0 eb:p-0\">\n <legend className=\"eb:sr-only\">{t('What is the email for?')}</legend>\n {groups.length === 0 && (\n <p className=\"eb:py-6 eb:text-center eb:text-[13px] eb:text-foreground-muted\">\n {t('Nothing matches. Describe your email in the details below.')}\n </p>\n )}\n <div className=\"eb:flex eb:flex-col eb:gap-4\">\n {groups.map((group) => (\n <section key={group.id} className=\"eb:flex eb:flex-col eb:gap-2\" aria-label={t(group.label)}>\n <SectionLabel>{t(group.label)}</SectionLabel>\n <div className=\"eb:flex eb:flex-wrap eb:gap-1.5\">\n {group.purposes.map((entry) => {\n const selected = purpose === entry.id;\n return (\n <button\n key={entry.id}\n type=\"button\"\n aria-pressed={selected}\n data-purpose={entry.id}\n onClick={() => setPurpose(selected ? null : entry.id)}\n className={chipClassName(selected)}\n >\n {selected && <Check className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t(entry.label)}\n </button>\n );\n })}\n </div>\n </section>\n ))}\n </div>\n </fieldset>\n </div>\n\n <div className=\"eb:flex eb:flex-col eb:gap-3 eb:px-6 eb:pt-3 eb:pb-6\">\n <Textarea\n value={details}\n onChange={(event) => setDetails(event.target.value.slice(0, MAX_DETAILS_LENGTH))}\n placeholder={t('Details (optional): product, dates, offer, tone, language…')}\n aria-label={t('Details (optional)')}\n rows={2}\n className=\"eb:min-h-14\"\n disabled={busy}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) generate();\n }}\n />\n {error ? (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n ) : (\n dirty &&\n !busy && (\n <p className=\"eb:text-[12px] eb:text-foreground-muted\">{t('The email being edited will be replaced, unsaved changes included.')}</p>\n )\n )}\n <div className=\"eb:flex eb:items-center eb:justify-end eb:gap-2\">\n {busy && (\n <p className=\"eb:mr-auto eb:text-[12px] eb:text-foreground-muted\" role=\"status\" aria-live=\"polite\">\n {t('Writing your email… {seconds} s', { seconds })}\n </p>\n )}\n {busy && (\n <Button variant=\"outline\" size=\"sm\" onClick={cancel}>\n {t('Cancel')}\n </Button>\n )}\n <Button size=\"sm\" onClick={generate} disabled={!canGenerate || busy} data-testid=\"eb-generate-submit\">\n {busy ? <Loader2 className=\"eb:animate-spin\" /> : <Sparkles />}\n {t('Generate')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n","import { cn } from '../ui/primitives/cn.js';\n\n/**\n * A rounded badge button: the purposes of \"Generate\", the rewrite actions.\n * @param {boolean} [selected]\n */\nexport const chipClassName = (selected = false) =>\n cn(\n 'eb:inline-flex eb:h-7 eb:items-center eb:gap-1 eb:rounded-full eb:border eb:px-3 eb:text-[12px] eb:font-medium eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50 eb:disabled:opacity-50',\n selected\n ? 'eb:border-primary eb:bg-primary eb:text-primary-foreground'\n : 'eb:border-border eb:bg-background eb:text-foreground eb:hover:border-primary/50 eb:hover:bg-primary/5'\n );\n","import { useEffect, useRef, useState } from 'react';\nimport { ChevronDown, Languages, Loader2, Sparkles, TriangleAlert } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { Input } from '../ui/primitives/fields.jsx';\nimport { SectionLabel } from '../ui/primitives/misc.jsx';\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../ui/primitives/overlays.jsx';\nimport { cleanRichText } from '../engine/richText.js';\nimport { chipClassName } from './chip.js';\nimport { MAX_REWRITE_INSTRUCTION, REWRITE_ACTIONS, REWRITE_LANGUAGES } from './rewrite.js';\n\n/**\n * REWRITE WITH AI — in the properties of a selected text or button.\n *\n * One click: shorter, longer, fixed, friendlier, more formal, translated —\n * or a change in the user's words. The result replaces the text at once, in\n * one step of the history: Ctrl+Z brings the previous text back, so there is\n * nothing to accept or compare.\n *\n * Mounted per element (keyed by its id): selecting another element cancels a\n * rewrite in progress. If the text is edited while the AI works, the user's\n * words win and the answer is dropped.\n */\n\n/** A language under its own name: \"Deutsch\", \"Українська\". @param {string} code */\nfunction nativeName(code) {\n try {\n const name = new Intl.DisplayNames([code], { type: 'language' }).of(code) || code;\n return name.charAt(0).toLocaleUpperCase(code) + name.slice(1);\n } catch {\n return code;\n }\n}\n\nexport default function RewriteText({ editor, selection, rewrite }) {\n const { t, notify } = useUi();\n const [running, setRunning] = useState(/** @type {string|null} */ (null));\n const [error, setError] = useState('');\n const [instruction, setInstruction] = useState('');\n const abortRef = useRef(/** @type {AbortController|null} */ (null));\n const label = selection.tagName === 'mj-button';\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n /** The text as stored: a button's label, a text block's HTML. */\n const currentText = () => {\n const element = editor.getElement(selection.id);\n return (label ? element?.content : element?.html) ?? '';\n };\n\n const run = async (action, extra = {}) => {\n if (running) return;\n const text = currentText();\n setError('');\n setRunning(action);\n const controller = new AbortController();\n abortRef.current = controller;\n try {\n const result = await rewrite({ action, text, format: label ? 'plain' : 'html', ...extra }, { signal: controller.signal });\n if (controller.signal.aborted) return;\n if (currentText() !== text) {\n setError(t('The text changed in the meantime, so it was left as it is.'));\n return;\n }\n editor.setContent(label ? result.text : cleanRichText(result.text, document), selection.id);\n if (action === 'custom') setInstruction('');\n notify('success', t('Text rewritten. Ctrl+Z brings back the previous one.'));\n } catch (err) {\n if (!controller.signal.aborted) setError(err?.message ? t(err.message) : t('The text could not be rewritten.'));\n } finally {\n if (abortRef.current === controller) abortRef.current = null;\n setRunning(null);\n }\n };\n\n const cancel = () => {\n abortRef.current?.abort();\n setRunning(null);\n };\n\n const busy = Boolean(running);\n const spinner = <Loader2 className=\"eb:size-3 eb:animate-spin\" aria-hidden=\"true\" />;\n\n return (\n <section className=\"eb:space-y-2.5\" aria-label={t('Rewrite with AI')} data-testid=\"eb-rewrite\">\n <SectionLabel className=\"eb:flex eb:items-center eb:gap-1.5\">\n <Sparkles className=\"eb:size-3\" aria-hidden=\"true\" />\n {t('Rewrite with AI')}\n </SectionLabel>\n <div className=\"eb:flex eb:flex-wrap eb:gap-1.5\">\n {REWRITE_ACTIONS.map((action) => (\n <button key={action.id} type=\"button\" className={chipClassName()} disabled={busy} onClick={() => run(action.id)}>\n {running === action.id && spinner}\n {t(action.label)}\n </button>\n ))}\n <DropdownMenu>\n <DropdownMenuTrigger asChild disabled={busy}>\n <button type=\"button\" className={chipClassName()}>\n {running === 'translate' ? spinner : <Languages className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t('Translate')}\n <ChevronDown className=\"eb:size-3\" aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" aria-label={t('Translate into')}>\n {REWRITE_LANGUAGES.map((code) => (\n <DropdownMenuItem key={code} lang={code} onSelect={() => run('translate', { targetLanguage: code })}>\n {nativeName(code)}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n <form\n className=\"eb:flex eb:gap-1.5\"\n onSubmit={(event) => {\n event.preventDefault();\n if (instruction.trim()) run('custom', { instruction: instruction.trim() });\n }}\n >\n <Input\n value={instruction}\n onChange={(event) => setInstruction(event.target.value)}\n maxLength={MAX_REWRITE_INSTRUCTION}\n placeholder={t('Or say what to change…')}\n aria-label={t('What to change')}\n disabled={busy}\n />\n <Button type=\"submit\" variant=\"outline\" size=\"icon-sm\" aria-label={t('Rewrite')} disabled={busy || !instruction.trim()}>\n {running === 'custom' ? <Loader2 className=\"eb:animate-spin\" /> : <Sparkles />}\n </Button>\n </form>\n {running && (\n <p className=\"eb:flex eb:items-center eb:gap-2 eb:text-[12px] eb:text-foreground-muted\" role=\"status\" aria-live=\"polite\">\n {t('Rewriting…')}\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"eb:h-auto eb:px-0\" onClick={cancel}>\n {t('Cancel')}\n </Button>\n </p>\n )}\n {error && (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n )}\n </section>\n );\n}\n","import { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { Check, Loader2, Sparkles, TriangleAlert } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { cn } from '../ui/primitives/cn.js';\nimport { chipClassName } from './chip.js';\nimport { emailWords } from './subjects.js';\n\n/** Ideas already shown that \"More ideas\" asks the model not to repeat (the server keeps 12). */\nconst MAX_AVOID = 12;\n\n/**\n * SUGGEST SUBJECTS — under the subject of the email.\n *\n * One click: three other subject lines, drawn from the words of the email.\n * They are a list to pick from, the user's own subject first: a click puts\n * one in the field, another click another one, the first row brings the\n * user's subject back. \"More ideas\" asks for three new ones (none of those\n * already shown; the one in the field stays in the list); \"Done\" closes the\n * list and keeps the subject in the field.\n *\n * \"Yours\" is what the user wrote: typed in the field while the list is open\n * (or while ideas are on their way), it becomes the new \"yours\". When the\n * email itself is replaced (Generate, a template), the list closes.\n *\n * Keyboard: the list is a radio group — one Tab stop, the arrows move and\n * pick. Focus goes to the list when it opens, back to the button after Done or\n * Cancel.\n */\nexport default function SubjectIdeas({ editor, meta, onSubject, suggest, variables = null }) {\n const { t, locale } = useUi();\n const [running, setRunning] = useState(false);\n const [error, setError] = useState('');\n const [ideas, setIdeas] = useState(/** @type {string[]|null} */ (null));\n const [original, setOriginal] = useState('');\n const shownRef = useRef(/** @type {string[]} */ ([]));\n const abortRef = useRef(/** @type {AbortController|null} */ (null));\n const subjectRef = useRef(meta.subject);\n subjectRef.current = meta.subject;\n const rootRef = useRef(/** @type {HTMLDivElement|null} */ (null));\n /** Where focus goes after the next render: the list, or the button that opened it. */\n const focusRef = useRef(/** @type {'list'|'button'|null} */ (null));\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n // Another email loaded in the editor: these ideas were for the previous one.\n useEffect(\n () =>\n editor.on('load', () => {\n abortRef.current?.abort();\n abortRef.current = null;\n shownRef.current = [];\n setRunning(false);\n setIdeas(null);\n setError('');\n setOriginal('');\n }),\n [editor]\n );\n\n // Typed in the field while the list is open: that is the user's subject now.\n const typed = meta.subject.trim();\n useEffect(() => {\n if (ideas && typed && !ideas.includes(meta.subject) && typed !== original) setOriginal(typed);\n }, [ideas, meta.subject, typed, original]);\n\n useLayoutEffect(() => {\n const target = focusRef.current;\n if (!target || running) return;\n focusRef.current = null;\n const root = rootRef.current;\n const element =\n target === 'list'\n ? root?.querySelector('[role=\"radio\"][tabindex=\"0\"]')\n : root?.querySelector('[data-subject-ideas-button]');\n /** @type {HTMLElement|null|undefined} */ (element)?.focus();\n });\n\n const ready = editor.status === 'ready';\n\n const run = async () => {\n if (running || editor.status !== 'ready') return;\n const first = !ideas;\n // The user's subject: as it was before the first idea was picked, or typed since.\n const yours = first ? subjectRef.current.trim() : original;\n const controller = new AbortController();\n abortRef.current = controller;\n setError('');\n setRunning(true);\n try {\n const result = await suggest(\n {\n subject: yours,\n preheader: meta.preheader,\n text: emailWords(editor.exportMjml()),\n language: locale,\n variables: variables?.map(({ key, label, kind }) => ({ key, label, kind })),\n avoid: shownRef.current.slice(-MAX_AVOID),\n },\n { signal: controller.signal }\n );\n if (controller.signal.aborted) return;\n const current = subjectRef.current;\n // The idea in the field stays in the list; the others give way to the new ones.\n const kept = ideas?.includes(current) ? [current] : [];\n const next = [...kept, ...result.subjects.filter((subject) => subject !== current)];\n shownRef.current = [...shownRef.current, ...result.subjects];\n // Typed during the wait: that is \"yours\" now.\n setOriginal(current.trim() && !ideas?.includes(current) ? current.trim() : yours);\n setIdeas(next);\n if (first) focusRef.current = 'list';\n } catch (err) {\n if (!controller.signal.aborted) setError(err?.message ? t(err.message) : t('No subject could be suggested.'));\n } finally {\n // A request cancelled earlier must not end the one that replaced it.\n if (abortRef.current === controller) {\n abortRef.current = null;\n setRunning(false);\n }\n }\n };\n\n const cancel = () => {\n abortRef.current?.abort();\n abortRef.current = null;\n setRunning(false);\n focusRef.current = 'button';\n };\n\n const done = () => {\n setIdeas(null);\n setError('');\n shownRef.current = [];\n focusRef.current = 'button';\n };\n\n const options = ideas ? [...(original ? [{ text: original, yours: true }] : []), ...ideas.map((text) => ({ text, yours: false }))] : [];\n const checked = options.findIndex((option) => option.text === meta.subject);\n const tabStop = checked === -1 ? 0 : checked;\n\n /** Arrows move through the ideas and pick, as in any radio group. */\n const onKeyDown = (/** @type {React.KeyboardEvent} */ event) => {\n const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];\n const to = step ? (tabStop + step + options.length) % options.length : event.key === 'Home' ? 0 : event.key === 'End' ? options.length - 1 : -1;\n if (to === -1 || !options.length) return;\n event.preventDefault();\n onSubject(options[to].text);\n /** @type {HTMLElement|undefined} */ (rootRef.current?.querySelectorAll('[role=\"radio\"]')[to])?.focus();\n };\n\n const spinner = <Loader2 className=\"eb:size-3 eb:animate-spin\" aria-hidden=\"true\" />;\n\n return (\n <div className=\"eb:space-y-2\" data-testid=\"eb-subject-ideas\" ref={rootRef}>\n {!ideas && (\n <button type=\"button\" className={chipClassName()} disabled={running || !ready} onClick={run} data-subject-ideas-button>\n {running ? spinner : <Sparkles className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t('Suggest subjects')}\n </button>\n )}\n {ideas && (\n <div className=\"eb:space-y-2 eb:rounded-md eb:border eb:border-border eb:p-1.5\">\n <div role=\"radiogroup\" aria-label={t('Subject ideas')} className=\"eb:space-y-0.5\" onKeyDown={onKeyDown}>\n {options.map((option, index) => {\n const chosen = index === checked;\n return (\n <button\n key={`${option.yours ? 'yours' : 'idea'}:${option.text}`}\n type=\"button\"\n role=\"radio\"\n aria-checked={chosen}\n tabIndex={index === tabStop ? 0 : -1}\n className={cn(\n 'eb:flex eb:w-full eb:items-start eb:gap-2 eb:rounded eb:px-2 eb:py-1.5 eb:text-left eb:text-[12.5px] eb:leading-snug eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n chosen ? 'eb:bg-primary/10 eb:text-foreground' : 'eb:text-foreground-lighter eb:hover:bg-muted'\n )}\n onClick={() => onSubject(option.text)}\n >\n <Check className={cn('eb:mt-0.5 eb:size-3.5 eb:shrink-0', chosen ? 'eb:text-primary' : 'eb:invisible')} aria-hidden=\"true\" />\n <span className=\"eb:min-w-0 eb:flex-1 eb:break-words\">{option.text}</span>\n {option.yours && <span className=\"eb:shrink-0 eb:text-[11px] eb:text-foreground-muted\">{t('yours')}</span>}\n </button>\n );\n })}\n </div>\n <div className=\"eb:flex eb:items-center eb:gap-1.5 eb:px-1 eb:pb-0.5\">\n <button type=\"button\" className={chipClassName()} disabled={running || !ready} onClick={run} data-subject-ideas-button>\n {running ? spinner : <Sparkles className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t('More ideas')}\n </button>\n <Button type=\"button\" variant=\"ghost\" size=\"xs\" className=\"eb:ml-auto\" disabled={running} onClick={done}>\n {t('Done')}\n </Button>\n </div>\n </div>\n )}\n {running && (\n <p className=\"eb:flex eb:items-center eb:gap-2 eb:text-[12px] eb:text-foreground-muted\" role=\"status\" aria-live=\"polite\">\n {t('Reading the email…')}\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"eb:h-auto eb:px-0\" onClick={cancel}>\n {t('Cancel')}\n </Button>\n </p>\n )}\n {error && (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n )}\n </div>\n );\n}\n","// @ts-check\n/**\n * THE AI MODULE — `createAi()`, given to the builder as `ai`.\n *\n * import { createAi } from 'best-email-builder/ai';\n * <EmailBuilder storage={storage} ai={createAi()} />\n *\n * Without it, the builder has no AI button, and none of the AI's code or\n * texts is in the application's bundle: the core never imports this folder.\n * With it, the builder gets what the module can do:\n *\n * generate the Generate button (an email written from a purpose)\n * rewrite \"Rewrite with AI\" in the properties of a text or a button\n * suggestSubjects \"Suggest subjects\" under the subject\n *\n * Where the model runs:\n *\n * createAi() the routes of createEmailBuilderHandler({ generator })\n * createAi({ baseUrl, headers }) the same routes elsewhere, or with the user's credentials\n * createAi({ provider, askKey: true }) the AI runs in the page with each user's own key\n * (OpenRouter, Gemini, Groq… — see providers.js);\n * the builder asks for it once (the Connect dialog)\n * createAi({ provider: [...], askKey: true })\n * the same, the user choosing the provider in that\n * dialog (Claude too: claudeProvider in claude.js),\n * and the model among those the provider lists\n * createAi({ provider, apiKey }) the same with one key for all (prototypes: the key\n * is then in the page)\n * createAi({ model }) the generator runs in the page with this model\n * createAi({ generate, rewrite, … }) functions of your own: only those given are on\n *\n * Each of `generate`, `rewrite` and `suggestSubjects` can also be `false`,\n * to leave that part out — its button with it.\n */\n\nimport { createHttpCaller } from '../storage/client.js';\nimport { createEmailGenerator } from './generator.js';\nimport { AI_MESSAGES } from './i18n/index.js';\nimport { createKeyStore } from './keys.js';\nimport { providerEmailModel, resolveProvider } from './providers.js';\nimport ConnectAi from './ConnectAi.jsx';\nimport GenerateButton from './GenerateButton.jsx';\nimport RewriteText from './RewriteText.jsx';\nimport SubjectIdeas from './SubjectIdeas.jsx';\n\n/**\n * @typedef {import('./generator.js').GenerateInput} GenerateInput\n * @typedef {import('./generator.js').GeneratedEmail} GeneratedEmail\n * @typedef {import('./rewrite.js').RewriteInput} RewriteInput\n * @typedef {import('./subjects.js').SubjectsInput} SubjectsInput\n * @typedef {{signal?: AbortSignal}} CallOptions\n * @typedef {(input: GenerateInput, options?: CallOptions) => Promise<GeneratedEmail | Omit<GeneratedEmail, 'warnings'|'attempts'>>} GenerateFn\n * @typedef {(input: RewriteInput, options?: CallOptions) => Promise<{text: string}>} RewriteFn\n * @typedef {(input: SubjectsInput, options?: CallOptions) => Promise<{subjects: string[]}>} SuggestSubjectsFn\n *\n * @typedef {{\n * baseUrl?: string,\n * headers?: Record<string, string> | (() => Record<string, string>),\n * fetch?: typeof fetch,\n * model?: import('./generator.js').EmailModel,\n * provider?: ProviderOption | ProviderOption[],\n * apiKey?: string,\n * askKey?: boolean,\n * compile?: (mjml: string) => Promise<{warnings?: {message: string}[]}>,\n * maxAttempts?: number,\n * generate?: GenerateFn | boolean,\n * rewrite?: RewriteFn | boolean,\n * suggestSubjects?: SuggestSubjectsFn | boolean,\n * }} AiOptions\n * `provider`: an id of `AI_PROVIDERS` ('openrouter', 'gemini', 'groq'…), or\n * `{ id, model, baseUrl, headers }` for another model or an endpoint of your\n * own — or a list of them, with `askKey`: the user chooses (the first is\n * offered first). `apiKey`: one key for every user. `askKey`: each user\n * connects their own (off by default). `compile`: with `provider` or `model`, MJML's own\n * warnings fed back to the model (see createEmailGenerator).\n *\n * @typedef {Readonly<{\n * generate: GenerateFn | null,\n * rewrite: RewriteFn | null,\n * suggestSubjects: SuggestSubjectsFn | null,\n * key: import('./keys.js').AiKey | null,\n * messages: Record<string, Record<string, string>>,\n * components: Readonly<{Generate: typeof GenerateButton, Rewrite: typeof RewriteText, Subjects: typeof SubjectIdeas, Connect: typeof ConnectAi | null}>,\n * }>} EmailBuilderAi\n * What the builder reads. `key`: the user's key, with `askKey` (keys.js).\n * `messages`: the module's own translations, by locale, merged under the\n * host's `messages`. `components.Connect`: the dialog that asks for the key.\n */\n\n/** @typedef {string | Partial<import('./providers.js').AiProvider>} ProviderOption */\n\nconst PARTS = /** @type {const} */ (['generate', 'rewrite', 'suggestSubjects']);\nconst ROUTES = { generate: '/generate', rewrite: '/rewrite', suggestSubjects: '/subjects' };\n\n/**\n * @param {AiOptions} [options]\n * @returns {EmailBuilderAi}\n */\nexport function createAi(options = {}) {\n if (!options || typeof options !== 'object') throw new TypeError('createAi: options must be an object');\n const { model, maxAttempts, compile, baseUrl, headers, fetch: fetchImpl, provider: providerOption, apiKey, askKey = false } = options;\n const usesProvider = providerOption !== undefined;\n const http = baseUrl !== undefined || headers !== undefined || (fetchImpl !== undefined && !usesProvider);\n if (model !== undefined && typeof model !== 'function') throw new TypeError('createAi: `model` must be a function');\n if (model && http) throw new TypeError('createAi: pass `model` (the AI runs in the page) or `baseUrl` (on the server), not both');\n if (usesProvider && (model || http)) {\n throw new TypeError('createAi: `provider` runs the AI in the page — not with `model`, `baseUrl` or `headers` (give the provider `{ id, model, baseUrl, headers }`)');\n }\n if (!usesProvider && (apiKey !== undefined || options.askKey !== undefined)) {\n throw new TypeError('createAi: `apiKey` and `askKey` go with a `provider` (e.g. provider: \"openrouter\")');\n }\n if (apiKey !== undefined && (typeof apiKey !== 'string' || !apiKey)) throw new TypeError('createAi: `apiKey` must be a non-empty string');\n if (compile !== undefined && (typeof compile !== 'function' || (!model && !usesProvider))) {\n throw new TypeError('createAi: `compile` must be a function, and goes with `provider` or `model` (the AI in the page)');\n }\n if (typeof askKey !== 'boolean') throw new TypeError('createAi: `askKey` must be true or false');\n if (apiKey && askKey) throw new TypeError('createAi: `apiKey` (one key for all) or `askKey` (each user\\'s own), not both');\n const choice = Array.isArray(providerOption);\n if (choice && (!providerOption.length || !askKey)) {\n throw new TypeError('createAi: a list of providers is for `askKey: true` — each user chooses one and connects their own key');\n }\n /** @type {import('./providers.js').AiProvider[]} */\n const providers = usesProvider ? (choice ? providerOption : [providerOption]).map(providerOf) : [];\n if (new Set(providers.map((provider) => provider.id)).size !== providers.length) throw new TypeError('createAi: each provider once');\n const [provider = null] = providers;\n if (provider && !apiKey && !askKey && !provider.keyless) {\n throw new TypeError(`createAi: ${provider.name} needs a key — \\`apiKey\\`, or \\`askKey: true\\` to let each user connect their own`);\n }\n for (const part of PARTS) {\n const value = options[part];\n if (value !== undefined && typeof value !== 'boolean' && typeof value !== 'function') {\n throw new TypeError(`createAi: \\`${part}\\` must be a function, true or false`);\n }\n }\n\n // Functions of the host's own and nothing else: only those are on.\n const ownOnly = !model && !http && PARTS.some((part) => typeof options[part] === 'function');\n /** @type {Partial<Record<typeof PARTS[number], Function>>} */\n let transport = {};\n const key = provider && askKey ? createKeyStore(providers, { fetch: fetchImpl }) : null;\n if (provider) {\n /** @type {Map<string, import('./generator.js').EmailModel>} */\n const models = new Map();\n const keyFor = () => (key ? key.get() : apiKey || '');\n /** The model the user chose of the provider in use, made once. @param {string} id */\n const modelOf = (id) => {\n const chosen = /** @type {import('./providers.js').AiProvider} */ (providers.find((item) => item.id === id));\n const name = key ? key.model(id) : chosen.model;\n const slot = `${id}\\n${name}`;\n if (!models.has(slot)) {\n models.set(\n slot,\n chosen.createModel\n ? chosen.createModel({ apiKey: keyFor, fetch: fetchImpl, ...(name ? { model: name } : {}) })\n : providerEmailModel({ provider: chosen, fetch: fetchImpl, apiKey: keyFor, ...(name ? { model: name } : {}) })\n );\n }\n return /** @type {import('./generator.js').EmailModel} */ (models.get(slot));\n };\n /** @type {import('./generator.js').EmailModel} */\n const model = async (request) => {\n // The user's key, asked when there is none — they may choose another\n // provider there ('' when they cancel: KEY_MISSING).\n if (key && !key.get() && !providers.find((item) => item.id === key.provider.id)?.keyless) await key.request();\n return modelOf(key ? key.provider.id : provider.id)(request);\n };\n const generator = createEmailGenerator({ model, maxAttempts, compile });\n for (const part of PARTS) transport[part] = key ? askedAgain(generator[part], key) : generator[part];\n } else if (model) {\n transport = createEmailGenerator({ model, maxAttempts, compile });\n } else if (!ownOnly) {\n const { call } = createHttpCaller({ baseUrl, headers, fetch: fetchImpl });\n for (const part of PARTS) {\n transport[part] = (/** @type {object} */ input, /** @type {CallOptions} */ { signal } = {}) =>\n call(ROUTES[part], { method: 'POST', body: JSON.stringify(input), signal });\n }\n }\n\n /** @param {typeof PARTS[number]} part */\n const resolve = (part) => {\n const value = options[part];\n if (typeof value === 'function') return value;\n if (value === false) return null;\n return transport[part] || null;\n };\n\n return Object.freeze({\n generate: /** @type {GenerateFn|null} */ (resolve('generate')),\n rewrite: /** @type {RewriteFn|null} */ (resolve('rewrite')),\n suggestSubjects: /** @type {SuggestSubjectsFn|null} */ (resolve('suggestSubjects')),\n key,\n messages: AI_MESSAGES,\n components: Object.freeze({ Generate: GenerateButton, Rewrite: RewriteText, Subjects: SubjectIdeas, Connect: key ? ConnectAi : null }),\n });\n}\n\n/**\n * A provider of the list: an id, `{ id, model, … }`, or one with its own\n * `createModel` (claudeProvider) taken as it is.\n * @param {ProviderOption} option\n * @returns {import('./providers.js').AiProvider}\n */\nfunction providerOf(option) {\n if (option && typeof option === 'object' && typeof option.createModel === 'function') {\n if (!option.id || !option.name) throw new TypeError('createAi: a provider with `createModel` needs an `id` and a `name`');\n return /** @type {import('./providers.js').AiProvider} */ (option);\n }\n return resolveProvider(/** @type {any} */ (option));\n}\n\n/**\n * A key the provider refuses is forgotten and asked again, once, in the same\n * action: the user pastes a good one and the email still comes.\n * @param {Function} run\n * @param {import('./keys.js').AiKey} key\n */\nfunction askedAgain(run, key) {\n return async (/** @type {any} */ input, /** @type {any} */ options) => {\n try {\n return await run(input, options);\n } catch (error) {\n if (/** @type {any} */ (error)?.code !== 'KEY_REFUSED' || options?.signal?.aborted) throw error;\n key.refuse();\n try {\n return await run(input, options);\n } catch (again) {\n if (/** @type {any} */ (again)?.code === 'KEY_REFUSED') key.refuse();\n throw again;\n }\n }\n };\n}\n"],"mappings":"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","names":["kept","useEffect","useId","useRef","useState","Gift","models","jsx","jsxs","useState","useRef","useId","useEffect","Gift","useEffect","useMemo","useRef","useState","Check","Search","TriangleAlert","Fragment","jsx","jsxs","useState","useRef","useEffect","useMemo","Search","Check","TriangleAlert","useEffect","useRef","useState","ChevronDown","Loader2","Sparkles","TriangleAlert","jsx","jsxs","useState","useRef","useEffect","Loader2","Sparkles","ChevronDown","TriangleAlert","useEffect","useRef","useState","Check","Loader2","Sparkles","TriangleAlert","jsx","jsxs","useState","useRef","useEffect","Loader2","Sparkles","Check","TriangleAlert","provider","model"]}
1
+ {"version":3,"sources":["../src/ai/keys.js","../src/ai/ConnectAi.jsx","../src/ai/ModelPicker.jsx","../src/ai/GenerateButton.jsx","../src/ai/chip.js","../src/ai/RewriteText.jsx","../src/ai/SubjectIdeas.jsx","../src/ai/createAi.js"],"sourcesContent":["// @ts-check\n/**\n * THE USER'S KEY — when the AI runs in the page with `askKey: true`, each\n * user connects their own provider key, once.\n *\n * The first AI action asks for it (the Connect dialog, `ConnectAi.jsx`); it is\n * kept in this browser (localStorage, one entry per provider) and sent only to\n * the provider. A key the provider refuses is forgotten, and asked again.\n * Given several providers, the user chooses one in the same dialog; the\n * choice is kept too, and so is the model chosen for each provider.\n *\n * ai.key.get(id?) the key in use, or the one kept for that provider ('' when none)\n * ai.key.set(key) connect a key without the dialog\n * ai.key.forget() disconnect: the next AI action asks again\n * ai.key.request() ask the user now → the key, or '' when they cancel\n * ai.key.choose(id) use another of the providers (its kept key, if any)\n * ai.key.model(id?) the model chosen for that provider (its own by default)\n * ai.key.chooseModel(model, id?) write with another of its models\n * ai.key.listModels(id?, { apiKey, address, signal }) the models it offers (providers.js)\n * ai.key.address(id?) where an OpenAI-compatible API of the user's own answers\n * ai.key.setAddress(url, id?) keep it (for a provider without an address of its own)\n * ai.key.ready() whether the provider in use can write: its key (unless it\n * needs none), its address, its model\n * ai.key.subscribe(fn) called on every change; returns the unsubscribe\n * ai.key.provider the provider in use: name, model, where to get a key, what is free\n * ai.key.providers all those the user can choose from\n */\n\nimport { listProviderModels } from './providers.js';\n\n/** @typedef {import('./providers.js').AiProvider} AiProvider */\n/** @typedef {import('./providers.js').ModelChoice} ModelChoice */\n\nconst STORAGE_PREFIX = 'best-email-builder:ai-key:';\nconst CHOICE_SLOT = 'best-email-builder:ai-provider';\nconst MODEL_PREFIX = 'best-email-builder:ai-model:';\nconst ADDRESS_PREFIX = 'best-email-builder:ai-address:';\n\n/** localStorage when there is one that works (not in private modes that throw, not on the server). */\nfunction browserStorage() {\n try {\n const storage = globalThis.localStorage;\n return storage && typeof storage.getItem === 'function' ? storage : null;\n } catch {\n return null;\n }\n}\n\n/**\n * @typedef {Readonly<{id: string, name: string, model: string|null, keyUrl: string|null, keyHint: string, free: string|null, publicModels: boolean, keyless: boolean, needsAddress: boolean}>} ProviderChoice\n * `keyless`: a key is optional (a server of the user's own may need none);\n * `needsAddress`: the user gives the address of the API (an\n * OpenAI-compatible one: LM Studio, vLLM, a proxy…).\n * @typedef {{\n * provider: ProviderChoice,\n * providers: ReadonlyArray<ProviderChoice>,\n * get: (id?: string) => string,\n * set: (key: string) => void,\n * forget: () => void,\n * request: () => Promise<string>,\n * choose: (id: string) => void,\n * model: (id?: string) => string,\n * chooseModel: (model: string, id?: string) => void,\n * listModels: (id?: string, options?: {apiKey?: string, address?: string, signal?: AbortSignal}) => Promise<ModelChoice[]>,\n * address: (id?: string) => string,\n * setAddress: (url: string, id?: string) => void,\n * ready: () => boolean,\n * subscribe: (listener: () => void) => () => void,\n * state: () => Readonly<{asking: boolean, refused: boolean, connected: boolean}>,\n * answer: (key: string|null) => void,\n * refuse: () => void,\n * }} AiKey\n * `state`, `answer` and `refuse` serve the Connect dialog and the module:\n * whether the user is being asked, whether the last key was refused;\n * `answer('')` connects without a key (a keyless provider), `answer(null)`\n * cancels. `listModels`: with the key and address given, else those kept\n * (no key is needed for a provider whose list is public, OpenRouter's).\n */\n\n/** @param {AiProvider} provider @returns {ProviderChoice} */\nconst choiceOf = (provider) =>\n Object.freeze({\n id: provider.id,\n name: provider.name,\n model: provider.model ?? null,\n keyUrl: provider.keyUrl,\n keyHint: provider.keyHint,\n free: provider.free,\n publicModels: Boolean(provider.publicModels),\n keyless: Boolean(provider.keyless),\n needsAddress: !provider.createModel && !provider.baseUrl,\n });\n\n/** An address of the user's own, as the API takes it: http(s), no trailing slash. @param {string} url */\nconst cleanAddress = (url) => String(url || '').trim().replace(/\\/+$/, '');\n\n/**\n * @param {AiProvider | AiProvider[]} providerOrList one provider, or those the user chooses from\n * @param {{fetch?: typeof fetch}} [options] the fetch that lists the models\n * @returns {AiKey}\n */\nexport function createKeyStore(providerOrList, { fetch: fetchImpl } = {}) {\n const originals = Array.isArray(providerOrList) ? providerOrList : [providerOrList];\n const providers = Object.freeze(originals.map(choiceOf));\n if (!providers.length) throw new TypeError('createKeyStore: at least one provider');\n const known = (/** @type {unknown} */ id) => providers.some((provider) => provider.id === id);\n /** Kept here too: a browser that does not store still works for the page's life. */\n const memory = new Map();\n let refused = false;\n /** @type {{promise: Promise<string>, resolve: (key: string) => void}|null} */\n let pending = null;\n /** @type {Set<() => void>} */\n const listeners = new Set();\n /** @type {ReturnType<AiKey['state']>} */\n let snapshot;\n\n /** @param {string} slot */\n const read = (slot) => {\n try {\n return browserStorage()?.getItem(slot) || '';\n } catch {\n return '';\n }\n };\n /** @param {string} slot @param {string} value */\n const write = (slot, value) => {\n try {\n const storage = browserStorage();\n if (value) storage?.setItem(slot, value);\n else storage?.removeItem(slot);\n } catch {\n // Full, or blocked: the key stays in memory.\n }\n };\n /** @param {string} id */\n const keyOf = (id) => (memory.has(id) ? memory.get(id) : read(STORAGE_PREFIX + id));\n /** @param {string} id @param {string} value */\n const keep = (id, value) => {\n memory.set(id, value);\n write(STORAGE_PREFIX + id, value);\n };\n // The provider chosen last time; else the first one with a key; else the first.\n const kept = read(CHOICE_SLOT);\n let current = known(kept) ? kept : (providers.find((provider) => keyOf(provider.id)) || providers[0]).id;\n /** @param {string} [id] */\n const get = (id) => keyOf(id === undefined ? current : String(id));\n /** @param {string} [id] */\n const providerId = (id) => {\n if (id === undefined) return current;\n if (!known(id)) throw new TypeError(`ai.key: no provider \"${id}\" — one of ${providers.map((provider) => provider.id).join(', ')}`);\n return id;\n };\n /** The models listed, by provider and key: a dialog opened again shows them at once. */\n const lists = new Map();\n /** @param {ProviderChoice} choice */\n const modelOf = (choice) => (memory.get(MODEL_PREFIX + choice.id) ?? read(MODEL_PREFIX + choice.id)) || choice.model || '';\n /** @param {ProviderChoice} choice */\n const addressOf = (choice) =>\n choice.needsAddress ? cleanAddress(memory.get(ADDRESS_PREFIX + choice.id) ?? read(ADDRESS_PREFIX + choice.id)) : '';\n /** The provider in use can write: its key (unless it needs none), its address, its model. */\n const ready = () => {\n const choice = /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === current));\n if (refused) return false;\n if (!choice.keyless && !get()) return false;\n if (choice.needsAddress && !addressOf(choice)) return false;\n return Boolean(modelOf(choice));\n };\n const changed = () => {\n snapshot = Object.freeze({ asking: Boolean(pending), refused, connected: ready() });\n for (const listener of [...listeners]) listener();\n };\n snapshot = Object.freeze({ asking: false, refused: false, connected: ready() });\n\n /** @param {string|null} key null: cancelled */\n const answer = (key) => {\n const value = String(key || '').trim();\n if (value) keep(current, value);\n // Connected, with a key or (keyless) without: the last refusal is over.\n if (key !== null && (value || get() || /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === current)).keyless)) refused = false;\n const waiting = pending;\n pending = null;\n changed();\n waiting?.resolve(value);\n };\n\n return Object.freeze({\n get provider() {\n return /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === current));\n },\n providers,\n get,\n set: (/** @type {string} */ key) => {\n if (typeof key !== 'string' || !key.trim()) throw new TypeError('ai.key.set: a non-empty key');\n answer(key);\n },\n forget: () => {\n keep(current, '');\n changed();\n },\n choose: (/** @type {string} */ id) => {\n if (!known(id)) throw new TypeError(`ai.key.choose: one of ${providers.map((provider) => provider.id).join(', ')}`);\n if (id === current) return;\n current = id;\n refused = false;\n write(CHOICE_SLOT, id);\n changed();\n },\n model: (/** @type {string|undefined} */ id) => modelOf(/** @type {ProviderChoice} */ (providers.find((provider) => provider.id === providerId(id)))),\n address: (/** @type {string|undefined} */ id) => addressOf(/** @type {ProviderChoice} */ (providers.find((provider) => provider.id === providerId(id)))),\n setAddress: (/** @type {string} */ url, /** @type {string|undefined} */ id) => {\n const choice = /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === providerId(id)));\n if (!choice.needsAddress) throw new TypeError(`ai.key.setAddress: ${choice.name} has an address of its own`);\n const value = cleanAddress(url);\n if (value && !/^https?:\\/\\/[^\\s/]+/i.test(value)) throw new TypeError('ai.key.setAddress: an http(s) address, e.g. http://localhost:1234/v1');\n memory.set(ADDRESS_PREFIX + choice.id, value);\n write(ADDRESS_PREFIX + choice.id, value);\n changed();\n },\n ready,\n chooseModel: (/** @type {string} */ model, /** @type {string|undefined} */ id) => {\n if (typeof model !== 'string' || !model.trim()) throw new TypeError('ai.key.chooseModel: a model id');\n const choice = /** @type {ProviderChoice} */ (providers.find((provider) => provider.id === providerId(id)));\n // The provider's own model is kept as no choice: a new default reaches the user.\n const value = model.trim() === choice.model ? '' : model.trim();\n memory.set(MODEL_PREFIX + choice.id, value);\n write(MODEL_PREFIX + choice.id, value);\n changed();\n },\n listModels: async (/** @type {string|undefined} */ id, { apiKey, address, signal } = {}) => {\n const at = providers.findIndex((provider) => provider.id === providerId(id));\n const key = String(apiKey ?? keyOf(providers[at].id)).trim();\n const where = providers[at].needsAddress ? cleanAddress(address ?? addressOf(providers[at])) : '';\n if (providers[at].needsAddress && !where) return [];\n const slot = `${providers[at].id}\\n${key}\\n${where}`;\n const provider = where ? { ...originals[at], baseUrl: where } : originals[at];\n if (!lists.has(slot)) lists.set(slot, await listProviderModels({ provider, apiKey: key, fetch: fetchImpl, signal }));\n return lists.get(slot);\n },\n request: () => {\n if (!pending) {\n /** @type {(key: string) => void} */\n let resolve = () => {};\n const promise = new Promise((done) => (resolve = done));\n pending = { promise, resolve };\n changed();\n }\n return pending.promise;\n },\n subscribe: (/** @type {() => void} */ listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n state: () => snapshot,\n answer: (/** @type {string|null} */ key) => answer(key ?? null),\n refuse: () => {\n keep(current, '');\n refused = true;\n changed();\n },\n });\n}\n","import { useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';\nimport { ArrowUpRight, Eye, EyeOff, KeyRound, ShieldCheck, TriangleAlert } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { cn } from '../ui/primitives/cn.js';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { Input, Label } from '../ui/primitives/fields.jsx';\nimport { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '../ui/primitives/overlays.jsx';\nimport ModelPicker from './ModelPicker.jsx';\n\n/** A key this long is complete enough to ask the provider for its models. */\nconst LISTABLE_KEY = 20;\n/** An address the API can be asked at (keys.js keeps it without the trailing slash). */\nconst ADDRESS = /^https?:\\/\\/[^\\s/]+/i;\n\n/**\n * CONNECT — the dialog that asks the user for their provider key, when the AI\n * runs in the page with `createAi({ provider, askKey: true })`.\n *\n * It opens by itself when an AI action needs a key and there is none, or the\n * provider refused the last one; the action goes on once the key is given.\n * Cancel ends the action (\"Connect an AI provider first\"). The key is kept in\n * this browser and sent only to the provider (see keys.js).\n *\n * Given several providers (`createAi({ provider: [...] })`), the user chooses\n * one at the top; a provider whose key this browser already keeps needs no\n * new one. Under the key, the model: the provider's own, or another of its\n * list (ModelPicker.jsx). An OpenAI-compatible API of the user's own\n * ('openai-compatible': LM Studio, vLLM, a proxy…) takes its address first,\n * and a key only if it needs one.\n */\nexport default function ConnectAi({ aiKey }) {\n const { t } = useUi();\n const state = useSyncExternalStore(aiKey.subscribe, aiKey.state, aiKey.state);\n const [value, setValue] = useState('');\n const [address, setAddress] = useState('');\n const [shown, setShown] = useState(false);\n const [chosenId, setChosenId] = useState(aiKey.provider.id);\n /** The models chosen in the dialog, by provider, until Connect. */\n const [picks, setPicks] = useState(/** @type {Record<string, string>} */ ({}));\n const [modelsOpen, setModelsOpen] = useState(false);\n const inputRef = useRef(/** @type {HTMLInputElement|null} */ (null));\n const addressRef = useRef(/** @type {HTMLInputElement|null} */ (null));\n const choicesRef = useRef(/** @type {HTMLDivElement|null} */ (null));\n const inputId = useId();\n const hintId = useId();\n const addressId = useId();\n const addressHintId = useId();\n const choices = aiKey.providers;\n const chosen = choices.find((provider) => provider.id === chosenId) || aiKey.provider;\n const { name, keyUrl, keyHint, free, keyless, needsAddress } = chosen;\n\n // Each time it opens: the provider in use, an empty field, the key hidden.\n useEffect(() => {\n if (!state.asking) return;\n setChosenId(aiKey.provider.id);\n setAddress(aiKey.address());\n setValue('');\n setShown(false);\n setPicks({});\n setModelsOpen(false);\n }, [state.asking, aiKey]);\n\n const key = value.trim();\n // The key this browser keeps for the provider chosen (none for one refused).\n const saved = aiKey.get(chosen.id);\n // A key from elsewhere (another provider's) is the usual mistake: said, not blocked.\n const unusual = Boolean(key && keyHint && !key.startsWith(keyHint));\n const refused = state.refused && chosen.id === aiKey.provider.id;\n /** @param {string} id */\n const modelFor = (id) => picks[id] ?? aiKey.model(id);\n const where = address.trim().replace(/\\/+$/, '');\n const addressOk = !needsAddress || ADDRESS.test(where);\n // Connect: a key (unless it needs none), the address, a model to write with.\n const ready = Boolean((key || saved || keyless) && (!needsAddress || (where && addressOk)) && modelFor(chosen.id));\n\n const submit = (/** @type {React.FormEvent} */ event) => {\n event.preventDefault();\n if (!ready) return;\n if (needsAddress && where !== aiKey.address(chosen.id)) aiKey.setAddress(where, chosen.id);\n if (chosen.id !== aiKey.provider.id) aiKey.choose(chosen.id);\n if (picks[chosen.id] && picks[chosen.id] !== aiKey.model(chosen.id)) aiKey.chooseModel(picks[chosen.id], chosen.id);\n aiKey.answer(key || saved);\n };\n\n /** @param {string} id */\n const choose = (id) => {\n setChosenId(id);\n setAddress(aiKey.address(id));\n setValue('');\n setModelsOpen(false);\n };\n\n /** Arrows move through the providers and choose, as in any radio group. */\n const onChoicesKeyDown = (/** @type {React.KeyboardEvent} */ event) => {\n const at = choices.findIndex((provider) => provider.id === chosen.id);\n const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];\n const to = step ? (at + step + choices.length) % choices.length : event.key === 'Home' ? 0 : event.key === 'End' ? choices.length - 1 : -1;\n if (to === -1) return;\n event.preventDefault();\n choose(choices[to].id);\n /** @type {HTMLElement|undefined} */ (choicesRef.current?.querySelectorAll('[role=\"radio\"]')[to])?.focus();\n };\n\n return (\n <Dialog open={state.asking} onOpenChange={(open) => !open && aiKey.answer(null)}>\n <DialogContent\n className={cn('eb:max-h-[calc(100dvh-2rem)] eb:gap-0 eb:overflow-y-auto eb:p-0', choices.length > 1 ? 'eb:sm:max-w-xl' : 'eb:sm:max-w-md')}\n data-testid=\"eb-ai-connect\"\n onOpenAutoFocus={(event) => {\n event.preventDefault();\n (needsAddress && !where ? addressRef : inputRef).current?.focus();\n }}\n // Esc closes the list of models first, then the dialog. From inside\n // the list, the list closes itself (and gives the focus back to its field).\n onEscapeKeyDown={(event) => {\n if (!modelsOpen) return;\n event.preventDefault();\n if (!(event.target instanceof Element && event.target.closest('[data-eb-models]'))) setModelsOpen(false);\n }}\n >\n {/* min-w-0: the long names of the list of models do not widen the dialog. */}\n <form onSubmit={submit} className=\"eb:min-w-0\">\n <div className=\"eb:flex eb:flex-col eb:gap-5 eb:px-6 eb:pt-6 eb:pb-5\">\n <DialogHeader className=\"eb:flex-row eb:items-start eb:gap-3.5 eb:pr-6\">\n <span className=\"eb:flex eb:size-10 eb:shrink-0 eb:items-center eb:justify-center eb:rounded-full eb:bg-primary/10 eb:text-primary\">\n <KeyRound className=\"eb:size-5\" aria-hidden=\"true\" />\n </span>\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\">\n <DialogTitle>{t('Connect {provider}', { provider: name })}</DialogTitle>\n <DialogDescription className=\"eb:text-[13px] eb:leading-relaxed eb:text-foreground-lighter\">\n {needsAddress\n ? t('The AI writes with an OpenAI-compatible API of your choice. Give its address, and its key if it needs one: this browser remembers them.')\n : t('The AI writes with your own {provider} key. Connect it once: this browser remembers it.', { provider: name })}\n </DialogDescription>\n </div>\n </DialogHeader>\n\n {choices.length > 1 && (\n <div\n ref={choicesRef}\n role=\"radiogroup\"\n aria-label={t('AI provider')}\n className=\"eb:grid eb:grid-cols-2 eb:gap-1.5 eb:sm:grid-cols-3\"\n onKeyDown={onChoicesKeyDown}\n data-testid=\"eb-ai-connect-providers\"\n >\n {choices.map((provider) => {\n const checked = provider.id === chosen.id;\n return (\n <button\n key={provider.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n tabIndex={checked ? 0 : -1}\n onClick={() => choose(provider.id)}\n className={cn(\n 'eb:flex eb:min-w-0 eb:flex-col eb:gap-0.5 eb:rounded-md eb:border eb:px-2.5 eb:py-2 eb:text-left eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n checked ? 'eb:border-primary eb:bg-primary/10' : 'eb:border-border eb:hover:bg-muted'\n )}\n >\n <span className=\"eb:flex eb:items-center eb:gap-1.5 eb:text-[13px] eb:leading-tight eb:font-medium eb:text-foreground\">\n <span className=\"eb:truncate\">{provider.name}</span>\n {aiKey.get(provider.id) && (\n <span className=\"eb:ml-auto eb:size-1.5 eb:shrink-0 eb:rounded-full eb:bg-success\" title={t('Key saved')} aria-label={t('Key saved')} />\n )}\n </span>\n {modelFor(provider.id) && (\n <span className=\"eb:truncate eb:font-mono eb:text-[10.5px] eb:text-foreground-muted\" title={modelFor(provider.id)}>\n {modelFor(provider.id)}\n </span>\n )}\n </button>\n );\n })}\n </div>\n )}\n\n {refused && (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-2 eb:rounded-md eb:border eb:border-destructive/30 eb:bg-destructive/10 eb:px-3 eb:py-2 eb:text-[12.5px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {t('{provider} refused the last key. Paste another one.', { provider: name })}\n </p>\n )}\n\n {(keyUrl || free) && (\n <div className=\"eb:flex eb:flex-wrap eb:items-center eb:gap-x-3 eb:gap-y-2\">\n {keyUrl && (\n <Button asChild variant=\"outline\" size=\"sm-compact\">\n <a\n href={keyUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n // A new tab, always — even where a link's target is ignored (webviews).\n onClick={(event) => {\n event.preventDefault();\n window.open(keyUrl, '_blank', 'noopener,noreferrer');\n }}\n data-testid=\"eb-ai-connect-get-key\"\n >\n {t('Get a key')}\n <ArrowUpRight aria-hidden=\"true\" />\n </a>\n </Button>\n )}\n {free && (\n <span className=\"eb:rounded-full eb:bg-success/10 eb:px-2.5 eb:py-1 eb:text-[11.5px] eb:font-medium eb:text-success\">\n {t(free)}\n </span>\n )}\n </div>\n )}\n\n {needsAddress && (\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\">\n <Label htmlFor={addressId}>{t('API address')}</Label>\n <Input\n ref={addressRef}\n id={addressId}\n type=\"url\"\n inputMode=\"url\"\n value={address}\n onChange={(event) => setAddress(event.target.value)}\n placeholder=\"http://localhost:1234/v1\"\n autoComplete=\"url\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n aria-invalid={where && !addressOk ? true : undefined}\n aria-describedby={addressHintId}\n className=\"eb:h-9 eb:font-mono eb:text-[12.5px]\"\n data-testid=\"eb-ai-connect-address\"\n />\n <p id={addressHintId} className=\"eb:text-[11.5px] eb:leading-relaxed eb:text-foreground-muted\">\n {where && !addressOk ? (\n <span className=\"eb:text-warning\">{t('An address starts with http:// or https://.')}</span>\n ) : (\n t('Any API that speaks OpenAI: LM Studio, vLLM, Ollama, a proxy of your own… It must accept requests from this page (CORS).')\n )}\n </p>\n </div>\n )}\n\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\">\n <Label htmlFor={inputId}>{keyless ? t('API key (optional)') : t('API key')}</Label>\n <div className=\"eb:relative\">\n <Input\n ref={inputRef}\n id={inputId}\n type={shown ? 'text' : 'password'}\n value={value}\n onChange={(event) => setValue(event.target.value)}\n placeholder={keyHint ? `${keyHint}…` : t('Paste the key')}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n aria-describedby={hintId}\n className=\"eb:h-9 eb:pr-10 eb:font-mono eb:text-[12.5px]\"\n data-testid=\"eb-ai-connect-key\"\n />\n <button\n type=\"button\"\n className=\"eb:absolute eb:top-1/2 eb:right-1 eb:flex eb:size-7 eb:-translate-y-1/2 eb:items-center eb:justify-center eb:rounded eb:text-foreground-muted eb:transition-colors eb:outline-none eb:hover:bg-muted eb:hover:text-foreground eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50\"\n onClick={() => setShown((current) => !current)}\n aria-label={shown ? t('Hide the key') : t('Show the key')}\n aria-pressed={shown}\n >\n {shown ? <EyeOff className=\"eb:size-4\" aria-hidden=\"true\" /> : <Eye className=\"eb:size-4\" aria-hidden=\"true\" />}\n </button>\n </div>\n <p id={hintId} className=\"eb:min-h-4 eb:text-[11.5px] eb:text-foreground-muted\">\n {unusual ? (\n <span className=\"eb:text-warning\">{t('{provider} keys usually start with {prefix}.', { provider: name, prefix: keyHint })}</span>\n ) : saved && !key ? (\n t('A key for {provider} is saved in this browser: Connect uses it, or paste another one.', { provider: name })\n ) : keyHint ? (\n t('{provider} keys start with {prefix}.', { provider: name, prefix: keyHint })\n ) : keyless ? (\n t('Leave it empty if the API needs no key.')\n ) : null}\n </p>\n </div>\n\n <ModelPicker\n aiKey={aiKey}\n provider={chosen}\n apiKey={key.length >= LISTABLE_KEY || (keyless && key) ? key : saved}\n address={addressOk ? where : ''}\n delay={key.length >= LISTABLE_KEY || (keyless && key) || where !== aiKey.address(chosen.id) ? 350 : 0}\n value={modelFor(chosen.id)}\n onChange={(model) => setPicks((current) => ({ ...current, [chosen.id]: model }))}\n open={modelsOpen}\n onOpenChange={setModelsOpen}\n />\n </div>\n\n <div className=\"eb:flex eb:items-start eb:gap-2 eb:border-t eb:border-border eb:bg-surface-75 eb:px-6 eb:py-3 eb:text-[11.5px] eb:leading-relaxed eb:text-foreground-lighter\">\n <ShieldCheck className=\"eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-foreground-muted\" aria-hidden=\"true\" />\n <span>\n {needsAddress\n ? t('The address and the key stay in this browser; the key is sent only to that address.')\n : t('The key stays in this browser and is sent only to {provider}.', { provider: name })}\n </span>\n </div>\n\n <DialogFooter className=\"eb:border-t eb:border-border eb:px-6 eb:py-4\">\n <Button type=\"button\" variant=\"ghost\" onClick={() => aiKey.answer(null)}>\n {t('Cancel')}\n </Button>\n <Button type=\"submit\" disabled={!ready} data-testid=\"eb-ai-connect-submit\">\n {t('Connect')}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n","import { useEffect, useId, useMemo, useRef, useState } from 'react';\nimport { Check, ChevronDown, LoaderCircle, Search } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { cn } from '../ui/primitives/cn.js';\n\n/**\n * MODEL — the model of the provider chosen, in the Connect dialog: a field\n * that opens the provider's list of models in place (not over the dialog, so\n * the list scrolls like the rest of it).\n *\n * The list comes from the provider with the key (OpenRouter's needs none;\n * an OpenAI-compatible API of the user's own, from the address given):\n * the default model first, then the free ones, then the newest. Type to\n * filter, ↑/↓ to move, Enter to choose, Esc to close. An id the list does not\n * have can be used as it is (\"Use “…”\"); without the list, the default model\n * still works.\n *\n * @param {{\n * aiKey: import('./keys.js').AiKey,\n * provider: import('./keys.js').ProviderChoice,\n * apiKey: string,\n * address?: string,\n * delay?: number,\n * value: string,\n * onChange: (model: string) => void,\n * open: boolean,\n * onOpenChange: (open: boolean) => void,\n * }} props\n * `apiKey`: the key the list is asked with ('' when there is none yet);\n * `address`: where the API answers, for a provider that needs one;\n * `delay`: how long to wait after they change (a key being pasted).\n */\nexport default function ModelPicker({ aiKey, provider, apiKey, address = '', delay = 0, value, onChange, open, onOpenChange }) {\n const { t } = useUi();\n const [list, setList] = useState(/** @type {{status: 'loading'|'ready'|'needs-key'|'needs-address'|'refused'|'failed', models: import('./providers.js').ModelChoice[]}} */ ({ status: 'loading', models: [] }));\n const [query, setQuery] = useState('');\n const [freeOnly, setFreeOnly] = useState(false);\n const [active, setActive] = useState(0);\n const triggerRef = useRef(/** @type {HTMLButtonElement|null} */ (null));\n const listRef = useRef(/** @type {HTMLDivElement|null} */ (null));\n const labelId = useId();\n const valueId = useId();\n const listId = useId();\n const hintId = useId();\n\n // The list of the provider, asked again when the key (or the address) changes.\n useEffect(() => {\n if (provider.needsAddress && !address) {\n setList({ status: 'needs-address', models: [] });\n return undefined;\n }\n if (!apiKey && !provider.publicModels && !provider.keyless) {\n setList({ status: 'needs-key', models: [] });\n return undefined;\n }\n const controller = new AbortController();\n setList({ status: 'loading', models: [] });\n const timer = setTimeout(() => {\n aiKey.listModels(provider.id, { apiKey, address, signal: controller.signal }).then(\n (models) => !controller.signal.aborted && setList({ status: 'ready', models }),\n (error) => !controller.signal.aborted && setList({ status: error?.code === 'KEY_REFUSED' ? 'refused' : 'failed', models: [] })\n );\n }, delay);\n return () => {\n clearTimeout(timer);\n controller.abort();\n };\n }, [aiKey, provider.id, provider.publicModels, provider.keyless, provider.needsAddress, apiKey, address, delay]);\n\n // Each time it opens: no search, the model in use under the keys.\n useEffect(() => {\n if (!open) return;\n setQuery('');\n setFreeOnly(false);\n }, [open]);\n\n // The default model first, then the one in use, then the list.\n const models = useMemo(() => {\n const listed = new Map(list.models.map((model) => [model.id, model]));\n const seen = new Set();\n /** @type {import('./providers.js').ModelChoice[]} */\n const all = [];\n const add = (/** @type {import('./providers.js').ModelChoice} */ model) => {\n if (model.id && !seen.has(model.id)) seen.add(model.id) && all.push(model);\n };\n for (const id of [provider.model, value]) if (id) add(listed.get(id) || { id, name: id, free: false });\n list.models.forEach(add);\n return all;\n }, [list.models, provider.model, value]);\n const hasFree = list.models.some((model) => model.free);\n const typed = query.trim();\n const matches = useMemo(() => {\n const words = typed.toLowerCase().split(/\\s+/).filter(Boolean);\n return models.filter((model) => (!freeOnly || model.free) && words.every((word) => `${model.id} ${model.name}`.toLowerCase().includes(word)));\n }, [models, typed, freeOnly]);\n // An id of one's own: a model the list does not have (or not yet).\n const custom = typed && !/\\s/.test(typed) && !models.some((model) => model.id === typed) ? typed : '';\n const count = matches.length + (custom ? 1 : 0);\n const current = models.find((model) => model.id === value);\n\n useEffect(() => setActive(Math.max(0, matches.findIndex((model) => model.id === value))), [matches, value]);\n useEffect(() => {\n if (open) listRef.current?.querySelector('[data-active=\"true\"]')?.scrollIntoView({ block: 'nearest' });\n }, [active, open]);\n\n const close = () => {\n triggerRef.current?.focus();\n onOpenChange(false);\n };\n /** @param {string} id */\n const pick = (id) => {\n onChange(id);\n close();\n };\n\n const onKeyDown = (/** @type {React.KeyboardEvent} */ event) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActive((at) => (count ? (at + (event.key === 'ArrowDown' ? 1 : -1) + count) % count : 0));\n } else if (event.key === 'Enter') {\n // The model, not the form: Connect is the button's.\n event.preventDefault();\n const id = active < matches.length ? matches[active]?.id : custom;\n if (id) pick(id);\n }\n };\n\n const optionId = (/** @type {number} */ at) => `${listId}-${at}`;\n const badges = (/** @type {import('./providers.js').ModelChoice} */ model) => (\n <>\n {model.free && (\n <span className=\"eb:shrink-0 eb:rounded-full eb:bg-success/10 eb:px-1.5 eb:py-px eb:text-[10.5px] eb:font-medium eb:text-success\">\n {t('Free')}\n </span>\n )}\n {model.id === provider.model && (\n <span className=\"eb:shrink-0 eb:rounded-full eb:bg-muted eb:px-1.5 eb:py-px eb:text-[10.5px] eb:font-medium eb:text-foreground-lighter\">{t('Default')}</span>\n )}\n </>\n );\n\n const status = {\n loading: (\n <>\n <LoaderCircle className=\"eb:size-3 eb:shrink-0 eb:animate-spin\" aria-hidden=\"true\" />\n {t('Loading the models of {provider}…', { provider: provider.name })}\n </>\n ),\n 'needs-key': t('Paste the key to see all the models of {provider}.', { provider: provider.name }),\n 'needs-address': t('Give the API address to see its models.'),\n refused: <span className=\"eb:text-warning\">{t('{provider} refused this key: its models could not be listed.', { provider: provider.name })}</span>,\n failed: provider.model\n ? t('The list of models could not be loaded. The default model works, or type the id of another.')\n : t('The list of models could not be loaded. Type the id of the model.'),\n ready: list.models.length\n ? hasFree\n ? t('{count} models, the free ones first.', { count: list.models.length })\n : t('{count} models.', { count: list.models.length })\n : t('{provider} lists no models here: type the id of the one you want.', { provider: provider.name }),\n }[list.status];\n\n return (\n <div className=\"eb:flex eb:flex-col eb:gap-1.5\" data-testid=\"eb-ai-connect-model-field\">\n <span id={labelId} className=\"eb:text-[12.5px] eb:leading-none eb:font-medium eb:text-foreground\">\n {t('Model')}\n </span>\n <button\n ref={triggerRef}\n type=\"button\"\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-labelledby={`${labelId} ${valueId}`}\n aria-describedby={hintId}\n onClick={() => onOpenChange(!open)}\n className={cn(\n 'eb:flex eb:h-9 eb:w-full eb:min-w-0 eb:items-center eb:gap-2 eb:rounded-md eb:border eb:border-input eb:bg-transparent eb:px-3 eb:text-left eb:shadow-xs eb:transition-[color,box-shadow] eb:outline-none eb:focus-visible:border-ring eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n open && 'eb:border-ring'\n )}\n data-testid=\"eb-ai-connect-model\"\n >\n <span id={valueId} className=\"eb:min-w-0 eb:truncate eb:font-mono eb:text-[12.5px] eb:text-foreground\">{value || t('Choose a model')}</span>\n {current && badges(current)}\n <ChevronDown className={cn('eb:ml-auto eb:size-4 eb:shrink-0 eb:text-foreground-muted eb:transition-transform', open && 'eb:rotate-180')} aria-hidden=\"true\" />\n </button>\n\n {open && (\n <div\n className=\"eb:flex eb:flex-col eb:overflow-hidden eb:rounded-md eb:border eb:border-border eb:bg-background\"\n onKeyDown={(event) => event.key === 'Escape' && close()}\n data-eb-models=\"\"\n data-testid=\"eb-ai-connect-models\"\n >\n <div className=\"eb:flex eb:items-center eb:gap-2 eb:border-b eb:border-border eb:p-2\">\n <div className=\"eb:relative eb:min-w-0 eb:flex-1\">\n <Search className=\"eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted\" aria-hidden=\"true\" />\n <input\n autoFocus\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listId}\n aria-autocomplete=\"list\"\n aria-activedescendant={count ? optionId(active) : undefined}\n aria-label={t('Search the models')}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={onKeyDown}\n placeholder={t('Search the models…')}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n autoCorrect=\"off\"\n spellCheck={false}\n className=\"eb:h-8 eb:w-full eb:rounded-md eb:bg-surface-75 eb:pr-2 eb:pl-7 eb:text-[13px] eb:text-foreground eb:outline-none eb:placeholder:text-foreground-muted eb:focus-visible:ring-2 eb:focus-visible:ring-ring/40\"\n data-testid=\"eb-ai-connect-model-search\"\n />\n </div>\n {hasFree && (\n <button\n type=\"button\"\n aria-pressed={freeOnly}\n onClick={() => setFreeOnly((on) => !on)}\n className={cn(\n 'eb:inline-flex eb:h-8 eb:shrink-0 eb:items-center eb:rounded-md eb:border eb:px-2.5 eb:text-[12px] eb:font-medium eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n freeOnly ? 'eb:border-success/40 eb:bg-success/10 eb:text-success' : 'eb:border-border eb:text-foreground-lighter eb:hover:bg-muted'\n )}\n >\n {t('Free only')}\n </button>\n )}\n </div>\n <div ref={listRef} id={listId} role=\"listbox\" aria-labelledby={labelId} className=\"eb:max-h-56 eb:overflow-y-auto eb:p-1\">\n {matches.map((model, at) => (\n <button\n key={model.id}\n id={optionId(at)}\n type=\"button\"\n role=\"option\"\n tabIndex={-1}\n aria-selected={model.id === value}\n data-active={at === active}\n data-model={model.id}\n onClick={() => pick(model.id)}\n onMouseMove={() => at !== active && setActive(at)}\n className={cn(\n 'eb:flex eb:w-full eb:min-w-0 eb:items-start eb:gap-2 eb:rounded-md eb:px-2 eb:py-1.5 eb:text-left',\n at === active ? 'eb:bg-accent eb:text-accent-foreground' : ''\n )}\n >\n <Check className={cn('eb:mt-0.5 eb:size-3.5 eb:shrink-0 eb:text-primary', model.id !== value && 'eb:invisible')} aria-hidden=\"true\" />\n <span className=\"eb:flex eb:min-w-0 eb:flex-1 eb:flex-col eb:gap-0.5\">\n <span className=\"eb:flex eb:min-w-0 eb:items-center eb:gap-1.5\">\n <span className=\"eb:truncate eb:text-[13px] eb:text-foreground\">{model.name}</span>\n {badges(model)}\n </span>\n {model.name !== model.id && <span className=\"eb:truncate eb:font-mono eb:text-[10.5px] eb:text-foreground-muted\">{model.id}</span>}\n </span>\n </button>\n ))}\n {custom && (\n <button\n id={optionId(matches.length)}\n type=\"button\"\n role=\"option\"\n tabIndex={-1}\n aria-selected={false}\n data-active={active === matches.length}\n onClick={() => pick(custom)}\n onMouseMove={() => active !== matches.length && setActive(matches.length)}\n className={cn(\n 'eb:flex eb:w-full eb:min-w-0 eb:items-center eb:gap-2 eb:rounded-md eb:px-2 eb:py-1.5 eb:text-left eb:text-[13px] eb:text-primary',\n active === matches.length ? 'eb:bg-accent' : ''\n )}\n data-testid=\"eb-ai-connect-model-custom\"\n >\n <span className=\"eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n <span className=\"eb:min-w-0 eb:truncate\">{t('Use “{model}”', { model: custom })}</span>\n </button>\n )}\n {!count && <p className=\"eb:px-2 eb:py-3 eb:text-center eb:text-[12px] eb:text-foreground-muted\">{t('No model matches.')}</p>}\n </div>\n </div>\n )}\n\n <p id={hintId} role=\"status\" className=\"eb:flex eb:min-h-4 eb:items-center eb:gap-1.5 eb:text-[11.5px] eb:text-foreground-muted\" data-testid=\"eb-ai-connect-models-status\">\n {status}\n </p>\n </div>\n );\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { Check, Loader2, Search, Sparkles, TriangleAlert } from 'lucide-react';\nimport { SEARCH_KEYS, SearchKeyHint, useSearchShortcut } from '../ui/primitives/searchShortcut.jsx';\nimport { useUi } from '../ui/context.jsx';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { Input, Textarea } from '../ui/primitives/fields.jsx';\nimport { SectionLabel } from '../ui/primitives/misc.jsx';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/primitives/overlays.jsx';\nimport { chipClassName } from './chip.js';\nimport { MAX_DETAILS_LENGTH } from './generator.js';\nimport { purposesByCategory } from './purposes.js';\nimport { brandTemplate, hasBrand } from '../features/brand/brand.js';\n\n/**\n * GENERATE — toolbar button and dialog: an email written from scratch.\n *\n * One decision for the user: what the email is for (a badge). Details are\n * optional; the language is the interface's, the sender and the variables\n * are the builder's own. The dialog says up front that the email in the\n * editor will be replaced — no confirmation after a minute of waiting.\n *\n * The result REPLACES the email in the editor, subject and preview text\n * included (they belong to the new email); the name is filled when empty.\n * A saved template counts as modified: the next save keeps it.\n */\n\n/** Lowercase, without accents: \"Été\" matches \"ete\". @param {string} text */\nconst fold = (text) =>\n text\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase();\n\nexport default function GenerateButton({ context, generate: generateEmail }) {\n const { editor, ready, isDirty, markDirty, template, setMeta, variables, sender, brand } = context;\n const { t, locale, notify } = useUi();\n const [open, setOpen] = useState(false);\n const [purpose, setPurpose] = useState(/** @type {string|null} */ (null));\n const [details, setDetails] = useState('');\n const [query, setQuery] = useState('');\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState('');\n const [seconds, setSeconds] = useState(0);\n const [dirty, setDirty] = useState(false);\n const abortRef = useRef(/** @type {AbortController|null} */ (null));\n const searchRef = useRef(/** @type {HTMLInputElement|null} */ (null));\n useSearchShortcut(searchRef);\n\n useEffect(() => () => abortRef.current?.abort(), []);\n useEffect(() => {\n if (!busy) return undefined;\n setSeconds(0);\n const timer = setInterval(() => setSeconds((s) => s + 1), 1000);\n return () => clearInterval(timer);\n }, [busy]);\n\n const groups = useMemo(() => {\n const words = fold(query.trim()).split(/\\s+/).filter(Boolean);\n return purposesByCategory()\n .map((group) => ({\n ...group,\n purposes: group.purposes.filter((entry) => {\n const label = fold(`${t(entry.label)} ${entry.label} ${t(group.label)}`);\n return words.every((word) => label.includes(word));\n }),\n }))\n .filter((group) => group.purposes.length > 0);\n }, [query, t]);\n\n const openDialog = () => {\n setError('');\n setDirty(isDirty());\n setOpen(true);\n };\n\n const close = (next) => {\n if (next) return;\n abortRef.current?.abort();\n setOpen(false);\n };\n\n const generate = async () => {\n if (busy || (!purpose && !details.trim())) return;\n setError('');\n setBusy(true);\n const controller = new AbortController();\n abortRef.current = controller;\n try {\n const email = await generateEmail(\n {\n purpose,\n details: details.trim(),\n language: String(locale || 'en').slice(0, 2),\n sender: sender || {},\n ...(hasBrand(brand) ? { brand: { primary: brand.primary, heading: brand.heading, font: brand.font } } : {}),\n variables: (variables || []).map((variable) => ({ key: variable.key, label: variable.label })),\n },\n { signal: controller.signal }\n );\n if (controller.signal.aborted) return;\n // The brand's logo where the model wrote the name (an uploaded logo\n // is not an https address the model may be given).\n editor.loadMjml(hasBrand(brand) ? brandTemplate(email.mjml, brand, { sender, t }) : email.mjml);\n setMeta((meta) => ({\n ...meta,\n name: meta.name || email.name,\n subject: email.subject || meta.subject,\n preheader: email.preheader || meta.preheader,\n }));\n if (template.id) markDirty();\n setOpen(false);\n notify('success', t('Your email is ready. Edit anything, then save.'));\n } catch (err) {\n if (!controller.signal.aborted) setError(err?.message ? t(err.message) : t('The email could not be generated.'));\n } finally {\n if (abortRef.current === controller) abortRef.current = null;\n setBusy(false);\n }\n };\n\n const cancel = () => {\n abortRef.current?.abort();\n setBusy(false);\n };\n\n const canGenerate = Boolean(purpose || details.trim());\n\n return (\n <>\n <Button variant=\"outline\" size=\"sm-compact\" disabled={!ready} onClick={openDialog}>\n <Sparkles />\n {t('Generate')}\n </Button>\n <Dialog open={open} onOpenChange={close}>\n <DialogContent\n className=\"eb:flex eb:max-h-[calc(100dvh-2rem)] eb:flex-col eb:gap-0 eb:p-0 eb:sm:max-w-2xl\"\n data-testid=\"eb-generate\"\n onOpenAutoFocus={(event) => {\n // The search field rather than the first badge.\n event.preventDefault();\n searchRef.current?.focus();\n }}\n >\n <DialogHeader className=\"eb:gap-1.5 eb:px-6 eb:pt-6 eb:pb-3\">\n <DialogTitle>{t('Generate an email')}</DialogTitle>\n <DialogDescription>{t('Pick what it is for. AI writes a complete email that opens here, ready to edit.')}</DialogDescription>\n </DialogHeader>\n\n <div className=\"eb:px-6 eb:pb-3\">\n <div className=\"eb:relative\">\n <Search className=\"eb:pointer-events-none eb:absolute eb:top-1/2 eb:left-2.5 eb:size-3.5 eb:-translate-y-1/2 eb:text-foreground-muted\" aria-hidden=\"true\" />\n <Input\n ref={searchRef}\n type=\"search\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder={t('Search: sale, password, birthday…')}\n aria-label={t('Search')}\n aria-keyshortcuts={SEARCH_KEYS}\n className=\"eb:pr-16 eb:pl-8 eb:[&::-webkit-search-cancel-button]:hidden\"\n disabled={busy}\n />\n <SearchKeyHint className=\"eb:absolute eb:top-1/2 eb:right-2 eb:-translate-y-1/2\" />\n </div>\n </div>\n\n {/* The scroll box is a div: a fieldset does not clip its overflow\n as a flex item, and the badges would run under the footer. */}\n <div className=\"eb:min-h-0 eb:flex-1 eb:overflow-y-auto eb:border-y eb:border-border eb:px-6 eb:py-4\" data-testid=\"eb-generate-purposes\">\n <fieldset disabled={busy} className=\"eb:m-0 eb:min-w-0 eb:border-0 eb:p-0\">\n <legend className=\"eb:sr-only\">{t('What is the email for?')}</legend>\n {groups.length === 0 && (\n <p className=\"eb:py-6 eb:text-center eb:text-[13px] eb:text-foreground-muted\">\n {t('Nothing matches. Describe your email in the details below.')}\n </p>\n )}\n <div className=\"eb:flex eb:flex-col eb:gap-4\">\n {groups.map((group) => (\n <section key={group.id} className=\"eb:flex eb:flex-col eb:gap-2\" aria-label={t(group.label)}>\n <SectionLabel>{t(group.label)}</SectionLabel>\n <div className=\"eb:flex eb:flex-wrap eb:gap-1.5\">\n {group.purposes.map((entry) => {\n const selected = purpose === entry.id;\n return (\n <button\n key={entry.id}\n type=\"button\"\n aria-pressed={selected}\n data-purpose={entry.id}\n onClick={() => setPurpose(selected ? null : entry.id)}\n className={chipClassName(selected)}\n >\n {selected && <Check className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t(entry.label)}\n </button>\n );\n })}\n </div>\n </section>\n ))}\n </div>\n </fieldset>\n </div>\n\n <div className=\"eb:flex eb:flex-col eb:gap-3 eb:px-6 eb:pt-3 eb:pb-6\">\n <Textarea\n value={details}\n onChange={(event) => setDetails(event.target.value.slice(0, MAX_DETAILS_LENGTH))}\n placeholder={t('Details (optional): product, dates, offer, tone, language…')}\n aria-label={t('Details (optional)')}\n rows={2}\n className=\"eb:min-h-14\"\n disabled={busy}\n onKeyDown={(event) => {\n if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) generate();\n }}\n />\n {error ? (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n ) : (\n dirty &&\n !busy && (\n <p className=\"eb:text-[12px] eb:text-foreground-muted\">{t('The email being edited will be replaced, unsaved changes included.')}</p>\n )\n )}\n <div className=\"eb:flex eb:items-center eb:justify-end eb:gap-2\">\n {busy && (\n <p className=\"eb:mr-auto eb:text-[12px] eb:text-foreground-muted\" role=\"status\" aria-live=\"polite\">\n {t('Writing your email… {seconds} s', { seconds })}\n </p>\n )}\n {busy && (\n <Button variant=\"outline\" size=\"sm\" onClick={cancel}>\n {t('Cancel')}\n </Button>\n )}\n <Button size=\"sm\" onClick={generate} disabled={!canGenerate || busy} data-testid=\"eb-generate-submit\">\n {busy ? <Loader2 className=\"eb:animate-spin\" /> : <Sparkles />}\n {t('Generate')}\n </Button>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n </>\n );\n}\n","import { cn } from '../ui/primitives/cn.js';\n\n/**\n * A rounded badge button: the purposes of \"Generate\", the rewrite actions.\n * @param {boolean} [selected]\n */\nexport const chipClassName = (selected = false) =>\n cn(\n 'eb:inline-flex eb:h-7 eb:items-center eb:gap-1 eb:rounded-full eb:border eb:px-3 eb:text-[12px] eb:font-medium eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50 eb:disabled:opacity-50',\n selected\n ? 'eb:border-primary eb:bg-primary eb:text-primary-foreground'\n : 'eb:border-border eb:bg-background eb:text-foreground eb:hover:border-primary/50 eb:hover:bg-primary/5'\n );\n","import { useEffect, useRef, useState } from 'react';\nimport { ChevronDown, Languages, Loader2, Sparkles, TriangleAlert } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { Input } from '../ui/primitives/fields.jsx';\nimport { SectionLabel } from '../ui/primitives/misc.jsx';\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../ui/primitives/overlays.jsx';\nimport { cleanRichText } from '../engine/richText.js';\nimport { chipClassName } from './chip.js';\nimport { MAX_REWRITE_INSTRUCTION, REWRITE_ACTIONS, REWRITE_LANGUAGES } from './rewrite.js';\n\n/**\n * REWRITE WITH AI — in the properties of a selected text or button.\n *\n * One click: shorter, longer, fixed, friendlier, more formal, translated —\n * or a change in the user's words. The result replaces the text at once, in\n * one step of the history: Ctrl+Z brings the previous text back, so there is\n * nothing to accept or compare.\n *\n * Mounted per element (keyed by its id): selecting another element cancels a\n * rewrite in progress. If the text is edited while the AI works, the user's\n * words win and the answer is dropped.\n */\n\n/** A language under its own name: \"Deutsch\", \"Українська\". @param {string} code */\nfunction nativeName(code) {\n try {\n const name = new Intl.DisplayNames([code], { type: 'language' }).of(code) || code;\n return name.charAt(0).toLocaleUpperCase(code) + name.slice(1);\n } catch {\n return code;\n }\n}\n\nexport default function RewriteText({ editor, selection, rewrite }) {\n const { t, notify } = useUi();\n const [running, setRunning] = useState(/** @type {string|null} */ (null));\n const [error, setError] = useState('');\n const [instruction, setInstruction] = useState('');\n const abortRef = useRef(/** @type {AbortController|null} */ (null));\n const label = selection.tagName === 'mj-button';\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n /** The text as stored: a button's label, a text block's HTML. */\n const currentText = () => {\n const element = editor.getElement(selection.id);\n return (label ? element?.content : element?.html) ?? '';\n };\n\n const run = async (action, extra = {}) => {\n if (running) return;\n const text = currentText();\n setError('');\n setRunning(action);\n const controller = new AbortController();\n abortRef.current = controller;\n try {\n const result = await rewrite({ action, text, format: label ? 'plain' : 'html', ...extra }, { signal: controller.signal });\n if (controller.signal.aborted) return;\n if (currentText() !== text) {\n setError(t('The text changed in the meantime, so it was left as it is.'));\n return;\n }\n editor.setContent(label ? result.text : cleanRichText(result.text, document), selection.id);\n if (action === 'custom') setInstruction('');\n notify('success', t('Text rewritten. Ctrl+Z brings back the previous one.'));\n } catch (err) {\n if (!controller.signal.aborted) setError(err?.message ? t(err.message) : t('The text could not be rewritten.'));\n } finally {\n if (abortRef.current === controller) abortRef.current = null;\n setRunning(null);\n }\n };\n\n const cancel = () => {\n abortRef.current?.abort();\n setRunning(null);\n };\n\n const busy = Boolean(running);\n const spinner = <Loader2 className=\"eb:size-3 eb:animate-spin\" aria-hidden=\"true\" />;\n\n return (\n <section className=\"eb:space-y-2.5\" aria-label={t('Rewrite with AI')} data-testid=\"eb-rewrite\">\n <SectionLabel className=\"eb:flex eb:items-center eb:gap-1.5\">\n <Sparkles className=\"eb:size-3\" aria-hidden=\"true\" />\n {t('Rewrite with AI')}\n </SectionLabel>\n <div className=\"eb:flex eb:flex-wrap eb:gap-1.5\">\n {REWRITE_ACTIONS.map((action) => (\n <button key={action.id} type=\"button\" className={chipClassName()} disabled={busy} onClick={() => run(action.id)}>\n {running === action.id && spinner}\n {t(action.label)}\n </button>\n ))}\n <DropdownMenu>\n <DropdownMenuTrigger asChild disabled={busy}>\n <button type=\"button\" className={chipClassName()}>\n {running === 'translate' ? spinner : <Languages className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t('Translate')}\n <ChevronDown className=\"eb:size-3\" aria-hidden=\"true\" />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" aria-label={t('Translate into')}>\n {REWRITE_LANGUAGES.map((code) => (\n <DropdownMenuItem key={code} lang={code} onSelect={() => run('translate', { targetLanguage: code })}>\n {nativeName(code)}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n <form\n className=\"eb:flex eb:gap-1.5\"\n onSubmit={(event) => {\n event.preventDefault();\n if (instruction.trim()) run('custom', { instruction: instruction.trim() });\n }}\n >\n <Input\n value={instruction}\n onChange={(event) => setInstruction(event.target.value)}\n maxLength={MAX_REWRITE_INSTRUCTION}\n placeholder={t('Or say what to change…')}\n aria-label={t('What to change')}\n disabled={busy}\n />\n <Button type=\"submit\" variant=\"outline\" size=\"icon-sm\" aria-label={t('Rewrite')} disabled={busy || !instruction.trim()}>\n {running === 'custom' ? <Loader2 className=\"eb:animate-spin\" /> : <Sparkles />}\n </Button>\n </form>\n {running && (\n <p className=\"eb:flex eb:items-center eb:gap-2 eb:text-[12px] eb:text-foreground-muted\" role=\"status\" aria-live=\"polite\">\n {t('Rewriting…')}\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"eb:h-auto eb:px-0\" onClick={cancel}>\n {t('Cancel')}\n </Button>\n </p>\n )}\n {error && (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n )}\n </section>\n );\n}\n","import { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { Check, Loader2, Sparkles, TriangleAlert } from 'lucide-react';\nimport { useUi } from '../ui/context.jsx';\nimport { Button } from '../ui/primitives/button.jsx';\nimport { cn } from '../ui/primitives/cn.js';\nimport { chipClassName } from './chip.js';\nimport { emailWords } from './subjects.js';\n\n/** Ideas already shown that \"More ideas\" asks the model not to repeat (the server keeps 12). */\nconst MAX_AVOID = 12;\n\n/**\n * SUGGEST SUBJECTS — under the subject of the email.\n *\n * One click: three other subject lines, drawn from the words of the email.\n * They are a list to pick from, the user's own subject first: a click puts\n * one in the field, another click another one, the first row brings the\n * user's subject back. \"More ideas\" asks for three new ones (none of those\n * already shown; the one in the field stays in the list); \"Done\" closes the\n * list and keeps the subject in the field.\n *\n * \"Yours\" is what the user wrote: typed in the field while the list is open\n * (or while ideas are on their way), it becomes the new \"yours\". When the\n * email itself is replaced (Generate, a template), the list closes.\n *\n * Keyboard: the list is a radio group — one Tab stop, the arrows move and\n * pick. Focus goes to the list when it opens, back to the button after Done or\n * Cancel.\n */\nexport default function SubjectIdeas({ editor, meta, onSubject, suggest, variables = null }) {\n const { t, locale } = useUi();\n const [running, setRunning] = useState(false);\n const [error, setError] = useState('');\n const [ideas, setIdeas] = useState(/** @type {string[]|null} */ (null));\n const [original, setOriginal] = useState('');\n const shownRef = useRef(/** @type {string[]} */ ([]));\n const abortRef = useRef(/** @type {AbortController|null} */ (null));\n const subjectRef = useRef(meta.subject);\n subjectRef.current = meta.subject;\n const rootRef = useRef(/** @type {HTMLDivElement|null} */ (null));\n /** Where focus goes after the next render: the list, or the button that opened it. */\n const focusRef = useRef(/** @type {'list'|'button'|null} */ (null));\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n // Another email loaded in the editor: these ideas were for the previous one.\n useEffect(\n () =>\n editor.on('load', () => {\n abortRef.current?.abort();\n abortRef.current = null;\n shownRef.current = [];\n setRunning(false);\n setIdeas(null);\n setError('');\n setOriginal('');\n }),\n [editor]\n );\n\n // Typed in the field while the list is open: that is the user's subject now.\n const typed = meta.subject.trim();\n useEffect(() => {\n if (ideas && typed && !ideas.includes(meta.subject) && typed !== original) setOriginal(typed);\n }, [ideas, meta.subject, typed, original]);\n\n useLayoutEffect(() => {\n const target = focusRef.current;\n if (!target || running) return;\n focusRef.current = null;\n const root = rootRef.current;\n const element =\n target === 'list'\n ? root?.querySelector('[role=\"radio\"][tabindex=\"0\"]')\n : root?.querySelector('[data-subject-ideas-button]');\n /** @type {HTMLElement|null|undefined} */ (element)?.focus();\n });\n\n const ready = editor.status === 'ready';\n\n const run = async () => {\n if (running || editor.status !== 'ready') return;\n const first = !ideas;\n // The user's subject: as it was before the first idea was picked, or typed since.\n const yours = first ? subjectRef.current.trim() : original;\n const controller = new AbortController();\n abortRef.current = controller;\n setError('');\n setRunning(true);\n try {\n const result = await suggest(\n {\n subject: yours,\n preheader: meta.preheader,\n text: emailWords(editor.exportMjml()),\n language: locale,\n variables: variables?.map(({ key, label, kind }) => ({ key, label, kind })),\n avoid: shownRef.current.slice(-MAX_AVOID),\n },\n { signal: controller.signal }\n );\n if (controller.signal.aborted) return;\n const current = subjectRef.current;\n // The idea in the field stays in the list; the others give way to the new ones.\n const kept = ideas?.includes(current) ? [current] : [];\n const next = [...kept, ...result.subjects.filter((subject) => subject !== current)];\n shownRef.current = [...shownRef.current, ...result.subjects];\n // Typed during the wait: that is \"yours\" now.\n setOriginal(current.trim() && !ideas?.includes(current) ? current.trim() : yours);\n setIdeas(next);\n if (first) focusRef.current = 'list';\n } catch (err) {\n if (!controller.signal.aborted) setError(err?.message ? t(err.message) : t('No subject could be suggested.'));\n } finally {\n // A request cancelled earlier must not end the one that replaced it.\n if (abortRef.current === controller) {\n abortRef.current = null;\n setRunning(false);\n }\n }\n };\n\n const cancel = () => {\n abortRef.current?.abort();\n abortRef.current = null;\n setRunning(false);\n focusRef.current = 'button';\n };\n\n const done = () => {\n setIdeas(null);\n setError('');\n shownRef.current = [];\n focusRef.current = 'button';\n };\n\n const options = ideas ? [...(original ? [{ text: original, yours: true }] : []), ...ideas.map((text) => ({ text, yours: false }))] : [];\n const checked = options.findIndex((option) => option.text === meta.subject);\n const tabStop = checked === -1 ? 0 : checked;\n\n /** Arrows move through the ideas and pick, as in any radio group. */\n const onKeyDown = (/** @type {React.KeyboardEvent} */ event) => {\n const step = { ArrowDown: 1, ArrowRight: 1, ArrowUp: -1, ArrowLeft: -1 }[event.key];\n const to = step ? (tabStop + step + options.length) % options.length : event.key === 'Home' ? 0 : event.key === 'End' ? options.length - 1 : -1;\n if (to === -1 || !options.length) return;\n event.preventDefault();\n onSubject(options[to].text);\n /** @type {HTMLElement|undefined} */ (rootRef.current?.querySelectorAll('[role=\"radio\"]')[to])?.focus();\n };\n\n const spinner = <Loader2 className=\"eb:size-3 eb:animate-spin\" aria-hidden=\"true\" />;\n\n return (\n <div className=\"eb:space-y-2\" data-testid=\"eb-subject-ideas\" ref={rootRef}>\n {!ideas && (\n <button type=\"button\" className={chipClassName()} disabled={running || !ready} onClick={run} data-subject-ideas-button>\n {running ? spinner : <Sparkles className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t('Suggest subjects')}\n </button>\n )}\n {ideas && (\n <div className=\"eb:space-y-2 eb:rounded-md eb:border eb:border-border eb:p-1.5\">\n <div role=\"radiogroup\" aria-label={t('Subject ideas')} className=\"eb:space-y-0.5\" onKeyDown={onKeyDown}>\n {options.map((option, index) => {\n const chosen = index === checked;\n return (\n <button\n key={`${option.yours ? 'yours' : 'idea'}:${option.text}`}\n type=\"button\"\n role=\"radio\"\n aria-checked={chosen}\n tabIndex={index === tabStop ? 0 : -1}\n className={cn(\n 'eb:flex eb:w-full eb:items-start eb:gap-2 eb:rounded eb:px-2 eb:py-1.5 eb:text-left eb:text-[12.5px] eb:leading-snug eb:transition-colors eb:outline-none eb:focus-visible:ring-[3px] eb:focus-visible:ring-ring/50',\n chosen ? 'eb:bg-primary/10 eb:text-foreground' : 'eb:text-foreground-lighter eb:hover:bg-muted'\n )}\n onClick={() => onSubject(option.text)}\n >\n <Check className={cn('eb:mt-0.5 eb:size-3.5 eb:shrink-0', chosen ? 'eb:text-primary' : 'eb:invisible')} aria-hidden=\"true\" />\n <span className=\"eb:min-w-0 eb:flex-1 eb:break-words\">{option.text}</span>\n {option.yours && <span className=\"eb:shrink-0 eb:text-[11px] eb:text-foreground-muted\">{t('yours')}</span>}\n </button>\n );\n })}\n </div>\n <div className=\"eb:flex eb:items-center eb:gap-1.5 eb:px-1 eb:pb-0.5\">\n <button type=\"button\" className={chipClassName()} disabled={running || !ready} onClick={run} data-subject-ideas-button>\n {running ? spinner : <Sparkles className=\"eb:size-3\" aria-hidden=\"true\" />}\n {t('More ideas')}\n </button>\n <Button type=\"button\" variant=\"ghost\" size=\"xs\" className=\"eb:ml-auto\" disabled={running} onClick={done}>\n {t('Done')}\n </Button>\n </div>\n </div>\n )}\n {running && (\n <p className=\"eb:flex eb:items-center eb:gap-2 eb:text-[12px] eb:text-foreground-muted\" role=\"status\" aria-live=\"polite\">\n {t('Reading the email…')}\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"eb:h-auto eb:px-0\" onClick={cancel}>\n {t('Cancel')}\n </Button>\n </p>\n )}\n {error && (\n <p role=\"alert\" className=\"eb:flex eb:items-start eb:gap-1.5 eb:text-[12px] eb:text-destructive\">\n <TriangleAlert className=\"eb:mt-px eb:size-3.5 eb:shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n )}\n </div>\n );\n}\n","// @ts-check\n/**\n * THE AI MODULE — `createAi()`, given to the builder as `ai`.\n *\n * import { createAi } from 'best-email-builder/ai';\n * <EmailBuilder storage={storage} ai={createAi()} />\n *\n * Without it, the builder has no AI button, and none of the AI's code or\n * texts is in the application's bundle: the core never imports this folder.\n * With it, the builder gets what the module can do:\n *\n * generate the Generate button (an email written from a purpose)\n * rewrite \"Rewrite with AI\" in the properties of a text or a button\n * suggestSubjects \"Suggest subjects\" under the subject\n *\n * Where the model runs:\n *\n * createAi() the routes of createEmailBuilderHandler({ generator })\n * createAi({ baseUrl, headers }) the same routes elsewhere, or with the user's credentials\n * createAi({ provider, askKey: true }) the AI runs in the page with each user's own key\n * (OpenRouter, Gemini, Groq… — see providers.js);\n * the builder asks for it once (the Connect dialog)\n * createAi({ provider: [...], askKey: true })\n * the same, the user choosing the provider in that\n * dialog (Claude too: claudeProvider in claude.js),\n * and the model among those the provider lists\n * createAi({ provider, apiKey }) the same with one key for all (prototypes: the key\n * is then in the page)\n * createAi({ model }) the generator runs in the page with this model\n * createAi({ generate, rewrite, … }) functions of your own: only those given are on\n *\n * Each of `generate`, `rewrite` and `suggestSubjects` can also be `false`,\n * to leave that part out — its button with it.\n */\n\nimport { createHttpCaller } from '../storage/client.js';\nimport { EmailGenerationError } from './draft.js';\nimport { createEmailGenerator } from './generator.js';\nimport { AI_MESSAGES } from './i18n/index.js';\nimport { createKeyStore } from './keys.js';\nimport { providerEmailModel, resolveProvider } from './providers.js';\nimport ConnectAi from './ConnectAi.jsx';\nimport GenerateButton from './GenerateButton.jsx';\nimport RewriteText from './RewriteText.jsx';\nimport SubjectIdeas from './SubjectIdeas.jsx';\n\n/**\n * @typedef {import('./generator.js').GenerateInput} GenerateInput\n * @typedef {import('./generator.js').GeneratedEmail} GeneratedEmail\n * @typedef {import('./rewrite.js').RewriteInput} RewriteInput\n * @typedef {import('./subjects.js').SubjectsInput} SubjectsInput\n * @typedef {{signal?: AbortSignal}} CallOptions\n * @typedef {(input: GenerateInput, options?: CallOptions) => Promise<GeneratedEmail | Omit<GeneratedEmail, 'warnings'|'attempts'>>} GenerateFn\n * @typedef {(input: RewriteInput, options?: CallOptions) => Promise<{text: string}>} RewriteFn\n * @typedef {(input: SubjectsInput, options?: CallOptions) => Promise<{subjects: string[]}>} SuggestSubjectsFn\n *\n * @typedef {{\n * baseUrl?: string,\n * headers?: Record<string, string> | (() => Record<string, string>),\n * fetch?: typeof fetch,\n * model?: import('./generator.js').EmailModel,\n * provider?: ProviderOption | ProviderOption[],\n * apiKey?: string,\n * askKey?: boolean,\n * compile?: (mjml: string) => Promise<{warnings?: {message: string}[]}>,\n * maxAttempts?: number,\n * generate?: GenerateFn | boolean,\n * rewrite?: RewriteFn | boolean,\n * suggestSubjects?: SuggestSubjectsFn | boolean,\n * }} AiOptions\n * `provider`: an id of `AI_PROVIDERS` ('openrouter', 'gemini', 'groq'…), or\n * `{ id, model, baseUrl, headers }` for another model or an endpoint of your\n * own — or a list of them, with `askKey`: the user chooses (the first is\n * offered first). `apiKey`: one key for every user. `askKey`: each user\n * connects their own (off by default). `compile`: with `provider` or `model`, MJML's own\n * warnings fed back to the model (see createEmailGenerator).\n *\n * @typedef {Readonly<{\n * generate: GenerateFn | null,\n * rewrite: RewriteFn | null,\n * suggestSubjects: SuggestSubjectsFn | null,\n * key: import('./keys.js').AiKey | null,\n * messages: Record<string, Record<string, string>>,\n * components: Readonly<{Generate: typeof GenerateButton, Rewrite: typeof RewriteText, Subjects: typeof SubjectIdeas, Connect: typeof ConnectAi | null}>,\n * }>} EmailBuilderAi\n * What the builder reads. `key`: the user's key, with `askKey` (keys.js).\n * `messages`: the module's own translations, by locale, merged under the\n * host's `messages`. `components.Connect`: the dialog that asks for the key.\n */\n\n/** @typedef {string | Partial<import('./providers.js').AiProvider>} ProviderOption */\n\nconst PARTS = /** @type {const} */ (['generate', 'rewrite', 'suggestSubjects']);\nconst ROUTES = { generate: '/generate', rewrite: '/rewrite', suggestSubjects: '/subjects' };\n\n/**\n * @param {AiOptions} [options]\n * @returns {EmailBuilderAi}\n */\nexport function createAi(options = {}) {\n if (!options || typeof options !== 'object') throw new TypeError('createAi: options must be an object');\n const { model, maxAttempts, compile, baseUrl, headers, fetch: fetchImpl, provider: providerOption, apiKey, askKey = false } = options;\n const usesProvider = providerOption !== undefined;\n const http = baseUrl !== undefined || headers !== undefined || (fetchImpl !== undefined && !usesProvider);\n if (model !== undefined && typeof model !== 'function') throw new TypeError('createAi: `model` must be a function');\n if (model && http) throw new TypeError('createAi: pass `model` (the AI runs in the page) or `baseUrl` (on the server), not both');\n if (usesProvider && (model || http)) {\n throw new TypeError('createAi: `provider` runs the AI in the page — not with `model`, `baseUrl` or `headers` (give the provider `{ id, model, baseUrl, headers }`)');\n }\n if (!usesProvider && (apiKey !== undefined || options.askKey !== undefined)) {\n throw new TypeError('createAi: `apiKey` and `askKey` go with a `provider` (e.g. provider: \"openrouter\")');\n }\n if (apiKey !== undefined && (typeof apiKey !== 'string' || !apiKey)) throw new TypeError('createAi: `apiKey` must be a non-empty string');\n if (compile !== undefined && (typeof compile !== 'function' || (!model && !usesProvider))) {\n throw new TypeError('createAi: `compile` must be a function, and goes with `provider` or `model` (the AI in the page)');\n }\n if (typeof askKey !== 'boolean') throw new TypeError('createAi: `askKey` must be true or false');\n if (apiKey && askKey) throw new TypeError('createAi: `apiKey` (one key for all) or `askKey` (each user\\'s own), not both');\n const choice = Array.isArray(providerOption);\n if (choice && (!providerOption.length || !askKey)) {\n throw new TypeError('createAi: a list of providers is for `askKey: true` — each user chooses one and connects their own key');\n }\n /** @type {import('./providers.js').AiProvider[]} */\n const providers = usesProvider ? (choice ? providerOption : [providerOption]).map(providerOf) : [];\n if (new Set(providers.map((provider) => provider.id)).size !== providers.length) throw new TypeError('createAi: each provider once');\n const [provider = null] = providers;\n if (provider && !apiKey && !askKey && !provider.keyless) {\n throw new TypeError(`createAi: ${provider.name} needs a key — \\`apiKey\\`, or \\`askKey: true\\` to let each user connect their own`);\n }\n for (const part of PARTS) {\n const value = options[part];\n if (value !== undefined && typeof value !== 'boolean' && typeof value !== 'function') {\n throw new TypeError(`createAi: \\`${part}\\` must be a function, true or false`);\n }\n }\n\n // Functions of the host's own and nothing else: only those are on.\n const ownOnly = !model && !http && PARTS.some((part) => typeof options[part] === 'function');\n /** @type {Partial<Record<typeof PARTS[number], Function>>} */\n let transport = {};\n const key = provider && askKey ? createKeyStore(providers, { fetch: fetchImpl }) : null;\n if (provider) {\n /** @type {Map<string, import('./generator.js').EmailModel>} */\n const models = new Map();\n const keyFor = () => (key ? key.get() : apiKey || '');\n /** The model the user chose of the provider in use, made once. @param {string} id */\n const modelOf = (id) => {\n const chosen = /** @type {import('./providers.js').AiProvider} */ (providers.find((item) => item.id === id));\n const name = key ? key.model(id) : chosen.model;\n // An OpenAI-compatible API of the user's own: where it answers is theirs too.\n const address = key && key.providers.find((item) => item.id === id)?.needsAddress ? key.address(id) : '';\n const slot = `${id}\\n${name}\\n${address}`;\n if (!models.has(slot)) {\n models.set(\n slot,\n chosen.createModel\n ? chosen.createModel({ apiKey: keyFor, fetch: fetchImpl, ...(name ? { model: name } : {}) })\n : providerEmailModel({ provider: chosen, fetch: fetchImpl, apiKey: keyFor, ...(name ? { model: name } : {}), ...(address ? { baseUrl: address } : {}) })\n );\n }\n return /** @type {import('./generator.js').EmailModel} */ (models.get(slot));\n };\n /** @type {import('./generator.js').EmailModel} */\n const model = async (request) => {\n // The user's key (and the address, the model it needs), asked when\n // missing — they may choose another provider there. Cancelled: KEY_MISSING.\n if (key && !key.ready()) {\n await key.request();\n if (!key.ready()) throw new EmailGenerationError('KEY_MISSING', `${key.provider.name}: not connected`);\n }\n return modelOf(key ? key.provider.id : provider.id)(request);\n };\n const generator = createEmailGenerator({ model, maxAttempts, compile });\n for (const part of PARTS) transport[part] = key ? askedAgain(generator[part], key) : generator[part];\n } else if (model) {\n transport = createEmailGenerator({ model, maxAttempts, compile });\n } else if (!ownOnly) {\n const { call } = createHttpCaller({ baseUrl, headers, fetch: fetchImpl });\n for (const part of PARTS) {\n transport[part] = (/** @type {object} */ input, /** @type {CallOptions} */ { signal } = {}) =>\n call(ROUTES[part], { method: 'POST', body: JSON.stringify(input), signal });\n }\n }\n\n /** @param {typeof PARTS[number]} part */\n const resolve = (part) => {\n const value = options[part];\n if (typeof value === 'function') return value;\n if (value === false) return null;\n return transport[part] || null;\n };\n\n return Object.freeze({\n generate: /** @type {GenerateFn|null} */ (resolve('generate')),\n rewrite: /** @type {RewriteFn|null} */ (resolve('rewrite')),\n suggestSubjects: /** @type {SuggestSubjectsFn|null} */ (resolve('suggestSubjects')),\n key,\n messages: AI_MESSAGES,\n components: Object.freeze({ Generate: GenerateButton, Rewrite: RewriteText, Subjects: SubjectIdeas, Connect: key ? ConnectAi : null }),\n });\n}\n\n/**\n * A provider of the list: an id, `{ id, model, … }`, or one with its own\n * `createModel` (claudeProvider) taken as it is.\n * @param {ProviderOption} option\n * @returns {import('./providers.js').AiProvider}\n */\nfunction providerOf(option) {\n if (option && typeof option === 'object' && typeof option.createModel === 'function') {\n if (!option.id || !option.name) throw new TypeError('createAi: a provider with `createModel` needs an `id` and a `name`');\n return /** @type {import('./providers.js').AiProvider} */ (option);\n }\n return resolveProvider(/** @type {any} */ (option));\n}\n\n/**\n * A key the provider refuses is forgotten and asked again, once, in the same\n * action: the user pastes a good one and the email still comes.\n * @param {Function} run\n * @param {import('./keys.js').AiKey} key\n */\nfunction askedAgain(run, key) {\n return async (/** @type {any} */ input, /** @type {any} */ options) => {\n try {\n return await run(input, options);\n } catch (error) {\n if (/** @type {any} */ (error)?.code !== 'KEY_REFUSED' || options?.signal?.aborted) throw error;\n key.refuse();\n try {\n return await run(input, options);\n } catch (again) {\n if (/** @type {any} */ (again)?.code === 'KEY_REFUSED') key.refuse();\n throw again;\n }\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAM,iBAAiB;AACvB,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,iBAAiB;AAGvB,SAAS,iBAAiB;AACxB,MAAI;AACF,UAAM,UAAU,WAAW;AAC3B,WAAO,WAAW,OAAO,QAAQ,YAAY,aAAa,UAAU;AAAA,EACtE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAkCA,IAAM,WAAW,CAAC,aAChB,OAAO,OAAO;AAAA,EACZ,IAAI,SAAS;AAAA,EACb,MAAM,SAAS;AAAA,EACf,OAAO,SAAS,SAAS;AAAA,EACzB,QAAQ,SAAS;AAAA,EACjB,SAAS,SAAS;AAAA,EAClB,MAAM,SAAS;AAAA,EACf,cAAc,QAAQ,SAAS,YAAY;AAAA,EAC3C,SAAS,QAAQ,SAAS,OAAO;AAAA,EACjC,cAAc,CAAC,SAAS,eAAe,CAAC,SAAS;AACnD,CAAC;AAGH,IAAM,eAAe,CAAC,QAAQ,OAAO,OAAO,EAAE,EAAE,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAOlE,SAAS,eAAe,gBAAgB,EAAE,OAAO,UAAU,IAAI,CAAC,GAAG;AACxE,QAAM,YAAY,MAAM,QAAQ,cAAc,IAAI,iBAAiB,CAAC,cAAc;AAClF,QAAM,YAAY,OAAO,OAAO,UAAU,IAAI,QAAQ,CAAC;AACvD,MAAI,CAAC,UAAU,OAAQ,OAAM,IAAI,UAAU,uCAAuC;AAClF,QAAM,QAAQ,CAAwB,OAAO,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,EAAE;AAE5F,QAAM,SAAS,oBAAI,IAAI;AACvB,MAAI,UAAU;AAEd,MAAI,UAAU;AAEd,QAAM,YAAY,oBAAI,IAAI;AAE1B,MAAI;AAGJ,QAAM,OAAO,CAAC,SAAS;AACrB,QAAI;AACF,aAAO,eAAe,GAAG,QAAQ,IAAI,KAAK;AAAA,IAC5C,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,QAAI;AACF,YAAM,UAAU,eAAe;AAC/B,UAAI,MAAO,UAAS,QAAQ,MAAM,KAAK;AAAA,UAClC,UAAS,WAAW,IAAI;AAAA,IAC/B,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,OAAQ,OAAO,IAAI,EAAE,IAAI,OAAO,IAAI,EAAE,IAAI,KAAK,iBAAiB,EAAE;AAEjF,QAAM,OAAO,CAAC,IAAI,UAAU;AAC1B,WAAO,IAAI,IAAI,KAAK;AACpB,UAAM,iBAAiB,IAAI,KAAK;AAAA,EAClC;AAEA,QAAM,OAAO,KAAK,WAAW;AAC7B,MAAI,UAAU,MAAM,IAAI,IAAI,QAAQ,UAAU,KAAK,CAAC,aAAa,MAAM,SAAS,EAAE,CAAC,KAAK,UAAU,CAAC,GAAG;AAEtG,QAAM,MAAM,CAAC,OAAO,MAAM,OAAO,SAAY,UAAU,OAAO,EAAE,CAAC;AAEjE,QAAM,aAAa,CAAC,OAAO;AACzB,QAAI,OAAO,OAAW,QAAO;AAC7B,QAAI,CAAC,MAAM,EAAE,EAAG,OAAM,IAAI,UAAU,wBAAwB,EAAE,mBAAc,UAAU,IAAI,CAAC,aAAa,SAAS,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AACjI,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,oBAAI,IAAI;AAEtB,QAAM,UAAU,CAAC,YAAY,OAAO,IAAI,eAAe,OAAO,EAAE,KAAK,KAAK,eAAe,OAAO,EAAE,MAAM,OAAO,SAAS;AAExH,QAAM,YAAY,CAAC,WACjB,OAAO,eAAe,aAAa,OAAO,IAAI,iBAAiB,OAAO,EAAE,KAAK,KAAK,iBAAiB,OAAO,EAAE,CAAC,IAAI;AAEnH,QAAM,QAAQ,MAAM;AAClB,UAAM;AAAA;AAAA,MAAwC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,OAAO;AAAA;AAClG,QAAI,QAAS,QAAO;AACpB,QAAI,CAAC,OAAO,WAAW,CAAC,IAAI,EAAG,QAAO;AACtC,QAAI,OAAO,gBAAgB,CAAC,UAAU,MAAM,EAAG,QAAO;AACtD,WAAO,QAAQ,QAAQ,MAAM,CAAC;AAAA,EAChC;AACA,QAAM,UAAU,MAAM;AACpB,eAAW,OAAO,OAAO,EAAE,QAAQ,QAAQ,OAAO,GAAG,SAAS,WAAW,MAAM,EAAE,CAAC;AAClF,eAAW,YAAY,CAAC,GAAG,SAAS,EAAG,UAAS;AAAA,EAClD;AACA,aAAW,OAAO,OAAO,EAAE,QAAQ,OAAO,SAAS,OAAO,WAAW,MAAM,EAAE,CAAC;AAG9E,QAAM,SAAS,CAAC,QAAQ;AACtB,UAAM,QAAQ,OAAO,OAAO,EAAE,EAAE,KAAK;AACrC,QAAI,MAAO,MAAK,SAAS,KAAK;AAE9B,QAAI,QAAQ,SAAS,SAAS,IAAI;AAAA,IAAoC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,OAAO,EAAG,SAAU,WAAU;AACjJ,UAAM,UAAU;AAChB,cAAU;AACV,YAAQ;AACR,aAAS,QAAQ,KAAK;AAAA,EACxB;AAEA,SAAO,OAAO,OAAO;AAAA,IACnB,IAAI,WAAW;AACb;AAAA;AAAA,QAAsC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,OAAO;AAAA;AAAA,IAC5F;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,CAAuB,QAAQ;AAClC,UAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,KAAK,EAAG,OAAM,IAAI,UAAU,6BAA6B;AAC7F,aAAO,GAAG;AAAA,IACZ;AAAA,IACA,QAAQ,MAAM;AACZ,WAAK,SAAS,EAAE;AAChB,cAAQ;AAAA,IACV;AAAA,IACA,QAAQ,CAAuB,OAAO;AACpC,UAAI,CAAC,MAAM,EAAE,EAAG,OAAM,IAAI,UAAU,yBAAyB,UAAU,IAAI,CAAC,aAAa,SAAS,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAClH,UAAI,OAAO,QAAS;AACpB,gBAAU;AACV,gBAAU;AACV,YAAM,aAAa,EAAE;AACrB,cAAQ;AAAA,IACV;AAAA,IACA,OAAO,CAAiC,OAAO;AAAA;AAAA,MAAuC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,WAAW,EAAE,CAAC;AAAA,IAAE;AAAA,IACnJ,SAAS,CAAiC,OAAO;AAAA;AAAA,MAAyC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,WAAW,EAAE,CAAC;AAAA,IAAE;AAAA,IACvJ,YAAY,CAAuB,KAAqC,OAAO;AAC7E,YAAM;AAAA;AAAA,QAAwC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,WAAW,EAAE,CAAC;AAAA;AACzG,UAAI,CAAC,OAAO,aAAc,OAAM,IAAI,UAAU,sBAAsB,OAAO,IAAI,4BAA4B;AAC3G,YAAM,QAAQ,aAAa,GAAG;AAC9B,UAAI,SAAS,CAAC,uBAAuB,KAAK,KAAK,EAAG,OAAM,IAAI,UAAU,sEAAsE;AAC5I,aAAO,IAAI,iBAAiB,OAAO,IAAI,KAAK;AAC5C,YAAM,iBAAiB,OAAO,IAAI,KAAK;AACvC,cAAQ;AAAA,IACV;AAAA,IACA;AAAA,IACA,aAAa,CAAuB,OAAuC,OAAO;AAChF,UAAI,OAAO,UAAU,YAAY,CAAC,MAAM,KAAK,EAAG,OAAM,IAAI,UAAU,gCAAgC;AACpG,YAAM;AAAA;AAAA,QAAwC,UAAU,KAAK,CAAC,aAAa,SAAS,OAAO,WAAW,EAAE,CAAC;AAAA;AAEzG,YAAM,QAAQ,MAAM,KAAK,MAAM,OAAO,QAAQ,KAAK,MAAM,KAAK;AAC9D,aAAO,IAAI,eAAe,OAAO,IAAI,KAAK;AAC1C,YAAM,eAAe,OAAO,IAAI,KAAK;AACrC,cAAQ;AAAA,IACV;AAAA,IACA,YAAY,OAAuC,IAAI,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC,MAAM;AAC1F,YAAM,KAAK,UAAU,UAAU,CAACA,cAAaA,UAAS,OAAO,WAAW,EAAE,CAAC;AAC3E,YAAM,MAAM,OAAO,UAAU,MAAM,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK;AAC3D,YAAM,QAAQ,UAAU,EAAE,EAAE,eAAe,aAAa,WAAW,UAAU,UAAU,EAAE,CAAC,CAAC,IAAI;AAC/F,UAAI,UAAU,EAAE,EAAE,gBAAgB,CAAC,MAAO,QAAO,CAAC;AAClD,YAAM,OAAO,GAAG,UAAU,EAAE,EAAE,EAAE;AAAA,EAAK,GAAG;AAAA,EAAK,KAAK;AAClD,YAAM,WAAW,QAAQ,EAAE,GAAG,UAAU,EAAE,GAAG,SAAS,MAAM,IAAI,UAAU,EAAE;AAC5E,UAAI,CAAC,MAAM,IAAI,IAAI,EAAG,OAAM,IAAI,MAAM,MAAM,mBAAmB,EAAE,UAAU,QAAQ,KAAK,OAAO,WAAW,OAAO,CAAC,CAAC;AACnH,aAAO,MAAM,IAAI,IAAI;AAAA,IACvB;AAAA,IACA,SAAS,MAAM;AACb,UAAI,CAAC,SAAS;AAEZ,YAAI,UAAU,MAAM;AAAA,QAAC;AACrB,cAAM,UAAU,IAAI,QAAQ,CAAC,SAAU,UAAU,IAAK;AACtD,kBAAU,EAAE,SAAS,QAAQ;AAC7B,gBAAQ;AAAA,MACV;AACA,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,WAAW,CAA2B,aAAa;AACjD,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM,UAAU,OAAO,QAAQ;AAAA,IACxC;AAAA,IACA,OAAO,MAAM;AAAA,IACb,QAAQ,CAA4B,QAAQ,OAAO,OAAO,IAAI;AAAA,IAC9D,QAAQ,MAAM;AACZ,WAAK,SAAS,EAAE;AAChB,gBAAU;AACV,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACH;;;ACpQA,SAAS,aAAAC,YAAW,SAAAC,QAAO,UAAAC,SAAQ,YAAAC,WAAU,4BAA4B;AACzE,SAAS,cAAc,KAAK,QAAQ,UAAU,aAAa,qBAAqB;;;ACDhF,SAAS,WAAW,OAAO,SAAS,QAAQ,gBAAgB;AAC5D,SAAS,OAAO,aAAa,cAAc,cAAc;AAgIrD,mBAEI,KAFJ;AAjGW,SAAR,YAA6B,EAAE,OAAO,UAAU,QAAQ,UAAU,IAAI,QAAQ,GAAG,OAAO,UAAU,MAAM,aAAa,GAAG;AAC7H,QAAM,EAAE,EAAE,IAAI,MAAM;AACpB,QAAM,CAAC,MAAM,OAAO,IAAI;AAAA;AAAA,IAAoJ,EAAE,QAAQ,WAAW,QAAQ,CAAC,EAAE;AAAA,EAAE;AAC9M,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,CAAC;AACtC,QAAM,aAAa;AAAA;AAAA,IAA8C;AAAA,EAAK;AACtE,QAAM,UAAU;AAAA;AAAA,IAA2C;AAAA,EAAK;AAChE,QAAM,UAAU,MAAM;AACtB,QAAM,UAAU,MAAM;AACtB,QAAM,SAAS,MAAM;AACrB,QAAM,SAAS,MAAM;AAGrB,YAAU,MAAM;AACd,QAAI,SAAS,gBAAgB,CAAC,SAAS;AACrC,cAAQ,EAAE,QAAQ,iBAAiB,QAAQ,CAAC,EAAE,CAAC;AAC/C,aAAO;AAAA,IACT;AACA,QAAI,CAAC,UAAU,CAAC,SAAS,gBAAgB,CAAC,SAAS,SAAS;AAC1D,cAAQ,EAAE,QAAQ,aAAa,QAAQ,CAAC,EAAE,CAAC;AAC3C,aAAO;AAAA,IACT;AACA,UAAM,aAAa,IAAI,gBAAgB;AACvC,YAAQ,EAAE,QAAQ,WAAW,QAAQ,CAAC,EAAE,CAAC;AACzC,UAAM,QAAQ,WAAW,MAAM;AAC7B,YAAM,WAAW,SAAS,IAAI,EAAE,QAAQ,SAAS,QAAQ,WAAW,OAAO,CAAC,EAAE;AAAA,QAC5E,CAACC,YAAW,CAAC,WAAW,OAAO,WAAW,QAAQ,EAAE,QAAQ,SAAS,QAAAA,QAAO,CAAC;AAAA,QAC7E,CAAC,UAAU,CAAC,WAAW,OAAO,WAAW,QAAQ,EAAE,QAAQ,OAAO,SAAS,gBAAgB,YAAY,UAAU,QAAQ,CAAC,EAAE,CAAC;AAAA,MAC/H;AAAA,IACF,GAAG,KAAK;AACR,WAAO,MAAM;AACX,mBAAa,KAAK;AAClB,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,OAAO,SAAS,IAAI,SAAS,cAAc,SAAS,SAAS,SAAS,cAAc,QAAQ,SAAS,KAAK,CAAC;AAG/G,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,aAAS,EAAE;AACX,gBAAY,KAAK;AAAA,EACnB,GAAG,CAAC,IAAI,CAAC;AAGT,QAAM,SAAS,QAAQ,MAAM;AAC3B,UAAM,SAAS,IAAI,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;AACpE,UAAM,OAAO,oBAAI,IAAI;AAErB,UAAM,MAAM,CAAC;AACb,UAAM,MAAM,CAAqD,UAAU;AACzE,UAAI,MAAM,MAAM,CAAC,KAAK,IAAI,MAAM,EAAE,EAAG,MAAK,IAAI,MAAM,EAAE,KAAK,IAAI,KAAK,KAAK;AAAA,IAC3E;AACA,eAAW,MAAM,CAAC,SAAS,OAAO,KAAK,EAAG,KAAI,GAAI,KAAI,OAAO,IAAI,EAAE,KAAK,EAAE,IAAI,MAAM,IAAI,MAAM,MAAM,CAAC;AACrG,SAAK,OAAO,QAAQ,GAAG;AACvB,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,QAAQ,SAAS,OAAO,KAAK,CAAC;AACvC,QAAM,UAAU,KAAK,OAAO,KAAK,CAAC,UAAU,MAAM,IAAI;AACtD,QAAM,QAAQ,MAAM,KAAK;AACzB,QAAM,UAAU,QAAQ,MAAM;AAC5B,UAAM,QAAQ,MAAM,YAAY,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AAC7D,WAAO,OAAO,OAAO,CAAC,WAAW,CAAC,YAAY,MAAM,SAAS,MAAM,MAAM,CAAC,SAAS,GAAG,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,YAAY,EAAE,SAAS,IAAI,CAAC,CAAC;AAAA,EAC9I,GAAG,CAAC,QAAQ,OAAO,QAAQ,CAAC;AAE5B,QAAM,SAAS,SAAS,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,KAAK,IAAI,QAAQ;AACnG,QAAM,QAAQ,QAAQ,UAAU,SAAS,IAAI;AAC7C,QAAM,UAAU,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,KAAK;AAEzD,YAAU,MAAM,UAAU,KAAK,IAAI,GAAG,QAAQ,UAAU,CAAC,UAAU,MAAM,OAAO,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC;AAC1G,YAAU,MAAM;AACd,QAAI,KAAM,SAAQ,SAAS,cAAc,sBAAsB,GAAG,eAAe,EAAE,OAAO,UAAU,CAAC;AAAA,EACvG,GAAG,CAAC,QAAQ,IAAI,CAAC;AAEjB,QAAM,QAAQ,MAAM;AAClB,eAAW,SAAS,MAAM;AAC1B,iBAAa,KAAK;AAAA,EACpB;AAEA,QAAM,OAAO,CAAC,OAAO;AACnB,aAAS,EAAE;AACX,UAAM;AAAA,EACR;AAEA,QAAM,YAAY,CAAoC,UAAU;AAC9D,QAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;AACxD,YAAM,eAAe;AACrB,gBAAU,CAAC,OAAQ,SAAS,MAAM,MAAM,QAAQ,cAAc,IAAI,MAAM,SAAS,QAAQ,CAAE;AAAA,IAC7F,WAAW,MAAM,QAAQ,SAAS;AAEhC,YAAM,eAAe;AACrB,YAAM,KAAK,SAAS,QAAQ,SAAS,QAAQ,MAAM,GAAG,KAAK;AAC3D,UAAI,GAAI,MAAK,EAAE;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,WAAW,CAAuB,OAAO,GAAG,MAAM,IAAI,EAAE;AAC9D,QAAM,SAAS,CAAqD,UAClE,iCACG;AAAA,UAAM,QACL,oBAAC,UAAK,WAAU,mHACb,YAAE,MAAM,GACX;AAAA,IAED,MAAM,OAAO,SAAS,SACrB,oBAAC,UAAK,WAAU,yHAAyH,YAAE,SAAS,GAAE;AAAA,KAE1J;AAGF,QAAM,SAAS;AAAA,IACb,SACE,iCACE;AAAA,0BAAC,gBAAa,WAAU,yCAAwC,eAAY,QAAO;AAAA,MAClF,EAAE,0CAAqC,EAAE,UAAU,SAAS,KAAK,CAAC;AAAA,OACrE;AAAA,IAEF,aAAa,EAAE,sDAAsD,EAAE,UAAU,SAAS,KAAK,CAAC;AAAA,IAChG,iBAAiB,EAAE,yCAAyC;AAAA,IAC5D,SAAS,oBAAC,UAAK,WAAU,mBAAmB,YAAE,gEAAgE,EAAE,UAAU,SAAS,KAAK,CAAC,GAAE;AAAA,IAC3I,QAAQ,SAAS,QACb,EAAE,6FAA6F,IAC/F,EAAE,mEAAmE;AAAA,IACzE,OAAO,KAAK,OAAO,SACf,UACE,EAAE,wCAAwC,EAAE,OAAO,KAAK,OAAO,OAAO,CAAC,IACvE,EAAE,mBAAmB,EAAE,OAAO,KAAK,OAAO,OAAO,CAAC,IACpD,EAAE,qEAAqE,EAAE,UAAU,SAAS,KAAK,CAAC;AAAA,EACxG,EAAE,KAAK,MAAM;AAEb,SACE,qBAAC,SAAI,WAAU,kCAAiC,eAAY,6BAC1D;AAAA,wBAAC,UAAK,IAAI,SAAS,WAAU,sEAC1B,YAAE,OAAO,GACZ;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAc;AAAA,QACd,iBAAe;AAAA,QACf,mBAAiB,GAAG,OAAO,IAAI,OAAO;AAAA,QACtC,oBAAkB;AAAA,QAClB,SAAS,MAAM,aAAa,CAAC,IAAI;AAAA,QACjC,WAAW;AAAA,UACT;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,QACA,eAAY;AAAA,QAEZ;AAAA,8BAAC,UAAK,IAAI,SAAS,WAAU,2EAA2E,mBAAS,EAAE,gBAAgB,GAAE;AAAA,UACpI,WAAW,OAAO,OAAO;AAAA,UAC1B,oBAAC,eAAY,WAAW,GAAG,qFAAqF,QAAQ,eAAe,GAAG,eAAY,QAAO;AAAA;AAAA;AAAA,IAC/J;AAAA,IAEC,QACC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,WAAW,CAAC,UAAU,MAAM,QAAQ,YAAY,MAAM;AAAA,QACtD,kBAAe;AAAA,QACf,eAAY;AAAA,QAEZ;AAAA,+BAAC,SAAI,WAAU,wEACb;AAAA,iCAAC,SAAI,WAAU,oCACb;AAAA,kCAAC,UAAO,WAAU,oHAAmH,eAAY,QAAO;AAAA,cACxJ;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAS;AAAA,kBACT,MAAK;AAAA,kBACL,iBAAc;AAAA,kBACd,iBAAe;AAAA,kBACf,qBAAkB;AAAA,kBAClB,yBAAuB,QAAQ,SAAS,MAAM,IAAI;AAAA,kBAClD,cAAY,EAAE,mBAAmB;AAAA,kBACjC,OAAO;AAAA,kBACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,kBAChD;AAAA,kBACA,aAAa,EAAE,yBAAoB;AAAA,kBACnC,cAAa;AAAA,kBACb,gBAAe;AAAA,kBACf,aAAY;AAAA,kBACZ,YAAY;AAAA,kBACZ,WAAU;AAAA,kBACV,eAAY;AAAA;AAAA,cACd;AAAA,eACF;AAAA,YACC,WACC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,gBAAc;AAAA,gBACd,SAAS,MAAM,YAAY,CAAC,OAAO,CAAC,EAAE;AAAA,gBACtC,WAAW;AAAA,kBACT;AAAA,kBACA,WAAW,0DAA0D;AAAA,gBACvE;AAAA,gBAEC,YAAE,WAAW;AAAA;AAAA,YAChB;AAAA,aAEJ;AAAA,UACA,qBAAC,SAAI,KAAK,SAAS,IAAI,QAAQ,MAAK,WAAU,mBAAiB,SAAS,WAAU,yCAC/E;AAAA,oBAAQ,IAAI,CAAC,OAAO,OACnB;AAAA,cAAC;AAAA;AAAA,gBAEC,IAAI,SAAS,EAAE;AAAA,gBACf,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,iBAAe,MAAM,OAAO;AAAA,gBAC5B,eAAa,OAAO;AAAA,gBACpB,cAAY,MAAM;AAAA,gBAClB,SAAS,MAAM,KAAK,MAAM,EAAE;AAAA,gBAC5B,aAAa,MAAM,OAAO,UAAU,UAAU,EAAE;AAAA,gBAChD,WAAW;AAAA,kBACT;AAAA,kBACA,OAAO,SAAS,2CAA2C;AAAA,gBAC7D;AAAA,gBAEA;AAAA,sCAAC,SAAM,WAAW,GAAG,qDAAqD,MAAM,OAAO,SAAS,cAAc,GAAG,eAAY,QAAO;AAAA,kBACpI,qBAAC,UAAK,WAAU,uDACd;AAAA,yCAAC,UAAK,WAAU,iDACd;AAAA,0CAAC,UAAK,WAAU,iDAAiD,gBAAM,MAAK;AAAA,sBAC3E,OAAO,KAAK;AAAA,uBACf;AAAA,oBACC,MAAM,SAAS,MAAM,MAAM,oBAAC,UAAK,WAAU,sEAAsE,gBAAM,IAAG;AAAA,qBAC7H;AAAA;AAAA;AAAA,cAtBK,MAAM;AAAA,YAuBb,CACD;AAAA,YACA,UACC;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI,SAAS,QAAQ,MAAM;AAAA,gBAC3B,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,iBAAe;AAAA,gBACf,eAAa,WAAW,QAAQ;AAAA,gBAChC,SAAS,MAAM,KAAK,MAAM;AAAA,gBAC1B,aAAa,MAAM,WAAW,QAAQ,UAAU,UAAU,QAAQ,MAAM;AAAA,gBACxE,WAAW;AAAA,kBACT;AAAA,kBACA,WAAW,QAAQ,SAAS,iBAAiB;AAAA,gBAC/C;AAAA,gBACA,eAAY;AAAA,gBAEZ;AAAA,sCAAC,UAAK,WAAU,2BAA0B,eAAY,QAAO;AAAA,kBAC7D,oBAAC,UAAK,WAAU,0BAA0B,YAAE,2BAAiB,EAAE,OAAO,OAAO,CAAC,GAAE;AAAA;AAAA;AAAA,YAClF;AAAA,YAED,CAAC,SAAS,oBAAC,OAAE,WAAU,0EAA0E,YAAE,mBAAmB,GAAE;AAAA,aAC3H;AAAA;AAAA;AAAA,IACF;AAAA,IAGF,oBAAC,OAAE,IAAI,QAAQ,MAAK,UAAS,WAAU,2FAA0F,eAAY,+BAC1I,kBACH;AAAA,KACF;AAEJ;;;ADlKgB,gBAAAC,MAEF,QAAAC,aAFE;AAnHhB,IAAM,eAAe;AAErB,IAAM,UAAU;AAkBD,SAAR,UAA2B,EAAE,MAAM,GAAG;AAC3C,QAAM,EAAE,EAAE,IAAI,MAAM;AACpB,QAAM,QAAQ,qBAAqB,MAAM,WAAW,MAAM,OAAO,MAAM,KAAK;AAC5E,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,EAAE;AACzC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,KAAK;AACxC,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,MAAM,SAAS,EAAE;AAE1D,QAAM,CAAC,OAAO,QAAQ,IAAIA;AAAA;AAAA,IAAgD,CAAC;AAAA,EAAE;AAC7E,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAClD,QAAM,WAAWC;AAAA;AAAA,IAA6C;AAAA,EAAK;AACnE,QAAM,aAAaA;AAAA;AAAA,IAA6C;AAAA,EAAK;AACrE,QAAM,aAAaA;AAAA;AAAA,IAA2C;AAAA,EAAK;AACnE,QAAM,UAAUC,OAAM;AACtB,QAAM,SAASA,OAAM;AACrB,QAAM,YAAYA,OAAM;AACxB,QAAM,gBAAgBA,OAAM;AAC5B,QAAM,UAAU,MAAM;AACtB,QAAM,SAAS,QAAQ,KAAK,CAAC,aAAa,SAAS,OAAO,QAAQ,KAAK,MAAM;AAC7E,QAAM,EAAE,MAAM,QAAQ,SAAS,MAAM,SAAS,aAAa,IAAI;AAG/D,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,MAAM,OAAQ;AACnB,gBAAY,MAAM,SAAS,EAAE;AAC7B,eAAW,MAAM,QAAQ,CAAC;AAC1B,aAAS,EAAE;AACX,aAAS,KAAK;AACd,aAAS,CAAC,CAAC;AACX,kBAAc,KAAK;AAAA,EACrB,GAAG,CAAC,MAAM,QAAQ,KAAK,CAAC;AAExB,QAAM,MAAM,MAAM,KAAK;AAEvB,QAAM,QAAQ,MAAM,IAAI,OAAO,EAAE;AAEjC,QAAM,UAAU,QAAQ,OAAO,WAAW,CAAC,IAAI,WAAW,OAAO,CAAC;AAClE,QAAM,UAAU,MAAM,WAAW,OAAO,OAAO,MAAM,SAAS;AAE9D,QAAM,WAAW,CAAC,OAAO,MAAM,EAAE,KAAK,MAAM,MAAM,EAAE;AACpD,QAAM,QAAQ,QAAQ,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAC/C,QAAM,YAAY,CAAC,gBAAgB,QAAQ,KAAK,KAAK;AAErD,QAAM,QAAQ,SAAS,OAAO,SAAS,aAAa,CAAC,gBAAiB,SAAS,cAAe,SAAS,OAAO,EAAE,CAAC;AAEjH,QAAM,SAAS,CAAgC,UAAU;AACvD,UAAM,eAAe;AACrB,QAAI,CAAC,MAAO;AACZ,QAAI,gBAAgB,UAAU,MAAM,QAAQ,OAAO,EAAE,EAAG,OAAM,WAAW,OAAO,OAAO,EAAE;AACzF,QAAI,OAAO,OAAO,MAAM,SAAS,GAAI,OAAM,OAAO,OAAO,EAAE;AAC3D,QAAI,MAAM,OAAO,EAAE,KAAK,MAAM,OAAO,EAAE,MAAM,MAAM,MAAM,OAAO,EAAE,EAAG,OAAM,YAAY,MAAM,OAAO,EAAE,GAAG,OAAO,EAAE;AAClH,UAAM,OAAO,OAAO,KAAK;AAAA,EAC3B;AAGA,QAAM,SAAS,CAAC,OAAO;AACrB,gBAAY,EAAE;AACd,eAAW,MAAM,QAAQ,EAAE,CAAC;AAC5B,aAAS,EAAE;AACX,kBAAc,KAAK;AAAA,EACrB;AAGA,QAAM,mBAAmB,CAAoC,UAAU;AACrE,UAAM,KAAK,QAAQ,UAAU,CAAC,aAAa,SAAS,OAAO,OAAO,EAAE;AACpE,UAAM,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,SAAS,IAAI,WAAW,GAAG,EAAE,MAAM,GAAG;AAClF,UAAM,KAAK,QAAQ,KAAK,OAAO,QAAQ,UAAU,QAAQ,SAAS,MAAM,QAAQ,SAAS,IAAI,MAAM,QAAQ,QAAQ,QAAQ,SAAS,IAAI;AACxI,QAAI,OAAO,GAAI;AACf,UAAM,eAAe;AACrB,WAAO,QAAQ,EAAE,EAAE,EAAE;AACgB,IAAC,WAAW,SAAS,iBAAiB,gBAAgB,EAAE,EAAE,GAAI,MAAM;AAAA,EAC3G;AAEA,SACE,gBAAAL,KAAC,UAAO,MAAM,MAAM,QAAQ,cAAc,CAAC,SAAS,CAAC,QAAQ,MAAM,OAAO,IAAI,GAC5E,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,mEAAmE,QAAQ,SAAS,IAAI,mBAAmB,gBAAgB;AAAA,MACzI,eAAY;AAAA,MACZ,iBAAiB,CAAC,UAAU;AAC1B,cAAM,eAAe;AACrB,SAAC,gBAAgB,CAAC,QAAQ,aAAa,UAAU,SAAS,MAAM;AAAA,MAClE;AAAA,MAGA,iBAAiB,CAAC,UAAU;AAC1B,YAAI,CAAC,WAAY;AACjB,cAAM,eAAe;AACrB,YAAI,EAAE,MAAM,kBAAkB,WAAW,MAAM,OAAO,QAAQ,kBAAkB,GAAI,eAAc,KAAK;AAAA,MACzG;AAAA,MAGA,0BAAAC,MAAC,UAAK,UAAU,QAAQ,WAAU,cAChC;AAAA,wBAAAA,MAAC,SAAI,WAAU,wDACb;AAAA,0BAAAA,MAAC,gBAAa,WAAU,iDACtB;AAAA,4BAAAD,KAAC,UAAK,WAAU,qHACd,0BAAAA,KAAC,YAAS,WAAU,aAAY,eAAY,QAAO,GACrD;AAAA,YACA,gBAAAC,MAAC,SAAI,WAAU,kCACb;AAAA,8BAAAD,KAAC,eAAa,YAAE,sBAAsB,EAAE,UAAU,KAAK,CAAC,GAAE;AAAA,cAC1D,gBAAAA,KAAC,qBAAkB,WAAU,gEAC1B,yBACG,EAAE,yIAAyI,IAC3I,EAAE,2FAA2F,EAAE,UAAU,KAAK,CAAC,GACrH;AAAA,eACF;AAAA,aACF;AAAA,UAEC,QAAQ,SAAS,KAChB,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,cAAY,EAAE,aAAa;AAAA,cAC3B,WAAU;AAAA,cACV,WAAW;AAAA,cACX,eAAY;AAAA,cAEX,kBAAQ,IAAI,CAAC,aAAa;AACzB,sBAAM,UAAU,SAAS,OAAO,OAAO;AACvC,uBACE,gBAAAC;AAAA,kBAAC;AAAA;AAAA,oBAEC,MAAK;AAAA,oBACL,MAAK;AAAA,oBACL,gBAAc;AAAA,oBACd,UAAU,UAAU,IAAI;AAAA,oBACxB,SAAS,MAAM,OAAO,SAAS,EAAE;AAAA,oBACjC,WAAW;AAAA,sBACT;AAAA,sBACA,UAAU,uCAAuC;AAAA,oBACnD;AAAA,oBAEA;AAAA,sCAAAA,MAAC,UAAK,WAAU,wGACd;AAAA,wCAAAD,KAAC,UAAK,WAAU,eAAe,mBAAS,MAAK;AAAA,wBAC5C,MAAM,IAAI,SAAS,EAAE,KACpB,gBAAAA,KAAC,UAAK,WAAU,oEAAmE,OAAO,EAAE,WAAW,GAAG,cAAY,EAAE,WAAW,GAAG;AAAA,yBAE1I;AAAA,sBACC,SAAS,SAAS,EAAE,KACnB,gBAAAA,KAAC,UAAK,WAAU,sEAAqE,OAAO,SAAS,SAAS,EAAE,GAC7G,mBAAS,SAAS,EAAE,GACvB;AAAA;AAAA;AAAA,kBApBG,SAAS;AAAA,gBAsBhB;AAAA,cAEJ,CAAC;AAAA;AAAA,UACH;AAAA,UAGD,WACC,gBAAAC,MAAC,OAAE,MAAK,SAAQ,WAAU,8JACxB;AAAA,4BAAAD,KAAC,iBAAc,WAAU,oCAAmC,eAAY,QAAO;AAAA,YAC9E,EAAE,uDAAuD,EAAE,UAAU,KAAK,CAAC;AAAA,aAC9E;AAAA,WAGA,UAAU,SACV,gBAAAC,MAAC,SAAI,WAAU,8DACZ;AAAA,sBACC,gBAAAD,KAAC,UAAO,SAAO,MAAC,SAAQ,WAAU,MAAK,cACrC,0BAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM;AAAA,gBACN,QAAO;AAAA,gBACP,KAAI;AAAA,gBAEJ,SAAS,CAAC,UAAU;AAClB,wBAAM,eAAe;AACrB,yBAAO,KAAK,QAAQ,UAAU,qBAAqB;AAAA,gBACrD;AAAA,gBACA,eAAY;AAAA,gBAEX;AAAA,oBAAE,WAAW;AAAA,kBACd,gBAAAD,KAAC,gBAAa,eAAY,QAAO;AAAA;AAAA;AAAA,YACnC,GACF;AAAA,YAED,QACC,gBAAAA,KAAC,UAAK,WAAU,sGACb,YAAE,IAAI,GACT;AAAA,aAEJ;AAAA,UAGD,gBACC,gBAAAC,MAAC,SAAI,WAAU,kCACb;AAAA,4BAAAD,KAAC,SAAM,SAAS,WAAY,YAAE,aAAa,GAAE;AAAA,YAC7C,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,IAAI;AAAA,gBACJ,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,OAAO;AAAA,gBACP,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA,gBAClD,aAAY;AAAA,gBACZ,cAAa;AAAA,gBACb,gBAAe;AAAA,gBACf,aAAY;AAAA,gBACZ,YAAY;AAAA,gBACZ,gBAAc,SAAS,CAAC,YAAY,OAAO;AAAA,gBAC3C,oBAAkB;AAAA,gBAClB,WAAU;AAAA,gBACV,eAAY;AAAA;AAAA,YACd;AAAA,YACA,gBAAAA,KAAC,OAAE,IAAI,eAAe,WAAU,gEAC7B,mBAAS,CAAC,YACT,gBAAAA,KAAC,UAAK,WAAU,mBAAmB,YAAE,6CAA6C,GAAE,IAEpF,EAAE,+HAA0H,GAEhI;AAAA,aACF;AAAA,UAGF,gBAAAC,MAAC,SAAI,WAAU,kCACb;AAAA,4BAAAD,KAAC,SAAM,SAAS,SAAU,oBAAU,EAAE,oBAAoB,IAAI,EAAE,SAAS,GAAE;AAAA,YAC3E,gBAAAC,MAAC,SAAI,WAAU,eACb;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,IAAI;AAAA,kBACJ,MAAM,QAAQ,SAAS;AAAA,kBACvB;AAAA,kBACA,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,kBAChD,aAAa,UAAU,GAAG,OAAO,WAAM,EAAE,eAAe;AAAA,kBACxD,cAAa;AAAA,kBACb,gBAAe;AAAA,kBACf,aAAY;AAAA,kBACZ,YAAY;AAAA,kBACZ,oBAAkB;AAAA,kBAClB,WAAU;AAAA,kBACV,eAAY;AAAA;AAAA,cACd;AAAA,cACA,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,SAAS,MAAM,SAAS,CAAC,YAAY,CAAC,OAAO;AAAA,kBAC7C,cAAY,QAAQ,EAAE,cAAc,IAAI,EAAE,cAAc;AAAA,kBACxD,gBAAc;AAAA,kBAEb,kBAAQ,gBAAAA,KAAC,UAAO,WAAU,aAAY,eAAY,QAAO,IAAK,gBAAAA,KAAC,OAAI,WAAU,aAAY,eAAY,QAAO;AAAA;AAAA,cAC/G;AAAA,eACF;AAAA,YACA,gBAAAA,KAAC,OAAE,IAAI,QAAQ,WAAU,wDACtB,oBACC,gBAAAA,KAAC,UAAK,WAAU,mBAAmB,YAAE,gDAAgD,EAAE,UAAU,MAAM,QAAQ,QAAQ,CAAC,GAAE,IACxH,SAAS,CAAC,MACZ,EAAE,yFAAyF,EAAE,UAAU,KAAK,CAAC,IAC3G,UACF,EAAE,wCAAwC,EAAE,UAAU,MAAM,QAAQ,QAAQ,CAAC,IAC3E,UACF,EAAE,yCAAyC,IACzC,MACN;AAAA,aACF;AAAA,UAEA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,UAAU;AAAA,cACV,QAAQ,IAAI,UAAU,gBAAiB,WAAW,MAAO,MAAM;AAAA,cAC/D,SAAS,YAAY,QAAQ;AAAA,cAC7B,OAAO,IAAI,UAAU,gBAAiB,WAAW,OAAQ,UAAU,MAAM,QAAQ,OAAO,EAAE,IAAI,MAAM;AAAA,cACpG,OAAO,SAAS,OAAO,EAAE;AAAA,cACzB,UAAU,CAAC,UAAU,SAAS,CAAC,aAAa,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,GAAG,MAAM,EAAE;AAAA,cAC/E,MAAM;AAAA,cACN,cAAc;AAAA;AAAA,UAChB;AAAA,WACF;AAAA,QAEA,gBAAAC,MAAC,SAAI,WAAU,gKACb;AAAA,0BAAAD,KAAC,eAAY,WAAU,8DAA6D,eAAY,QAAO;AAAA,UACvG,gBAAAA,KAAC,UACE,yBACG,EAAE,qFAAqF,IACvF,EAAE,iEAAiE,EAAE,UAAU,KAAK,CAAC,GAC3F;AAAA,WACF;AAAA,QAEA,gBAAAC,MAAC,gBAAa,WAAU,gDACtB;AAAA,0BAAAD,KAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,SAAS,MAAM,MAAM,OAAO,IAAI,GACnE,YAAE,QAAQ,GACb;AAAA,UACA,gBAAAA,KAAC,UAAO,MAAK,UAAS,UAAU,CAAC,OAAO,eAAY,wBACjD,YAAE,SAAS,GACd;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EACF,GACF;AAEJ;;;AE9TA,SAAS,aAAAM,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AACrD,SAAS,SAAAC,QAAO,SAAS,UAAAC,SAAQ,UAAU,iBAAAC,sBAAqB;;;ACKzD,IAAM,gBAAgB,CAAC,WAAW,UACvC;AAAA,EACE;AAAA,EACA,WACI,+DACA;AACN;;;ADoHE,qBAAAC,WAEI,OAAAC,MADF,QAAAC,aADF;AArGJ,IAAM,OAAO,CAAC,SACZ,KACG,UAAU,KAAK,EACf,QAAQ,UAAU,EAAE,EACpB,YAAY;AAEF,SAAR,eAAgC,EAAE,SAAS,UAAU,cAAc,GAAG;AAC3E,QAAM,EAAE,QAAQ,OAAO,SAAS,WAAW,UAAU,SAAS,WAAW,QAAQ,MAAM,IAAI;AAC3F,QAAM,EAAE,GAAG,QAAQ,OAAO,IAAI,MAAM;AACpC,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAS,KAAK;AACtC,QAAM,CAAC,SAAS,UAAU,IAAIA;AAAA;AAAA,IAAqC;AAAA,EAAK;AACxE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,EAAE;AACzC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,CAAC;AACxC,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,KAAK;AACxC,QAAM,WAAWC;AAAA;AAAA,IAA4C;AAAA,EAAK;AAClE,QAAM,YAAYA;AAAA;AAAA,IAA6C;AAAA,EAAK;AACpE,oBAAkB,SAAS;AAE3B,EAAAC,WAAU,MAAM,MAAM,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC;AACnD,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,KAAM,QAAO;AAClB,eAAW,CAAC;AACZ,UAAM,QAAQ,YAAY,MAAM,WAAW,CAAC,MAAM,IAAI,CAAC,GAAG,GAAI;AAC9D,WAAO,MAAM,cAAc,KAAK;AAAA,EAClC,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,SAASC,SAAQ,MAAM;AAC3B,UAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AAC5D,WAAO,mBAAmB,EACvB,IAAI,CAAC,WAAW;AAAA,MACf,GAAG;AAAA,MACH,UAAU,MAAM,SAAS,OAAO,CAAC,UAAU;AACzC,cAAM,QAAQ,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI,EAAE,MAAM,KAAK,CAAC,EAAE;AACvE,eAAO,MAAM,MAAM,CAAC,SAAS,MAAM,SAAS,IAAI,CAAC;AAAA,MACnD,CAAC;AAAA,IACH,EAAE,EACD,OAAO,CAAC,UAAU,MAAM,SAAS,SAAS,CAAC;AAAA,EAChD,GAAG,CAAC,OAAO,CAAC,CAAC;AAEb,QAAM,aAAa,MAAM;AACvB,aAAS,EAAE;AACX,aAAS,QAAQ,CAAC;AAClB,YAAQ,IAAI;AAAA,EACd;AAEA,QAAM,QAAQ,CAAC,SAAS;AACtB,QAAI,KAAM;AACV,aAAS,SAAS,MAAM;AACxB,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,WAAW,YAAY;AAC3B,QAAI,QAAS,CAAC,WAAW,CAAC,QAAQ,KAAK,EAAI;AAC3C,aAAS,EAAE;AACX,YAAQ,IAAI;AACZ,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS,UAAU;AACnB,QAAI;AACF,YAAM,QAAQ,MAAM;AAAA,QAClB;AAAA,UACE;AAAA,UACA,SAAS,QAAQ,KAAK;AAAA,UACtB,UAAU,OAAO,UAAU,IAAI,EAAE,MAAM,GAAG,CAAC;AAAA,UAC3C,QAAQ,UAAU,CAAC;AAAA,UACnB,GAAI,SAAS,KAAK,IAAI,EAAE,OAAO,EAAE,SAAS,MAAM,SAAS,SAAS,MAAM,SAAS,MAAM,MAAM,KAAK,EAAE,IAAI,CAAC;AAAA,UACzG,YAAY,aAAa,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,SAAS,KAAK,OAAO,SAAS,MAAM,EAAE;AAAA,QAC/F;AAAA,QACA,EAAE,QAAQ,WAAW,OAAO;AAAA,MAC9B;AACA,UAAI,WAAW,OAAO,QAAS;AAG/B,aAAO,SAAS,SAAS,KAAK,IAAI,cAAc,MAAM,MAAM,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,MAAM,IAAI;AAC9F,cAAQ,CAAC,UAAU;AAAA,QACjB,GAAG;AAAA,QACH,MAAM,KAAK,QAAQ,MAAM;AAAA,QACzB,SAAS,MAAM,WAAW,KAAK;AAAA,QAC/B,WAAW,MAAM,aAAa,KAAK;AAAA,MACrC,EAAE;AACF,UAAI,SAAS,GAAI,WAAU;AAC3B,cAAQ,KAAK;AACb,aAAO,WAAW,EAAE,gDAAgD,CAAC;AAAA,IACvE,SAAS,KAAK;AACZ,UAAI,CAAC,WAAW,OAAO,QAAS,UAAS,KAAK,UAAU,EAAE,IAAI,OAAO,IAAI,EAAE,mCAAmC,CAAC;AAAA,IACjH,UAAE;AACA,UAAI,SAAS,YAAY,WAAY,UAAS,UAAU;AACxD,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACnB,aAAS,SAAS,MAAM;AACxB,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,cAAc,QAAQ,WAAW,QAAQ,KAAK,CAAC;AAErD,SACE,gBAAAJ,MAAAF,WAAA,EACE;AAAA,oBAAAE,MAAC,UAAO,SAAQ,WAAU,MAAK,cAAa,UAAU,CAAC,OAAO,SAAS,YACrE;AAAA,sBAAAD,KAAC,YAAS;AAAA,MACT,EAAE,UAAU;AAAA,OACf;AAAA,IACA,gBAAAA,KAAC,UAAO,MAAY,cAAc,OAChC,0BAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,eAAY;AAAA,QACZ,iBAAiB,CAAC,UAAU;AAE1B,gBAAM,eAAe;AACrB,oBAAU,SAAS,MAAM;AAAA,QAC3B;AAAA,QAEA;AAAA,0BAAAA,MAAC,gBAAa,WAAU,sCACtB;AAAA,4BAAAD,KAAC,eAAa,YAAE,mBAAmB,GAAE;AAAA,YACrC,gBAAAA,KAAC,qBAAmB,YAAE,iFAAiF,GAAE;AAAA,aAC3G;AAAA,UAEA,gBAAAA,KAAC,SAAI,WAAU,mBACb,0BAAAC,MAAC,SAAI,WAAU,eACb;AAAA,4BAAAD,KAACM,SAAA,EAAO,WAAU,sHAAqH,eAAY,QAAO;AAAA,YAC1J,gBAAAN;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,MAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,gBAChD,aAAa,EAAE,wCAAmC;AAAA,gBAClD,cAAY,EAAE,QAAQ;AAAA,gBACtB,qBAAmB;AAAA,gBACnB,WAAU;AAAA,gBACV,UAAU;AAAA;AAAA,YACZ;AAAA,YACA,gBAAAA,KAAC,iBAAc,WAAU,yDAAwD;AAAA,aACnF,GACF;AAAA,UAIA,gBAAAA,KAAC,SAAI,WAAU,wFAAuF,eAAY,wBAChH,0BAAAC,MAAC,cAAS,UAAU,MAAM,WAAU,wCAClC;AAAA,4BAAAD,KAAC,YAAO,WAAU,cAAc,YAAE,wBAAwB,GAAE;AAAA,YAC3D,OAAO,WAAW,KACjB,gBAAAA,KAAC,OAAE,WAAU,kEACV,YAAE,4DAA4D,GACjE;AAAA,YAEF,gBAAAA,KAAC,SAAI,WAAU,gCACZ,iBAAO,IAAI,CAAC,UACX,gBAAAC,MAAC,aAAuB,WAAU,gCAA+B,cAAY,EAAE,MAAM,KAAK,GACxF;AAAA,8BAAAD,KAAC,gBAAc,YAAE,MAAM,KAAK,GAAE;AAAA,cAC9B,gBAAAA,KAAC,SAAI,WAAU,mCACZ,gBAAM,SAAS,IAAI,CAAC,UAAU;AAC7B,sBAAM,WAAW,YAAY,MAAM;AACnC,uBACE,gBAAAC;AAAA,kBAAC;AAAA;AAAA,oBAEC,MAAK;AAAA,oBACL,gBAAc;AAAA,oBACd,gBAAc,MAAM;AAAA,oBACpB,SAAS,MAAM,WAAW,WAAW,OAAO,MAAM,EAAE;AAAA,oBACpD,WAAW,cAAc,QAAQ;AAAA,oBAEhC;AAAA,kCAAY,gBAAAD,KAACO,QAAA,EAAM,WAAU,aAAY,eAAY,QAAO;AAAA,sBAC5D,EAAE,MAAM,KAAK;AAAA;AAAA;AAAA,kBART,MAAM;AAAA,gBASb;AAAA,cAEJ,CAAC,GACH;AAAA,iBAnBY,MAAM,EAoBpB,CACD,GACH;AAAA,aACF,GACF;AAAA,UAEA,gBAAAN,MAAC,SAAI,WAAU,wDACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,MAAM,MAAM,GAAG,kBAAkB,CAAC;AAAA,gBAC/E,aAAa,EAAE,iEAA4D;AAAA,gBAC3E,cAAY,EAAE,oBAAoB;AAAA,gBAClC,MAAM;AAAA,gBACN,WAAU;AAAA,gBACV,UAAU;AAAA,gBACV,WAAW,CAAC,UAAU;AACpB,sBAAI,MAAM,QAAQ,YAAY,MAAM,WAAW,MAAM,SAAU,UAAS;AAAA,gBAC1E;AAAA;AAAA,YACF;AAAA,YACC,QACC,gBAAAC,MAAC,OAAE,MAAK,SAAQ,WAAU,wEACxB;AAAA,8BAAAD,KAACQ,gBAAA,EAAc,WAAU,oCAAmC,eAAY,QAAO;AAAA,cAC9E;AAAA,eACH,IAEA,SACA,CAAC,QACC,gBAAAR,KAAC,OAAE,WAAU,2CAA2C,YAAE,oEAAoE,GAAE;AAAA,YAGpI,gBAAAC,MAAC,SAAI,WAAU,mDACZ;AAAA,sBACC,gBAAAD,KAAC,OAAE,WAAU,sDAAqD,MAAK,UAAS,aAAU,UACvF,YAAE,wCAAmC,EAAE,QAAQ,CAAC,GACnD;AAAA,cAED,QACC,gBAAAA,KAAC,UAAO,SAAQ,WAAU,MAAK,MAAK,SAAS,QAC1C,YAAE,QAAQ,GACb;AAAA,cAEF,gBAAAC,MAAC,UAAO,MAAK,MAAK,SAAS,UAAU,UAAU,CAAC,eAAe,MAAM,eAAY,sBAC9E;AAAA,uBAAO,gBAAAD,KAAC,WAAQ,WAAU,mBAAkB,IAAK,gBAAAA,KAAC,YAAS;AAAA,gBAC3D,EAAE,UAAU;AAAA,iBACf;AAAA,eACF;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;;;AEzPA,SAAS,aAAAS,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAC5C,SAAS,eAAAC,cAAa,WAAW,WAAAC,UAAS,YAAAC,WAAU,iBAAAC,sBAAqB;AAgFvD,gBAAAC,MAIZ,QAAAC,aAJY;AAxDlB,SAAS,WAAW,MAAM;AACxB,MAAI;AACF,UAAM,OAAO,IAAI,KAAK,aAAa,CAAC,IAAI,GAAG,EAAE,MAAM,WAAW,CAAC,EAAE,GAAG,IAAI,KAAK;AAC7E,WAAO,KAAK,OAAO,CAAC,EAAE,kBAAkB,IAAI,IAAI,KAAK,MAAM,CAAC;AAAA,EAC9D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEe,SAAR,YAA6B,EAAE,QAAQ,WAAW,QAAQ,GAAG;AAClE,QAAM,EAAE,GAAG,OAAO,IAAI,MAAM;AAC5B,QAAM,CAAC,SAAS,UAAU,IAAIC;AAAA;AAAA,IAAqC;AAAA,EAAK;AACxE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,EAAE;AACjD,QAAM,WAAWC;AAAA;AAAA,IAA4C;AAAA,EAAK;AAClE,QAAM,QAAQ,UAAU,YAAY;AAEpC,EAAAC,WAAU,MAAM,MAAM,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC;AAGnD,QAAM,cAAc,MAAM;AACxB,UAAM,UAAU,OAAO,WAAW,UAAU,EAAE;AAC9C,YAAQ,QAAQ,SAAS,UAAU,SAAS,SAAS;AAAA,EACvD;AAEA,QAAM,MAAM,OAAO,QAAQ,QAAQ,CAAC,MAAM;AACxC,QAAI,QAAS;AACb,UAAM,OAAO,YAAY;AACzB,aAAS,EAAE;AACX,eAAW,MAAM;AACjB,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS,UAAU;AACnB,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,EAAE,QAAQ,MAAM,QAAQ,QAAQ,UAAU,QAAQ,GAAG,MAAM,GAAG,EAAE,QAAQ,WAAW,OAAO,CAAC;AACxH,UAAI,WAAW,OAAO,QAAS;AAC/B,UAAI,YAAY,MAAM,MAAM;AAC1B,iBAAS,EAAE,4DAA4D,CAAC;AACxE;AAAA,MACF;AACA,aAAO,WAAW,QAAQ,OAAO,OAAO,cAAc,OAAO,MAAM,QAAQ,GAAG,UAAU,EAAE;AAC1F,UAAI,WAAW,SAAU,gBAAe,EAAE;AAC1C,aAAO,WAAW,EAAE,sDAAsD,CAAC;AAAA,IAC7E,SAAS,KAAK;AACZ,UAAI,CAAC,WAAW,OAAO,QAAS,UAAS,KAAK,UAAU,EAAE,IAAI,OAAO,IAAI,EAAE,kCAAkC,CAAC;AAAA,IAChH,UAAE;AACA,UAAI,SAAS,YAAY,WAAY,UAAS,UAAU;AACxD,iBAAW,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACnB,aAAS,SAAS,MAAM;AACxB,eAAW,IAAI;AAAA,EACjB;AAEA,QAAM,OAAO,QAAQ,OAAO;AAC5B,QAAM,UAAU,gBAAAJ,KAACK,UAAA,EAAQ,WAAU,6BAA4B,eAAY,QAAO;AAElF,SACE,gBAAAJ,MAAC,aAAQ,WAAU,kBAAiB,cAAY,EAAE,iBAAiB,GAAG,eAAY,cAChF;AAAA,oBAAAA,MAAC,gBAAa,WAAU,sCACtB;AAAA,sBAAAD,KAACM,WAAA,EAAS,WAAU,aAAY,eAAY,QAAO;AAAA,MAClD,EAAE,iBAAiB;AAAA,OACtB;AAAA,IACA,gBAAAL,MAAC,SAAI,WAAU,mCACZ;AAAA,sBAAgB,IAAI,CAAC,WACpB,gBAAAA,MAAC,YAAuB,MAAK,UAAS,WAAW,cAAc,GAAG,UAAU,MAAM,SAAS,MAAM,IAAI,OAAO,EAAE,GAC3G;AAAA,oBAAY,OAAO,MAAM;AAAA,QACzB,EAAE,OAAO,KAAK;AAAA,WAFJ,OAAO,EAGpB,CACD;AAAA,MACD,gBAAAA,MAAC,gBACC;AAAA,wBAAAD,KAAC,uBAAoB,SAAO,MAAC,UAAU,MACrC,0BAAAC,MAAC,YAAO,MAAK,UAAS,WAAW,cAAc,GAC5C;AAAA,sBAAY,cAAc,UAAU,gBAAAD,KAAC,aAAU,WAAU,aAAY,eAAY,QAAO;AAAA,UACxF,EAAE,WAAW;AAAA,UACd,gBAAAA,KAACO,cAAA,EAAY,WAAU,aAAY,eAAY,QAAO;AAAA,WACxD,GACF;AAAA,QACA,gBAAAP,KAAC,uBAAoB,OAAM,SAAQ,cAAY,EAAE,gBAAgB,GAC9D,4BAAkB,IAAI,CAAC,SACtB,gBAAAA,KAAC,oBAA4B,MAAM,MAAM,UAAU,MAAM,IAAI,aAAa,EAAE,gBAAgB,KAAK,CAAC,GAC/F,qBAAW,IAAI,KADK,IAEvB,CACD,GACH;AAAA,SACF;AAAA,OACF;AAAA,IACA,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,UAAU,CAAC,UAAU;AACnB,gBAAM,eAAe;AACrB,cAAI,YAAY,KAAK,EAAG,KAAI,UAAU,EAAE,aAAa,YAAY,KAAK,EAAE,CAAC;AAAA,QAC3E;AAAA,QAEA;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,UAAU,eAAe,MAAM,OAAO,KAAK;AAAA,cACtD,WAAW;AAAA,cACX,aAAa,EAAE,6BAAwB;AAAA,cACvC,cAAY,EAAE,gBAAgB;AAAA,cAC9B,UAAU;AAAA;AAAA,UACZ;AAAA,UACA,gBAAAA,KAAC,UAAO,MAAK,UAAS,SAAQ,WAAU,MAAK,WAAU,cAAY,EAAE,SAAS,GAAG,UAAU,QAAQ,CAAC,YAAY,KAAK,GAClH,sBAAY,WAAW,gBAAAA,KAACK,UAAA,EAAQ,WAAU,mBAAkB,IAAK,gBAAAL,KAACM,WAAA,EAAS,GAC9E;AAAA;AAAA;AAAA,IACF;AAAA,IACC,WACC,gBAAAL,MAAC,OAAE,WAAU,4EAA2E,MAAK,UAAS,aAAU,UAC7G;AAAA,QAAE,iBAAY;AAAA,MACf,gBAAAD,KAAC,UAAO,MAAK,UAAS,SAAQ,QAAO,MAAK,MAAK,WAAU,qBAAoB,SAAS,QACnF,YAAE,QAAQ,GACb;AAAA,OACF;AAAA,IAED,SACC,gBAAAC,MAAC,OAAE,MAAK,SAAQ,WAAU,wEACxB;AAAA,sBAAAD,KAACQ,gBAAA,EAAc,WAAU,oCAAmC,eAAY,QAAO;AAAA,MAC9E;AAAA,OACH;AAAA,KAEJ;AAEJ;;;ACpJA,SAAS,aAAAC,YAAW,iBAAiB,UAAAC,SAAQ,YAAAC,iBAAgB;AAC7D,SAAS,SAAAC,QAAO,WAAAC,UAAS,YAAAC,WAAU,iBAAAC,sBAAqB;AAqJtC,gBAAAC,MAKV,QAAAC,aALU;AA7IlB,IAAM,YAAY;AAoBH,SAAR,aAA8B,EAAE,QAAQ,MAAM,WAAW,SAAS,YAAY,KAAK,GAAG;AAC3F,QAAM,EAAE,GAAG,OAAO,IAAI,MAAM;AAC5B,QAAM,CAAC,SAAS,UAAU,IAAIC,UAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,EAAE;AACrC,QAAM,CAAC,OAAO,QAAQ,IAAIA;AAAA;AAAA,IAAuC;AAAA,EAAK;AACtE,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,EAAE;AAC3C,QAAM,WAAWC;AAAA;AAAA,IAAgC,CAAC;AAAA,EAAE;AACpD,QAAM,WAAWA;AAAA;AAAA,IAA4C;AAAA,EAAK;AAClE,QAAM,aAAaA,QAAO,KAAK,OAAO;AACtC,aAAW,UAAU,KAAK;AAC1B,QAAM,UAAUA;AAAA;AAAA,IAA2C;AAAA,EAAK;AAEhE,QAAM,WAAWA;AAAA;AAAA,IAA4C;AAAA,EAAK;AAElE,EAAAC,WAAU,MAAM,MAAM,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC;AAGnD,EAAAA;AAAA,IACE,MACE,OAAO,GAAG,QAAQ,MAAM;AACtB,eAAS,SAAS,MAAM;AACxB,eAAS,UAAU;AACnB,eAAS,UAAU,CAAC;AACpB,iBAAW,KAAK;AAChB,eAAS,IAAI;AACb,eAAS,EAAE;AACX,kBAAY,EAAE;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,MAAM;AAAA,EACT;AAGA,QAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,EAAAA,WAAU,MAAM;AACd,QAAI,SAAS,SAAS,CAAC,MAAM,SAAS,KAAK,OAAO,KAAK,UAAU,SAAU,aAAY,KAAK;AAAA,EAC9F,GAAG,CAAC,OAAO,KAAK,SAAS,OAAO,QAAQ,CAAC;AAEzC,kBAAgB,MAAM;AACpB,UAAM,SAAS,SAAS;AACxB,QAAI,CAAC,UAAU,QAAS;AACxB,aAAS,UAAU;AACnB,UAAM,OAAO,QAAQ;AACrB,UAAM,UACJ,WAAW,SACP,MAAM,cAAc,8BAA8B,IAClD,MAAM,cAAc,6BAA6B;AACb,IAAC,SAAU,MAAM;AAAA,EAC7D,CAAC;AAED,QAAM,QAAQ,OAAO,WAAW;AAEhC,QAAM,MAAM,YAAY;AACtB,QAAI,WAAW,OAAO,WAAW,QAAS;AAC1C,UAAM,QAAQ,CAAC;AAEf,UAAM,QAAQ,QAAQ,WAAW,QAAQ,KAAK,IAAI;AAClD,UAAM,aAAa,IAAI,gBAAgB;AACvC,aAAS,UAAU;AACnB,aAAS,EAAE;AACX,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,UACE,SAAS;AAAA,UACT,WAAW,KAAK;AAAA,UAChB,MAAM,WAAW,OAAO,WAAW,CAAC;AAAA,UACpC,UAAU;AAAA,UACV,WAAW,WAAW,IAAI,CAAC,EAAE,KAAK,OAAO,KAAK,OAAO,EAAE,KAAK,OAAO,KAAK,EAAE;AAAA,UAC1E,OAAO,SAAS,QAAQ,MAAM,CAAC,SAAS;AAAA,QAC1C;AAAA,QACA,EAAE,QAAQ,WAAW,OAAO;AAAA,MAC9B;AACA,UAAI,WAAW,OAAO,QAAS;AAC/B,YAAM,UAAU,WAAW;AAE3B,YAAM,OAAO,OAAO,SAAS,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC;AACrD,YAAM,OAAO,CAAC,GAAG,MAAM,GAAG,OAAO,SAAS,OAAO,CAAC,YAAY,YAAY,OAAO,CAAC;AAClF,eAAS,UAAU,CAAC,GAAG,SAAS,SAAS,GAAG,OAAO,QAAQ;AAE3D,kBAAY,QAAQ,KAAK,KAAK,CAAC,OAAO,SAAS,OAAO,IAAI,QAAQ,KAAK,IAAI,KAAK;AAChF,eAAS,IAAI;AACb,UAAI,MAAO,UAAS,UAAU;AAAA,IAChC,SAAS,KAAK;AACZ,UAAI,CAAC,WAAW,OAAO,QAAS,UAAS,KAAK,UAAU,EAAE,IAAI,OAAO,IAAI,EAAE,gCAAgC,CAAC;AAAA,IAC9G,UAAE;AAEA,UAAI,SAAS,YAAY,YAAY;AACnC,iBAAS,UAAU;AACnB,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,MAAM;AACnB,aAAS,SAAS,MAAM;AACxB,aAAS,UAAU;AACnB,eAAW,KAAK;AAChB,aAAS,UAAU;AAAA,EACrB;AAEA,QAAM,OAAO,MAAM;AACjB,aAAS,IAAI;AACb,aAAS,EAAE;AACX,aAAS,UAAU,CAAC;AACpB,aAAS,UAAU;AAAA,EACrB;AAEA,QAAM,UAAU,QAAQ,CAAC,GAAI,WAAW,CAAC,EAAE,MAAM,UAAU,OAAO,KAAK,CAAC,IAAI,CAAC,GAAI,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,MAAM,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC;AACtI,QAAM,UAAU,QAAQ,UAAU,CAAC,WAAW,OAAO,SAAS,KAAK,OAAO;AAC1E,QAAM,UAAU,YAAY,KAAK,IAAI;AAGrC,QAAM,YAAY,CAAoC,UAAU;AAC9D,UAAM,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,SAAS,IAAI,WAAW,GAAG,EAAE,MAAM,GAAG;AAClF,UAAM,KAAK,QAAQ,UAAU,OAAO,QAAQ,UAAU,QAAQ,SAAS,MAAM,QAAQ,SAAS,IAAI,MAAM,QAAQ,QAAQ,QAAQ,SAAS,IAAI;AAC7I,QAAI,OAAO,MAAM,CAAC,QAAQ,OAAQ;AAClC,UAAM,eAAe;AACrB,cAAU,QAAQ,EAAE,EAAE,IAAI;AACW,IAAC,QAAQ,SAAS,iBAAiB,gBAAgB,EAAE,EAAE,GAAI,MAAM;AAAA,EACxG;AAEA,QAAM,UAAU,gBAAAJ,KAACK,UAAA,EAAQ,WAAU,6BAA4B,eAAY,QAAO;AAElF,SACE,gBAAAJ,MAAC,SAAI,WAAU,gBAAe,eAAY,oBAAmB,KAAK,SAC/D;AAAA,KAAC,SACA,gBAAAA,MAAC,YAAO,MAAK,UAAS,WAAW,cAAc,GAAG,UAAU,WAAW,CAAC,OAAO,SAAS,KAAK,6BAAyB,MACnH;AAAA,gBAAU,UAAU,gBAAAD,KAACM,WAAA,EAAS,WAAU,aAAY,eAAY,QAAO;AAAA,MACvE,EAAE,kBAAkB;AAAA,OACvB;AAAA,IAED,SACC,gBAAAL,MAAC,SAAI,WAAU,kEACb;AAAA,sBAAAD,KAAC,SAAI,MAAK,cAAa,cAAY,EAAE,eAAe,GAAG,WAAU,kBAAiB,WAC/E,kBAAQ,IAAI,CAAC,QAAQ,UAAU;AAC9B,cAAM,SAAS,UAAU;AACzB,eACE,gBAAAC;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,gBAAc;AAAA,YACd,UAAU,UAAU,UAAU,IAAI;AAAA,YAClC,WAAW;AAAA,cACT;AAAA,cACA,SAAS,wCAAwC;AAAA,YACnD;AAAA,YACA,SAAS,MAAM,UAAU,OAAO,IAAI;AAAA,YAEpC;AAAA,8BAAAD,KAACO,QAAA,EAAM,WAAW,GAAG,qCAAqC,SAAS,oBAAoB,cAAc,GAAG,eAAY,QAAO;AAAA,cAC3H,gBAAAP,KAAC,UAAK,WAAU,uCAAuC,iBAAO,MAAK;AAAA,cAClE,OAAO,SAAS,gBAAAA,KAAC,UAAK,WAAU,uDAAuD,YAAE,OAAO,GAAE;AAAA;AAAA;AAAA,UAb9F,GAAG,OAAO,QAAQ,UAAU,MAAM,IAAI,OAAO,IAAI;AAAA,QAcxD;AAAA,MAEJ,CAAC,GACH;AAAA,MACA,gBAAAC,MAAC,SAAI,WAAU,wDACb;AAAA,wBAAAA,MAAC,YAAO,MAAK,UAAS,WAAW,cAAc,GAAG,UAAU,WAAW,CAAC,OAAO,SAAS,KAAK,6BAAyB,MACnH;AAAA,oBAAU,UAAU,gBAAAD,KAACM,WAAA,EAAS,WAAU,aAAY,eAAY,QAAO;AAAA,UACvE,EAAE,YAAY;AAAA,WACjB;AAAA,QACA,gBAAAN,KAAC,UAAO,MAAK,UAAS,SAAQ,SAAQ,MAAK,MAAK,WAAU,cAAa,UAAU,SAAS,SAAS,MAChG,YAAE,MAAM,GACX;AAAA,SACF;AAAA,OACF;AAAA,IAED,WACC,gBAAAC,MAAC,OAAE,WAAU,4EAA2E,MAAK,UAAS,aAAU,UAC7G;AAAA,QAAE,yBAAoB;AAAA,MACvB,gBAAAD,KAAC,UAAO,MAAK,UAAS,SAAQ,QAAO,MAAK,MAAK,WAAU,qBAAoB,SAAS,QACnF,YAAE,QAAQ,GACb;AAAA,OACF;AAAA,IAED,SACC,gBAAAC,MAAC,OAAE,MAAK,SAAQ,WAAU,wEACxB;AAAA,sBAAAD,KAACQ,gBAAA,EAAc,WAAU,oCAAmC,eAAY,QAAO;AAAA,MAC9E;AAAA,OACH;AAAA,KAEJ;AAEJ;;;ACxHA,IAAM;AAAA;AAAA,EAA8B,CAAC,YAAY,WAAW,iBAAiB;AAAA;AAC7E,IAAM,SAAS,EAAE,UAAU,aAAa,SAAS,YAAY,iBAAiB,YAAY;AAMnF,SAAS,SAAS,UAAU,CAAC,GAAG;AACrC,MAAI,CAAC,WAAW,OAAO,YAAY,SAAU,OAAM,IAAI,UAAU,qCAAqC;AACtG,QAAM,EAAE,OAAO,aAAa,SAAS,SAAS,SAAS,OAAO,WAAW,UAAU,gBAAgB,QAAQ,SAAS,MAAM,IAAI;AAC9H,QAAM,eAAe,mBAAmB;AACxC,QAAM,OAAO,YAAY,UAAa,YAAY,UAAc,cAAc,UAAa,CAAC;AAC5F,MAAI,UAAU,UAAa,OAAO,UAAU,WAAY,OAAM,IAAI,UAAU,sCAAsC;AAClH,MAAI,SAAS,KAAM,OAAM,IAAI,UAAU,yFAAyF;AAChI,MAAI,iBAAiB,SAAS,OAAO;AACnC,UAAM,IAAI,UAAU,oJAA+I;AAAA,EACrK;AACA,MAAI,CAAC,iBAAiB,WAAW,UAAa,QAAQ,WAAW,SAAY;AAC3E,UAAM,IAAI,UAAU,oFAAoF;AAAA,EAC1G;AACA,MAAI,WAAW,WAAc,OAAO,WAAW,YAAY,CAAC,QAAS,OAAM,IAAI,UAAU,+CAA+C;AACxI,MAAI,YAAY,WAAc,OAAO,YAAY,cAAe,CAAC,SAAS,CAAC,eAAgB;AACzF,UAAM,IAAI,UAAU,kGAAkG;AAAA,EACxH;AACA,MAAI,OAAO,WAAW,UAAW,OAAM,IAAI,UAAU,0CAA0C;AAC/F,MAAI,UAAU,OAAQ,OAAM,IAAI,UAAU,8EAA+E;AACzH,QAAM,SAAS,MAAM,QAAQ,cAAc;AAC3C,MAAI,WAAW,CAAC,eAAe,UAAU,CAAC,SAAS;AACjD,UAAM,IAAI,UAAU,6GAAwG;AAAA,EAC9H;AAEA,QAAM,YAAY,gBAAgB,SAAS,iBAAiB,CAAC,cAAc,GAAG,IAAI,UAAU,IAAI,CAAC;AACjG,MAAI,IAAI,IAAI,UAAU,IAAI,CAACC,cAAaA,UAAS,EAAE,CAAC,EAAE,SAAS,UAAU,OAAQ,OAAM,IAAI,UAAU,8BAA8B;AACnI,QAAM,CAAC,WAAW,IAAI,IAAI;AAC1B,MAAI,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,SAAS;AACvD,UAAM,IAAI,UAAU,aAAa,SAAS,IAAI,wFAAmF;AAAA,EACnI;AACA,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAI,UAAU,UAAa,OAAO,UAAU,aAAa,OAAO,UAAU,YAAY;AACpF,YAAM,IAAI,UAAU,eAAe,IAAI,sCAAsC;AAAA,IAC/E;AAAA,EACF;AAGA,QAAM,UAAU,CAAC,SAAS,CAAC,QAAQ,MAAM,KAAK,CAAC,SAAS,OAAO,QAAQ,IAAI,MAAM,UAAU;AAE3F,MAAI,YAAY,CAAC;AACjB,QAAM,MAAM,YAAY,SAAS,eAAe,WAAW,EAAE,OAAO,UAAU,CAAC,IAAI;AACnF,MAAI,UAAU;AAEZ,UAAM,SAAS,oBAAI,IAAI;AACvB,UAAM,SAAS,MAAO,MAAM,IAAI,IAAI,IAAI,UAAU;AAElD,UAAM,UAAU,CAAC,OAAO;AACtB,YAAM;AAAA;AAAA,QAA6D,UAAU,KAAK,CAAC,SAAS,KAAK,OAAO,EAAE;AAAA;AAC1G,YAAM,OAAO,MAAM,IAAI,MAAM,EAAE,IAAI,OAAO;AAE1C,YAAM,UAAU,OAAO,IAAI,UAAU,KAAK,CAAC,SAAS,KAAK,OAAO,EAAE,GAAG,eAAe,IAAI,QAAQ,EAAE,IAAI;AACtG,YAAM,OAAO,GAAG,EAAE;AAAA,EAAK,IAAI;AAAA,EAAK,OAAO;AACvC,UAAI,CAAC,OAAO,IAAI,IAAI,GAAG;AACrB,eAAO;AAAA,UACL;AAAA,UACA,OAAO,cACH,OAAO,YAAY,EAAE,QAAQ,QAAQ,OAAO,WAAW,GAAI,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,EAAG,CAAC,IACzF,mBAAmB,EAAE,UAAU,QAAQ,OAAO,WAAW,QAAQ,QAAQ,GAAI,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC,GAAI,GAAI,UAAU,EAAE,SAAS,QAAQ,IAAI,CAAC,EAAG,CAAC;AAAA,QAC3J;AAAA,MACF;AACA;AAAA;AAAA,QAA2D,OAAO,IAAI,IAAI;AAAA;AAAA,IAC5E;AAEA,UAAMC,SAAQ,OAAO,YAAY;AAG/B,UAAI,OAAO,CAAC,IAAI,MAAM,GAAG;AACvB,cAAM,IAAI,QAAQ;AAClB,YAAI,CAAC,IAAI,MAAM,EAAG,OAAM,IAAI,qBAAqB,eAAe,GAAG,IAAI,SAAS,IAAI,iBAAiB;AAAA,MACvG;AACA,aAAO,QAAQ,MAAM,IAAI,SAAS,KAAK,SAAS,EAAE,EAAE,OAAO;AAAA,IAC7D;AACA,UAAM,YAAY,qBAAqB,EAAE,OAAAA,QAAO,aAAa,QAAQ,CAAC;AACtE,eAAW,QAAQ,MAAO,WAAU,IAAI,IAAI,MAAM,WAAW,UAAU,IAAI,GAAG,GAAG,IAAI,UAAU,IAAI;AAAA,EACrG,WAAW,OAAO;AAChB,gBAAY,qBAAqB,EAAE,OAAO,aAAa,QAAQ,CAAC;AAAA,EAClE,WAAW,CAAC,SAAS;AACnB,UAAM,EAAE,KAAK,IAAI,iBAAiB,EAAE,SAAS,SAAS,OAAO,UAAU,CAAC;AACxE,eAAW,QAAQ,OAAO;AACxB,gBAAU,IAAI,IAAI,CAAuB,OAAkC,EAAE,OAAO,IAAI,CAAC,MACvF,KAAK,OAAO,IAAI,GAAG,EAAE,QAAQ,QAAQ,MAAM,KAAK,UAAU,KAAK,GAAG,OAAO,CAAC;AAAA,IAC9E;AAAA,EACF;AAGA,QAAM,UAAU,CAAC,SAAS;AACxB,UAAM,QAAQ,QAAQ,IAAI;AAC1B,QAAI,OAAO,UAAU,WAAY,QAAO;AACxC,QAAI,UAAU,MAAO,QAAO;AAC5B,WAAO,UAAU,IAAI,KAAK;AAAA,EAC5B;AAEA,SAAO,OAAO,OAAO;AAAA,IACnB;AAAA;AAAA,MAA0C,QAAQ,UAAU;AAAA;AAAA,IAC5D;AAAA;AAAA,MAAwC,QAAQ,SAAS;AAAA;AAAA,IACzD;AAAA;AAAA,MAAwD,QAAQ,iBAAiB;AAAA;AAAA,IACjF;AAAA,IACA,UAAU;AAAA,IACV,YAAY,OAAO,OAAO,EAAE,UAAU,gBAAgB,SAAS,aAAa,UAAU,cAAc,SAAS,MAAM,YAAY,KAAK,CAAC;AAAA,EACvI,CAAC;AACH;AAQA,SAAS,WAAW,QAAQ;AAC1B,MAAI,UAAU,OAAO,WAAW,YAAY,OAAO,OAAO,gBAAgB,YAAY;AACpF,QAAI,CAAC,OAAO,MAAM,CAAC,OAAO,KAAM,OAAM,IAAI,UAAU,oEAAoE;AACxH;AAAA;AAAA,MAA2D;AAAA;AAAA,EAC7D;AACA,SAAO;AAAA;AAAA,IAAoC;AAAA,EAAO;AACpD;AAQA,SAAS,WAAW,KAAK,KAAK;AAC5B,SAAO,OAA0B,OAA0B,YAAY;AACrE,QAAI;AACF,aAAO,MAAM,IAAI,OAAO,OAAO;AAAA,IACjC,SAAS,OAAO;AACd;AAAA;AAAA,QAAwB,OAAQ,SAAS,iBAAiB,SAAS,QAAQ;AAAA,OAAS,OAAM;AAC1F,UAAI,OAAO;AACX,UAAI;AACF,eAAO,MAAM,IAAI,OAAO,OAAO;AAAA,MACjC,SAAS,OAAO;AACd;AAAA;AAAA,UAAwB,OAAQ,SAAS;AAAA,SAAe,KAAI,OAAO;AACnE,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;","names":["provider","useEffect","useId","useRef","useState","models","jsx","jsxs","useState","useRef","useId","useEffect","useEffect","useMemo","useRef","useState","Check","Search","TriangleAlert","Fragment","jsx","jsxs","useState","useRef","useEffect","useMemo","Search","Check","TriangleAlert","useEffect","useRef","useState","ChevronDown","Loader2","Sparkles","TriangleAlert","jsx","jsxs","useState","useRef","useEffect","Loader2","Sparkles","ChevronDown","TriangleAlert","useEffect","useRef","useState","Check","Loader2","Sparkles","TriangleAlert","jsx","jsxs","useState","useRef","useEffect","Loader2","Sparkles","Check","TriangleAlert","provider","model"]}