@mjml-agent-editor/editor 0.1.14 → 0.1.15

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/canvas.ts","../src/components/editor/EmailEditor.tsx","../src/stores.tsx","../src/labels.ts","../src/index.ts","../src/lib/utils.ts","../src/components/ui/button.tsx","../src/components/ui/input.tsx","../src/components/ui/spinner.tsx","../src/components/ui/popover.tsx","../src/components/ui/dialog.tsx","../src/lib/refusal.ts","../src/components/ui/textarea.tsx","../src/components/comments/CanvasComments.tsx","../src/lib/save-queue.ts","../src/components/ui/switch.tsx","../src/components/editor/ImagePicker.tsx","../src/components/editor/ColorField.tsx","../src/components/editor/ImageField.tsx"],"sourcesContent":["/**\n * The GrapesJS canvas, isolated in its own entry point.\n *\n * GrapesJS touches `window` when it is imported, so this module can only be loaded in the\n * browser. Hosts should reach it through a client-only dynamic import — in Next.js that\n * is `dynamic(() => import(\"@mjml-agent-editor/editor/canvas\"), { ssr: false })`.\n */\n\nexport { default as EmailEditor } from \"./components/editor/EmailEditor.js\";\nexport type { EditorApi, EditorState, SaveStatus } from \"./components/editor/EmailEditor.js\";\n","\"use client\";\n\nimport grapesjs, { Component, Editor } from \"grapesjs\";\nimport GjsEditor, { Canvas, useEditorMaybe } from \"@grapesjs/react\";\nimport grapesjsMJML from \"grapesjs-mjml\";\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport { STARTER_MJML } from \"@mjml-agent-editor/core\";\nimport { useDocumentStore, useLabels, type CommentTarget, type EditorLabels } from \"../../index.js\";\nimport { refusal, saveToastId } from \"../../lib/refusal.js\";\nimport { createSaveQueue } from \"../../lib/save-queue.js\";\nimport { useAgentTurn } from \"../../stores.js\";\nimport { cn } from \"../../lib/utils\";\nimport { toast } from \"sonner\";\nimport { ChevronRight, ImageIcon, ImageUp } from \"lucide-react\";\n\nimport { Button } from \"../ui/button\";\nimport { Spinner } from \"../ui/spinner\";\nimport { Switch } from \"../ui/switch\";\nimport { Input } from \"../ui/input\";\nimport { Search } from \"lucide-react\";\nimport CanvasComments from \"../comments/CanvasComments.js\";\nimport ImagePicker from \"./ImagePicker.js\";\nimport ColorField, { toHex } from \"./ColorField.js\";\nimport ImageField, { imageUrl } from \"./ImageField.js\";\nimport { createRoot, type Root } from \"react-dom/client\";\n\nexport type SaveStatus = \"idle\" | \"saving\" | \"saved\" | \"error\";\n\n/** Snapshot of editor state pushed to the header (device, undo, save, zoom, width). */\nexport type EditorState = {\n device: string;\n canUndo: boolean;\n canRedo: boolean;\n saveStatus: SaveStatus;\n zoom: number;\n contentWidth: string;\n /** True during an agent turn, until its result is loaded. */\n readOnly: boolean;\n};\n\nexport type EditorApi = {\n /** Current MJML source from the editor. */\n getMjml: () => string;\n /** Compiled email HTML (via grapesjs-mjml's mjml-get-code). */\n getCompiledHtml: () => string;\n /** Immediately saves the editor state to the DB. */\n flushSave: () => Promise<void>;\n /** Reloads the document from the DB (after agent changes). */\n reloadFromDb: () => Promise<void>;\n /** Highlights a section in the canvas while the agent edits it. */\n highlightSection: (sectionId: string, on: boolean) => void;\n /** Device manager control (used by the top header). */\n getDevices: () => { id: string; name: string }[];\n setDevice: (name: string) => void;\n undo: () => void;\n redo: () => void;\n /** Canvas zoom (percentage). */\n setZoom: (z: number) => void;\n /** Email body width (mj-body width attribute, e.g. \"600px\"). */\n setContentWidth: (w: string) => void;\n /** Subscribe to editor state changes (device/undo/save/zoom/width). Returns unsubscribe. */\n onEditorState: (cb: (s: EditorState) => void) => () => void;\n};\n\ntype Props = {\n docId: string;\n onReady: (api: EditorApi) => void;\n /** Bump to force the canvas comment layer to refetch (after agent turns). */\n commentsRefresh: number;\n /** Reports the number of open comments (for the header indicator). */\n onOpenCountChange: (n: number) => void;\n};\n\nconst SEC_ID_RE = /\\bsec-([A-Za-z0-9_-]+)\\b/;\nconst OBJ_ID_RE = /\\bobj-([A-Za-z0-9_-]+)\\b/;\n\n// Element types that can receive a comment (besides a section).\nconst TYPE_LABEL: Record<string, string> = {\n \"mj-text\": \"Text\",\n \"mj-button\": \"Button\",\n \"mj-image\": \"Image\",\n \"mj-column\": \"Column\",\n};\n\nfunction tagOf(c: Component): string {\n return String(c.get(\"tagName\") || c.get(\"type\") || \"\");\n}\n\n// \"Sparkles\" icon (lucide Sparkles) used as a label in the GrapesJS toolbar.\nconst SPARKLES_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\"/><path d=\"M20 3v4\"/><path d=\"M22 5h-4\"/><path d=\"M4 17v2\"/><path d=\"M5 18H3\"/></svg>';\n\n// \"Image up\" icon (lucide ImageUp), the toolbar button that opens the image picker.\nconst IMAGE_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21\"/><path d=\"m14 19.5 3-3 3 3\"/><path d=\"M17 22v-5.5\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/></svg>';\n\nfunction isSection(c: Component): boolean {\n return tagOf(c) === \"mj-section\";\n}\n\nfunction isCommentable(c: Component): boolean {\n const t = tagOf(c);\n return t === \"mj-section\" || t in TYPE_LABEL;\n}\n\nfunction classMatch(c: Component, re: RegExp): string | null {\n const cssClass = String(c.getAttributes()[\"css-class\"] ?? \"\");\n return cssClass.match(re)?.[1] ?? null;\n}\n\nfunction sectionIdOf(c: Component): string | null {\n return classMatch(c, SEC_ID_RE);\n}\n\nfunction addIdClass(c: Component, prefix: \"sec\" | \"obj\"): string {\n const existing = classMatch(c, prefix === \"sec\" ? SEC_ID_RE : OBJ_ID_RE);\n if (existing) return existing;\n const cssClass = String(c.getAttributes()[\"css-class\"] ?? \"\");\n const id = Math.random().toString(36).slice(2, 10);\n c.addAttributes({ \"css-class\": `${cssClass} ${prefix}-${id}`.trim() });\n return id;\n}\n\nfunction closestSection(c: Component | undefined): Component | null {\n let cur: Component | undefined = c;\n while (cur) {\n if (isSection(cur)) return cur;\n cur = cur.parent() ?? undefined;\n }\n return null;\n}\n\n// Friendly names for the canvas breadcrumb.\nconst CRUMB_NAME: Record<string, string> = {\n \"mj-section\": \"Section\",\n \"mj-column\": \"Column\",\n \"mj-group\": \"Group\",\n \"mj-text\": \"Text\",\n \"mj-button\": \"Button\",\n \"mj-image\": \"Image\",\n \"mj-divider\": \"Divider\",\n \"mj-spacer\": \"Spacer\",\n \"mj-hero\": \"Hero\",\n \"mj-navbar\": \"Navbar\",\n \"mj-social\": \"Social\",\n \"mj-social-element\": \"Social\",\n \"mj-wrapper\": \"Wrapper\",\n};\n\nfunction crumbName(c: Component): string {\n const t = tagOf(c);\n return CRUMB_NAME[t] ?? t.replace(/^mj-/, \"\") ?? \"Element\";\n}\n\nfunction objectLabel(c: Component): string {\n const base = TYPE_LABEL[tagOf(c)] ?? tagOf(c);\n const text = String((c.getEl?.() as HTMLElement | undefined)?.textContent ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n .slice(0, 30);\n return text ? `${base}: “${text}”` : base;\n}\n\nconst RTE_FONTS = [\n \"Arial\",\n \"Helvetica\",\n \"Georgia\",\n \"Times New Roman\",\n \"Verdana\",\n \"Tahoma\",\n \"Trebuchet MS\",\n \"Courier New\",\n];\nconst RTE_SIZES = [11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 40];\n\n// Wraps the current selection (in the canvas document) in a <span> with inline style.\nfunction wrapSelectionStyle(el: HTMLElement | undefined, style: Partial<CSSStyleDeclaration>) {\n const doc = el?.ownerDocument;\n const sel = doc?.getSelection?.();\n if (!doc || !sel || sel.rangeCount === 0 || sel.isCollapsed) return;\n const range = sel.getRangeAt(0);\n const span = doc.createElement(\"span\");\n Object.assign(span.style, style);\n try {\n span.appendChild(range.extractContents());\n range.insertNode(span);\n sel.removeAllRanges();\n const nr = doc.createRange();\n nr.selectNodeContents(span);\n sel.addRange(nr);\n } catch {\n /* ignore */\n }\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Swaps the Style Manager's color and image controls for ColorField and ImageField.\n *\n * Built on the base property view rather than extending GrapesJS's own views, which mount\n * their widgets in onRender whatever `create` returns. Each control is its own React root\n * because GrapesJS creates and destroys these views outside React.\n */\nfunction setupStyleFields(editor: Editor, labels: EditorLabels) {\n const sm = editor.StyleManager;\n const base = (sm.getType(\"base\") as any).view;\n type View = { __root?: Root; __render?: () => void };\n\n // Distinct colors already in the email, most frequent first, read when the picker opens.\n // Only values of color-bearing attributes and declarations: a bare `#[0-9a-f]+` also\n // matches entities such as `&#039;` and anchors such as `href=\"#top\"`.\n const documentColors = (): string[] => {\n const source = `${editor.getHtml()} ${editor.getCss() ?? \"\"}`;\n const counts = new Map<string, number>();\n const colorValue =\n /(?:color|background|border)[\\w-]*\\s*[:=]\\s*[\"']?[^\"';>{}]*?(#[0-9a-f]{3,8}\\b|rgba?\\([^)]*\\))/gi;\n for (const match of source.matchAll(colorValue)) {\n const hex = toHex(match[1]!, { opaque: true });\n if (hex) counts.set(hex, (counts.get(hex) ?? 0) + 1);\n }\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 14)\n .map(([hex]) => hex);\n };\n\n // The value set on the selection, or \"\" when the property only shows its default:\n // GrapesJS substitutes the default before update(), which would present it as set.\n const ownValue = (property: any): string =>\n property.hasValue?.({ noParent: true }) ? String(property.getValue?.() ?? \"\") : \"\";\n\n // A view that renders `field(property, value)` into its own React root.\n const reactView = (field: (property: any, value: string) => ReactNode) =>\n base.extend({\n create(this: View, { property }: { property: any }) {\n const el = document.createElement(\"div\");\n // The base view writes the value of any input that fires `change`; the field\n // commits its own, so letting the event through wrote every edit twice.\n el.addEventListener(\"change\", (event) => event.stopPropagation());\n const root = createRoot(el);\n this.__root = root;\n this.__render = () => root.render(field(property, ownValue(property)));\n this.__render();\n return el;\n },\n update(this: View) {\n this.__render?.();\n },\n destroy(this: View) {\n // GrapesJS can still call update() on a destroyed view; drop the renderer first so\n // that is a no-op rather than \"Cannot update an unmounted root\". The unmount itself\n // is deferred because the view may be removed while React is mid-render.\n const root = this.__root;\n this.__root = undefined;\n this.__render = undefined;\n setTimeout(() => root?.unmount());\n },\n });\n\n sm.addType(\"color\", {\n view: reactView((property, value) => (\n <ColorField\n value={value}\n placeholder={String(property.getDefaultValue?.() ?? \"\")}\n documentColors={documentColors}\n labels={labels}\n onChange={(next, partial) => property.upValue(next, { partial })}\n />\n )),\n } as never);\n\n // Opens the same picker an mj-image uses; the property wraps the URL in url() itself\n // where it needs to (CSS background-image), and keeps it bare where it does not (MJML).\n sm.addType(\"file\", {\n view: reactView((property, value) => (\n <ImageField\n value={value}\n labels={labels}\n onClear={() => property.upValue(\"\")}\n onChoose={() => {\n const am = editor.AssetManager;\n am.open({\n types: [\"image\"],\n current: imageUrl(value) ?? undefined,\n select(asset: string | { getSrc: () => string }, complete?: boolean) {\n const url = typeof asset === \"string\" ? asset : asset.getSrc();\n property.upValue(url, { partial: !complete });\n if (complete) am.close();\n },\n } as never);\n }}\n />\n )),\n } as never);\n}\n\n// Extends the default GrapesJS RTE (mjml-compatible) with font/size/colors.\nfunction setupRichText(editor: Editor, labels: EditorLabels) {\n const rte = editor.RichTextEditor as any;\n if (!rte || rte.__customActions) return;\n rte.__customActions = true;\n const val = (action: any, sel: string) =>\n (action.btn?.querySelector(sel) as HTMLInputElement | HTMLSelectElement | null)?.value ?? \"\";\n\n rte.add(\"fontName\", {\n icon: `<select class=\"gjs-rte-select\" title=\"${labels.rteFont}\"><option value=\"\">${labels.rteFont}</option>${RTE_FONTS.map(\n (f) => `<option value=\"${f}\">${f}</option>`,\n ).join(\"\")}</select>`,\n event: \"change\",\n result: (r: any, action: any) => {\n const v = val(action, \"select\");\n if (v) r.exec(\"fontName\", v);\n },\n });\n\n rte.add(\"fontSize\", {\n icon: `<select class=\"gjs-rte-select\" title=\"${labels.rteSize}\"><option value=\"\">${labels.rteSize}</option>${RTE_SIZES.map(\n (s) => `<option value=\"${s}px\">${s}</option>`,\n ).join(\"\")}</select>`,\n event: \"change\",\n result: (r: any, action: any) => {\n const v = val(action, \"select\");\n if (v) wrapSelectionStyle(r.el, { fontSize: v });\n },\n });\n\n rte.add(\"forecolor\", {\n icon: `<input type=\"color\" class=\"gjs-rte-color\" title=\"${labels.rteTextColor}\" value=\"#000000\">`,\n event: \"change\",\n result: (r: any, action: any) => r.exec(\"foreColor\", val(action, \"input\")),\n });\n\n rte.add(\"hilitecolor\", {\n icon: `<input type=\"color\" class=\"gjs-rte-color\" title=\"${labels.rteBackgroundColor}\" value=\"#ffff00\">`,\n event: \"change\",\n result: (r: any, action: any) => r.exec(\"hiliteColor\", val(action, \"input\")),\n });\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport default function EmailEditor({ docId, onReady, commentsRefresh, onOpenCountChange }: Props) {\n // Injected by the host rather than imported, so the editor carries no database.\n const documents = useDocumentStore();\n const labels = useLabels();\n // Refs instead of state — GrapesJS lives outside React's lifecycle.\n const loadingRef = useRef(false);\n const saveStatusRef = useRef<SaveStatus>(\"idle\");\n const stateListeners = useRef(new Set<(s: EditorState) => void>());\n const [loading, setLoading] = useState(true);\n const [sidebarView, setSidebarView] = useState<SidebarView>(\"blocks\");\n // Comment being composed (sparkles button) — opens the canvas popover.\n const [composeTarget, setComposeTarget] = useState<CommentTarget | null>(null);\n // Where the current selection came from — canvas click opens Settings,\n // selecting from the Layers tree does not (keeps you on Layers).\n const selectionSource = useRef<\"canvas\" | \"layers\" | \"other\">(\"other\");\n // Breadcrumb of the selected component's ancestry (Section › Column › …).\n const editorRef = useRef<Editor | null>(null);\n // Read only from a turn's start until the document it left is loaded, not just until it\n // ends: an edit made before that final reload would be replaced by it.\n const { running: turnRunning, locked: readOnly, setLocked } = useAgentTurn();\n const readOnlyRef = useRef(false);\n const applyReadOnly = useRef<(on: boolean) => Promise<void>>(async () => {});\n useEffect(() => void applyReadOnly.current(turnRunning), [turnRunning]);\n useEffect(() => () => setLocked(false), [setLocked]);\n const [crumbs, setCrumbs] = useState<Component[]>([]);\n // `src` of the selected mj-image, or null when the selection is not one.\n const [selectedImageSrc, setSelectedImageSrc] = useState<string | null>(null);\n // The open picker: GrapesJS hands over how to apply a choice to the image being edited.\n const [picker, setPicker] = useState<{ current?: string; select: (url: string) => void } | null>(\n null,\n );\n\n const onEditor = async (editor: Editor) => {\n editorRef.current = editor;\n\n // Build the breadcrumb chain from a component up to (but excluding) mj-body.\n const buildCrumbs = (c: Component | undefined) => {\n const chain: Component[] = [];\n let cur: Component | undefined = c;\n while (cur) {\n const t = tagOf(cur);\n if (!t || t === \"mj-body\" || t === \"wrapper\" || t === \"Wrapper\") break;\n chain.unshift(cur);\n cur = cur.parent() ?? undefined;\n }\n setCrumbs(chain);\n };\n\n // GrapesJS find() by MJML tag is unreliable — walk the tree instead.\n const findByTag = (tag: string): Component | undefined => {\n let found: Component | undefined;\n editor.getWrapper()?.onAll((c) => {\n if (!found && (c.get(\"tagName\") === tag || c.get(\"type\") === tag)) found = c;\n });\n return found;\n };\n const bodyWidth = (): string => {\n const body = findByTag(\"mj-body\");\n return String((body?.getAttributes?.() as Record<string, unknown>)?.width ?? \"600px\");\n };\n const snapshot = (): EditorState => ({\n device: editor.getDevice(),\n canUndo: !readOnlyRef.current && editor.UndoManager.hasUndo(),\n canRedo: !readOnlyRef.current && editor.UndoManager.hasRedo(),\n saveStatus: saveStatusRef.current,\n zoom: Math.round((editor.Canvas.getZoom?.() as number | undefined) ?? 100),\n contentWidth: bodyWidth(),\n readOnly: readOnlyRef.current,\n });\n const notifyState = () => {\n const s = snapshot();\n stateListeners.current.forEach((l) => l(s));\n };\n const setSave = (s: SaveStatus) => {\n saveStatusRef.current = s;\n notifyState();\n };\n\n // Comment target for a component: its section + (if it's not the section\n // itself) the specific element. Assigns stable IDs (sec-/obj-) when needed.\n const targetOf = (c: Component | undefined): CommentTarget | null => {\n const section = closestSection(c);\n if (!section || !c) return null;\n const sectionId = addIdClass(section, \"sec\");\n if (isSection(c)) {\n return { sectionId, objectId: null, objectLabel: null };\n }\n const objectId = addIdClass(c, \"obj\");\n return { sectionId, objectId, objectLabel: objectLabel(c) };\n };\n\n // Gives the section a sec-<id> and adds a comment button to the toolbar of\n // every commentable element (section + text/button/image/column).\n const decorate = (c: Component) => {\n if (isSection(c)) addIdClass(c, \"sec\");\n if (!isCommentable(c)) return;\n const toolbar = [...((c.get(\"toolbar\") as { command?: string }[]) ?? [])];\n if (tagOf(c) === \"mj-image\" && !toolbar.some((t) => t.command === \"open-image-picker\")) {\n toolbar.push({\n command: \"open-image-picker\",\n label: IMAGE_SVG,\n attributes: { title: labels.chooseImage },\n } as never);\n c.set(\"toolbar\", toolbar as never);\n }\n if (!toolbar.some((t) => t.command === \"open-comments\")) {\n toolbar.push({\n command: \"open-comments\",\n label: SPARKLES_SVG,\n attributes: { title: \"Comment on element / section\", \"data-role\": \"sparkles\" },\n } as never);\n c.set(\"toolbar\", toolbar as never);\n }\n };\n\n // What a double-click on an image does in GrapesJS, reachable from a button.\n editor.Commands.add(\"open-image-picker\", {\n run(ed: Editor) {\n const target = ed.getSelected();\n if (!target || tagOf(target) !== \"mj-image\") return;\n const am = ed.AssetManager;\n am.open({\n target,\n types: [\"image\"],\n select(asset: { getSrc: () => string }, complete?: boolean) {\n target.set({ src: asset.getSrc() });\n if (complete) am.close();\n },\n } as never);\n },\n });\n\n editor.Commands.add(\"open-comments\", {\n run(ed: Editor) {\n const target = targetOf(ed.getSelected() ?? undefined);\n if (target) setComposeTarget(target);\n },\n });\n\n setupRichText(editor, labels);\n setupStyleFields(editor, labels);\n\n // Group the palette blocks into categories (the plugin ships them flat).\n const blockCategory = (label: string): string => {\n const l = label.toLowerCase();\n if (/(column|section)/.test(l)) return \"Layout\";\n if (/(text|image|button|divider|spacer|social)/.test(l)) return \"Basic\";\n return \"Advanced\";\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const bm = editor.BlockManager as any;\n bm.getAll().forEach(\n (b: { get: (k: string) => string; set: (k: string, v: unknown) => void }) => {\n b.set(\"category\", blockCategory(b.get(\"label\") || \"\"));\n },\n );\n bm.render();\n\n editor.on(\"component:add\", decorate);\n // Track selection origin: a mousedown inside the canvas iframe marks the\n // next selection as coming from the canvas (→ open Settings). Selecting via\n // the Layers tree leaves the source as \"layers\" (set by the sidebar).\n editor.on(\"load\", () => {\n const body = editor.Canvas.getBody();\n body?.addEventListener(\n \"mousedown\",\n () => {\n selectionSource.current = \"canvas\";\n },\n true,\n );\n });\n editor.on(\"component:selected\", (c: Component) => {\n if (selectionSource.current === \"canvas\") setSidebarView(\"settings\");\n selectionSource.current = \"other\";\n buildCrumbs(c);\n setSelectedImageSrc(c && tagOf(c) === \"mj-image\" ? String(c.get(\"src\") ?? \"\") : null);\n // mj-image has no src trait by default (src changes via the Asset\n // Manager) — add a URL field so the image can be set from the panel.\n if (c?.get(\"type\") === \"mj-image\" && !c.getTrait(\"src\")) {\n c.addTrait(\n {\n type: \"text\",\n name: \"src\",\n label: \"Image (URL)\",\n placeholder: \"https://…\",\n changeProp: true,\n } as never,\n { at: 0 },\n );\n }\n });\n // With multi-select a deselection can leave something selected; follow what remains.\n editor.on(\"component:deselected\", () => {\n const rest = editor.getSelected();\n buildCrumbs(rest);\n setSelectedImageSrc(\n rest && tagOf(rest) === \"mj-image\" ? String(rest.get(\"src\") ?? \"\") : null,\n );\n });\n editor.on(\"component:update:src\", (c: Component) => {\n if (c === editor.getSelected()) setSelectedImageSrc(String(c.get(\"src\") ?? \"\"));\n });\n\n // The MJML the store holds, as far as this editor knows. A save writes only when the\n // canvas differs from it — the canvas is the truth, not the events that changed it.\n let stored = \"\";\n\n const save = async () => {\n // An open rich-text editor holds its text until editing ends.\n const view = editor.getEditing()?.getView() as\n { syncContent?: () => Promise<void> } | undefined;\n await view?.syncContent?.();\n const mjml = editor.getHtml();\n if (mjml === stored) return;\n setSave(\"saving\");\n try {\n await documents.save(docId, { mjml, projectData: editor.getProjectData() });\n stored = mjml;\n setSave(\"saved\");\n } catch (e) {\n setSave(\"error\");\n toast.error(refusal(e) ?? labels.documentSaveFailed, { id: saveToastId(docId) });\n throw e;\n }\n };\n\n const queue = createSaveQueue(save, 1200);\n\n editor.on(\"update\", () => {\n notifyState();\n if (!loadingRef.current && !readOnlyRef.current) queue.schedule();\n });\n editor.on(\"change:device\", notifyState);\n\n // While a turn runs the agent writes the document, so the visitor cannot: edits made\n // meanwhile would be overwritten by the agent's next reload, or overwrite its work.\n const blocked = [\n \"core:undo\",\n \"core:redo\",\n \"core:paste\",\n \"core:component-delete\",\n \"tlb-delete\",\n \"tlb-clone\",\n \"tlb-move\",\n \"open-assets\",\n \"open-image-picker\",\n ];\n for (const id of blocked) {\n editor.on(`command:run:before:${id}`, ({ options }: { options: { abort?: boolean } }) => {\n if (readOnlyRef.current) options.abort = true;\n });\n }\n const lock = (on: boolean) => {\n readOnlyRef.current = on;\n setLocked(on);\n notifyState();\n };\n applyReadOnly.current = async (on) => {\n if (on) {\n lock(true);\n queue.cancel();\n editor.select([]);\n // Through GrapesJS, which otherwise keeps its asset command active and will not reopen.\n editor.AssetManager.close();\n setPicker(null);\n return;\n }\n try {\n await reload();\n } catch (e) {\n // Unlocking a stale canvas would let the next save overwrite what the agent wrote.\n console.error(e);\n toast.error(labels.documentLoadFailed, {\n id: `reload-${docId}`,\n duration: Infinity,\n action: { label: labels.retry, onClick: () => void applyReadOnly.current(false) },\n });\n return;\n }\n lock(false);\n };\n\n // Only the agent writes during a turn, so the store differs from `stored` exactly when it\n // changed something. Otherwise the canvas is kept, including edits a failed save left unsaved.\n // Queued behind saves and earlier reloads, so a slow one never lands over a newer one.\n const reload = () =>\n queue.run(async () => {\n const fresh = await documents.get(docId);\n if (fresh.mjml !== stored) loadMjml(fresh.mjml);\n });\n\n const loadMjml = (mjml: string) => {\n // Loading reports changes of its own. What it adds — ids, the starter body — is still\n // written by the next save, which compares against what the store returned.\n loadingRef.current = true;\n try {\n queue.cancel();\n stored = mjml;\n editor.setComponents(mjml || STARTER_MJML);\n editor.getWrapper()?.find(\"mj-section\").forEach(decorate);\n for (const t of Object.keys(TYPE_LABEL)) {\n editor.getWrapper()?.find(t).forEach(decorate);\n }\n } finally {\n loadingRef.current = false;\n }\n };\n\n // Highlight for the section the agent is editing. The canvas is an iframe with its\n // own document, so the host's stylesheet does not reach it and these rules have to be\n // injected. The colour is still read from the shared token rather than written out\n // again — the literal that used to live here was the pre-redesign violet and had\n // silently drifted from the brand for as long as the redesign had been in place.\n const ensureHighlightStyles = () => {\n const cdoc = editor.Canvas.getDocument();\n if (!cdoc || cdoc.getElementById(\"agent-edit-style\")) return;\n\n const accent =\n getComputedStyle(document.documentElement)\n .getPropertyValue(\"--mjml-editor-accent\")\n .trim() || \"#7c5cfc\";\n\n const style = cdoc.createElement(\"style\");\n style.id = \"agent-edit-style\";\n style.textContent = `\n @keyframes agentEditPulse {\n 0%, 100% { outline-color: color-mix(in srgb, ${accent} 25%, transparent); }\n 50% { outline-color: color-mix(in srgb, ${accent} 95%, transparent); }\n }\n .agent-editing {\n outline: 3px solid color-mix(in srgb, ${accent} 90%, transparent) !important;\n outline-offset: -3px;\n animation: agentEditPulse 1s ease-in-out infinite;\n transition: outline-color 0.2s;\n }`;\n cdoc.head.appendChild(style);\n };\n\n const sectionEl = (sectionId: string): HTMLElement | undefined => {\n const comp = editor\n .getWrapper()\n ?.find(\"mj-section\")\n .find((c) => sectionIdOf(c) === sectionId);\n return (comp?.getEl?.() as HTMLElement | undefined) ?? undefined;\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const compiledHtml = (): string => {\n try {\n // grapesjs-mjml compiles MJML → email HTML via this command ({ html, errors }).\n const res = editor.runCommand(\"mjml-code-to-html\") as { html?: string } | undefined;\n return res?.html ?? editor.getHtml();\n } catch {\n return editor.getHtml();\n }\n };\n\n try {\n const doc = await documents.get(docId);\n loadMjml(doc.mjml);\n } catch (e) {\n toast.error(labels.documentLoadFailed);\n console.error(e);\n } finally {\n setLoading(false);\n }\n\n onReady({\n getMjml: () => editor.getHtml(),\n getCompiledHtml: compiledHtml,\n flushSave: () => queue.flush(),\n reloadFromDb: reload,\n highlightSection: (sectionId, on) => {\n ensureHighlightStyles();\n const el = sectionEl(sectionId);\n if (!el) return;\n el.classList.toggle(\"agent-editing\", on);\n },\n getDevices: () =>\n editor.Devices.getDevices().map((d) => ({\n id: String(d.id),\n name: d.getName() || String(d.id),\n })),\n setDevice: (name) => {\n editor.setDevice(name);\n notifyState();\n },\n undo: () => {\n if (readOnlyRef.current) return;\n editor.UndoManager.undo();\n notifyState();\n },\n redo: () => {\n if (readOnlyRef.current) return;\n editor.UndoManager.redo();\n notifyState();\n },\n setZoom: (z) => {\n editor.Canvas.setZoom?.(Math.max(25, Math.min(200, z)));\n notifyState();\n },\n setContentWidth: (w) => {\n if (readOnlyRef.current) return;\n findByTag(\"mj-body\")?.addAttributes({ width: w });\n notifyState();\n },\n onEditorState: (cb) => {\n stateListeners.current.add(cb);\n cb(snapshot());\n return () => stateListeners.current.delete(cb);\n },\n });\n };\n\n return (\n <GjsEditor\n grapesjs={grapesjs}\n className=\"h-full\"\n options={{\n height: \"100%\",\n storageManager: false,\n fromElement: false,\n panels: { defaults: [] },\n blockManager: { appendTo: \"#gjs-blocks\" },\n styleManager: { appendTo: \"#gjs-styles\" },\n traitManager: { appendTo: \"#gjs-traits\" },\n layerManager: { appendTo: \"#gjs-layers\" },\n assetManager: {\n // Never inline files: mail clients block `data:` images (see ImageLibrary).\n embedAsBase64: false,\n // Our own dialog instead of GrapesJS's modal. GrapesJS still decides when it opens\n // and what a choice applies to; `select(url, true)` sets the image and closes.\n custom: {\n open: (props: {\n select: (asset: string, complete?: boolean) => void;\n // `current` is ours, passed by style properties that have no target component.\n options?: { target?: Component; current?: string };\n }) =>\n setPicker({\n current:\n props.options?.current ??\n (String(props.options?.target?.get(\"src\") ?? \"\") || undefined),\n select: (url) => props.select(url, true),\n }),\n close: () => setPicker(null),\n },\n },\n deviceManager: {\n devices: [\n { id: \"desktop\", name: \"Desktop\", width: \"\" },\n { id: \"mobile\", name: \"Mobile\", width: \"375px\", widthMedia: \"480px\" },\n ],\n },\n plugins: [grapesjsMJML],\n }}\n onEditor={onEditor}\n >\n {/* Inert as well as covered, so the keyboard cannot reach the controls either. */}\n <div className=\"relative flex h-full min-h-0 flex-1\" inert={readOnly}>\n {readOnly && (\n <div className=\"absolute inset-0 z-30 flex cursor-not-allowed items-start justify-center bg-white/20 pt-3\">\n <span className=\"flex items-center gap-2 rounded-full bg-panel px-3 py-1 text-xs text-panel-fg shadow\">\n <Spinner /> {labels.agentWorking}\n </span>\n </div>\n )}\n <LeftSidebar\n view={sidebarView}\n onViewChange={setSidebarView}\n markLayersSource={() => (selectionSource.current = \"layers\")}\n selectedImageSrc={selectedImageSrc}\n onChooseImage={() => editorRef.current?.runCommand(\"open-image-picker\")}\n />\n <div className=\"flex min-w-0 flex-1 flex-col bg-surface-muted\">\n <div className=\"relative min-h-0 flex-1\">\n <Canvas className=\"h-full\" />\n {/* Canvas comment layer (pins + thread popovers) overlays the canvas. */}\n <CanvasComments\n docId={docId}\n composeTarget={composeTarget}\n onComposeConsumed={() => setComposeTarget(null)}\n refreshSignal={commentsRefresh}\n onOpenCountChange={onOpenCountChange}\n />\n <ImagePicker\n open={picker !== null}\n currentUrl={picker?.current}\n onSelect={(url) => {\n // An upload that finished after a turn locked the canvas is not applied.\n if (!readOnlyRef.current) picker?.select(url);\n setPicker(null);\n }}\n // Closed here as well as through GrapesJS: its close() is a no-op whenever it\n // does not consider its asset command active, which left the dialog stuck open.\n onClose={() => {\n setPicker(null);\n editorRef.current?.AssetManager.close();\n }}\n />\n {loading && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center gap-2 bg-white/70 text-sm text-zinc-500\">\n <Spinner /> Loading editor…\n </div>\n )}\n </div>\n <Breadcrumb crumbs={crumbs} onSelect={(c) => editorRef.current?.select(c)} />\n </div>\n </div>\n </GjsEditor>\n );\n}\n\ntype SidebarView = \"blocks\" | \"settings\" | \"layers\";\n\n// Collapsible section — content ALWAYS mounted (appendTo target), hidden via `hidden`.\nfunction CollapseSection({\n title,\n defaultOpen = true,\n children,\n}: {\n title: string;\n defaultOpen?: boolean;\n children: ReactNode;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n return (\n <div className=\"border-b border-panel-border\">\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-1.5 px-3 py-2 text-xs font-semibold tracking-wide text-panel-muted-fg uppercase hover:text-panel-fg\"\n >\n <ChevronRight className={cn(\"size-3.5 transition-transform\", open && \"rotate-90\")} />\n {title}\n </button>\n <div className={cn(!open && \"hidden\")}>{children}</div>\n </div>\n );\n}\n\n// Left sidebar (dark): Blocks / Settings / Layers. All managers are always\n// mounted (appendTo targets for GrapesJS) — visibility toggled via `hidden`.\nfunction LeftSidebar({\n view,\n onViewChange,\n markLayersSource,\n selectedImageSrc,\n onChooseImage,\n}: {\n view: SidebarView;\n onViewChange: (v: SidebarView) => void;\n markLayersSource: () => void;\n /** `src` of the selected image, or null when no image is selected. */\n selectedImageSrc: string | null;\n onChooseImage: () => void;\n}) {\n const editor = useEditorMaybe();\n const labels = useLabels();\n const [layersExpanded, setLayersExpanded] = useState(false);\n const [blockSearch, setBlockSearch] = useState(\"\");\n\n const changeView = (v: SidebarView) => {\n if (v === \"settings\" && editor && !editor.getSelected()) {\n const wrapper = editor.getWrapper();\n const body = wrapper?.find(\"mj-body\")[0] ?? wrapper;\n if (body) editor.select(body);\n }\n onViewChange(v);\n };\n\n const toggleLayers = (expand: boolean) => {\n setLayersExpanded(expand);\n editor?.getWrapper()?.onAll((c) => c.set(\"open\", expand));\n };\n\n // Filter the GrapesJS-rendered blocks (DOM lives outside React) by label;\n // hide category groups that end up empty.\n const filterBlocks = (q: string) => {\n setBlockSearch(q);\n const root = document.getElementById(\"gjs-blocks\");\n if (!root) return;\n const term = q.trim().toLowerCase();\n root.querySelectorAll<HTMLElement>(\".gjs-block\").forEach((b) => {\n const label = (b.querySelector(\".gjs-block-label\")?.textContent ?? b.textContent ?? \"\")\n .toLowerCase()\n .trim();\n b.style.display = !term || label.includes(term) ? \"\" : \"none\";\n });\n root.querySelectorAll<HTMLElement>(\".gjs-block-category\").forEach((cat) => {\n const anyVisible = [...cat.querySelectorAll<HTMLElement>(\".gjs-block\")].some(\n (b) => b.style.display !== \"none\",\n );\n cat.style.display = anyVisible ? \"\" : \"none\";\n });\n };\n\n return (\n <div className=\"editor-dark flex w-64 shrink-0 flex-col border-r border-panel-border bg-panel text-panel-fg\">\n <div className=\"flex border-b border-panel-border\">\n {(\n [\n [\"blocks\", \"Blocks\"],\n [\"settings\", \"Settings\"],\n [\"layers\", \"Layers\"],\n ] as const\n ).map(([value, label]) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => changeView(value)}\n className={cn(\n \"relative flex-1 px-3 py-2.5 text-sm transition-colors\",\n \"after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:transition-colors\",\n view === value\n ? \"font-medium text-panel-fg after:bg-brand\"\n : \"text-panel-muted-fg after:bg-transparent hover:text-panel-fg\",\n )}\n >\n {label}\n </button>\n ))}\n </div>\n\n {/* Blocks: search + categorized block palette */}\n <div className={cn(\"flex min-h-0 flex-1 flex-col\", view !== \"blocks\" && \"hidden\")}>\n <div className=\"relative border-b border-panel-border p-2\">\n <Search className=\"pointer-events-none absolute top-1/2 left-4 size-3.5 -translate-y-1/2 text-panel-muted-fg\" />\n <Input\n value={blockSearch}\n onChange={(e) => filterBlocks(e.target.value)}\n placeholder={labels.searchBlocks}\n className=\"h-8 border-panel-border bg-panel-elevated pr-10 pl-7 text-sm text-panel-fg placeholder:text-panel-muted-fg\"\n />\n <kbd className=\"pointer-events-none absolute top-1/2 right-4 -translate-y-1/2 rounded border border-panel-border bg-panel px-1 py-0.5 font-mono text-[10px] text-panel-muted-fg\">\n ⌘K\n </kbd>\n </div>\n <div id=\"gjs-blocks\" className=\"min-h-0 flex-1 overflow-y-auto\" />\n </div>\n\n {/* Settings: Attributes + Style (collapsible, stacked) */}\n <div className={cn(\"min-h-0 flex-1 overflow-y-auto\", view !== \"settings\" && \"hidden\")}>\n {selectedImageSrc !== null && (\n <div className=\"border-b border-panel-border p-3\">\n <button\n type=\"button\"\n onClick={onChooseImage}\n title={labels.chooseImage}\n className=\"group relative block w-full overflow-hidden rounded-lg border border-panel-border bg-panel-elevated\"\n >\n {selectedImageSrc ? (\n <img\n src={selectedImageSrc}\n alt=\"\"\n className=\"h-32 w-full bg-black/25 object-contain\"\n />\n ) : (\n <span className=\"flex h-32 items-center justify-center text-panel-muted-fg\">\n <ImageIcon className=\"size-8\" />\n </span>\n )}\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/50 text-sm font-medium text-white opacity-0 transition group-hover:opacity-100\">\n {labels.chooseImage}\n </span>\n </button>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"mt-2 w-full bg-brand text-brand-fg hover:bg-brand/90\"\n onClick={onChooseImage}\n >\n <ImageUp />\n {labels.chooseImage}\n </Button>\n </div>\n )}\n <CollapseSection title={labels.attributes}>\n <div id=\"gjs-traits\" />\n </CollapseSection>\n <CollapseSection title={labels.style}>\n <div id=\"gjs-styles\" />\n </CollapseSection>\n </div>\n\n {/* Layers — with an expand/collapse-all switch. A pointer-down here marks\n the next selection as coming from the tree (→ do NOT open Settings). */}\n <div\n className={cn(\"flex min-h-0 flex-1 flex-col\", view !== \"layers\" && \"hidden\")}\n onMouseDownCapture={markLayersSource}\n >\n <label className=\"flex items-center justify-between gap-2 border-b border-panel-border px-3 py-2 text-xs text-panel-muted-fg\">\n <span>{labels.expandAll}</span>\n <Switch\n checked={layersExpanded}\n onCheckedChange={toggleLayers}\n className=\"data-[state=checked]:bg-brand data-[state=unchecked]:bg-panel-border\"\n />\n </label>\n <div id=\"gjs-layers\" className=\"min-h-0 flex-1 overflow-y-auto\" />\n </div>\n </div>\n );\n}\n\n// Breadcrumb under the canvas: the selected component's ancestry (clickable).\nfunction Breadcrumb({\n crumbs,\n onSelect,\n}: {\n crumbs: Component[];\n onSelect: (c: Component) => void;\n}) {\n const labels = useLabels();\n return (\n <div className=\"flex h-9 shrink-0 items-center gap-0.5 border-t border-border bg-surface px-3 text-xs text-muted-foreground\">\n {crumbs.length === 0 ? (\n <span className=\"text-muted-foreground/60\">{labels.selectAnElement}</span>\n ) : (\n crumbs.map((c, i) => (\n <span key={i} className=\"flex items-center gap-0.5\">\n {i > 0 && <ChevronRight className=\"size-3 opacity-40\" />}\n <button\n type=\"button\"\n onClick={() => onSelect(c)}\n className={cn(\n \"rounded px-1.5 py-0.5 hover:bg-muted hover:text-foreground\",\n i === crumbs.length - 1 && \"font-medium text-brand\",\n )}\n >\n {crumbName(c)}\n </button>\n </span>\n ))\n )}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * How the editor reaches its data.\n *\n * In the spike every component imported `@/lib/documents`, which imported a Supabase\n * client — so adopting the editor meant adopting Supabase, and the components could not\n * be tested without a database. They now read whatever the host provides through this\n * context, and the host is free to back it with Supabase, plain Postgres, an HTTP API or\n * an in-memory object.\n *\n * The shapes are the same `DocumentStore` and `CommentStore` the agent uses\n * (`@mjml-agent-editor/core`), so one adapter serves both sides.\n */\n\nimport type { CommentStore, DocumentStore } from \"@mjml-agent-editor/core\";\nimport { createContext, useContext, useMemo, useState, type ReactNode } from \"react\";\n\nimport { mergeLabels, type EditorLabels } from \"./labels.js\";\n\n/** An image the host has stored on the user's behalf. */\nexport interface ImageAsset {\n readonly id: string;\n readonly url: string;\n readonly name?: string;\n}\n\n/**\n * The user's own images: uploaded once, then picked from a gallery in any email.\n *\n * Without one the picker accepts only URLs. GrapesJS would otherwise inline a dropped file\n * as base64: most mail clients block `data:` images, and every agent turn pays for the bytes.\n */\nexport interface ImageLibrary {\n /** Newest first. `quota` is shown in the picker when given. */\n list(): Promise<{\n readonly images: readonly ImageAsset[];\n readonly quota?: { readonly used: number; readonly limit: number };\n }>;\n upload(file: File): Promise<ImageAsset>;\n /**\n * What `upload` takes, as an `<input accept>` value (\"image/png,image/jpeg\"). The picker\n * refuses anything else before sending it. Defaults to any image.\n */\n readonly accept?: string;\n /**\n * Omit to make the gallery read-only — for a host that caps uploads and cannot reclaim\n * the storage, deleting would only be a way round the cap.\n */\n remove?(id: string): Promise<void>;\n}\n\nexport interface EditorStores {\n readonly documents: DocumentStore;\n readonly comments: CommentStore;\n readonly images?: ImageLibrary;\n}\n\nconst StoreContext = createContext<EditorStores | null>(null);\nconst LabelContext = createContext<EditorLabels | null>(null);\ntype AgentTurn = {\n /** Set by the chat panel, from its pre-turn save to the turn's end. */\n running: boolean;\n setRunning: (running: boolean) => void;\n /**\n * Set by the canvas, from a turn's start until the document it left is loaded. The chat\n * waits for it before the next turn, so a turn never starts on a stale canvas.\n */\n locked: boolean;\n setLocked: (locked: boolean) => void;\n};\n\nconst TurnContext = createContext<AgentTurn>({\n running: false,\n setRunning: () => {},\n locked: false,\n setLocked: () => {},\n});\n\nexport function EditorStoreProvider({\n stores,\n labels,\n children,\n}: {\n stores: EditorStores;\n /**\n * Overrides for the copy the editor renders. Omitted, everything is English.\n *\n * It rides on this provider rather than a second one because the host already has to\n * wrap the editor in exactly one place, and a component that needs a store almost always\n * needs a word too.\n */\n labels?: Partial<EditorLabels>;\n children: ReactNode;\n}) {\n const merged = useMemo(() => mergeLabels(labels), [labels]);\n const [running, setRunning] = useState(false);\n const [locked, setLocked] = useState(false);\n const turn = useMemo(() => ({ running, setRunning, locked, setLocked }), [running, locked]);\n return (\n <StoreContext.Provider value={stores}>\n <LabelContext.Provider value={merged}>\n <TurnContext.Provider value={turn}>{children}</TurnContext.Provider>\n </LabelContext.Provider>\n </StoreContext.Provider>\n );\n}\n\n/**\n * The agent turn, shared by the chat and the canvas. The canvas is read only meanwhile: the\n * agent and the visitor would otherwise both write the document.\n */\nexport function useAgentTurn(): AgentTurn {\n return useContext(TurnContext);\n}\n\n/**\n * Falls back to the defaults instead of throwing, unlike the stores.\n *\n * A missing store means the editor cannot work at all and should say so loudly; missing copy\n * means English, which is a perfectly good outcome and keeps the components renderable in\n * isolation.\n */\nexport function useLabels(): EditorLabels {\n return useContext(LabelContext) ?? mergeLabels();\n}\n\nfunction useStores(): EditorStores {\n const stores = useContext(StoreContext);\n if (!stores) {\n throw new Error(\n \"The editor needs an <EditorStoreProvider>. Wrap the editor and pass a DocumentStore \" +\n \"and a CommentStore — see packages/editor/README.md.\",\n );\n }\n return stores;\n}\n\nexport function useDocumentStore(): DocumentStore {\n return useStores().documents;\n}\n\nexport function useCommentStore(): CommentStore {\n return useStores().comments;\n}\n\nexport function useImageLibrary(): ImageLibrary | undefined {\n return useStores().images;\n}\n","/**\n * Every word the editor puts on screen.\n *\n * It was all inline English, which quietly made the package unusable to anyone building in\n * another language: adopting it meant forking it to translate a button. The defaults below\n * keep existing hosts working unchanged, and a host overrides only the keys it cares about.\n *\n * Flat and shallow-merged on purpose. A nested dictionary reads better and merges worse —\n * overriding one key inside a group would silently drop its siblings. `toolLabels` is the\n * single nested map, and it is merged key by key for exactly that reason.\n *\n * Values that interpolate are functions rather than templates with placeholders, so the\n * translator controls word order instead of the format string.\n */\n\nexport interface EditorLabels {\n // Header\n readonly appName: string;\n readonly emailNameAria: string;\n readonly renameFailed: string;\n readonly saving: string;\n readonly saved: string;\n readonly saveFailed: string;\n readonly undo: string;\n readonly redo: string;\n readonly contentWidth: string;\n readonly preview: string;\n readonly closePreview: string;\n readonly previewFrameTitle: string;\n readonly sourceCode: string;\n readonly exportHtml: string;\n readonly exportMjml: string;\n /** `kind` is already upper-cased (\"MJML\", \"HTML\"). */\n readonly copied: (kind: string) => string;\n\n // Canvas\n readonly searchBlocks: string;\n readonly expandAll: string;\n readonly selectAnElement: string;\n readonly attributes: string;\n readonly style: string;\n readonly rteFont: string;\n readonly rteSize: string;\n readonly rteTextColor: string;\n readonly rteBackgroundColor: string;\n readonly documentLoadFailed: string;\n readonly documentSaveFailed: string;\n readonly retry: string;\n\n // Image picker\n readonly chooseImage: string;\n readonly imagePickerTitle: string;\n readonly imagePickerDrop: string;\n readonly imagePickerBrowse: string;\n readonly imagePickerUrlPlaceholder: string;\n readonly imagePickerUseUrl: string;\n readonly imagePickerGallery: string;\n readonly imagePickerEmpty: string;\n readonly imagePickerQuota: (used: number, limit: number) => string;\n readonly imagePickerQuotaFull: string;\n readonly imagePickerDelete: string;\n readonly imagePickerConfirmDelete: string;\n readonly imagePickerLoadFailed: string;\n readonly imageUploadFailed: string;\n readonly imageDeleteFailed: string;\n readonly imagePickerUnsupported: (fileName: string) => string;\n readonly imageFieldRemove: string;\n\n // Color picker\n readonly colorPickerOpen: string;\n readonly colorPickerEyedropper: string;\n readonly colorPickerDocument: string;\n readonly colorPickerPresets: string;\n readonly colorPickerTransparent: string;\n\n // Comments\n readonly commentOnSection: string;\n readonly closeComments: string;\n readonly noCommentsYet: string;\n readonly resolveComment: string;\n readonly addComment: string;\n readonly addCommentPlaceholder: string;\n\n // Chat\n readonly chatEmptyTitle: string;\n readonly chatEmptyDescription: string;\n readonly agentWorking: string;\n readonly reasoning: string;\n readonly completedSteps: (count: number, failed: number) => string;\n readonly applyComments: string;\n readonly chatPlaceholder: string;\n readonly stop: string;\n readonly send: string;\n readonly chatError: string;\n /** Keyed by tool name; merged key by key rather than replaced wholesale. */\n readonly toolLabels: Readonly<Record<string, string>>;\n}\n\nexport const DEFAULT_LABELS: EditorLabels = {\n appName: \"MJML Editor\",\n emailNameAria: \"Email name\",\n renameFailed: \"Failed to rename.\",\n saving: \"Saving…\",\n saved: \"Saved\",\n saveFailed: \"Save failed\",\n undo: \"Undo\",\n redo: \"Redo\",\n contentWidth: \"Content width\",\n preview: \"Preview\",\n closePreview: \"Close preview\",\n previewFrameTitle: \"Email preview\",\n sourceCode: \"Source code\",\n exportHtml: \"Export HTML\",\n exportMjml: \"Export MJML\",\n copied: (kind) => `${kind} copied.`,\n\n searchBlocks: \"Search blocks…\",\n expandAll: \"Expand all\",\n selectAnElement: \"Select an element…\",\n attributes: \"Attributes\",\n style: \"Style\",\n rteFont: \"Font\",\n rteSize: \"Size\",\n rteTextColor: \"Text color\",\n rteBackgroundColor: \"Background color\",\n documentLoadFailed: \"Failed to load the document.\",\n documentSaveFailed: \"Failed to save changes.\",\n retry: \"Retry\",\n\n chooseImage: \"Choose image\",\n imagePickerTitle: \"Choose an image\",\n imagePickerDrop: \"Drag images here, or\",\n imagePickerBrowse: \"Browse files\",\n imagePickerUrlPlaceholder: \"…or paste an image URL\",\n imagePickerUseUrl: \"Use URL\",\n imagePickerGallery: \"Your images\",\n imagePickerEmpty: \"No images yet — the ones you upload will be kept here.\",\n imagePickerQuota: (used, limit) => `${used} of ${limit} uploads used`,\n imagePickerQuotaFull: \"Upload limit reached — pick an image below or paste a URL.\",\n imagePickerDelete: \"Delete image\",\n imagePickerConfirmDelete: \"Click again to delete\",\n imagePickerLoadFailed: \"Failed to load your images.\",\n imageUploadFailed: \"Failed to upload the image.\",\n imageDeleteFailed: \"Failed to delete the image.\",\n imagePickerUnsupported: (fileName) => `${fileName} is not a supported image type.`,\n imageFieldRemove: \"Remove image\",\n\n colorPickerOpen: \"Pick a color\",\n colorPickerEyedropper: \"Pick from the screen\",\n colorPickerDocument: \"In this email\",\n colorPickerPresets: \"Presets\",\n colorPickerTransparent: \"Transparent\",\n\n commentOnSection: \"Comment on the whole section\",\n closeComments: \"Close\",\n noCommentsYet: \"No comments yet — add the first one.\",\n resolveComment: \"Resolve\",\n addComment: \"Add\",\n addCommentPlaceholder: \"Add a comment… (⌘/Ctrl+Enter to send)\",\n\n chatEmptyTitle: \"Start a conversation with the agent\",\n chatEmptyDescription:\n \"Describe the email (goal, tone, content) and paste your data — the agent will design the sections and images.\",\n agentWorking: \"Agent is working…\",\n reasoning: \"Reasoning\",\n completedSteps: (count, failed) =>\n failed > 0 ? `Completed steps (${count}, ${failed} failed)` : `Completed steps (${count})`,\n applyComments: \"Apply changes from comments\",\n chatPlaceholder: \"Describe what you want to change… (Enter = send, Shift+Enter = new line)\",\n stop: \"Stop\",\n send: \"Send\",\n chatError: \"Agent chat error.\",\n\n toolLabels: {\n get_document: \"Reading document\",\n get_section: \"Reading section\",\n set_document: \"Saving whole email\",\n set_section: \"Replacing section\",\n insert_section: \"Adding section\",\n remove_section: \"Removing section\",\n generate_image: \"Generating image\",\n list_open_comments: \"Reading comments\",\n resolve_comment: \"Resolving comment\",\n },\n};\n\n/**\n * Overrides on top of the defaults. `toolLabels` merges rather than replaces, so a host\n * renaming one step does not lose the other eight.\n */\nexport function mergeLabels(overrides?: Partial<EditorLabels>): EditorLabels {\n if (!overrides) return DEFAULT_LABELS;\n return {\n ...DEFAULT_LABELS,\n ...overrides,\n toolLabels: { ...DEFAULT_LABELS.toolLabels, ...overrides.toolLabels },\n };\n}\n","export {\n EditorStoreProvider,\n useCommentStore,\n useDocumentStore,\n useImageLibrary,\n useLabels,\n type EditorStores,\n type ImageAsset,\n type ImageLibrary,\n} from \"./stores.js\";\n\nexport { DEFAULT_LABELS, mergeLabels, type EditorLabels } from \"./labels.js\";\n\n/**\n * Re-exported from `core`, where it moved so a server could import it. Kept here because\n * removing an export is a breaking change for no gain — but a route handler should reach\n * for `@mjml-agent-editor/core`, since everything in this entry point is client-only.\n */\nexport { STARTER_MJML } from \"@mjml-agent-editor/core\";\n\nexport { default as EditorHeader } from \"./components/editor/EditorHeader.js\";\nexport { default as ChatPanel } from \"./components/chat/ChatPanel.js\";\nexport { default as CanvasComments } from \"./components/comments/CanvasComments.js\";\nexport { Markdown } from \"./components/chat/Markdown.js\";\n\n// Types only — erased at build time, so this does not pull the canvas in.\nexport type { EditorApi, EditorState, SaveStatus } from \"./components/editor/EmailEditor.js\";\n\nexport type {\n CommentStore,\n CommentTarget,\n DocumentPatch,\n DocumentStore,\n EmailDocument,\n SectionComment,\n} from \"@mjml-agent-editor/core\";\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\n outline:\n \"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\n ghost:\n \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: \"h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n icon: \"size-8\",\n \"icon-xs\":\n \"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\":\n \"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg\",\n \"icon-lg\": \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Input };\n","import { cn } from \"../../lib/utils\";\nimport { Loader2Icon } from \"lucide-react\";\n\nfunction Spinner({ className, ...props }: React.ComponentProps<\"svg\">) {\n return (\n <Loader2Icon\n data-slot=\"spinner\"\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(\"size-4 animate-spin\", className)}\n {...props}\n />\n );\n}\n\nexport { Spinner };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\n}\n\nfunction PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n}\n\nfunction PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden\",\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n );\n}\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"./button\";\nimport { XIcon } from \"lucide-react\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close data-slot=\"dialog-close\" asChild>\n <Button variant=\"ghost\" className=\"absolute top-2 right-2\" size=\"icon-sm\">\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </Button>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div data-slot=\"dialog-header\" className={cn(\"flex flex-col gap-2\", className)} {...props} />\n );\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean;\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"font-heading text-base leading-none font-medium\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\n \"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","/**\n * The host's own sentence when a save or a turn was refused, or null for anything else.\n *\n * A turn refusal is a `{\"error\": \"...\"}` body (the agent contract); a store refusal is the\n * host's exception with a 4xx `status`. A non-4xx status rules the message out first, so a\n * 500 whose body happens to be `{\"error\": ...}` is never shown verbatim.\n */\nexport function refusal(error: unknown): string | null {\n if (!(error instanceof Error)) return null;\n\n const status = (error as { status?: unknown }).status;\n // null: no status at all, which is every turn rejection.\n const deliberate = typeof status === \"number\" ? status >= 400 && status < 500 : null;\n if (deliberate === false) return null;\n\n if (error.message.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(error.message) as { error?: unknown };\n return typeof parsed.error === \"string\" && parsed.error.trim() !== \"\" ? parsed.error : null;\n } catch {\n return null;\n }\n }\n\n // A proxy answering for the host sends an HTML page, not a sentence.\n const message = error.message.trim();\n return deliberate && message !== \"\" && !message.startsWith(\"<\") ? error.message : null;\n}\n\n/** Shared by autosave and the pre-turn flush, so one failing save shows one toast. */\nexport function saveToastId(docId: string): string {\n return `document-save-${docId}`;\n}\n","import * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n","\"use client\";\n\nimport { useEditorMaybe } from \"@grapesjs/react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Check, MessageSquarePlus, X } from \"lucide-react\";\n\nimport {\n useLabels,\n useCommentStore,\n type CommentTarget,\n type SectionComment,\n} from \"../../index.js\";\nimport { Popover, PopoverAnchor, PopoverContent } from \"../ui/popover\";\nimport { Button } from \"../ui/button\";\nimport { Textarea } from \"../ui/textarea\";\n\ntype Props = {\n docId: string;\n /** Sparkles-button target → opens the compose popover for that element. */\n composeTarget: CommentTarget | null;\n onComposeConsumed: () => void;\n /** Bump to refetch comments (after agent turns). */\n refreshSignal: number;\n onOpenCountChange: (n: number) => void;\n};\n\ntype Pos = { top: number; left: number };\n\n// Anchor key = the stable css-class the comment is attached to.\nconst keyOf = (c: Pick<SectionComment, \"objectId\" | \"sectionId\">) =>\n c.objectId ? `obj-${c.objectId}` : `sec-${c.sectionId}`;\nconst keyOfTarget = (t: CommentTarget) => (t.objectId ? `obj-${t.objectId}` : `sec-${t.sectionId}`);\n\nexport default function CanvasComments({\n docId,\n composeTarget,\n onComposeConsumed,\n refreshSignal,\n onOpenCountChange,\n}: Props) {\n // Injected by the host rather than imported, so comments carry no database.\n const commentStore = useCommentStore();\n const labels = useLabels();\n const editor = useEditorMaybe();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [comments, setComments] = useState<SectionComment[]>([]);\n const [positions, setPositions] = useState<Record<string, Pos>>({});\n const [activeKey, setActiveKey] = useState<string | null>(null);\n const [composeInfo, setComposeInfo] = useState<CommentTarget | null>(null);\n const [body, setBody] = useState(\"\");\n\n const reload = useCallback(() => {\n commentStore.list(docId).then(setComments).catch(console.error);\n }, [docId]);\n\n useEffect(() => {\n reload();\n }, [reload, refreshSignal]);\n\n const open = useMemo(() => comments.filter((c) => c.status === \"open\"), [comments]);\n\n useEffect(() => {\n onOpenCountChange(open.length);\n }, [open.length, onOpenCountChange]);\n\n // One pin per commented anchor (element or section), with its open comments.\n const pins = useMemo(() => {\n const map = new Map<string, SectionComment[]>();\n for (const c of open) {\n const k = keyOf(c);\n const arr = map.get(k) ?? [];\n arr.push(c);\n map.set(k, arr);\n }\n return [...map.entries()].map(([key, list]) => ({ key, list }));\n }, [open]);\n\n // Refs so the event-driven recompute reads fresh values without re-subscribing.\n const openRef = useRef(open);\n openRef.current = open;\n const activeRef = useRef(activeKey);\n activeRef.current = activeKey;\n const positionsRef = useRef<Record<string, Pos>>({});\n\n // Positions of pins/popover, computed from the elements' rects in the canvas iframe.\n const recompute = useCallback(() => {\n const ed = editor;\n const container = containerRef.current;\n if (!ed || !container) return;\n const frame = ed.Canvas.getFrameEl?.();\n const cdoc = ed.Canvas.getDocument?.();\n if (!frame || !cdoc) return;\n const cRect = container.getBoundingClientRect();\n const fRect = frame.getBoundingClientRect();\n // The iframe is scaled by GrapesJS zoom (transform), so its on-screen rect\n // (fRect) is scaled while elements INSIDE it report unscaled coordinates.\n // Map an inner coordinate to screen by this zoom factor.\n const zoom = frame.offsetWidth ? fRect.width / frame.offsetWidth : 1;\n const keys = new Set<string>(openRef.current.map(keyOf));\n if (activeRef.current) keys.add(activeRef.current);\n const next: Record<string, Pos> = {};\n keys.forEach((k) => {\n const el = cdoc.querySelector(`.${k}`) as HTMLElement | null;\n if (!el) return;\n const r = el.getBoundingClientRect();\n // Element's top-right corner mapped from iframe coords to screen (× zoom),\n // then a small constant offset so the pin sits just past the right edge.\n next[k] = {\n top: fRect.top - cRect.top + r.top * zoom + 4,\n left: fRect.left - cRect.left + (r.left + r.width) * zoom + 8,\n };\n });\n if (JSON.stringify(positionsRef.current) !== JSON.stringify(next)) {\n positionsRef.current = next;\n setPositions(next);\n }\n }, [editor]);\n\n // Recompute on canvas activity (edits, scroll, device change, resize) + a\n // low-frequency safety net for layout shifts the events miss.\n useEffect(() => {\n const ed = editor;\n if (!ed) return;\n const evts = [\n \"canvas:update\",\n \"update\",\n \"change:device\",\n \"load\",\n \"component:update\",\n \"component:mount\",\n ];\n evts.forEach((e) => ed.on(e, recompute));\n const win = ed.Canvas.getWindow?.();\n win?.addEventListener(\"scroll\", recompute, true);\n window.addEventListener(\"resize\", recompute);\n const id = window.setInterval(recompute, 500);\n return () => {\n evts.forEach((e) => ed.off(e, recompute));\n win?.removeEventListener(\"scroll\", recompute, true);\n window.removeEventListener(\"resize\", recompute);\n window.clearInterval(id);\n };\n }, [editor, recompute]);\n\n useEffect(() => {\n recompute();\n }, [comments, activeKey, recompute]);\n\n // Sparkles button → open the compose popover anchored to that element.\n useEffect(() => {\n if (!composeTarget) return;\n setComposeInfo(composeTarget);\n setActiveKey(keyOfTarget(composeTarget));\n setBody(\"\");\n requestAnimationFrame(recompute);\n }, [composeTarget, recompute]);\n\n const activeComments = activeKey ? open.filter((c) => keyOf(c) === activeKey) : [];\n const activeTarget: CommentTarget | null = (() => {\n if (!activeKey) return null;\n const c = activeComments[0];\n if (c) return { sectionId: c.sectionId, objectId: c.objectId, objectLabel: c.objectLabel };\n if (composeInfo && keyOfTarget(composeInfo) === activeKey) return composeInfo;\n return null;\n })();\n const activePos = activeKey ? positions[activeKey] : undefined;\n // Keep the last real anchor position through the popover's close animation.\n // On close activeKey→null makes activePos undefined; without this the anchor\n // would snap to (0,0) and the still-animating popover would flash there.\n const lastAnchorPos = useRef<Pos>({ top: 0, left: 0 });\n if (activePos) lastAnchorPos.current = activePos;\n const anchorPos = activePos ?? lastAnchorPos.current;\n\n const closePopover = useCallback(() => {\n setActiveKey(null);\n setBody(\"\");\n if (composeInfo) {\n setComposeInfo(null);\n onComposeConsumed();\n }\n }, [composeInfo, onComposeConsumed]);\n\n // Close the popover on a click inside the canvas iframe (Radix's outside-click\n // detection lives in the top document and misses iframe clicks).\n useEffect(() => {\n if (!activeKey || !editor) return;\n const doc = editor.Canvas.getDocument?.();\n if (!doc) return;\n const onDown = () => closePopover();\n doc.addEventListener(\"mousedown\", onDown, true);\n return () => doc.removeEventListener(\"mousedown\", onDown, true);\n }, [activeKey, editor, closePopover]);\n\n const submit = async () => {\n if (!activeTarget || !body.trim()) return;\n try {\n await commentStore.add(docId, activeTarget, body.trim());\n setBody(\"\");\n if (composeInfo) {\n setComposeInfo(null);\n onComposeConsumed();\n }\n reload();\n } catch (e) {\n console.error(e);\n }\n };\n\n const resolve = async (id: string) => {\n try {\n await commentStore.resolve(id);\n reload();\n } catch (e) {\n console.error(e);\n }\n };\n\n return (\n <div ref={containerRef} className=\"pointer-events-none absolute inset-0 z-20 overflow-hidden\">\n {pins.map(({ key, list }) => {\n const pos = positions[key];\n if (!pos) return null;\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => {\n setActiveKey((k) => (k === key ? null : key));\n setBody(\"\");\n }}\n style={{ top: pos.top, left: pos.left }}\n className=\"pointer-events-auto absolute flex h-6 min-w-6 items-center justify-center rounded-full rounded-bl-none bg-brand px-1.5 text-xs font-semibold text-brand-fg shadow-md ring-2 ring-white transition-transform hover:scale-110\"\n title={`${list.length} comment(s)`}\n >\n {list.length}\n </button>\n );\n })}\n\n <Popover open={!!activeKey && !!activePos} onOpenChange={(o) => !o && closePopover()}>\n <PopoverAnchor asChild>\n <div\n style={{\n position: \"absolute\",\n top: anchorPos.top,\n left: anchorPos.left,\n width: 1,\n height: 1,\n }}\n />\n </PopoverAnchor>\n <PopoverContent\n side=\"right\"\n align=\"start\"\n // Disable the close animation: while it plays, Radix keeps the content\n // mounted but floating-ui drops its positioning for a frame, flashing\n // the popover at (0,0). No exit animation → instant unmount, no flash.\n className=\"pointer-events-auto w-80 p-0 data-[state=closed]:!animate-none data-[state=closed]:!duration-0\"\n >\n <div className=\"flex items-start justify-between gap-2 border-b px-3 py-2 text-xs text-muted-foreground\">\n <span className=\"min-w-0\">\n {activeTarget?.objectId ? (\n <>\n Comment on{\" \"}\n <span className=\"font-medium text-foreground\">{activeTarget.objectLabel}</span>\n </>\n ) : (\n <>\n {labels.commentOnSection}{\" \"}\n <code className=\"font-mono\">{activeTarget?.sectionId}</code>\n </>\n )}\n </span>\n <button\n type=\"button\"\n onClick={closePopover}\n aria-label={labels.closeComments}\n className=\"-mr-1 -mt-0.5 shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n <div className=\"max-h-64 space-y-2 overflow-y-auto p-3\">\n {activeComments.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">{labels.noCommentsYet}</p>\n )}\n {activeComments.map((c) => (\n <div key={c.id} className=\"rounded-md border bg-muted/40 p-2 text-sm\">\n <p className=\"whitespace-pre-wrap\">{c.body}</p>\n <button\n onClick={() => void resolve(c.id)}\n className=\"mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <Check className=\"size-3\" /> {labels.resolveComment}\n </button>\n </div>\n ))}\n </div>\n <div className=\"border-t p-3\">\n <Textarea\n autoFocus\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder={labels.addCommentPlaceholder}\n className=\"min-h-14 resize-none text-sm\"\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n void submit();\n }\n }}\n />\n <div className=\"mt-2 flex justify-end\">\n <Button size=\"sm\" disabled={!body.trim()} onClick={() => void submit()}>\n <MessageSquarePlus /> {labels.addComment}\n </Button>\n </div>\n </div>\n </PopoverContent>\n </Popover>\n </div>\n );\n}\n","/** Debounced autosave, and the flush a turn waits on. Whether to write is `save`'s call. */\nexport interface SaveQueue {\n /** Restarts the debounce. */\n schedule(): void;\n /** Drops a scheduled autosave. */\n cancel(): void;\n /** Runs `save` after any work under way; rejects if it fails. */\n flush(): Promise<void>;\n /** Runs `task` after any work under way, such as a reload that must not race a save. */\n run<T>(task: () => Promise<T>): Promise<T>;\n}\n\nexport function createSaveQueue(save: () => Promise<void>, delayMs: number): SaveQueue {\n let timer: ReturnType<typeof setTimeout> | null = null;\n // Work runs one after another, so an older write or read can never land after a newer one.\n let tail: Promise<unknown> = Promise.resolve();\n\n const cancel = () => {\n if (timer) clearTimeout(timer);\n timer = null;\n };\n\n const run = <T>(task: () => Promise<T>): Promise<T> => {\n const next = tail.then(task);\n tail = next.catch(() => {});\n return next;\n };\n\n return {\n schedule() {\n cancel();\n timer = setTimeout(() => void run(save).catch(() => {}), delayMs);\n },\n cancel,\n flush() {\n cancel();\n return run(save);\n },\n run,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Switch as SwitchPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Switch({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof SwitchPrimitive.Root> & {\n size?: \"sm\" | \"default\";\n}) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n data-size={size}\n className={cn(\n \"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className=\"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground\"\n />\n </SwitchPrimitive.Root>\n );\n}\n\nexport { Switch };\n","\"use client\";\n\n/**\n * The image picker, replacing GrapesJS's asset modal.\n *\n * GrapesJS's own modal could not be styled into the editor, and its \"click to upload\" area\n * was a transparent file input that did not open the file browser at all. This one is\n * opened by GrapesJS (`assetManager.custom`), so the canvas double-click, the toolbar button\n * and the sidebar all land here, and selecting goes back through GrapesJS to the image.\n */\n\nimport { useCallback, useEffect, useRef, useState, type DragEvent } from \"react\";\nimport { Check, ImageUp, Link2, Trash2 } from \"lucide-react\";\nimport { toast } from \"sonner\";\n\nimport { useImageLibrary, useLabels, type ImageAsset } from \"../../index.js\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"../ui/dialog\";\nimport { Input } from \"../ui/input\";\nimport { Spinner } from \"../ui/spinner\";\n\ntype Quota = { readonly used: number; readonly limit: number };\n\nexport default function ImagePicker({\n open,\n currentUrl,\n onSelect,\n onClose,\n}: {\n open: boolean;\n /** The image being replaced, marked in the gallery. */\n currentUrl?: string;\n onSelect: (url: string) => void;\n onClose: () => void;\n}) {\n const library = useImageLibrary();\n const labels = useLabels();\n const [images, setImages] = useState<readonly ImageAsset[]>([]);\n const [quota, setQuota] = useState<Quota>();\n const [loading, setLoading] = useState(false);\n const [pending, setPending] = useState(0);\n const [dragging, setDragging] = useState(false);\n const [url, setUrl] = useState(\"\");\n const [confirmId, setConfirmId] = useState<string | null>(null);\n const fileInput = useRef<HTMLInputElement>(null);\n // Bumped on every open and close. An upload still running when the picker closes, or\n // reopens for another image, must not apply its result to whatever is open now.\n const session = useRef(0);\n // Read through refs so neither a new `labels` object nor a new `onSelect` from a parent\n // render re-runs the open effect — that would wipe a half-typed URL.\n const latest = useRef({ labels, onSelect });\n latest.current = { labels, onSelect };\n // Opening, uploading and deleting each list the gallery; only the newest answer is shown.\n const lists = useRef(0);\n\n const refresh = useCallback(async () => {\n if (!library) return;\n const mine = ++lists.current;\n setLoading(true);\n try {\n const result = await library.list();\n if (mine !== lists.current) return;\n setImages(result.images);\n setQuota(result.quota);\n } catch (err) {\n console.error(err);\n if (mine === lists.current) toast.error(latest.current.labels.imagePickerLoadFailed);\n } finally {\n if (mine === lists.current) setLoading(false);\n }\n }, [library]);\n\n useEffect(() => {\n session.current += 1;\n if (!open) return;\n setUrl(\"\");\n setConfirmId(null);\n void refresh();\n // Once per opening; `refresh` reads the latest props through `latest`.\n }, [open]);\n // Nor after unmount, when the editor it would select into is gone.\n useEffect(() => () => void (session.current += 1), []);\n\n const accepts = (file: File) => matchesAccept(file, library?.accept);\n\n const full = quota !== undefined && quota.used >= quota.limit;\n\n const upload = async (files: FileList | null) => {\n if (!library || !files?.length) return;\n const mine = session.current;\n // The input's `accept` covers browsing; a drop bypasses it, so check here too rather\n // than send a whole file only to have the host refuse it.\n const list = Array.from(files).filter((f) => {\n if (accepts(f)) return true;\n toast.error(labels.imagePickerUnsupported(f.name));\n return false;\n });\n if (!list.length) return;\n setPending((n) => n + list.length);\n // Settled rather than all: an upload that succeeded has already been stored and\n // counted by the host, so one failure must not hide it.\n // Async, so an adapter that throws rather than rejects still settles.\n const results = await Promise.allSettled(list.map(async (f) => library.upload(f)));\n setPending((n) => n - list.length);\n for (const r of results) {\n if (r.status === \"rejected\") {\n console.error(r.reason);\n // The host's message is usually the actionable part (\"limit reached\", \"too large\").\n toast.error(r.reason instanceof Error ? r.reason.message : labels.imageUploadFailed);\n }\n }\n if (session.current !== mine) return;\n await refresh();\n // One file dropped while editing an image is meant for that image.\n const [only] = results;\n if (results.length === 1 && only?.status === \"fulfilled\" && session.current === mine) {\n latest.current.onSelect(only.value.url);\n }\n };\n\n const remove = async (image: ImageAsset) => {\n if (!library?.remove) return;\n // Two clicks rather than a confirm(): an image in use breaks every email showing it.\n if (confirmId !== image.id) {\n setConfirmId(image.id);\n return;\n }\n setConfirmId(null);\n try {\n await library.remove(image.id);\n await refresh();\n } catch (err) {\n console.error(err);\n toast.error(err instanceof Error ? err.message : labels.imageDeleteFailed);\n }\n };\n\n const onDrop = (e: DragEvent) => {\n e.preventDefault();\n setDragging(false);\n if (!full) void upload(e.dataTransfer.files);\n };\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && onClose()}>\n <DialogContent className=\"editor-dark gap-0 overflow-hidden border border-panel-border bg-panel p-0 text-panel-fg ring-0 sm:max-w-3xl\">\n <DialogHeader className=\"border-b border-panel-border px-5 py-4\">\n <DialogTitle>{labels.imagePickerTitle}</DialogTitle>\n {quota && (\n <DialogDescription className=\"text-panel-muted-fg\">\n {labels.imagePickerQuota(quota.used, quota.limit)}\n </DialogDescription>\n )}\n </DialogHeader>\n\n <div className=\"flex max-h-[70vh] flex-col gap-4 overflow-y-auto p-5\">\n {library &&\n (full ? (\n <p className=\"rounded-lg border border-panel-border bg-panel-elevated px-4 py-3 text-sm text-panel-muted-fg\">\n {labels.imagePickerQuotaFull}\n </p>\n ) : (\n <div\n onDragOver={(e) => {\n e.preventDefault();\n setDragging(true);\n }}\n onDragLeave={() => setDragging(false)}\n onDrop={onDrop}\n className={cn(\n \"flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed px-6 py-8 text-center transition-colors\",\n dragging ? \"border-brand bg-brand-soft\" : \"border-panel-border bg-panel-elevated\",\n )}\n >\n <ImageUp className=\"size-8 text-panel-muted-fg\" />\n <div className=\"flex flex-wrap items-center justify-center gap-2 text-sm text-panel-muted-fg\">\n <span>{labels.imagePickerDrop}</span>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"bg-brand text-brand-fg hover:bg-brand/90\"\n onClick={() => fileInput.current?.click()}\n >\n {labels.imagePickerBrowse}\n </Button>\n </div>\n <input\n ref={fileInput}\n type=\"file\"\n accept={library.accept ?? \"image/*\"}\n multiple\n hidden\n onChange={(e) => {\n void upload(e.target.files);\n e.target.value = \"\";\n }}\n />\n </div>\n ))}\n\n <form\n className=\"flex gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n const value = url.trim();\n if (value) onSelect(value);\n }}\n >\n <div className=\"relative flex-1\">\n <Link2 className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-panel-muted-fg\" />\n <Input\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder={labels.imagePickerUrlPlaceholder}\n className=\"h-9 border-panel-border bg-panel-elevated pl-8 text-panel-fg placeholder:text-panel-muted-fg\"\n />\n </div>\n <Button\n type=\"submit\"\n size=\"lg\"\n variant=\"outline\"\n disabled={!url.trim()}\n className=\"border-panel-border bg-panel-elevated text-panel-fg hover:bg-panel-hover hover:text-panel-fg\"\n >\n {labels.imagePickerUseUrl}\n </Button>\n </form>\n\n {library && (\n <section>\n <h3 className=\"mb-2 text-xs font-medium tracking-wide text-panel-muted-fg uppercase\">\n {labels.imagePickerGallery}\n </h3>\n {loading && images.length === 0 && pending === 0 ? (\n <div className=\"flex justify-center py-8\">\n <Spinner />\n </div>\n ) : images.length === 0 && pending === 0 ? (\n <p className=\"py-6 text-center text-sm text-panel-muted-fg\">\n {labels.imagePickerEmpty}\n </p>\n ) : (\n <ul className=\"grid grid-cols-3 gap-3 sm:grid-cols-4\">\n {Array.from({ length: pending }, (_, i) => (\n <li\n key={`pending-${i}`}\n className=\"flex aspect-[4/3] items-center justify-center rounded-lg border border-panel-border bg-panel-elevated\"\n >\n <Spinner />\n </li>\n ))}\n {images.map((image) => {\n const selected = image.url === currentUrl;\n const confirming = confirmId === image.id;\n return (\n <li key={image.id} className=\"group relative\">\n <button\n type=\"button\"\n onClick={() => onSelect(image.url)}\n title={image.name}\n className={cn(\n \"block aspect-[4/3] w-full overflow-hidden rounded-lg border bg-black/25 transition outline-none focus-visible:ring-2 focus-visible:ring-brand\",\n selected\n ? \"border-brand ring-2 ring-brand\"\n : \"border-panel-border hover:border-panel-muted-fg\",\n )}\n >\n {/* Contained, not cropped: email images are mostly wide banners, and\n choosing one means seeing its whole composition. */}\n <img\n src={image.url}\n alt={image.name ?? \"\"}\n loading=\"lazy\"\n className=\"size-full object-contain\"\n />\n </button>\n {selected && (\n <span className=\"pointer-events-none absolute top-1.5 left-1.5 flex size-5 items-center justify-center rounded-full bg-brand text-brand-fg\">\n <Check className=\"size-3\" />\n </span>\n )}\n {image.name && (\n <span className=\"pointer-events-none absolute inset-x-0 bottom-0 truncate rounded-b-lg bg-black/60 px-2 py-1 text-[11px] text-white opacity-0 transition group-hover:opacity-100\">\n {image.name}\n </span>\n )}\n {library.remove && (\n <button\n type=\"button\"\n onClick={() => void remove(image)}\n onBlur={() => confirming && setConfirmId(null)}\n title={\n confirming\n ? labels.imagePickerConfirmDelete\n : labels.imagePickerDelete\n }\n aria-label={labels.imagePickerDelete}\n className={cn(\n \"absolute top-1.5 right-1.5 flex h-6 items-center gap-1 rounded-md px-1.5 text-xs text-white transition focus-visible:opacity-100\",\n confirming\n ? \"bg-red-600 opacity-100\"\n : \"bg-black/60 opacity-0 group-hover:opacity-100 hover:bg-black/80\",\n )}\n >\n <Trash2 className=\"size-3.5\" />\n {confirming && labels.imagePickerConfirmDelete}\n </button>\n )}\n </li>\n );\n })}\n </ul>\n )}\n </section>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n/** The subset of `<input accept>` a picker needs: MIME types, `type/*`, and `.ext`. */\nfunction matchesAccept(file: File, accept: string | undefined): boolean {\n if (!accept) return file.type.startsWith(\"image/\");\n return accept.split(\",\").some((raw) => {\n const rule = raw.trim().toLowerCase();\n if (rule.startsWith(\".\")) return file.name.toLowerCase().endsWith(rule);\n if (rule.endsWith(\"/*\")) return file.type.startsWith(rule.slice(0, -1));\n return file.type === rule;\n });\n}\n","\"use client\";\n\n/**\n * The color control for every color property in the Style Manager.\n *\n * GrapesJS's own is a text box and a swatch that opens Spectrum, a jQuery picker that\n * cannot be styled into the editor. This keeps the text box — typing a hex is often the\n * fastest way — and opens a picker with the colors the email already uses, which is what\n * keeps a design consistent.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { HexAlphaColorPicker, HexColorPicker } from \"react-colorful\";\nimport { Pipette } from \"lucide-react\";\n\nimport type { EditorLabels } from \"../../labels.js\";\nimport { cn } from \"../../lib/utils\";\nimport { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from \"../ui/popover\";\n\n/** Web-safe neutrals and a few saturated hues; emails rarely want more than this. */\nconst PRESETS = [\n \"#000000\",\n \"#374151\",\n \"#6b7280\",\n \"#d1d5db\",\n \"#f3f4f6\",\n \"#ffffff\",\n \"#dc2626\",\n \"#ea580c\",\n \"#ca8a04\",\n \"#16a34a\",\n \"#0284c7\",\n \"#4f46e5\",\n \"#9333ea\",\n \"#db2777\",\n];\n\n// Chrome's EyeDropper; absent elsewhere, and so is the button.\ntype EyeDropperCtor = new () => { open: () => Promise<{ sRGBHex: string }> };\nconst eyeDropper = (): EyeDropperCtor | undefined =>\n (globalThis as { EyeDropper?: EyeDropperCtor }).EyeDropper;\n\nexport default function ColorField({\n value,\n placeholder,\n documentColors,\n labels,\n onChange,\n}: {\n value: string;\n /** The property's default, shown when no value is set. */\n placeholder?: string;\n /** Read when the picker opens, so it reflects the email as it is now. */\n documentColors: () => string[];\n /** Passed in rather than read from context: this renders in its own React root. */\n labels: EditorLabels;\n /** `partial` while dragging, so GrapesJS records one undo step per change, not per pixel. */\n onChange: (value: string, partial: boolean) => void;\n}) {\n const [text, setText] = useState(value);\n const [open, setOpen] = useState(false);\n const [used, setUsed] = useState<string[]>([]);\n // Whether a pointer is down on the picker, and the value its drag left as a partial update.\n const pressed = useRef(false);\n const dragged = useRef<string | null>(null);\n const fieldRef = useRef<HTMLDivElement>(null);\n // The picker opens beside the panel the field sits in, level with the field. Anchored to\n // the field itself it covered the panel, since a property can be half the panel wide.\n const anchor = useRef({\n getBoundingClientRect: () => {\n const field = fieldRef.current?.getBoundingClientRect() ?? new DOMRect();\n const panel = fieldRef.current?.closest(\".editor-dark\")?.getBoundingClientRect();\n return new DOMRect(panel?.right ?? field.right, field.top, 0, field.height);\n },\n });\n\n useEffect(() => setText(value), [value]);\n const latestOnChange = useRef(onChange);\n latestOnChange.current = onChange;\n // A drag ends as a real change, with an undo step, when the pointer is released: GrapesJS\n // writes to whatever is selected at that moment, and nothing else can be selected while\n // the pointer is captured. The view going away mid-drag ends it too.\n const release = useCallback(() => {\n pressed.current = false;\n if (dragged.current !== null) latestOnChange.current(dragged.current, false);\n dragged.current = null;\n }, []);\n // On the document: when the captured element is removed mid-drag (the popover closing, or\n // the alpha slider appearing), the browser sends the release there rather than to it.\n useEffect(() => {\n document.addEventListener(\"lostpointercapture\", release);\n return () => {\n document.removeEventListener(\"lostpointercapture\", release);\n release();\n };\n }, [release]);\n\n const hex = toHex(value) ?? toHex(placeholder ?? \"\") ?? \"#000000\";\n // Translucent colors keep their alpha, and get the slider to change it.\n const translucent = hex.length === 9;\n const empty = !value;\n\n const commit = (next: string) => {\n dragged.current = null;\n onChange(next, false);\n };\n\n // react-colorful reports no drag end, and moves by arrow keys as well; those are whole changes.\n const drag = (next: string) => {\n setText(next);\n if (!pressed.current) return commit(next);\n dragged.current = next;\n onChange(next, true);\n };\n\n const pickFromScreen = async () => {\n const Ctor = eyeDropper();\n if (!Ctor) return;\n try {\n const { sRGBHex } = await new Ctor().open();\n commit(sRGBHex);\n } catch {\n // Escape cancels the eyedropper; nothing to do.\n }\n };\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n if (next) setUsed(documentColors());\n // Closing by Escape or a click outside unmounts the hex input without a blur, so\n // a value typed there is committed here rather than lost.\n else if (text.trim() !== value) commit(text.trim());\n setOpen(next);\n }}\n >\n <PopoverAnchor virtualRef={anchor} />\n <div\n ref={fieldRef}\n className=\"flex h-8 w-full items-center gap-1.5 rounded-md border border-panel-border bg-panel-elevated pr-1.5 pl-2 focus-within:border-brand\"\n >\n <input\n value={text}\n placeholder={placeholder}\n spellCheck={false}\n onChange={(e) => setText(e.target.value)}\n onBlur={() => text !== value && commit(text.trim())}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") commit(text.trim());\n if (e.key === \"Escape\") setText(value);\n }}\n className=\"min-w-0 flex-1 bg-transparent font-mono text-xs text-panel-fg outline-none placeholder:text-panel-muted-fg\"\n />\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n title={labels.colorPickerOpen}\n aria-label={labels.colorPickerOpen}\n className=\"relative size-5 shrink-0 overflow-hidden rounded border border-white/20 outline-none focus-visible:ring-2 focus-visible:ring-brand\"\n >\n <Checkerboard />\n {!empty && <span className=\"absolute inset-0\" style={{ background: value }} />}\n </button>\n </PopoverTrigger>\n </div>\n <PopoverContent\n side=\"right\"\n align=\"start\"\n sideOffset={8}\n className=\"editor-dark flex w-52 flex-col gap-2.5 border border-panel-border bg-panel p-2.5 text-panel-fg\"\n >\n <div\n onPointerDownCapture={(e) => {\n pressed.current = true;\n // Captured, so a release over the canvas iframe still reaches this document. On the\n // target, not this wrapper: the mousedown react-colorful handles follows the capture.\n (e.target as Element).setPointerCapture(e.pointerId);\n }}\n >\n {translucent ? (\n <HexAlphaColorPicker color={hex} onChange={drag} className=\"color-field-picker\" />\n ) : (\n <HexColorPicker color={hex} onChange={drag} className=\"color-field-picker\" />\n )}\n </div>\n\n <div className=\"flex items-center gap-1.5\">\n <span\n className=\"size-6 shrink-0 rounded border border-white/20\"\n style={{ background: hex }}\n />\n <input\n value={text}\n spellCheck={false}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => e.key === \"Enter\" && commit(text.trim())}\n onBlur={() => text !== value && commit(text.trim())}\n className=\"h-6 min-w-0 flex-1 rounded border border-panel-border bg-panel-elevated px-1.5 font-mono text-[11px] outline-none focus:border-brand\"\n />\n {eyeDropper() && (\n <button\n type=\"button\"\n onClick={() => void pickFromScreen()}\n title={labels.colorPickerEyedropper}\n aria-label={labels.colorPickerEyedropper}\n className=\"flex size-6 shrink-0 items-center justify-center rounded border border-panel-border bg-panel-elevated text-panel-muted-fg hover:text-panel-fg\"\n >\n <Pipette className=\"size-3\" />\n </button>\n )}\n </div>\n\n {used.length > 0 && (\n <Swatches\n title={labels.colorPickerDocument}\n colors={used}\n current={hex}\n onPick={commit}\n />\n )}\n <Swatches\n title={labels.colorPickerPresets}\n colors={PRESETS}\n current={hex}\n onPick={commit}\n transparentLabel={labels.colorPickerTransparent}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction Swatches({\n title,\n colors,\n current,\n onPick,\n transparentLabel,\n}: {\n title: string;\n colors: string[];\n current: string;\n onPick: (color: string) => void;\n /** Adds a \"transparent\" swatch at the end when given. */\n transparentLabel?: string;\n}) {\n return (\n <div>\n <p className=\"mb-1 text-[10px] font-medium tracking-wide text-panel-muted-fg uppercase\">\n {title}\n </p>\n <div className=\"grid grid-cols-8 gap-1\">\n {colors.map((color) => (\n <button\n key={color}\n type=\"button\"\n title={color}\n onClick={() => onPick(color)}\n className={cn(\n \"aspect-square rounded border border-white/15 outline-none hover:scale-110 focus-visible:ring-2 focus-visible:ring-brand\",\n color.toLowerCase() === current.toLowerCase() && \"ring-2 ring-brand\",\n )}\n style={{ background: color }}\n />\n ))}\n {transparentLabel && (\n <button\n type=\"button\"\n title={transparentLabel}\n onClick={() => onPick(\"transparent\")}\n className=\"relative aspect-square overflow-hidden rounded border border-white/15 outline-none hover:scale-110 focus-visible:ring-2 focus-visible:ring-brand\"\n >\n <Checkerboard />\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction Checkerboard() {\n return (\n <span\n className=\"absolute inset-0\"\n style={{\n backgroundImage: \"repeating-conic-gradient(#9ca3af 0% 25%, #e5e7eb 0% 50%)\",\n backgroundSize: \"8px 8px\",\n }}\n />\n );\n}\n\n/**\n * `#rgb`, `#rrggbb(aa)` and `rgb()/rgba()` to `#rrggbb`, or `#rrggbbaa` when translucent\n * unless `opaque` is set; anything else (names, `transparent`) to null.\n */\nexport function toHex(value: string, { opaque = false } = {}): string | null {\n const v = value.trim().toLowerCase();\n const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/.exec(v);\n if (short) return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`;\n if (/^#[0-9a-f]{6}$/.test(v)) return v;\n if (/^#[0-9a-f]{8}$/.test(v)) return opaque || v.endsWith(\"ff\") ? v.slice(0, 7) : v;\n const rgb = /^rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)(?:[\\s,/]+([\\d.]+%?))?/.exec(v);\n if (rgb) {\n const channels = [rgb[1], rgb[2], rgb[3]].map((n) => Math.min(255, Number(n)));\n const alphaText = rgb[4];\n const alpha = alphaText?.endsWith(\"%\") ? parseFloat(alphaText) / 100 : Number(alphaText ?? 1);\n if (!opaque && alpha < 1) channels.push(Math.round(Math.max(0, alpha) * 255));\n return `#${channels.map((n) => n.toString(16).padStart(2, \"0\")).join(\"\")}`;\n }\n return null;\n}\n","\"use client\";\n\n/**\n * The control for image properties in the Style Manager, such as a section's\n * `background-url`.\n *\n * GrapesJS's own set the preview's `background-image` to the raw value. MJML stores a bare\n * URL there, not `url(...)`, so the preview was invalid CSS and stayed empty. This one shows\n * the image itself and opens the same picker, with the gallery, as an `mj-image` does.\n */\n\nimport { ImageIcon, X } from \"lucide-react\";\n\nimport type { EditorLabels } from \"../../labels.js\";\n\nexport default function ImageField({\n value,\n labels,\n onChoose,\n onClear,\n}: {\n value: string;\n labels: EditorLabels;\n onChoose: () => void;\n /** The label's own clear button is missing on sub-properties, such as a background layer. */\n onClear: () => void;\n}) {\n const url = imageUrl(value);\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={onChoose}\n title={labels.chooseImage}\n className=\"group relative block w-full overflow-hidden rounded-md border border-panel-border bg-panel-elevated outline-none focus-visible:ring-2 focus-visible:ring-brand\"\n >\n {url ? (\n <img src={url} alt=\"\" className=\"h-24 w-full bg-black/25 object-contain\" />\n ) : (\n <span className=\"flex h-16 flex-col items-center justify-center gap-1 text-xs text-panel-muted-fg\">\n <ImageIcon className=\"size-5\" />\n {labels.chooseImage}\n </span>\n )}\n {url && (\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/50 text-xs font-medium text-white opacity-0 transition group-hover:opacity-100\">\n {labels.chooseImage}\n </span>\n )}\n </button>\n {value && (\n <button\n type=\"button\"\n onClick={onClear}\n title={labels.imageFieldRemove}\n aria-label={labels.imageFieldRemove}\n className=\"absolute top-1 right-1 flex size-5 items-center justify-center rounded bg-black/60 text-white hover:bg-black/80\"\n >\n <X className=\"size-3\" />\n </button>\n )}\n </div>\n );\n}\n\n/**\n * A bare URL (MJML attributes) or `url(\"…\")` (CSS) to the URL. Anything else — `none`, a\n * gradient or another CSS image function — to null, as there is no image to show.\n */\nexport function imageUrl(value: string): string | null {\n const v = value.trim();\n if (!v || v === \"none\") return null;\n const css = /^url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)$/i.exec(v);\n if (css) return css[2] || null;\n return /^[a-z-]+\\(/i.test(v) ? null : v;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,sBAA4C;AAC5C,IAAAA,gBAAkD;AAClD,2BAAyB;AACzB,IAAAA,gBAA4D;AAE5D,IAAAC,eAA6B;;;ACS7B,mBAA6E;;;ACkFtE,IAAM,iBAA+B;AAAA,EAC1C,SAAS;AAAA,EACT,eAAe;AAAA,EACf,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,cAAc;AAAA,EACd,SAAS;AAAA,EACT,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ,CAAC,SAAS,GAAG,IAAI;AAAA,EAEzB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,SAAS;AAAA,EACT,SAAS;AAAA,EACT,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,OAAO;AAAA,EAEP,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,2BAA2B;AAAA,EAC3B,mBAAmB;AAAA,EACnB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,kBAAkB,CAAC,MAAM,UAAU,GAAG,IAAI,OAAO,KAAK;AAAA,EACtD,sBAAsB;AAAA,EACtB,mBAAmB;AAAA,EACnB,0BAA0B;AAAA,EAC1B,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,wBAAwB,CAAC,aAAa,GAAG,QAAQ;AAAA,EACjD,kBAAkB;AAAA,EAElB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EAExB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,uBAAuB;AAAA,EAEvB,gBAAgB;AAAA,EAChB,sBACE;AAAA,EACF,cAAc;AAAA,EACd,WAAW;AAAA,EACX,gBAAgB,CAAC,OAAO,WACtB,SAAS,IAAI,oBAAoB,KAAK,KAAK,MAAM,aAAa,oBAAoB,KAAK;AAAA,EACzF,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EAEX,YAAY;AAAA,IACV,cAAc;AAAA,IACd,aAAa;AAAA,IACb,cAAc;AAAA,IACd,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,EACnB;AACF;AAMO,SAAS,YAAY,WAAiD;AAC3E,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,YAAY,EAAE,GAAG,eAAe,YAAY,GAAG,UAAU,WAAW;AAAA,EACtE;AACF;;;AD/FQ;AA5CR,IAAM,mBAAe,4BAAmC,IAAI;AAC5D,IAAM,mBAAe,4BAAmC,IAAI;AAa5D,IAAM,kBAAc,4BAAyB;AAAA,EAC3C,SAAS;AAAA,EACT,YAAY,MAAM;AAAA,EAAC;AAAA,EACnB,QAAQ;AAAA,EACR,WAAW,MAAM;AAAA,EAAC;AACpB,CAAC;AAmCM,SAAS,eAA0B;AACxC,aAAO,yBAAW,WAAW;AAC/B;AASO,SAAS,YAA0B;AACxC,aAAO,yBAAW,YAAY,KAAK,YAAY;AACjD;AAEA,SAAS,YAA0B;AACjC,QAAM,aAAS,yBAAW,YAAY;AACtC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,mBAAkC;AAChD,SAAO,UAAU,EAAE;AACrB;AAEO,SAAS,kBAAgC;AAC9C,SAAO,UAAU,EAAE;AACrB;AAEO,SAAS,kBAA4C;AAC1D,SAAO,UAAU,EAAE;AACrB;;;AElIA,kBAA6B;;;AClB7B,kBAAsC;AACtC,4BAAwB;AAEjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;ACLA,YAAuB;AACvB,sCAAuC;AACvC,sBAAqB;AAsDjB,IAAAC,sBAAA;AAlDJ,IAAM,qBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SACE;AAAA,QACF,WACE;AAAA,QACF,OACE;AAAA,QACF,aACE;AAAA,QACF,MAAM;AAAA,MACR;AAAA,MACA,MAAM;AAAA,QACJ,SACE;AAAA,QACF,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,WACE;AAAA,QACF,WACE;AAAA,QACF,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,OAAO;AAAA,EACd;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA,EACV,GAAG;AACL,GAGK;AACH,QAAM,OAAO,UAAU,qBAAK,OAAO;AAEnC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,gBAAc;AAAA,MACd,aAAW;AAAA,MACX,WAAW,GAAG,eAAe,EAAE,SAAS,MAAM,UAAU,CAAC,CAAC;AAAA,MACzD,GAAG;AAAA;AAAA,EACN;AAEJ;;;AChEA,IAAAC,SAAuB;AAMnB,IAAAC,sBAAA;AAFJ,SAAS,MAAM,EAAE,WAAW,MAAM,GAAG,MAAM,GAAkC;AAC3E,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;;;ACfA,0BAA4B;AAIxB,IAAAC,sBAAA;AAFJ,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAgC;AACrE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,MAAK;AAAA,MACL,cAAW;AAAA,MACX,WAAW,GAAG,uBAAuB,SAAS;AAAA,MAC7C,GAAG;AAAA;AAAA,EACN;AAEJ;;;ACXA,IAAAC,SAAuB;AACvB,IAAAC,mBAA4C;AAKnC,IAAAC,sBAAA;AADT,SAAS,QAAQ,EAAE,GAAG,MAAM,GAAuD;AACjF,SAAO,6CAAC,iBAAAC,QAAiB,MAAjB,EAAsB,aAAU,WAAW,GAAG,OAAO;AAC/D;AAEA,SAAS,eAAe,EAAE,GAAG,MAAM,GAA0D;AAC3F,SAAO,6CAAC,iBAAAA,QAAiB,SAAjB,EAAyB,aAAU,mBAAmB,GAAG,OAAO;AAC1E;AAEA,SAAS,cAAc,EAAE,GAAG,MAAM,GAAyD;AACzF,SAAO,6CAAC,iBAAAA,QAAiB,QAAjB,EAAwB,aAAU,kBAAkB,GAAG,OAAO;AACxE;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,GAAG;AACL,GAA0D;AACxD,SACE,6CAAC,iBAAAA,QAAiB,QAAjB,EACC;AAAA,IAAC,iBAAAA,QAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;;;ACrCA,IAAAC,SAAuB;AACvB,IAAAC,mBAA0C;AAI1C,IAAAC,uBAAsB;AAGb,IAAAC,sBAAA;AADT,SAAS,OAAO,EAAE,GAAG,MAAM,GAAsD;AAC/E,SAAO,6CAAC,iBAAAC,OAAgB,MAAhB,EAAqB,aAAU,UAAU,GAAG,OAAO;AAC7D;AAMA,SAAS,aAAa,EAAE,GAAG,MAAM,GAAwD;AACvF,SAAO,6CAAC,iBAAAC,OAAgB,QAAhB,EAAuB,aAAU,iBAAiB,GAAG,OAAO;AACtE;AAMA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA,GAAG;AACL,GAAyD;AACvD,SACE;AAAA,IAAC,iBAAAC,OAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB,GAAG;AACL,GAEG;AACD,SACE,8CAAC,gBACC;AAAA,iDAAC,iBAAc;AAAA,IACf;AAAA,MAAC,iBAAAA,OAAgB;AAAA,MAAhB;AAAA,QACC,aAAU;AAAA,QACV,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAEH;AAAA;AAAA,UACA,mBACC,6CAAC,iBAAAA,OAAgB,OAAhB,EAAsB,aAAU,gBAAe,SAAO,MACrD,wDAAC,UAAO,SAAQ,SAAQ,WAAU,0BAAyB,MAAK,WAC9D;AAAA,yDAAC,8BAAM;AAAA,YACP,6CAAC,UAAK,WAAU,WAAU,mBAAK;AAAA,aACjC,GACF;AAAA;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAEA,SAAS,aAAa,EAAE,WAAW,GAAG,MAAM,GAAgC;AAC1E,SACE,6CAAC,SAAI,aAAU,iBAAgB,WAAW,GAAG,uBAAuB,SAAS,GAAI,GAAG,OAAO;AAE/F;AA6BA,SAAS,YAAY,EAAE,WAAW,GAAG,MAAM,GAAuD;AAChG,SACE;AAAA,IAAC,iBAAAC,OAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,mDAAmD,SAAS;AAAA,MACzE,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,kBAAkB;AAAA,EACzB;AAAA,EACA,GAAG;AACL,GAA6D;AAC3D,SACE;AAAA,IAAC,iBAAAA,OAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;;;AC5HO,SAAS,QAAQ,OAA+B;AACrD,MAAI,EAAE,iBAAiB,OAAQ,QAAO;AAEtC,QAAM,SAAU,MAA+B;AAE/C,QAAM,aAAa,OAAO,WAAW,WAAW,UAAU,OAAO,SAAS,MAAM;AAChF,MAAI,eAAe,MAAO,QAAO;AAEjC,MAAI,MAAM,QAAQ,WAAW,GAAG,GAAG;AACjC,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,MAAM,OAAO;AACvC,aAAO,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,MAAM,KAAK,OAAO,QAAQ;AAAA,IACzF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,SAAO,cAAc,YAAY,MAAM,CAAC,QAAQ,WAAW,GAAG,IAAI,MAAM,UAAU;AACpF;AAGO,SAAS,YAAY,OAAuB;AACjD,SAAO,iBAAiB,KAAK;AAC/B;;;AChCA,IAAAC,SAAuB;AAMnB,IAAAC,sBAAA;AAFJ,SAAS,SAAS,EAAE,WAAW,GAAG,MAAM,GAAqC;AAC3E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;;;ACbA,IAAAC,gBAA+B;AAC/B,IAAAA,gBAAkE;AAClE,IAAAC,uBAA4C;AA2NlC,IAAAC,sBAAA;AAlMV,IAAM,QAAQ,CAAC,MACb,EAAE,WAAW,OAAO,EAAE,QAAQ,KAAK,OAAO,EAAE,SAAS;AACvD,IAAM,cAAc,CAAC,MAAsB,EAAE,WAAW,OAAO,EAAE,QAAQ,KAAK,OAAO,EAAE,SAAS;AAEjF,SAAR,eAAgC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAU;AAER,QAAM,eAAe,gBAAgB;AACrC,QAAM,SAAS,UAAU;AACzB,QAAM,aAAS,8BAAe;AAC9B,QAAM,mBAAe,sBAA8B,IAAI;AACvD,QAAM,CAAC,UAAU,WAAW,QAAI,wBAA2B,CAAC,CAAC;AAC7D,QAAM,CAAC,WAAW,YAAY,QAAI,wBAA8B,CAAC,CAAC;AAClE,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAwB,IAAI;AAC9D,QAAM,CAAC,aAAa,cAAc,QAAI,wBAA+B,IAAI;AACzE,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,EAAE;AAEnC,QAAM,aAAS,2BAAY,MAAM;AAC/B,iBAAa,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,MAAM,QAAQ,KAAK;AAAA,EAChE,GAAG,CAAC,KAAK,CAAC;AAEV,+BAAU,MAAM;AACd,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,aAAa,CAAC;AAE1B,QAAM,WAAO,uBAAQ,MAAM,SAAS,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,GAAG,CAAC,QAAQ,CAAC;AAElF,+BAAU,MAAM;AACd,sBAAkB,KAAK,MAAM;AAAA,EAC/B,GAAG,CAAC,KAAK,QAAQ,iBAAiB,CAAC;AAGnC,QAAM,WAAO,uBAAQ,MAAM;AACzB,UAAM,MAAM,oBAAI,IAA8B;AAC9C,eAAW,KAAK,MAAM;AACpB,YAAM,IAAI,MAAM,CAAC;AACjB,YAAM,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC;AAC3B,UAAI,KAAK,CAAC;AACV,UAAI,IAAI,GAAG,GAAG;AAAA,IAChB;AACA,WAAO,CAAC,GAAG,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,OAAO,EAAE,KAAK,KAAK,EAAE;AAAA,EAChE,GAAG,CAAC,IAAI,CAAC;AAGT,QAAM,cAAU,sBAAO,IAAI;AAC3B,UAAQ,UAAU;AAClB,QAAM,gBAAY,sBAAO,SAAS;AAClC,YAAU,UAAU;AACpB,QAAM,mBAAe,sBAA4B,CAAC,CAAC;AAGnD,QAAM,gBAAY,2BAAY,MAAM;AAClC,UAAM,KAAK;AACX,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,MAAM,CAAC,UAAW;AACvB,UAAM,QAAQ,GAAG,OAAO,aAAa;AACrC,UAAM,OAAO,GAAG,OAAO,cAAc;AACrC,QAAI,CAAC,SAAS,CAAC,KAAM;AACrB,UAAM,QAAQ,UAAU,sBAAsB;AAC9C,UAAM,QAAQ,MAAM,sBAAsB;AAI1C,UAAM,OAAO,MAAM,cAAc,MAAM,QAAQ,MAAM,cAAc;AACnE,UAAM,OAAO,IAAI,IAAY,QAAQ,QAAQ,IAAI,KAAK,CAAC;AACvD,QAAI,UAAU,QAAS,MAAK,IAAI,UAAU,OAAO;AACjD,UAAM,OAA4B,CAAC;AACnC,SAAK,QAAQ,CAAC,MAAM;AAClB,YAAM,KAAK,KAAK,cAAc,IAAI,CAAC,EAAE;AACrC,UAAI,CAAC,GAAI;AACT,YAAM,IAAI,GAAG,sBAAsB;AAGnC,WAAK,CAAC,IAAI;AAAA,QACR,KAAK,MAAM,MAAM,MAAM,MAAM,EAAE,MAAM,OAAO;AAAA,QAC5C,MAAM,MAAM,OAAO,MAAM,QAAQ,EAAE,OAAO,EAAE,SAAS,OAAO;AAAA,MAC9D;AAAA,IACF,CAAC;AACD,QAAI,KAAK,UAAU,aAAa,OAAO,MAAM,KAAK,UAAU,IAAI,GAAG;AACjE,mBAAa,UAAU;AACvB,mBAAa,IAAI;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAIX,+BAAU,MAAM;AACd,UAAM,KAAK;AACX,QAAI,CAAC,GAAI;AACT,UAAM,OAAO;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,SAAK,QAAQ,CAAC,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC;AACvC,UAAM,MAAM,GAAG,OAAO,YAAY;AAClC,SAAK,iBAAiB,UAAU,WAAW,IAAI;AAC/C,WAAO,iBAAiB,UAAU,SAAS;AAC3C,UAAM,KAAK,OAAO,YAAY,WAAW,GAAG;AAC5C,WAAO,MAAM;AACX,WAAK,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AACxC,WAAK,oBAAoB,UAAU,WAAW,IAAI;AAClD,aAAO,oBAAoB,UAAU,SAAS;AAC9C,aAAO,cAAc,EAAE;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,QAAQ,SAAS,CAAC;AAEtB,+BAAU,MAAM;AACd,cAAU;AAAA,EACZ,GAAG,CAAC,UAAU,WAAW,SAAS,CAAC;AAGnC,+BAAU,MAAM;AACd,QAAI,CAAC,cAAe;AACpB,mBAAe,aAAa;AAC5B,iBAAa,YAAY,aAAa,CAAC;AACvC,YAAQ,EAAE;AACV,0BAAsB,SAAS;AAAA,EACjC,GAAG,CAAC,eAAe,SAAS,CAAC;AAE7B,QAAM,iBAAiB,YAAY,KAAK,OAAO,CAAC,MAAM,MAAM,CAAC,MAAM,SAAS,IAAI,CAAC;AACjF,QAAM,gBAAsC,MAAM;AAChD,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,IAAI,eAAe,CAAC;AAC1B,QAAI,EAAG,QAAO,EAAE,WAAW,EAAE,WAAW,UAAU,EAAE,UAAU,aAAa,EAAE,YAAY;AACzF,QAAI,eAAe,YAAY,WAAW,MAAM,UAAW,QAAO;AAClE,WAAO;AAAA,EACT,GAAG;AACH,QAAM,YAAY,YAAY,UAAU,SAAS,IAAI;AAIrD,QAAM,oBAAgB,sBAAY,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;AACrD,MAAI,UAAW,eAAc,UAAU;AACvC,QAAM,YAAY,aAAa,cAAc;AAE7C,QAAM,mBAAe,2BAAY,MAAM;AACrC,iBAAa,IAAI;AACjB,YAAQ,EAAE;AACV,QAAI,aAAa;AACf,qBAAe,IAAI;AACnB,wBAAkB;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,aAAa,iBAAiB,CAAC;AAInC,+BAAU,MAAM;AACd,QAAI,CAAC,aAAa,CAAC,OAAQ;AAC3B,UAAM,MAAM,OAAO,OAAO,cAAc;AACxC,QAAI,CAAC,IAAK;AACV,UAAM,SAAS,MAAM,aAAa;AAClC,QAAI,iBAAiB,aAAa,QAAQ,IAAI;AAC9C,WAAO,MAAM,IAAI,oBAAoB,aAAa,QAAQ,IAAI;AAAA,EAChE,GAAG,CAAC,WAAW,QAAQ,YAAY,CAAC;AAEpC,QAAM,SAAS,YAAY;AACzB,QAAI,CAAC,gBAAgB,CAAC,KAAK,KAAK,EAAG;AACnC,QAAI;AACF,YAAM,aAAa,IAAI,OAAO,cAAc,KAAK,KAAK,CAAC;AACvD,cAAQ,EAAE;AACV,UAAI,aAAa;AACf,uBAAe,IAAI;AACnB,0BAAkB;AAAA,MACpB;AACA,aAAO;AAAA,IACT,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,OAAe;AACpC,QAAI;AACF,YAAM,aAAa,QAAQ,EAAE;AAC7B,aAAO;AAAA,IACT,SAAS,GAAG;AACV,cAAQ,MAAM,CAAC;AAAA,IACjB;AAAA,EACF;AAEA,SACE,8CAAC,SAAI,KAAK,cAAc,WAAU,6DAC/B;AAAA,SAAK,IAAI,CAAC,EAAE,KAAK,KAAK,MAAM;AAC3B,YAAM,MAAM,UAAU,GAAG;AACzB,UAAI,CAAC,IAAK,QAAO;AACjB,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,SAAS,MAAM;AACb,yBAAa,CAAC,MAAO,MAAM,MAAM,OAAO,GAAI;AAC5C,oBAAQ,EAAE;AAAA,UACZ;AAAA,UACA,OAAO,EAAE,KAAK,IAAI,KAAK,MAAM,IAAI,KAAK;AAAA,UACtC,WAAU;AAAA,UACV,OAAO,GAAG,KAAK,MAAM;AAAA,UAEpB,eAAK;AAAA;AAAA,QAVD;AAAA,MAWP;AAAA,IAEJ,CAAC;AAAA,IAED,8CAAC,WAAQ,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC,WAAW,cAAc,CAAC,MAAM,CAAC,KAAK,aAAa,GACjF;AAAA,mDAAC,iBAAc,SAAO,MACpB;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,YACL,UAAU;AAAA,YACV,KAAK,UAAU;AAAA,YACf,MAAM,UAAU;AAAA,YAChB,OAAO;AAAA,YACP,QAAQ;AAAA,UACV;AAAA;AAAA,MACF,GACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAM;AAAA,UAIN,WAAU;AAAA,UAEV;AAAA,0DAAC,SAAI,WAAU,2FACb;AAAA,2DAAC,UAAK,WAAU,WACb,wBAAc,WACb,8EAAE;AAAA;AAAA,gBACW;AAAA,gBACX,6CAAC,UAAK,WAAU,+BAA+B,uBAAa,aAAY;AAAA,iBAC1E,IAEA,8EACG;AAAA,uBAAO;AAAA,gBAAkB;AAAA,gBAC1B,6CAAC,UAAK,WAAU,aAAa,wBAAc,WAAU;AAAA,iBACvD,GAEJ;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,cAAY,OAAO;AAAA,kBACnB,WAAU;AAAA,kBAEV,uDAAC,0BAAE,WAAU,UAAS;AAAA;AAAA,cACxB;AAAA,eACF;AAAA,YACA,8CAAC,SAAI,WAAU,0CACZ;AAAA,6BAAe,WAAW,KACzB,6CAAC,OAAE,WAAU,iCAAiC,iBAAO,eAAc;AAAA,cAEpE,eAAe,IAAI,CAAC,MACnB,8CAAC,SAAe,WAAU,6CACxB;AAAA,6DAAC,OAAE,WAAU,uBAAuB,YAAE,MAAK;AAAA,gBAC3C;AAAA,kBAAC;AAAA;AAAA,oBACC,SAAS,MAAM,KAAK,QAAQ,EAAE,EAAE;AAAA,oBAChC,WAAU;AAAA,oBAEV;AAAA,mEAAC,8BAAM,WAAU,UAAS;AAAA,sBAAE;AAAA,sBAAE,OAAO;AAAA;AAAA;AAAA,gBACvC;AAAA,mBAPQ,EAAE,EAQZ,CACD;AAAA,eACH;AAAA,YACA,8CAAC,SAAI,WAAU,gBACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAS;AAAA,kBACT,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,kBACvC,aAAa,OAAO;AAAA,kBACpB,WAAU;AAAA,kBACV,WAAW,CAAC,MAAM;AAChB,wBAAI,EAAE,QAAQ,YAAY,EAAE,WAAW,EAAE,UAAU;AACjD,wBAAE,eAAe;AACjB,2BAAK,OAAO;AAAA,oBACd;AAAA,kBACF;AAAA;AAAA,cACF;AAAA,cACA,6CAAC,SAAI,WAAU,yBACb,wDAAC,UAAO,MAAK,MAAK,UAAU,CAAC,KAAK,KAAK,GAAG,SAAS,MAAM,KAAK,OAAO,GACnE;AAAA,6DAAC,0CAAkB;AAAA,gBAAE;AAAA,gBAAE,OAAO;AAAA,iBAChC,GACF;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA,OACF;AAAA,KACF;AAEJ;;;ACtTO,SAAS,gBAAgB,MAA2B,SAA4B;AACrF,MAAI,QAA8C;AAElD,MAAI,OAAyB,QAAQ,QAAQ;AAE7C,QAAM,SAAS,MAAM;AACnB,QAAI,MAAO,cAAa,KAAK;AAC7B,YAAQ;AAAA,EACV;AAEA,QAAM,MAAM,CAAI,SAAuC;AACrD,UAAM,OAAO,KAAK,KAAK,IAAI;AAC3B,WAAO,KAAK,MAAM,MAAM;AAAA,IAAC,CAAC;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,WAAW;AACT,aAAO;AACP,cAAQ,WAAW,MAAM,KAAK,IAAI,IAAI,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC,GAAG,OAAO;AAAA,IAClE;AAAA,IACA;AAAA,IACA,QAAQ;AACN,aAAO;AACP,aAAO,IAAI,IAAI;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AACF;;;Ab3BA,IAAAC,iBAAsB;AACtB,IAAAC,uBAAiD;;;AcZjD,IAAAC,SAAuB;AACvB,IAAAC,mBAA0C;AAqBpC,IAAAC,sBAAA;AAjBN,SAAS,OAAO;AAAA,EACd;AAAA,EACA,OAAO;AAAA,EACP,GAAG;AACL,GAEG;AACD,SACE;AAAA,IAAC,iBAAAC,OAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,aAAW;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC,iBAAAA,OAAgB;AAAA,QAAhB;AAAA,UACC,aAAU;AAAA,UACV,WAAU;AAAA;AAAA,MACZ;AAAA;AAAA,EACF;AAEJ;;;AdVA,IAAAC,uBAAuB;;;AeTvB,IAAAC,gBAAyE;AACzE,IAAAC,uBAA8C;AAC9C,oBAAsB;AAsId,IAAAC,uBAAA;AA3HO,SAAR,YAA6B;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,UAAU,gBAAgB;AAChC,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAgC,CAAC,CAAC;AAC9D,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAgB;AAC1C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAC5C,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,CAAC;AACxC,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAS,KAAK;AAC9C,QAAM,CAAC,KAAK,MAAM,QAAI,wBAAS,EAAE;AACjC,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAwB,IAAI;AAC9D,QAAM,gBAAY,sBAAyB,IAAI;AAG/C,QAAM,cAAU,sBAAO,CAAC;AAGxB,QAAM,aAAS,sBAAO,EAAE,QAAQ,SAAS,CAAC;AAC1C,SAAO,UAAU,EAAE,QAAQ,SAAS;AAEpC,QAAM,YAAQ,sBAAO,CAAC;AAEtB,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,EAAE,MAAM;AACrB,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,KAAK;AAClC,UAAI,SAAS,MAAM,QAAS;AAC5B,gBAAU,OAAO,MAAM;AACvB,eAAS,OAAO,KAAK;AAAA,IACvB,SAAS,KAAK;AACZ,cAAQ,MAAM,GAAG;AACjB,UAAI,SAAS,MAAM,QAAS,qBAAM,MAAM,OAAO,QAAQ,OAAO,qBAAqB;AAAA,IACrF,UAAE;AACA,UAAI,SAAS,MAAM,QAAS,YAAW,KAAK;AAAA,IAC9C;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,+BAAU,MAAM;AACd,YAAQ,WAAW;AACnB,QAAI,CAAC,KAAM;AACX,WAAO,EAAE;AACT,iBAAa,IAAI;AACjB,SAAK,QAAQ;AAAA,EAEf,GAAG,CAAC,IAAI,CAAC;AAET,+BAAU,MAAM,MAAM,MAAM,QAAQ,WAAW,IAAI,CAAC,CAAC;AAErD,QAAM,UAAU,CAAC,SAAe,cAAc,MAAM,SAAS,MAAM;AAEnE,QAAM,OAAO,UAAU,UAAa,MAAM,QAAQ,MAAM;AAExD,QAAM,SAAS,OAAO,UAA2B;AAC/C,QAAI,CAAC,WAAW,CAAC,OAAO,OAAQ;AAChC,UAAM,OAAO,QAAQ;AAGrB,UAAM,OAAO,MAAM,KAAK,KAAK,EAAE,OAAO,CAAC,MAAM;AAC3C,UAAI,QAAQ,CAAC,EAAG,QAAO;AACvB,0BAAM,MAAM,OAAO,uBAAuB,EAAE,IAAI,CAAC;AACjD,aAAO;AAAA,IACT,CAAC;AACD,QAAI,CAAC,KAAK,OAAQ;AAClB,eAAW,CAAC,MAAM,IAAI,KAAK,MAAM;AAIjC,UAAM,UAAU,MAAM,QAAQ,WAAW,KAAK,IAAI,OAAO,MAAM,QAAQ,OAAO,CAAC,CAAC,CAAC;AACjF,eAAW,CAAC,MAAM,IAAI,KAAK,MAAM;AACjC,eAAW,KAAK,SAAS;AACvB,UAAI,EAAE,WAAW,YAAY;AAC3B,gBAAQ,MAAM,EAAE,MAAM;AAEtB,4BAAM,MAAM,EAAE,kBAAkB,QAAQ,EAAE,OAAO,UAAU,OAAO,iBAAiB;AAAA,MACrF;AAAA,IACF;AACA,QAAI,QAAQ,YAAY,KAAM;AAC9B,UAAM,QAAQ;AAEd,UAAM,CAAC,IAAI,IAAI;AACf,QAAI,QAAQ,WAAW,KAAK,MAAM,WAAW,eAAe,QAAQ,YAAY,MAAM;AACpF,aAAO,QAAQ,SAAS,KAAK,MAAM,GAAG;AAAA,IACxC;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,UAAsB;AAC1C,QAAI,CAAC,SAAS,OAAQ;AAEtB,QAAI,cAAc,MAAM,IAAI;AAC1B,mBAAa,MAAM,EAAE;AACrB;AAAA,IACF;AACA,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,QAAQ,OAAO,MAAM,EAAE;AAC7B,YAAM,QAAQ;AAAA,IAChB,SAAS,KAAK;AACZ,cAAQ,MAAM,GAAG;AACjB,0BAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,iBAAiB;AAAA,IAC3E;AAAA,EACF;AAEA,QAAM,SAAS,CAAC,MAAiB;AAC/B,MAAE,eAAe;AACjB,gBAAY,KAAK;AACjB,QAAI,CAAC,KAAM,MAAK,OAAO,EAAE,aAAa,KAAK;AAAA,EAC7C;AAEA,SACE,8CAAC,UAAO,MAAY,cAAc,CAAC,SAAS,CAAC,QAAQ,QAAQ,GAC3D,yDAAC,iBAAc,WAAU,+GACvB;AAAA,mDAAC,gBAAa,WAAU,0CACtB;AAAA,oDAAC,eAAa,iBAAO,kBAAiB;AAAA,MACrC,SACC,8CAAC,qBAAkB,WAAU,uBAC1B,iBAAO,iBAAiB,MAAM,MAAM,MAAM,KAAK,GAClD;AAAA,OAEJ;AAAA,IAEA,+CAAC,SAAI,WAAU,wDACZ;AAAA,kBACE,OACC,8CAAC,OAAE,WAAU,iGACV,iBAAO,sBACV,IAEA;AAAA,QAAC;AAAA;AAAA,UACC,YAAY,CAAC,MAAM;AACjB,cAAE,eAAe;AACjB,wBAAY,IAAI;AAAA,UAClB;AAAA,UACA,aAAa,MAAM,YAAY,KAAK;AAAA,UACpC;AAAA,UACA,WAAW;AAAA,YACT;AAAA,YACA,WAAW,+BAA+B;AAAA,UAC5C;AAAA,UAEA;AAAA,0DAAC,gCAAQ,WAAU,8BAA6B;AAAA,YAChD,+CAAC,SAAI,WAAU,gFACb;AAAA,4DAAC,UAAM,iBAAO,iBAAgB;AAAA,cAC9B;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,SAAS,MAAM,UAAU,SAAS,MAAM;AAAA,kBAEvC,iBAAO;AAAA;AAAA,cACV;AAAA,eACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,MAAK;AAAA,gBACL,QAAQ,QAAQ,UAAU;AAAA,gBAC1B,UAAQ;AAAA,gBACR,QAAM;AAAA,gBACN,UAAU,CAAC,MAAM;AACf,uBAAK,OAAO,EAAE,OAAO,KAAK;AAC1B,oBAAE,OAAO,QAAQ;AAAA,gBACnB;AAAA;AAAA,YACF;AAAA;AAAA;AAAA,MACF;AAAA,MAGJ;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,UAAU,CAAC,MAAM;AACf,cAAE,eAAe;AACjB,kBAAM,QAAQ,IAAI,KAAK;AACvB,gBAAI,MAAO,UAAS,KAAK;AAAA,UAC3B;AAAA,UAEA;AAAA,2DAAC,SAAI,WAAU,mBACb;AAAA,4DAAC,8BAAM,WAAU,6FAA4F;AAAA,cAC7G;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,OAAO,EAAE,OAAO,KAAK;AAAA,kBACtC,aAAa,OAAO;AAAA,kBACpB,WAAU;AAAA;AAAA,cACZ;AAAA,eACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,UAAU,CAAC,IAAI,KAAK;AAAA,gBACpB,WAAU;AAAA,gBAET,iBAAO;AAAA;AAAA,YACV;AAAA;AAAA;AAAA,MACF;AAAA,MAEC,WACC,+CAAC,aACC;AAAA,sDAAC,QAAG,WAAU,wEACX,iBAAO,oBACV;AAAA,QACC,WAAW,OAAO,WAAW,KAAK,YAAY,IAC7C,8CAAC,SAAI,WAAU,4BACb,wDAAC,WAAQ,GACX,IACE,OAAO,WAAW,KAAK,YAAY,IACrC,8CAAC,OAAE,WAAU,gDACV,iBAAO,kBACV,IAEA,+CAAC,QAAG,WAAU,yCACX;AAAA,gBAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,CAAC,GAAG,MACnC;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cAEV,wDAAC,WAAQ;AAAA;AAAA,YAHJ,WAAW,CAAC;AAAA,UAInB,CACD;AAAA,UACA,OAAO,IAAI,CAAC,UAAU;AACrB,kBAAM,WAAW,MAAM,QAAQ;AAC/B,kBAAM,aAAa,cAAc,MAAM;AACvC,mBACE,+CAAC,QAAkB,WAAU,kBAC3B;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,MAAM,SAAS,MAAM,GAAG;AAAA,kBACjC,OAAO,MAAM;AAAA,kBACb,WAAW;AAAA,oBACT;AAAA,oBACA,WACI,mCACA;AAAA,kBACN;AAAA,kBAIA;AAAA,oBAAC;AAAA;AAAA,sBACC,KAAK,MAAM;AAAA,sBACX,KAAK,MAAM,QAAQ;AAAA,sBACnB,SAAQ;AAAA,sBACR,WAAU;AAAA;AAAA,kBACZ;AAAA;AAAA,cACF;AAAA,cACC,YACC,8CAAC,UAAK,WAAU,6HACd,wDAAC,8BAAM,WAAU,UAAS,GAC5B;AAAA,cAED,MAAM,QACL,8CAAC,UAAK,WAAU,mKACb,gBAAM,MACT;AAAA,cAED,QAAQ,UACP;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS,MAAM,KAAK,OAAO,KAAK;AAAA,kBAChC,QAAQ,MAAM,cAAc,aAAa,IAAI;AAAA,kBAC7C,OACE,aACI,OAAO,2BACP,OAAO;AAAA,kBAEb,cAAY,OAAO;AAAA,kBACnB,WAAW;AAAA,oBACT;AAAA,oBACA,aACI,2BACA;AAAA,kBACN;AAAA,kBAEA;AAAA,kEAAC,+BAAO,WAAU,YAAW;AAAA,oBAC5B,cAAc,OAAO;AAAA;AAAA;AAAA,cACxB;AAAA,iBAnDK,MAAM,EAqDf;AAAA,UAEJ,CAAC;AAAA,WACH;AAAA,SAEJ;AAAA,OAEJ;AAAA,KACF,GACF;AAEJ;AAGA,SAAS,cAAc,MAAY,QAAqC;AACtE,MAAI,CAAC,OAAQ,QAAO,KAAK,KAAK,WAAW,QAAQ;AACjD,SAAO,OAAO,MAAM,GAAG,EAAE,KAAK,CAAC,QAAQ;AACrC,UAAM,OAAO,IAAI,KAAK,EAAE,YAAY;AACpC,QAAI,KAAK,WAAW,GAAG,EAAG,QAAO,KAAK,KAAK,YAAY,EAAE,SAAS,IAAI;AACtE,QAAI,KAAK,SAAS,IAAI,EAAG,QAAO,KAAK,KAAK,WAAW,KAAK,MAAM,GAAG,EAAE,CAAC;AACtE,WAAO,KAAK,SAAS;AAAA,EACvB,CAAC;AACH;;;AChUA,IAAAC,gBAAyD;AACzD,4BAAoD;AACpD,IAAAC,uBAAwB;AA4HlB,IAAAC,uBAAA;AArHN,IAAM,UAAU;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,IAAM,aAAa,MAChB,WAA+C;AAEnC,SAAR,WAA4B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAUG;AACD,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,KAAK;AACtC,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,KAAK;AACtC,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAmB,CAAC,CAAC;AAE7C,QAAM,cAAU,sBAAO,KAAK;AAC5B,QAAM,cAAU,sBAAsB,IAAI;AAC1C,QAAM,eAAW,sBAAuB,IAAI;AAG5C,QAAM,aAAS,sBAAO;AAAA,IACpB,uBAAuB,MAAM;AAC3B,YAAM,QAAQ,SAAS,SAAS,sBAAsB,KAAK,IAAI,QAAQ;AACvE,YAAM,QAAQ,SAAS,SAAS,QAAQ,cAAc,GAAG,sBAAsB;AAC/E,aAAO,IAAI,QAAQ,OAAO,SAAS,MAAM,OAAO,MAAM,KAAK,GAAG,MAAM,MAAM;AAAA,IAC5E;AAAA,EACF,CAAC;AAED,+BAAU,MAAM,QAAQ,KAAK,GAAG,CAAC,KAAK,CAAC;AACvC,QAAM,qBAAiB,sBAAO,QAAQ;AACtC,iBAAe,UAAU;AAIzB,QAAM,cAAU,2BAAY,MAAM;AAChC,YAAQ,UAAU;AAClB,QAAI,QAAQ,YAAY,KAAM,gBAAe,QAAQ,QAAQ,SAAS,KAAK;AAC3E,YAAQ,UAAU;AAAA,EACpB,GAAG,CAAC,CAAC;AAGL,+BAAU,MAAM;AACd,aAAS,iBAAiB,sBAAsB,OAAO;AACvD,WAAO,MAAM;AACX,eAAS,oBAAoB,sBAAsB,OAAO;AAC1D,cAAQ;AAAA,IACV;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,MAAM,MAAM,KAAK,KAAK,MAAM,eAAe,EAAE,KAAK;AAExD,QAAM,cAAc,IAAI,WAAW;AACnC,QAAM,QAAQ,CAAC;AAEf,QAAM,SAAS,CAAC,SAAiB;AAC/B,YAAQ,UAAU;AAClB,aAAS,MAAM,KAAK;AAAA,EACtB;AAGA,QAAM,OAAO,CAAC,SAAiB;AAC7B,YAAQ,IAAI;AACZ,QAAI,CAAC,QAAQ,QAAS,QAAO,OAAO,IAAI;AACxC,YAAQ,UAAU;AAClB,aAAS,MAAM,IAAI;AAAA,EACrB;AAEA,QAAM,iBAAiB,YAAY;AACjC,UAAM,OAAO,WAAW;AACxB,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,EAAE,QAAQ,IAAI,MAAM,IAAI,KAAK,EAAE,KAAK;AAC1C,aAAO,OAAO;AAAA,IAChB,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAc,CAAC,SAAS;AACtB,YAAI,KAAM,SAAQ,eAAe,CAAC;AAAA,iBAGzB,KAAK,KAAK,MAAM,MAAO,QAAO,KAAK,KAAK,CAAC;AAClD,gBAAQ,IAAI;AAAA,MACd;AAAA,MAEA;AAAA,sDAAC,iBAAc,YAAY,QAAQ;AAAA,QACnC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,WAAU;AAAA,YAEV;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO;AAAA,kBACP;AAAA,kBACA,YAAY;AAAA,kBACZ,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,kBACvC,QAAQ,MAAM,SAAS,SAAS,OAAO,KAAK,KAAK,CAAC;AAAA,kBAClD,WAAW,CAAC,MAAM;AAChB,wBAAI,EAAE,QAAQ,QAAS,QAAO,KAAK,KAAK,CAAC;AACzC,wBAAI,EAAE,QAAQ,SAAU,SAAQ,KAAK;AAAA,kBACvC;AAAA,kBACA,WAAU;AAAA;AAAA,cACZ;AAAA,cACA,8CAAC,kBAAe,SAAO,MACrB;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,OAAO,OAAO;AAAA,kBACd,cAAY,OAAO;AAAA,kBACnB,WAAU;AAAA,kBAEV;AAAA,kEAAC,gBAAa;AAAA,oBACb,CAAC,SAAS,8CAAC,UAAK,WAAU,oBAAmB,OAAO,EAAE,YAAY,MAAM,GAAG;AAAA;AAAA;AAAA,cAC9E,GACF;AAAA;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAM;AAAA,YACN,YAAY;AAAA,YACZ,WAAU;AAAA,YAEV;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,sBAAsB,CAAC,MAAM;AAC3B,4BAAQ,UAAU;AAGlB,oBAAC,EAAE,OAAmB,kBAAkB,EAAE,SAAS;AAAA,kBACrD;AAAA,kBAEC,wBACC,8CAAC,6CAAoB,OAAO,KAAK,UAAU,MAAM,WAAU,sBAAqB,IAEhF,8CAAC,wCAAe,OAAO,KAAK,UAAU,MAAM,WAAU,sBAAqB;AAAA;AAAA,cAE/E;AAAA,cAEA,+CAAC,SAAI,WAAU,6BACb;AAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAU;AAAA,oBACV,OAAO,EAAE,YAAY,IAAI;AAAA;AAAA,gBAC3B;AAAA,gBACA;AAAA,kBAAC;AAAA;AAAA,oBACC,OAAO;AAAA,oBACP,YAAY;AAAA,oBACZ,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,oBACvC,WAAW,CAAC,MAAM,EAAE,QAAQ,WAAW,OAAO,KAAK,KAAK,CAAC;AAAA,oBACzD,QAAQ,MAAM,SAAS,SAAS,OAAO,KAAK,KAAK,CAAC;AAAA,oBAClD,WAAU;AAAA;AAAA,gBACZ;AAAA,gBACC,WAAW,KACV;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM,KAAK,eAAe;AAAA,oBACnC,OAAO,OAAO;AAAA,oBACd,cAAY,OAAO;AAAA,oBACnB,WAAU;AAAA,oBAEV,wDAAC,gCAAQ,WAAU,UAAS;AAAA;AAAA,gBAC9B;AAAA,iBAEJ;AAAA,cAEC,KAAK,SAAS,KACb;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,OAAO;AAAA,kBACd,QAAQ;AAAA,kBACR,SAAS;AAAA,kBACT,QAAQ;AAAA;AAAA,cACV;AAAA,cAEF;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,OAAO;AAAA,kBACd,QAAQ;AAAA,kBACR,SAAS;AAAA,kBACT,QAAQ;AAAA,kBACR,kBAAkB,OAAO;AAAA;AAAA,cAC3B;AAAA;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,SAAS;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,SACE,+CAAC,SACC;AAAA,kDAAC,OAAE,WAAU,4EACV,iBACH;AAAA,IACA,+CAAC,SAAI,WAAU,0BACZ;AAAA,aAAO,IAAI,CAAC,UACX;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,SAAS,MAAM,OAAO,KAAK;AAAA,UAC3B,WAAW;AAAA,YACT;AAAA,YACA,MAAM,YAAY,MAAM,QAAQ,YAAY,KAAK;AAAA,UACnD;AAAA,UACA,OAAO,EAAE,YAAY,MAAM;AAAA;AAAA,QARtB;AAAA,MASP,CACD;AAAA,MACA,oBACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,OAAO;AAAA,UACP,SAAS,MAAM,OAAO,aAAa;AAAA,UACnC,WAAU;AAAA,UAEV,wDAAC,gBAAa;AAAA;AAAA,MAChB;AAAA,OAEJ;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe;AACtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,QACL,iBAAiB;AAAA,QACjB,gBAAgB;AAAA,MAClB;AAAA;AAAA,EACF;AAEJ;AAMO,SAAS,MAAM,OAAe,EAAE,SAAS,MAAM,IAAI,CAAC,GAAkB;AAC3E,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAM,QAAQ,oCAAoC,KAAK,CAAC;AACxD,MAAI,MAAO,QAAO,IAAI,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AACrF,MAAI,iBAAiB,KAAK,CAAC,EAAG,QAAO;AACrC,MAAI,iBAAiB,KAAK,CAAC,EAAG,QAAO,UAAU,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,GAAG,CAAC,IAAI;AAClF,QAAM,MAAM,+DAA+D,KAAK,CAAC;AACjF,MAAI,KAAK;AACP,UAAM,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC;AAC7E,UAAM,YAAY,IAAI,CAAC;AACvB,UAAM,QAAQ,WAAW,SAAS,GAAG,IAAI,WAAW,SAAS,IAAI,MAAM,OAAO,aAAa,CAAC;AAC5F,QAAI,CAAC,UAAU,QAAQ,EAAG,UAAS,KAAK,KAAK,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC;AAC5E,WAAO,IAAI,SAAS,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AAAA,EAC1E;AACA,SAAO;AACT;;;AC7SA,IAAAC,uBAA6B;AA0BnB,IAAAC,uBAAA;AAtBK,SAAR,WAA4B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,MAAM,SAAS,KAAK;AAC1B,SACE,+CAAC,SAAI,WAAU,YACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,QACd,WAAU;AAAA,QAET;AAAA,gBACC,8CAAC,SAAI,KAAK,KAAK,KAAI,IAAG,WAAU,0CAAyC,IAEzE,+CAAC,UAAK,WAAU,oFACd;AAAA,0DAAC,kCAAU,WAAU,UAAS;AAAA,YAC7B,OAAO;AAAA,aACV;AAAA,UAED,OACC,8CAAC,UAAK,WAAU,6IACb,iBAAO,aACV;AAAA;AAAA;AAAA,IAEJ;AAAA,IACC,SACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,QACd,cAAY,OAAO;AAAA,QACnB,WAAU;AAAA,QAEV,wDAAC,0BAAE,WAAU,UAAS;AAAA;AAAA,IACxB;AAAA,KAEJ;AAEJ;AAMO,SAAS,SAAS,OAA8B;AACrD,QAAM,IAAI,MAAM,KAAK;AACrB,MAAI,CAAC,KAAK,MAAM,OAAQ,QAAO;AAC/B,QAAM,MAAM,iCAAiC,KAAK,CAAC;AACnD,MAAI,IAAK,QAAO,IAAI,CAAC,KAAK;AAC1B,SAAO,cAAc,KAAK,CAAC,IAAI,OAAO;AACxC;;;AjBlDA,oBAAsC;AA6OhC,IAAAC,uBAAA;AA5LN,IAAM,YAAY;AAClB,IAAM,YAAY;AAGlB,IAAM,aAAqC;AAAA,EACzC,WAAW;AAAA,EACX,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,aAAa;AACf;AAEA,SAAS,MAAM,GAAsB;AACnC,SAAO,OAAO,EAAE,IAAI,SAAS,KAAK,EAAE,IAAI,MAAM,KAAK,EAAE;AACvD;AAGA,IAAM,eACJ;AAGF,IAAM,YACJ;AAEF,SAAS,UAAU,GAAuB;AACxC,SAAO,MAAM,CAAC,MAAM;AACtB;AAEA,SAAS,cAAc,GAAuB;AAC5C,QAAM,IAAI,MAAM,CAAC;AACjB,SAAO,MAAM,gBAAgB,KAAK;AACpC;AAEA,SAAS,WAAW,GAAc,IAA2B;AAC3D,QAAM,WAAW,OAAO,EAAE,cAAc,EAAE,WAAW,KAAK,EAAE;AAC5D,SAAO,SAAS,MAAM,EAAE,IAAI,CAAC,KAAK;AACpC;AAEA,SAAS,YAAY,GAA6B;AAChD,SAAO,WAAW,GAAG,SAAS;AAChC;AAEA,SAAS,WAAW,GAAc,QAA+B;AAC/D,QAAM,WAAW,WAAW,GAAG,WAAW,QAAQ,YAAY,SAAS;AACvE,MAAI,SAAU,QAAO;AACrB,QAAM,WAAW,OAAO,EAAE,cAAc,EAAE,WAAW,KAAK,EAAE;AAC5D,QAAM,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE;AACjD,IAAE,cAAc,EAAE,aAAa,GAAG,QAAQ,IAAI,MAAM,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;AACrE,SAAO;AACT;AAEA,SAAS,eAAe,GAA4C;AAClE,MAAI,MAA6B;AACjC,SAAO,KAAK;AACV,QAAI,UAAU,GAAG,EAAG,QAAO;AAC3B,UAAM,IAAI,OAAO,KAAK;AAAA,EACxB;AACA,SAAO;AACT;AAGA,IAAM,aAAqC;AAAA,EACzC,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,cAAc;AAChB;AAEA,SAAS,UAAU,GAAsB;AACvC,QAAM,IAAI,MAAM,CAAC;AACjB,SAAO,WAAW,CAAC,KAAK,EAAE,QAAQ,QAAQ,EAAE,KAAK;AACnD;AAEA,SAAS,YAAY,GAAsB;AACzC,QAAM,OAAO,WAAW,MAAM,CAAC,CAAC,KAAK,MAAM,CAAC;AAC5C,QAAM,OAAO,OAAQ,EAAE,QAAQ,GAA+B,eAAe,EAAE,EAC5E,QAAQ,QAAQ,GAAG,EACnB,KAAK,EACL,MAAM,GAAG,EAAE;AACd,SAAO,OAAO,GAAG,IAAI,WAAM,IAAI,WAAM;AACvC;AAEA,IAAM,YAAY;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,IAAM,YAAY,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAG7D,SAAS,mBAAmB,IAA6B,OAAqC;AAC5F,QAAM,MAAM,IAAI;AAChB,QAAM,MAAM,KAAK,eAAe;AAChC,MAAI,CAAC,OAAO,CAAC,OAAO,IAAI,eAAe,KAAK,IAAI,YAAa;AAC7D,QAAM,QAAQ,IAAI,WAAW,CAAC;AAC9B,QAAM,OAAO,IAAI,cAAc,MAAM;AACrC,SAAO,OAAO,KAAK,OAAO,KAAK;AAC/B,MAAI;AACF,SAAK,YAAY,MAAM,gBAAgB,CAAC;AACxC,UAAM,WAAW,IAAI;AACrB,QAAI,gBAAgB;AACpB,UAAM,KAAK,IAAI,YAAY;AAC3B,OAAG,mBAAmB,IAAI;AAC1B,QAAI,SAAS,EAAE;AAAA,EACjB,QAAQ;AAAA,EAER;AACF;AAUA,SAAS,iBAAiB,QAAgB,QAAsB;AAC9D,QAAM,KAAK,OAAO;AAClB,QAAM,OAAQ,GAAG,QAAQ,MAAM,EAAU;AAMzC,QAAM,iBAAiB,MAAgB;AACrC,UAAM,SAAS,GAAG,OAAO,QAAQ,CAAC,IAAI,OAAO,OAAO,KAAK,EAAE;AAC3D,UAAM,SAAS,oBAAI,IAAoB;AACvC,UAAM,aACJ;AACF,eAAW,SAAS,OAAO,SAAS,UAAU,GAAG;AAC/C,YAAM,MAAM,MAAM,MAAM,CAAC,GAAI,EAAE,QAAQ,KAAK,CAAC;AAC7C,UAAI,IAAK,QAAO,IAAI,MAAM,OAAO,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,IACrD;AACA,WAAO,CAAC,GAAG,OAAO,QAAQ,CAAC,EACxB,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG;AAAA,EACvB;AAIA,QAAM,WAAW,CAAC,aAChB,SAAS,WAAW,EAAE,UAAU,KAAK,CAAC,IAAI,OAAO,SAAS,WAAW,KAAK,EAAE,IAAI;AAGlF,QAAM,YAAY,CAAC,UACjB,KAAK,OAAO;AAAA,IACV,OAAmB,EAAE,SAAS,GAAsB;AAClD,YAAM,KAAK,SAAS,cAAc,KAAK;AAGvC,SAAG,iBAAiB,UAAU,CAAC,UAAU,MAAM,gBAAgB,CAAC;AAChE,YAAM,WAAO,0BAAW,EAAE;AAC1B,WAAK,SAAS;AACd,WAAK,WAAW,MAAM,KAAK,OAAO,MAAM,UAAU,SAAS,QAAQ,CAAC,CAAC;AACrE,WAAK,SAAS;AACd,aAAO;AAAA,IACT;AAAA,IACA,SAAmB;AACjB,WAAK,WAAW;AAAA,IAClB;AAAA,IACA,UAAoB;AAIlB,YAAM,OAAO,KAAK;AAClB,WAAK,SAAS;AACd,WAAK,WAAW;AAChB,iBAAW,MAAM,MAAM,QAAQ,CAAC;AAAA,IAClC;AAAA,EACF,CAAC;AAEH,KAAG,QAAQ,SAAS;AAAA,IAClB,MAAM,UAAU,CAAC,UAAU,UACzB;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,aAAa,OAAO,SAAS,kBAAkB,KAAK,EAAE;AAAA,QACtD;AAAA,QACA;AAAA,QACA,UAAU,CAAC,MAAM,YAAY,SAAS,QAAQ,MAAM,EAAE,QAAQ,CAAC;AAAA;AAAA,IACjE,CACD;AAAA,EACH,CAAU;AAIV,KAAG,QAAQ,QAAQ;AAAA,IACjB,MAAM,UAAU,CAAC,UAAU,UACzB;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,SAAS,MAAM,SAAS,QAAQ,EAAE;AAAA,QAClC,UAAU,MAAM;AACd,gBAAM,KAAK,OAAO;AAClB,aAAG,KAAK;AAAA,YACN,OAAO,CAAC,OAAO;AAAA,YACf,SAAS,SAAS,KAAK,KAAK;AAAA,YAC5B,OAAO,OAA0C,UAAoB;AACnE,oBAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,MAAM,OAAO;AAC7D,uBAAS,QAAQ,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC;AAC5C,kBAAI,SAAU,IAAG,MAAM;AAAA,YACzB;AAAA,UACF,CAAU;AAAA,QACZ;AAAA;AAAA,IACF,CACD;AAAA,EACH,CAAU;AACZ;AAGA,SAAS,cAAc,QAAgB,QAAsB;AAC3D,QAAM,MAAM,OAAO;AACnB,MAAI,CAAC,OAAO,IAAI,gBAAiB;AACjC,MAAI,kBAAkB;AACtB,QAAM,MAAM,CAAC,QAAa,QACvB,OAAO,KAAK,cAAc,GAAG,GAAmD,SAAS;AAE5F,MAAI,IAAI,YAAY;AAAA,IAClB,MAAM,yCAAyC,OAAO,OAAO,sBAAsB,OAAO,OAAO,YAAY,UAAU;AAAA,MACrH,CAAC,MAAM,kBAAkB,CAAC,KAAK,CAAC;AAAA,IAClC,EAAE,KAAK,EAAE,CAAC;AAAA,IACV,OAAO;AAAA,IACP,QAAQ,CAAC,GAAQ,WAAgB;AAC/B,YAAM,IAAI,IAAI,QAAQ,QAAQ;AAC9B,UAAI,EAAG,GAAE,KAAK,YAAY,CAAC;AAAA,IAC7B;AAAA,EACF,CAAC;AAED,MAAI,IAAI,YAAY;AAAA,IAClB,MAAM,yCAAyC,OAAO,OAAO,sBAAsB,OAAO,OAAO,YAAY,UAAU;AAAA,MACrH,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC;AAAA,IACpC,EAAE,KAAK,EAAE,CAAC;AAAA,IACV,OAAO;AAAA,IACP,QAAQ,CAAC,GAAQ,WAAgB;AAC/B,YAAM,IAAI,IAAI,QAAQ,QAAQ;AAC9B,UAAI,EAAG,oBAAmB,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAAA,IACjD;AAAA,EACF,CAAC;AAED,MAAI,IAAI,aAAa;AAAA,IACnB,MAAM,oDAAoD,OAAO,YAAY;AAAA,IAC7E,OAAO;AAAA,IACP,QAAQ,CAAC,GAAQ,WAAgB,EAAE,KAAK,aAAa,IAAI,QAAQ,OAAO,CAAC;AAAA,EAC3E,CAAC;AAED,MAAI,IAAI,eAAe;AAAA,IACrB,MAAM,oDAAoD,OAAO,kBAAkB;AAAA,IACnF,OAAO;AAAA,IACP,QAAQ,CAAC,GAAQ,WAAgB,EAAE,KAAK,eAAe,IAAI,QAAQ,OAAO,CAAC;AAAA,EAC7E,CAAC;AACH;AAGe,SAAR,YAA6B,EAAE,OAAO,SAAS,iBAAiB,kBAAkB,GAAU;AAEjG,QAAM,YAAY,iBAAiB;AACnC,QAAM,SAAS,UAAU;AAEzB,QAAM,iBAAa,sBAAO,KAAK;AAC/B,QAAM,oBAAgB,sBAAmB,MAAM;AAC/C,QAAM,qBAAiB,sBAAO,oBAAI,IAA8B,CAAC;AACjE,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,IAAI;AAC3C,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAsB,QAAQ;AAEpE,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAA+B,IAAI;AAG7E,QAAM,sBAAkB,sBAAsC,OAAO;AAErE,QAAM,gBAAY,sBAAsB,IAAI;AAG5C,QAAM,EAAE,SAAS,aAAa,QAAQ,UAAU,UAAU,IAAI,aAAa;AAC3E,QAAM,kBAAc,sBAAO,KAAK;AAChC,QAAM,oBAAgB,sBAAuC,YAAY;AAAA,EAAC,CAAC;AAC3E,+BAAU,MAAM,KAAK,cAAc,QAAQ,WAAW,GAAG,CAAC,WAAW,CAAC;AACtE,+BAAU,MAAM,MAAM,UAAU,KAAK,GAAG,CAAC,SAAS,CAAC;AACnD,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAsB,CAAC,CAAC;AAEpD,QAAM,CAAC,kBAAkB,mBAAmB,QAAI,wBAAwB,IAAI;AAE5E,QAAM,CAAC,QAAQ,SAAS,QAAI;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,WAAmB;AACzC,cAAU,UAAU;AAGpB,UAAM,cAAc,CAAC,MAA6B;AAChD,YAAM,QAAqB,CAAC;AAC5B,UAAI,MAA6B;AACjC,aAAO,KAAK;AACV,cAAM,IAAI,MAAM,GAAG;AACnB,YAAI,CAAC,KAAK,MAAM,aAAa,MAAM,aAAa,MAAM,UAAW;AACjE,cAAM,QAAQ,GAAG;AACjB,cAAM,IAAI,OAAO,KAAK;AAAA,MACxB;AACA,gBAAU,KAAK;AAAA,IACjB;AAGA,UAAM,YAAY,CAAC,QAAuC;AACxD,UAAI;AACJ,aAAO,WAAW,GAAG,MAAM,CAAC,MAAM;AAChC,YAAI,CAAC,UAAU,EAAE,IAAI,SAAS,MAAM,OAAO,EAAE,IAAI,MAAM,MAAM,KAAM,SAAQ;AAAA,MAC7E,CAAC;AACD,aAAO;AAAA,IACT;AACA,UAAM,YAAY,MAAc;AAC9B,YAAM,OAAO,UAAU,SAAS;AAChC,aAAO,OAAQ,MAAM,gBAAgB,GAA+B,SAAS,OAAO;AAAA,IACtF;AACA,UAAM,WAAW,OAAoB;AAAA,MACnC,QAAQ,OAAO,UAAU;AAAA,MACzB,SAAS,CAAC,YAAY,WAAW,OAAO,YAAY,QAAQ;AAAA,MAC5D,SAAS,CAAC,YAAY,WAAW,OAAO,YAAY,QAAQ;AAAA,MAC5D,YAAY,cAAc;AAAA,MAC1B,MAAM,KAAK,MAAO,OAAO,OAAO,UAAU,KAA4B,GAAG;AAAA,MACzE,cAAc,UAAU;AAAA,MACxB,UAAU,YAAY;AAAA,IACxB;AACA,UAAM,cAAc,MAAM;AACxB,YAAM,IAAI,SAAS;AACnB,qBAAe,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AAAA,IAC5C;AACA,UAAM,UAAU,CAAC,MAAkB;AACjC,oBAAc,UAAU;AACxB,kBAAY;AAAA,IACd;AAIA,UAAM,WAAW,CAAC,MAAmD;AACnE,YAAM,UAAU,eAAe,CAAC;AAChC,UAAI,CAAC,WAAW,CAAC,EAAG,QAAO;AAC3B,YAAM,YAAY,WAAW,SAAS,KAAK;AAC3C,UAAI,UAAU,CAAC,GAAG;AAChB,eAAO,EAAE,WAAW,UAAU,MAAM,aAAa,KAAK;AAAA,MACxD;AACA,YAAM,WAAW,WAAW,GAAG,KAAK;AACpC,aAAO,EAAE,WAAW,UAAU,aAAa,YAAY,CAAC,EAAE;AAAA,IAC5D;AAIA,UAAM,WAAW,CAAC,MAAiB;AACjC,UAAI,UAAU,CAAC,EAAG,YAAW,GAAG,KAAK;AACrC,UAAI,CAAC,cAAc,CAAC,EAAG;AACvB,YAAM,UAAU,CAAC,GAAK,EAAE,IAAI,SAAS,KAAgC,CAAC,CAAE;AACxE,UAAI,MAAM,CAAC,MAAM,cAAc,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,mBAAmB,GAAG;AACtF,gBAAQ,KAAK;AAAA,UACX,SAAS;AAAA,UACT,OAAO;AAAA,UACP,YAAY,EAAE,OAAO,OAAO,YAAY;AAAA,QAC1C,CAAU;AACV,UAAE,IAAI,WAAW,OAAgB;AAAA,MACnC;AACA,UAAI,CAAC,QAAQ,KAAK,CAAC,MAAM,EAAE,YAAY,eAAe,GAAG;AACvD,gBAAQ,KAAK;AAAA,UACX,SAAS;AAAA,UACT,OAAO;AAAA,UACP,YAAY,EAAE,OAAO,gCAAgC,aAAa,WAAW;AAAA,QAC/E,CAAU;AACV,UAAE,IAAI,WAAW,OAAgB;AAAA,MACnC;AAAA,IACF;AAGA,WAAO,SAAS,IAAI,qBAAqB;AAAA,MACvC,IAAI,IAAY;AACd,cAAM,SAAS,GAAG,YAAY;AAC9B,YAAI,CAAC,UAAU,MAAM,MAAM,MAAM,WAAY;AAC7C,cAAM,KAAK,GAAG;AACd,WAAG,KAAK;AAAA,UACN;AAAA,UACA,OAAO,CAAC,OAAO;AAAA,UACf,OAAO,OAAiC,UAAoB;AAC1D,mBAAO,IAAI,EAAE,KAAK,MAAM,OAAO,EAAE,CAAC;AAClC,gBAAI,SAAU,IAAG,MAAM;AAAA,UACzB;AAAA,QACF,CAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAED,WAAO,SAAS,IAAI,iBAAiB;AAAA,MACnC,IAAI,IAAY;AACd,cAAM,SAAS,SAAS,GAAG,YAAY,KAAK,MAAS;AACrD,YAAI,OAAQ,kBAAiB,MAAM;AAAA,MACrC;AAAA,IACF,CAAC;AAED,kBAAc,QAAQ,MAAM;AAC5B,qBAAiB,QAAQ,MAAM;AAG/B,UAAM,gBAAgB,CAAC,UAA0B;AAC/C,YAAM,IAAI,MAAM,YAAY;AAC5B,UAAI,mBAAmB,KAAK,CAAC,EAAG,QAAO;AACvC,UAAI,4CAA4C,KAAK,CAAC,EAAG,QAAO;AAChE,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,OAAO;AAClB,OAAG,OAAO,EAAE;AAAA,MACV,CAAC,MAA4E;AAC3E,UAAE,IAAI,YAAY,cAAc,EAAE,IAAI,OAAO,KAAK,EAAE,CAAC;AAAA,MACvD;AAAA,IACF;AACA,OAAG,OAAO;AAEV,WAAO,GAAG,iBAAiB,QAAQ;AAInC,WAAO,GAAG,QAAQ,MAAM;AACtB,YAAM,OAAO,OAAO,OAAO,QAAQ;AACnC,YAAM;AAAA,QACJ;AAAA,QACA,MAAM;AACJ,0BAAgB,UAAU;AAAA,QAC5B;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,GAAG,sBAAsB,CAAC,MAAiB;AAChD,UAAI,gBAAgB,YAAY,SAAU,gBAAe,UAAU;AACnE,sBAAgB,UAAU;AAC1B,kBAAY,CAAC;AACb,0BAAoB,KAAK,MAAM,CAAC,MAAM,aAAa,OAAO,EAAE,IAAI,KAAK,KAAK,EAAE,IAAI,IAAI;AAGpF,UAAI,GAAG,IAAI,MAAM,MAAM,cAAc,CAAC,EAAE,SAAS,KAAK,GAAG;AACvD,UAAE;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,YACP,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA,EAAE,IAAI,EAAE;AAAA,QACV;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,GAAG,wBAAwB,MAAM;AACtC,YAAM,OAAO,OAAO,YAAY;AAChC,kBAAY,IAAI;AAChB;AAAA,QACE,QAAQ,MAAM,IAAI,MAAM,aAAa,OAAO,KAAK,IAAI,KAAK,KAAK,EAAE,IAAI;AAAA,MACvE;AAAA,IACF,CAAC;AACD,WAAO,GAAG,wBAAwB,CAAC,MAAiB;AAClD,UAAI,MAAM,OAAO,YAAY,EAAG,qBAAoB,OAAO,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;AAAA,IAChF,CAAC;AAID,QAAI,SAAS;AAEb,UAAM,OAAO,YAAY;AAEvB,YAAM,OAAO,OAAO,WAAW,GAAG,QAAQ;AAE1C,YAAM,MAAM,cAAc;AAC1B,YAAM,OAAO,OAAO,QAAQ;AAC5B,UAAI,SAAS,OAAQ;AACrB,cAAQ,QAAQ;AAChB,UAAI;AACF,cAAM,UAAU,KAAK,OAAO,EAAE,MAAM,aAAa,OAAO,eAAe,EAAE,CAAC;AAC1E,iBAAS;AACT,gBAAQ,OAAO;AAAA,MACjB,SAAS,GAAG;AACV,gBAAQ,OAAO;AACf,6BAAM,MAAM,QAAQ,CAAC,KAAK,OAAO,oBAAoB,EAAE,IAAI,YAAY,KAAK,EAAE,CAAC;AAC/E,cAAM;AAAA,MACR;AAAA,IACF;AAEA,UAAM,QAAQ,gBAAgB,MAAM,IAAI;AAExC,WAAO,GAAG,UAAU,MAAM;AACxB,kBAAY;AACZ,UAAI,CAAC,WAAW,WAAW,CAAC,YAAY,QAAS,OAAM,SAAS;AAAA,IAClE,CAAC;AACD,WAAO,GAAG,iBAAiB,WAAW;AAItC,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,MAAM,SAAS;AACxB,aAAO,GAAG,sBAAsB,EAAE,IAAI,CAAC,EAAE,QAAQ,MAAwC;AACvF,YAAI,YAAY,QAAS,SAAQ,QAAQ;AAAA,MAC3C,CAAC;AAAA,IACH;AACA,UAAM,OAAO,CAAC,OAAgB;AAC5B,kBAAY,UAAU;AACtB,gBAAU,EAAE;AACZ,kBAAY;AAAA,IACd;AACA,kBAAc,UAAU,OAAO,OAAO;AACpC,UAAI,IAAI;AACN,aAAK,IAAI;AACT,cAAM,OAAO;AACb,eAAO,OAAO,CAAC,CAAC;AAEhB,eAAO,aAAa,MAAM;AAC1B,kBAAU,IAAI;AACd;AAAA,MACF;AACA,UAAI;AACF,cAAM,OAAO;AAAA,MACf,SAAS,GAAG;AAEV,gBAAQ,MAAM,CAAC;AACf,6BAAM,MAAM,OAAO,oBAAoB;AAAA,UACrC,IAAI,UAAU,KAAK;AAAA,UACnB,UAAU;AAAA,UACV,QAAQ,EAAE,OAAO,OAAO,OAAO,SAAS,MAAM,KAAK,cAAc,QAAQ,KAAK,EAAE;AAAA,QAClF,CAAC;AACD;AAAA,MACF;AACA,WAAK,KAAK;AAAA,IACZ;AAKA,UAAM,SAAS,MACb,MAAM,IAAI,YAAY;AACpB,YAAM,QAAQ,MAAM,UAAU,IAAI,KAAK;AACvC,UAAI,MAAM,SAAS,OAAQ,UAAS,MAAM,IAAI;AAAA,IAChD,CAAC;AAEH,UAAM,WAAW,CAAC,SAAiB;AAGjC,iBAAW,UAAU;AACrB,UAAI;AACF,cAAM,OAAO;AACb,iBAAS;AACT,eAAO,cAAc,QAAQ,yBAAY;AACzC,eAAO,WAAW,GAAG,KAAK,YAAY,EAAE,QAAQ,QAAQ;AACxD,mBAAW,KAAK,OAAO,KAAK,UAAU,GAAG;AACvC,iBAAO,WAAW,GAAG,KAAK,CAAC,EAAE,QAAQ,QAAQ;AAAA,QAC/C;AAAA,MACF,UAAE;AACA,mBAAW,UAAU;AAAA,MACvB;AAAA,IACF;AAOA,UAAM,wBAAwB,MAAM;AAClC,YAAM,OAAO,OAAO,OAAO,YAAY;AACvC,UAAI,CAAC,QAAQ,KAAK,eAAe,kBAAkB,EAAG;AAEtD,YAAM,SACJ,iBAAiB,SAAS,eAAe,EACtC,iBAAiB,sBAAsB,EACvC,KAAK,KAAK;AAEf,YAAM,QAAQ,KAAK,cAAc,OAAO;AACxC,YAAM,KAAK;AACX,YAAM,cAAc;AAAA;AAAA,yDAE+B,MAAM;AAAA,yDACN,MAAM;AAAA;AAAA;AAAA,kDAGb,MAAM;AAAA;AAAA;AAAA;AAAA;AAKlD,WAAK,KAAK,YAAY,KAAK;AAAA,IAC7B;AAEA,UAAM,YAAY,CAAC,cAA+C;AAChE,YAAM,OAAO,OACV,WAAW,GACV,KAAK,YAAY,EAClB,KAAK,CAAC,MAAM,YAAY,CAAC,MAAM,SAAS;AAC3C,aAAQ,MAAM,QAAQ,KAAiC;AAAA,IACzD;AAGA,UAAM,eAAe,MAAc;AACjC,UAAI;AAEF,cAAM,MAAM,OAAO,WAAW,mBAAmB;AACjD,eAAO,KAAK,QAAQ,OAAO,QAAQ;AAAA,MACrC,QAAQ;AACN,eAAO,OAAO,QAAQ;AAAA,MACxB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,MAAM,MAAM,UAAU,IAAI,KAAK;AACrC,eAAS,IAAI,IAAI;AAAA,IACnB,SAAS,GAAG;AACV,2BAAM,MAAM,OAAO,kBAAkB;AACrC,cAAQ,MAAM,CAAC;AAAA,IACjB,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAEA,YAAQ;AAAA,MACN,SAAS,MAAM,OAAO,QAAQ;AAAA,MAC9B,iBAAiB;AAAA,MACjB,WAAW,MAAM,MAAM,MAAM;AAAA,MAC7B,cAAc;AAAA,MACd,kBAAkB,CAAC,WAAW,OAAO;AACnC,8BAAsB;AACtB,cAAM,KAAK,UAAU,SAAS;AAC9B,YAAI,CAAC,GAAI;AACT,WAAG,UAAU,OAAO,iBAAiB,EAAE;AAAA,MACzC;AAAA,MACA,YAAY,MACV,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,OAAO;AAAA,QACtC,IAAI,OAAO,EAAE,EAAE;AAAA,QACf,MAAM,EAAE,QAAQ,KAAK,OAAO,EAAE,EAAE;AAAA,MAClC,EAAE;AAAA,MACJ,WAAW,CAAC,SAAS;AACnB,eAAO,UAAU,IAAI;AACrB,oBAAY;AAAA,MACd;AAAA,MACA,MAAM,MAAM;AACV,YAAI,YAAY,QAAS;AACzB,eAAO,YAAY,KAAK;AACxB,oBAAY;AAAA,MACd;AAAA,MACA,MAAM,MAAM;AACV,YAAI,YAAY,QAAS;AACzB,eAAO,YAAY,KAAK;AACxB,oBAAY;AAAA,MACd;AAAA,MACA,SAAS,CAAC,MAAM;AACd,eAAO,OAAO,UAAU,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC;AACtD,oBAAY;AAAA,MACd;AAAA,MACA,iBAAiB,CAAC,MAAM;AACtB,YAAI,YAAY,QAAS;AACzB,kBAAU,SAAS,GAAG,cAAc,EAAE,OAAO,EAAE,CAAC;AAChD,oBAAY;AAAA,MACd;AAAA,MACA,eAAe,CAAC,OAAO;AACrB,uBAAe,QAAQ,IAAI,EAAE;AAC7B,WAAG,SAAS,CAAC;AACb,eAAO,MAAM,eAAe,QAAQ,OAAO,EAAE;AAAA,MAC/C;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SACE;AAAA,IAAC,cAAAC;AAAA,IAAA;AAAA,MACC,UAAU,gBAAAC;AAAA,MACV,WAAU;AAAA,MACV,SAAS;AAAA,QACP,QAAQ;AAAA,QACR,gBAAgB;AAAA,QAChB,aAAa;AAAA,QACb,QAAQ,EAAE,UAAU,CAAC,EAAE;AAAA,QACvB,cAAc,EAAE,UAAU,cAAc;AAAA,QACxC,cAAc,EAAE,UAAU,cAAc;AAAA,QACxC,cAAc,EAAE,UAAU,cAAc;AAAA,QACxC,cAAc,EAAE,UAAU,cAAc;AAAA,QACxC,cAAc;AAAA;AAAA,UAEZ,eAAe;AAAA;AAAA;AAAA,UAGf,QAAQ;AAAA,YACN,MAAM,CAAC,UAKL,UAAU;AAAA,cACR,SACE,MAAM,SAAS,YACd,OAAO,MAAM,SAAS,QAAQ,IAAI,KAAK,KAAK,EAAE,KAAK;AAAA,cACtD,QAAQ,CAAC,QAAQ,MAAM,OAAO,KAAK,IAAI;AAAA,YACzC,CAAC;AAAA,YACH,OAAO,MAAM,UAAU,IAAI;AAAA,UAC7B;AAAA,QACF;AAAA,QACA,eAAe;AAAA,UACb,SAAS;AAAA,YACP,EAAE,IAAI,WAAW,MAAM,WAAW,OAAO,GAAG;AAAA,YAC5C,EAAE,IAAI,UAAU,MAAM,UAAU,OAAO,SAAS,YAAY,QAAQ;AAAA,UACtE;AAAA,QACF;AAAA,QACA,SAAS,CAAC,qBAAAC,OAAY;AAAA,MACxB;AAAA,MACA;AAAA,MAGA,yDAAC,SAAI,WAAU,uCAAsC,OAAO,UACzD;AAAA,oBACC,8CAAC,SAAI,WAAU,6FACb,yDAAC,UAAK,WAAU,wFACd;AAAA,wDAAC,WAAQ;AAAA,UAAE;AAAA,UAAE,OAAO;AAAA,WACtB,GACF;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,cAAc;AAAA,YACd,kBAAkB,MAAO,gBAAgB,UAAU;AAAA,YACnD;AAAA,YACA,eAAe,MAAM,UAAU,SAAS,WAAW,mBAAmB;AAAA;AAAA,QACxE;AAAA,QACA,+CAAC,SAAI,WAAU,iDACb;AAAA,yDAAC,SAAI,WAAU,2BACb;AAAA,0DAAC,wBAAO,WAAU,UAAS;AAAA,YAE3B;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA;AAAA,gBACA,mBAAmB,MAAM,iBAAiB,IAAI;AAAA,gBAC9C,eAAe;AAAA,gBACf;AAAA;AAAA,YACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,WAAW;AAAA,gBACjB,YAAY,QAAQ;AAAA,gBACpB,UAAU,CAAC,QAAQ;AAEjB,sBAAI,CAAC,YAAY,QAAS,SAAQ,OAAO,GAAG;AAC5C,4BAAU,IAAI;AAAA,gBAChB;AAAA,gBAGA,SAAS,MAAM;AACb,4BAAU,IAAI;AACd,4BAAU,SAAS,aAAa,MAAM;AAAA,gBACxC;AAAA;AAAA,YACF;AAAA,YACC,WACC,+CAAC,SAAI,WAAU,kGACb;AAAA,4DAAC,WAAQ;AAAA,cAAE;AAAA,eACb;AAAA,aAEJ;AAAA,UACA,8CAAC,cAAW,QAAgB,UAAU,CAAC,MAAM,UAAU,SAAS,OAAO,CAAC,GAAG;AAAA,WAC7E;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;AAKA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAIG;AACD,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAS,WAAW;AAC5C,SACE,+CAAC,SAAI,WAAU,gCACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,QAChC,WAAU;AAAA,QAEV;AAAA,wDAAC,qCAAa,WAAW,GAAG,iCAAiC,QAAQ,WAAW,GAAG;AAAA,UAClF;AAAA;AAAA;AAAA,IACH;AAAA,IACA,8CAAC,SAAI,WAAW,GAAG,CAAC,QAAQ,QAAQ,GAAI,UAAS;AAAA,KACnD;AAEJ;AAIA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,QAAM,aAAS,8BAAe;AAC9B,QAAM,SAAS,UAAU;AACzB,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,wBAAS,KAAK;AAC1D,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,EAAE;AAEjD,QAAM,aAAa,CAAC,MAAmB;AACrC,QAAI,MAAM,cAAc,UAAU,CAAC,OAAO,YAAY,GAAG;AACvD,YAAM,UAAU,OAAO,WAAW;AAClC,YAAM,OAAO,SAAS,KAAK,SAAS,EAAE,CAAC,KAAK;AAC5C,UAAI,KAAM,QAAO,OAAO,IAAI;AAAA,IAC9B;AACA,iBAAa,CAAC;AAAA,EAChB;AAEA,QAAM,eAAe,CAAC,WAAoB;AACxC,sBAAkB,MAAM;AACxB,YAAQ,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,QAAQ,MAAM,CAAC;AAAA,EAC1D;AAIA,QAAM,eAAe,CAAC,MAAc;AAClC,mBAAe,CAAC;AAChB,UAAM,OAAO,SAAS,eAAe,YAAY;AACjD,QAAI,CAAC,KAAM;AACX,UAAM,OAAO,EAAE,KAAK,EAAE,YAAY;AAClC,SAAK,iBAA8B,YAAY,EAAE,QAAQ,CAAC,MAAM;AAC9D,YAAM,SAAS,EAAE,cAAc,kBAAkB,GAAG,eAAe,EAAE,eAAe,IACjF,YAAY,EACZ,KAAK;AACR,QAAE,MAAM,UAAU,CAAC,QAAQ,MAAM,SAAS,IAAI,IAAI,KAAK;AAAA,IACzD,CAAC;AACD,SAAK,iBAA8B,qBAAqB,EAAE,QAAQ,CAAC,QAAQ;AACzE,YAAM,aAAa,CAAC,GAAG,IAAI,iBAA8B,YAAY,CAAC,EAAE;AAAA,QACtE,CAAC,MAAM,EAAE,MAAM,YAAY;AAAA,MAC7B;AACA,UAAI,MAAM,UAAU,aAAa,KAAK;AAAA,IACxC,CAAC;AAAA,EACH;AAEA,SACE,+CAAC,SAAI,WAAU,+FACb;AAAA,kDAAC,SAAI,WAAU,qCAEX;AAAA,MACE,CAAC,UAAU,QAAQ;AAAA,MACnB,CAAC,YAAY,UAAU;AAAA,MACvB,CAAC,UAAU,QAAQ;AAAA,IACrB,EACA,IAAI,CAAC,CAAC,OAAO,KAAK,MAClB;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,SAAS,MAAM,WAAW,KAAK;AAAA,QAC/B,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,SAAS,QACL,6CACA;AAAA,QACN;AAAA,QAEC;AAAA;AAAA,MAXI;AAAA,IAYP,CACD,GACH;AAAA,IAGA,+CAAC,SAAI,WAAW,GAAG,gCAAgC,SAAS,YAAY,QAAQ,GAC9E;AAAA,qDAAC,SAAI,WAAU,6CACb;AAAA,sDAAC,+BAAO,WAAU,6FAA4F;AAAA,QAC9G;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,aAAa,EAAE,OAAO,KAAK;AAAA,YAC5C,aAAa,OAAO;AAAA,YACpB,WAAU;AAAA;AAAA,QACZ;AAAA,QACA,8CAAC,SAAI,WAAU,mKAAkK,qBAEjL;AAAA,SACF;AAAA,MACA,8CAAC,SAAI,IAAG,cAAa,WAAU,kCAAiC;AAAA,OAClE;AAAA,IAGA,+CAAC,SAAI,WAAW,GAAG,kCAAkC,SAAS,cAAc,QAAQ,GACjF;AAAA,2BAAqB,QACpB,+CAAC,SAAI,WAAU,oCACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,OAAO,OAAO;AAAA,YACd,WAAU;AAAA,YAET;AAAA,iCACC;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,KAAI;AAAA,kBACJ,WAAU;AAAA;AAAA,cACZ,IAEA,8CAAC,UAAK,WAAU,6DACd,wDAAC,kCAAU,WAAU,UAAS,GAChC;AAAA,cAEF,8CAAC,UAAK,WAAU,6IACb,iBAAO,aACV;AAAA;AAAA;AAAA,QACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS;AAAA,YAET;AAAA,4DAAC,gCAAQ;AAAA,cACR,OAAO;AAAA;AAAA;AAAA,QACV;AAAA,SACF;AAAA,MAEF,8CAAC,mBAAgB,OAAO,OAAO,YAC7B,wDAAC,SAAI,IAAG,cAAa,GACvB;AAAA,MACA,8CAAC,mBAAgB,OAAO,OAAO,OAC7B,wDAAC,SAAI,IAAG,cAAa,GACvB;AAAA,OACF;AAAA,IAIA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,GAAG,gCAAgC,SAAS,YAAY,QAAQ;AAAA,QAC3E,oBAAoB;AAAA,QAEpB;AAAA,yDAAC,WAAM,WAAU,8GACf;AAAA,0DAAC,UAAM,iBAAO,WAAU;AAAA,YACxB;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS;AAAA,gBACT,iBAAiB;AAAA,gBACjB,WAAU;AAAA;AAAA,YACZ;AAAA,aACF;AAAA,UACA,8CAAC,SAAI,IAAG,cAAa,WAAU,kCAAiC;AAAA;AAAA;AAAA,IAClE;AAAA,KACF;AAEJ;AAGA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AACF,GAGG;AACD,QAAM,SAAS,UAAU;AACzB,SACE,8CAAC,SAAI,WAAU,+GACZ,iBAAO,WAAW,IACjB,8CAAC,UAAK,WAAU,4BAA4B,iBAAO,iBAAgB,IAEnE,OAAO,IAAI,CAAC,GAAG,MACb,+CAAC,UAAa,WAAU,6BACrB;AAAA,QAAI,KAAK,8CAAC,qCAAa,WAAU,qBAAoB;AAAA,IACtD;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,SAAS,CAAC;AAAA,QACzB,WAAW;AAAA,UACT;AAAA,UACA,MAAM,OAAO,SAAS,KAAK;AAAA,QAC7B;AAAA,QAEC,oBAAU,CAAC;AAAA;AAAA,IACd;AAAA,OAXS,CAYX,CACD,GAEL;AAEJ;","names":["import_react","import_core","import_jsx_runtime","React","import_jsx_runtime","import_jsx_runtime","React","import_radix_ui","import_jsx_runtime","PopoverPrimitive","React","import_radix_ui","import_lucide_react","import_jsx_runtime","DialogPrimitive","DialogPrimitive","DialogPrimitive","DialogPrimitive","React","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_sonner","import_lucide_react","React","import_radix_ui","import_jsx_runtime","SwitchPrimitive","import_lucide_react","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_lucide_react","import_jsx_runtime","import_jsx_runtime","GjsEditor","grapesjs","grapesjsMJML"]}
1
+ {"version":3,"sources":["../src/canvas.ts","../src/components/editor/EmailEditor.tsx","../src/stores.tsx","../src/labels.ts","../src/index.ts","../src/lib/utils.ts","../src/components/ui/button.tsx","../src/components/ui/input.tsx","../src/components/ui/spinner.tsx","../src/components/ui/popover.tsx","../src/components/ui/dialog.tsx","../src/lib/refusal.ts","../src/components/ui/textarea.tsx","../src/components/comments/CanvasComments.tsx","../src/lib/save-queue.ts","../src/components/ui/switch.tsx","../src/components/editor/ImagePicker.tsx","../src/components/editor/ColorField.tsx","../src/components/editor/ImageField.tsx"],"sourcesContent":["/**\n * The GrapesJS canvas, isolated in its own entry point.\n *\n * GrapesJS touches `window` when it is imported, so this module can only be loaded in the\n * browser. Hosts should reach it through a client-only dynamic import — in Next.js that\n * is `dynamic(() => import(\"@mjml-agent-editor/editor/canvas\"), { ssr: false })`.\n */\n\nexport { default as EmailEditor } from \"./components/editor/EmailEditor.js\";\nexport type { EditorApi, EditorState, SaveStatus } from \"./components/editor/EmailEditor.js\";\n","\"use client\";\n\nimport grapesjs, { Component, Editor } from \"grapesjs\";\nimport GjsEditor, { Canvas, useEditorMaybe } from \"@grapesjs/react\";\nimport grapesjsMJML from \"grapesjs-mjml\";\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport { STARTER_MJML } from \"@mjml-agent-editor/core\";\nimport { useDocumentStore, useLabels, type CommentTarget, type EditorLabels } from \"../../index.js\";\nimport { refusal, saveToastId } from \"../../lib/refusal.js\";\nimport { createSaveQueue } from \"../../lib/save-queue.js\";\nimport { useAgentTurn } from \"../../stores.js\";\nimport { cn } from \"../../lib/utils\";\nimport { toast } from \"sonner\";\nimport { ChevronRight, ImageIcon, ImageUp } from \"lucide-react\";\n\nimport { Button } from \"../ui/button\";\nimport { Spinner } from \"../ui/spinner\";\nimport { Switch } from \"../ui/switch\";\nimport { Input } from \"../ui/input\";\nimport { Search } from \"lucide-react\";\nimport CanvasComments from \"../comments/CanvasComments.js\";\nimport ImagePicker from \"./ImagePicker.js\";\nimport ColorField, { toHex } from \"./ColorField.js\";\nimport ImageField, { imageUrl } from \"./ImageField.js\";\nimport { createRoot, type Root } from \"react-dom/client\";\n\nexport type SaveStatus = \"idle\" | \"saving\" | \"saved\" | \"error\";\n\n/** Snapshot of editor state pushed to the header (device, undo, save, zoom, width). */\nexport type EditorState = {\n device: string;\n canUndo: boolean;\n canRedo: boolean;\n saveStatus: SaveStatus;\n zoom: number;\n contentWidth: string;\n /** True during an agent turn, until its result is loaded. */\n readOnly: boolean;\n};\n\nexport type EditorApi = {\n /** Current MJML source from the editor. */\n getMjml: () => string;\n /** Compiled email HTML (via grapesjs-mjml's mjml-get-code). */\n getCompiledHtml: () => string;\n /** Immediately saves the editor state to the DB. */\n flushSave: () => Promise<void>;\n /** Reloads the document from the DB (after agent changes). */\n reloadFromDb: () => Promise<void>;\n /** Highlights a section in the canvas while the agent edits it. */\n highlightSection: (sectionId: string, on: boolean) => void;\n /** Device manager control (used by the top header). */\n getDevices: () => { id: string; name: string }[];\n setDevice: (name: string) => void;\n undo: () => void;\n redo: () => void;\n /** Canvas zoom (percentage). */\n setZoom: (z: number) => void;\n /** Email body width (mj-body width attribute, e.g. \"600px\"). */\n setContentWidth: (w: string) => void;\n /** Subscribe to editor state changes (device/undo/save/zoom/width). Returns unsubscribe. */\n onEditorState: (cb: (s: EditorState) => void) => () => void;\n};\n\ntype Props = {\n docId: string;\n onReady: (api: EditorApi) => void;\n /** Bump to force the canvas comment layer to refetch (after agent turns). */\n commentsRefresh: number;\n /** Reports the number of open comments (for the header indicator). */\n onOpenCountChange: (n: number) => void;\n};\n\nconst SEC_ID_RE = /\\bsec-([A-Za-z0-9_-]+)\\b/;\nconst OBJ_ID_RE = /\\bobj-([A-Za-z0-9_-]+)\\b/;\n\n// Element types that can receive a comment (besides a section).\nconst TYPE_LABEL: Record<string, string> = {\n \"mj-text\": \"Text\",\n \"mj-button\": \"Button\",\n \"mj-image\": \"Image\",\n \"mj-column\": \"Column\",\n};\n\nfunction tagOf(c: Component): string {\n return String(c.get(\"tagName\") || c.get(\"type\") || \"\");\n}\n\n// \"Sparkles\" icon (lucide Sparkles) used as a label in the GrapesJS toolbar.\nconst SPARKLES_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\"/><path d=\"M20 3v4\"/><path d=\"M22 5h-4\"/><path d=\"M4 17v2\"/><path d=\"M5 18H3\"/></svg>';\n\n// \"Image up\" icon (lucide ImageUp), the toolbar button that opens the image picker.\nconst IMAGE_SVG =\n '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10.3 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v10l-3.1-3.1a2 2 0 0 0-2.814.014L6 21\"/><path d=\"m14 19.5 3-3 3 3\"/><path d=\"M17 22v-5.5\"/><circle cx=\"9\" cy=\"9\" r=\"2\"/></svg>';\n\nfunction isSection(c: Component): boolean {\n return tagOf(c) === \"mj-section\";\n}\n\nfunction isCommentable(c: Component): boolean {\n const t = tagOf(c);\n return t === \"mj-section\" || t in TYPE_LABEL;\n}\n\nfunction classMatch(c: Component, re: RegExp): string | null {\n const cssClass = String(c.getAttributes()[\"css-class\"] ?? \"\");\n return cssClass.match(re)?.[1] ?? null;\n}\n\nfunction sectionIdOf(c: Component): string | null {\n return classMatch(c, SEC_ID_RE);\n}\n\nfunction addIdClass(c: Component, prefix: \"sec\" | \"obj\"): string {\n const existing = classMatch(c, prefix === \"sec\" ? SEC_ID_RE : OBJ_ID_RE);\n if (existing) return existing;\n const cssClass = String(c.getAttributes()[\"css-class\"] ?? \"\");\n const id = Math.random().toString(36).slice(2, 10);\n c.addAttributes({ \"css-class\": `${cssClass} ${prefix}-${id}`.trim() });\n return id;\n}\n\nfunction closestSection(c: Component | undefined): Component | null {\n let cur: Component | undefined = c;\n while (cur) {\n if (isSection(cur)) return cur;\n cur = cur.parent() ?? undefined;\n }\n return null;\n}\n\n// Friendly names for the canvas breadcrumb.\nconst CRUMB_NAME: Record<string, string> = {\n \"mj-section\": \"Section\",\n \"mj-column\": \"Column\",\n \"mj-group\": \"Group\",\n \"mj-text\": \"Text\",\n \"mj-button\": \"Button\",\n \"mj-image\": \"Image\",\n \"mj-divider\": \"Divider\",\n \"mj-spacer\": \"Spacer\",\n \"mj-hero\": \"Hero\",\n \"mj-navbar\": \"Navbar\",\n \"mj-social\": \"Social\",\n \"mj-social-element\": \"Social\",\n \"mj-wrapper\": \"Wrapper\",\n};\n\nfunction crumbName(c: Component): string {\n const t = tagOf(c);\n return CRUMB_NAME[t] ?? t.replace(/^mj-/, \"\") ?? \"Element\";\n}\n\nfunction objectLabel(c: Component): string {\n const base = TYPE_LABEL[tagOf(c)] ?? tagOf(c);\n const text = String((c.getEl?.() as HTMLElement | undefined)?.textContent ?? \"\")\n .replace(/\\s+/g, \" \")\n .trim()\n .slice(0, 30);\n return text ? `${base}: “${text}”` : base;\n}\n\nconst RTE_FONTS = [\n \"Arial\",\n \"Helvetica\",\n \"Georgia\",\n \"Times New Roman\",\n \"Verdana\",\n \"Tahoma\",\n \"Trebuchet MS\",\n \"Courier New\",\n];\nconst RTE_SIZES = [11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 40];\n\n// Wraps the current selection (in the canvas document) in a <span> with inline style.\nfunction wrapSelectionStyle(el: HTMLElement | undefined, style: Partial<CSSStyleDeclaration>) {\n const doc = el?.ownerDocument;\n const sel = doc?.getSelection?.();\n if (!doc || !sel || sel.rangeCount === 0 || sel.isCollapsed) return;\n const range = sel.getRangeAt(0);\n const span = doc.createElement(\"span\");\n Object.assign(span.style, style);\n try {\n span.appendChild(range.extractContents());\n range.insertNode(span);\n sel.removeAllRanges();\n const nr = doc.createRange();\n nr.selectNodeContents(span);\n sel.addRange(nr);\n } catch {\n /* ignore */\n }\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n/**\n * Swaps the Style Manager's color and image controls for ColorField and ImageField.\n *\n * Built on the base property view rather than extending GrapesJS's own views, which mount\n * their widgets in onRender whatever `create` returns. Each control is its own React root\n * because GrapesJS creates and destroys these views outside React.\n */\nfunction setupStyleFields(editor: Editor, labels: EditorLabels) {\n const sm = editor.StyleManager;\n const base = (sm.getType(\"base\") as any).view;\n type View = { __root?: Root; __render?: () => void };\n\n // Distinct colors already in the email, most frequent first, read when the picker opens.\n // Only values of color-bearing attributes and declarations: a bare `#[0-9a-f]+` also\n // matches entities such as `&#039;` and anchors such as `href=\"#top\"`.\n const documentColors = (): string[] => {\n const source = `${editor.getHtml()} ${editor.getCss() ?? \"\"}`;\n const counts = new Map<string, number>();\n const colorValue =\n /(?:color|background|border)[\\w-]*\\s*[:=]\\s*[\"']?[^\"';>{}]*?(#[0-9a-f]{3,8}\\b|rgba?\\([^)]*\\))/gi;\n for (const match of source.matchAll(colorValue)) {\n const hex = toHex(match[1]!, { opaque: true });\n if (hex) counts.set(hex, (counts.get(hex) ?? 0) + 1);\n }\n return [...counts.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, 14)\n .map(([hex]) => hex);\n };\n\n // The value set on the selection, or \"\" when the property only shows its default:\n // GrapesJS substitutes the default before update(), which would present it as set.\n const ownValue = (property: any): string =>\n property.hasValue?.({ noParent: true }) ? String(property.getValue?.() ?? \"\") : \"\";\n\n // A view that renders `field(property, value)` into its own React root.\n const reactView = (field: (property: any, value: string) => ReactNode) =>\n base.extend({\n create(this: View, { property }: { property: any }) {\n const el = document.createElement(\"div\");\n // The base view writes the value of any input that fires `change`; the field\n // commits its own, so letting the event through wrote every edit twice.\n el.addEventListener(\"change\", (event) => event.stopPropagation());\n const root = createRoot(el);\n this.__root = root;\n this.__render = () => root.render(field(property, ownValue(property)));\n this.__render();\n return el;\n },\n update(this: View) {\n this.__render?.();\n },\n destroy(this: View) {\n // GrapesJS can still call update() on a destroyed view; drop the renderer first so\n // that is a no-op rather than \"Cannot update an unmounted root\". The unmount itself\n // is deferred because the view may be removed while React is mid-render.\n const root = this.__root;\n this.__root = undefined;\n this.__render = undefined;\n setTimeout(() => root?.unmount());\n },\n });\n\n sm.addType(\"color\", {\n view: reactView((property, value) => (\n <ColorField\n value={value}\n placeholder={String(property.getDefaultValue?.() ?? \"\")}\n documentColors={documentColors}\n labels={labels}\n onChange={(next, partial) => property.upValue(next, { partial })}\n />\n )),\n } as never);\n\n // Opens the same picker an mj-image uses; the property wraps the URL in url() itself\n // where it needs to (CSS background-image), and keeps it bare where it does not (MJML).\n sm.addType(\"file\", {\n view: reactView((property, value) => (\n <ImageField\n value={value}\n labels={labels}\n onClear={() => property.upValue(\"\")}\n onChoose={() => {\n const am = editor.AssetManager;\n am.open({\n types: [\"image\"],\n current: imageUrl(value) ?? undefined,\n select(asset: string | { getSrc: () => string }, complete?: boolean) {\n const url = typeof asset === \"string\" ? asset : asset.getSrc();\n property.upValue(url, { partial: !complete });\n if (complete) am.close();\n },\n } as never);\n }}\n />\n )),\n } as never);\n}\n\n// Extends the default GrapesJS RTE (mjml-compatible) with font/size/colors.\nfunction setupRichText(editor: Editor, labels: EditorLabels) {\n const rte = editor.RichTextEditor as any;\n if (!rte || rte.__customActions) return;\n rte.__customActions = true;\n const val = (action: any, sel: string) =>\n (action.btn?.querySelector(sel) as HTMLInputElement | HTMLSelectElement | null)?.value ?? \"\";\n\n rte.add(\"fontName\", {\n icon: `<select class=\"gjs-rte-select\" title=\"${labels.rteFont}\"><option value=\"\">${labels.rteFont}</option>${RTE_FONTS.map(\n (f) => `<option value=\"${f}\">${f}</option>`,\n ).join(\"\")}</select>`,\n event: \"change\",\n result: (r: any, action: any) => {\n const v = val(action, \"select\");\n if (v) r.exec(\"fontName\", v);\n },\n });\n\n rte.add(\"fontSize\", {\n icon: `<select class=\"gjs-rte-select\" title=\"${labels.rteSize}\"><option value=\"\">${labels.rteSize}</option>${RTE_SIZES.map(\n (s) => `<option value=\"${s}px\">${s}</option>`,\n ).join(\"\")}</select>`,\n event: \"change\",\n result: (r: any, action: any) => {\n const v = val(action, \"select\");\n if (v) wrapSelectionStyle(r.el, { fontSize: v });\n },\n });\n\n rte.add(\"forecolor\", {\n icon: `<input type=\"color\" class=\"gjs-rte-color\" title=\"${labels.rteTextColor}\" value=\"#000000\">`,\n event: \"change\",\n result: (r: any, action: any) => r.exec(\"foreColor\", val(action, \"input\")),\n });\n\n rte.add(\"hilitecolor\", {\n icon: `<input type=\"color\" class=\"gjs-rte-color\" title=\"${labels.rteBackgroundColor}\" value=\"#ffff00\">`,\n event: \"change\",\n result: (r: any, action: any) => r.exec(\"hiliteColor\", val(action, \"input\")),\n });\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\nexport default function EmailEditor({ docId, onReady, commentsRefresh, onOpenCountChange }: Props) {\n // Injected by the host rather than imported, so the editor carries no database.\n const documents = useDocumentStore();\n const labels = useLabels();\n // Refs instead of state — GrapesJS lives outside React's lifecycle.\n const loadingRef = useRef(false);\n const saveStatusRef = useRef<SaveStatus>(\"idle\");\n const stateListeners = useRef(new Set<(s: EditorState) => void>());\n const [loading, setLoading] = useState(true);\n const [sidebarView, setSidebarView] = useState<SidebarView>(\"blocks\");\n // Comment being composed (sparkles button) — opens the canvas popover.\n const [composeTarget, setComposeTarget] = useState<CommentTarget | null>(null);\n // Where the current selection came from — canvas click opens Settings,\n // selecting from the Layers tree does not (keeps you on Layers).\n const selectionSource = useRef<\"canvas\" | \"layers\" | \"other\">(\"other\");\n // Breadcrumb of the selected component's ancestry (Section › Column › …).\n const editorRef = useRef<Editor | null>(null);\n // Read only from a turn's start until the document it left is loaded, not just until it\n // ends: an edit made before that final reload would be replaced by it.\n const { running: turnRunning, locked: readOnly, setLocked } = useAgentTurn();\n const readOnlyRef = useRef(false);\n const applyReadOnly = useRef<(on: boolean) => Promise<void>>(async () => {});\n useEffect(() => void applyReadOnly.current(turnRunning), [turnRunning]);\n useEffect(() => () => setLocked(false), [setLocked]);\n const [crumbs, setCrumbs] = useState<Component[]>([]);\n // `src` of the selected mj-image, or null when the selection is not one.\n const [selectedImageSrc, setSelectedImageSrc] = useState<string | null>(null);\n // The open picker: GrapesJS hands over how to apply a choice to the image being edited.\n const [picker, setPicker] = useState<{ current?: string; select: (url: string) => void } | null>(\n null,\n );\n\n const onEditor = async (editor: Editor) => {\n editorRef.current = editor;\n\n // Build the breadcrumb chain from a component up to (but excluding) mj-body.\n const buildCrumbs = (c: Component | undefined) => {\n const chain: Component[] = [];\n let cur: Component | undefined = c;\n while (cur) {\n const t = tagOf(cur);\n if (!t || t === \"mj-body\" || t === \"wrapper\" || t === \"Wrapper\") break;\n chain.unshift(cur);\n cur = cur.parent() ?? undefined;\n }\n setCrumbs(chain);\n };\n\n // GrapesJS find() by MJML tag is unreliable — walk the tree instead.\n const findByTag = (tag: string): Component | undefined => {\n let found: Component | undefined;\n editor.getWrapper()?.onAll((c) => {\n if (!found && (c.get(\"tagName\") === tag || c.get(\"type\") === tag)) found = c;\n });\n return found;\n };\n const bodyWidth = (): string => {\n const body = findByTag(\"mj-body\");\n return String((body?.getAttributes?.() as Record<string, unknown>)?.width ?? \"600px\");\n };\n const snapshot = (): EditorState => ({\n device: editor.getDevice(),\n canUndo: !readOnlyRef.current && editor.UndoManager.hasUndo(),\n canRedo: !readOnlyRef.current && editor.UndoManager.hasRedo(),\n saveStatus: saveStatusRef.current,\n zoom: Math.round((editor.Canvas.getZoom?.() as number | undefined) ?? 100),\n contentWidth: bodyWidth(),\n readOnly: readOnlyRef.current,\n });\n const notifyState = () => {\n const s = snapshot();\n stateListeners.current.forEach((l) => l(s));\n };\n const setSave = (s: SaveStatus) => {\n saveStatusRef.current = s;\n notifyState();\n };\n\n // Comment target for a component: its section + (if it's not the section\n // itself) the specific element. Assigns stable IDs (sec-/obj-) when needed.\n const targetOf = (c: Component | undefined): CommentTarget | null => {\n const section = closestSection(c);\n if (!section || !c) return null;\n const sectionId = addIdClass(section, \"sec\");\n if (isSection(c)) {\n return { sectionId, objectId: null, objectLabel: null };\n }\n const objectId = addIdClass(c, \"obj\");\n return { sectionId, objectId, objectLabel: objectLabel(c) };\n };\n\n // Gives the section a sec-<id> and adds a comment button to the toolbar of\n // every commentable element (section + text/button/image/column).\n const decorate = (c: Component) => {\n if (isSection(c)) addIdClass(c, \"sec\");\n if (!isCommentable(c)) return;\n const toolbar = [...((c.get(\"toolbar\") as { command?: string }[]) ?? [])];\n if (tagOf(c) === \"mj-image\" && !toolbar.some((t) => t.command === \"open-image-picker\")) {\n toolbar.push({\n command: \"open-image-picker\",\n label: IMAGE_SVG,\n attributes: { title: labels.chooseImage },\n } as never);\n c.set(\"toolbar\", toolbar as never);\n }\n if (!toolbar.some((t) => t.command === \"open-comments\")) {\n toolbar.push({\n command: \"open-comments\",\n label: SPARKLES_SVG,\n attributes: { title: \"Comment on element / section\", \"data-role\": \"sparkles\" },\n } as never);\n c.set(\"toolbar\", toolbar as never);\n }\n };\n\n // What a double-click on an image does in GrapesJS, reachable from a button.\n editor.Commands.add(\"open-image-picker\", {\n run(ed: Editor) {\n const target = ed.getSelected();\n if (!target || tagOf(target) !== \"mj-image\") return;\n const am = ed.AssetManager;\n am.open({\n target,\n types: [\"image\"],\n select(asset: { getSrc: () => string }, complete?: boolean) {\n target.set({ src: asset.getSrc() });\n if (complete) am.close();\n },\n } as never);\n },\n });\n\n editor.Commands.add(\"open-comments\", {\n run(ed: Editor) {\n const target = targetOf(ed.getSelected() ?? undefined);\n if (target) setComposeTarget(target);\n },\n });\n\n setupRichText(editor, labels);\n setupStyleFields(editor, labels);\n\n // Group the palette blocks into categories (the plugin ships them flat).\n const blockCategory = (label: string): string => {\n const l = label.toLowerCase();\n if (/(column|section)/.test(l)) return \"Layout\";\n if (/(text|image|button|divider|spacer|social)/.test(l)) return \"Basic\";\n return \"Advanced\";\n };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const bm = editor.BlockManager as any;\n bm.getAll().forEach(\n (b: { get: (k: string) => string; set: (k: string, v: unknown) => void }) => {\n b.set(\"category\", blockCategory(b.get(\"label\") || \"\"));\n },\n );\n bm.render();\n\n editor.on(\"component:add\", decorate);\n // Track selection origin: a mousedown inside the canvas iframe marks the\n // next selection as coming from the canvas (→ open Settings). Selecting via\n // the Layers tree leaves the source as \"layers\" (set by the sidebar).\n editor.on(\"load\", () => {\n const body = editor.Canvas.getBody();\n body?.addEventListener(\n \"mousedown\",\n () => {\n selectionSource.current = \"canvas\";\n },\n true,\n );\n });\n editor.on(\"component:selected\", (c: Component) => {\n if (selectionSource.current === \"canvas\") setSidebarView(\"settings\");\n selectionSource.current = \"other\";\n buildCrumbs(c);\n setSelectedImageSrc(c && tagOf(c) === \"mj-image\" ? String(c.get(\"src\") ?? \"\") : null);\n // mj-image has no src trait by default (src changes via the Asset\n // Manager) — add a URL field so the image can be set from the panel.\n if (c?.get(\"type\") === \"mj-image\" && !c.getTrait(\"src\")) {\n c.addTrait(\n {\n type: \"text\",\n name: \"src\",\n label: \"Image (URL)\",\n placeholder: \"https://…\",\n changeProp: true,\n } as never,\n { at: 0 },\n );\n }\n });\n // With multi-select a deselection can leave something selected; follow what remains.\n editor.on(\"component:deselected\", () => {\n const rest = editor.getSelected();\n buildCrumbs(rest);\n setSelectedImageSrc(\n rest && tagOf(rest) === \"mj-image\" ? String(rest.get(\"src\") ?? \"\") : null,\n );\n });\n editor.on(\"component:update:src\", (c: Component) => {\n if (c === editor.getSelected()) setSelectedImageSrc(String(c.get(\"src\") ?? \"\"));\n });\n\n // The MJML the store holds, as far as this editor knows. A save writes only when the\n // canvas differs from it — the canvas is the truth, not the events that changed it.\n let stored = \"\";\n\n const save = async () => {\n // An open rich-text editor holds its text until editing ends.\n const view = editor.getEditing()?.getView() as\n { syncContent?: () => Promise<void> } | undefined;\n await view?.syncContent?.();\n const mjml = editor.getHtml();\n if (mjml === stored) return;\n setSave(\"saving\");\n try {\n await documents.save(docId, { mjml, projectData: editor.getProjectData() });\n stored = mjml;\n setSave(\"saved\");\n } catch (e) {\n setSave(\"error\");\n toast.error(refusal(e) ?? labels.documentSaveFailed, { id: saveToastId(docId) });\n throw e;\n }\n };\n\n const queue = createSaveQueue(save, 1200);\n\n editor.on(\"update\", () => {\n notifyState();\n if (!loadingRef.current && !readOnlyRef.current) queue.schedule();\n });\n editor.on(\"change:device\", notifyState);\n\n // While a turn runs the agent writes the document, so the visitor cannot: edits made\n // meanwhile would be overwritten by the agent's next reload, or overwrite its work.\n const blocked = [\n \"core:undo\",\n \"core:redo\",\n \"core:paste\",\n \"core:component-delete\",\n \"tlb-delete\",\n \"tlb-clone\",\n \"tlb-move\",\n \"open-assets\",\n \"open-image-picker\",\n ];\n for (const id of blocked) {\n editor.on(`command:run:before:${id}`, ({ options }: { options: { abort?: boolean } }) => {\n if (readOnlyRef.current) options.abort = true;\n });\n }\n const lock = (on: boolean) => {\n readOnlyRef.current = on;\n setLocked(on);\n notifyState();\n };\n applyReadOnly.current = async (on) => {\n if (on) {\n lock(true);\n queue.cancel();\n editor.select([]);\n // Through GrapesJS, which otherwise keeps its asset command active and will not reopen.\n editor.AssetManager.close();\n setPicker(null);\n return;\n }\n try {\n await reload();\n } catch (e) {\n // Unlocking a stale canvas would let the next save overwrite what the agent wrote.\n console.error(e);\n toast.error(labels.documentLoadFailed, {\n id: `reload-${docId}`,\n duration: Infinity,\n action: { label: labels.retry, onClick: () => void applyReadOnly.current(false) },\n });\n return;\n }\n lock(false);\n };\n\n // Only the agent writes during a turn, so the store differs from `stored` exactly when it\n // changed something. Otherwise the canvas is kept, including edits a failed save left unsaved.\n // Queued behind saves and earlier reloads, so a slow one never lands over a newer one.\n const reload = () =>\n queue.run(async () => {\n const fresh = await documents.get(docId);\n if (fresh.mjml !== stored) loadMjml(fresh.mjml);\n });\n\n const loadMjml = (mjml: string) => {\n // Loading reports changes of its own. What it adds — ids, the starter body — is still\n // written by the next save, which compares against what the store returned.\n loadingRef.current = true;\n try {\n queue.cancel();\n stored = mjml;\n editor.setComponents(mjml || STARTER_MJML);\n editor.getWrapper()?.find(\"mj-section\").forEach(decorate);\n for (const t of Object.keys(TYPE_LABEL)) {\n editor.getWrapper()?.find(t).forEach(decorate);\n }\n } finally {\n loadingRef.current = false;\n }\n };\n\n // Highlight for the section the agent is editing. The canvas is an iframe with its\n // own document, so the host's stylesheet does not reach it and these rules have to be\n // injected. The colour is still read from the shared token rather than written out\n // again — the literal that used to live here was the pre-redesign violet and had\n // silently drifted from the brand for as long as the redesign had been in place.\n const ensureHighlightStyles = () => {\n const cdoc = editor.Canvas.getDocument();\n if (!cdoc || cdoc.getElementById(\"agent-edit-style\")) return;\n\n const accent =\n getComputedStyle(document.documentElement)\n .getPropertyValue(\"--mjml-editor-accent\")\n .trim() || \"#7c5cfc\";\n\n const style = cdoc.createElement(\"style\");\n style.id = \"agent-edit-style\";\n style.textContent = `\n @keyframes agentEditPulse {\n 0%, 100% { outline-color: color-mix(in srgb, ${accent} 25%, transparent); }\n 50% { outline-color: color-mix(in srgb, ${accent} 95%, transparent); }\n }\n .agent-editing {\n outline: 3px solid color-mix(in srgb, ${accent} 90%, transparent) !important;\n outline-offset: -3px;\n animation: agentEditPulse 1s ease-in-out infinite;\n transition: outline-color 0.2s;\n }`;\n cdoc.head.appendChild(style);\n };\n\n const sectionEl = (sectionId: string): HTMLElement | undefined => {\n const comp = editor\n .getWrapper()\n ?.find(\"mj-section\")\n .find((c) => sectionIdOf(c) === sectionId);\n return (comp?.getEl?.() as HTMLElement | undefined) ?? undefined;\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const compiledHtml = (): string => {\n try {\n // grapesjs-mjml compiles MJML → email HTML via this command ({ html, errors }).\n const res = editor.runCommand(\"mjml-code-to-html\") as { html?: string } | undefined;\n return res?.html ?? editor.getHtml();\n } catch {\n return editor.getHtml();\n }\n };\n\n try {\n const doc = await documents.get(docId);\n loadMjml(doc.mjml);\n } catch (e) {\n toast.error(labels.documentLoadFailed);\n console.error(e);\n } finally {\n setLoading(false);\n }\n\n onReady({\n getMjml: () => editor.getHtml(),\n getCompiledHtml: compiledHtml,\n flushSave: () => queue.flush(),\n reloadFromDb: reload,\n highlightSection: (sectionId, on) => {\n ensureHighlightStyles();\n const el = sectionEl(sectionId);\n if (!el) return;\n el.classList.toggle(\"agent-editing\", on);\n },\n getDevices: () =>\n editor.Devices.getDevices().map((d) => ({\n id: String(d.id),\n name: d.getName() || String(d.id),\n })),\n setDevice: (name) => {\n editor.setDevice(name);\n notifyState();\n },\n undo: () => {\n if (readOnlyRef.current) return;\n editor.UndoManager.undo();\n notifyState();\n },\n redo: () => {\n if (readOnlyRef.current) return;\n editor.UndoManager.redo();\n notifyState();\n },\n setZoom: (z) => {\n editor.Canvas.setZoom?.(Math.max(25, Math.min(200, z)));\n notifyState();\n },\n setContentWidth: (w) => {\n if (readOnlyRef.current) return;\n findByTag(\"mj-body\")?.addAttributes({ width: w });\n notifyState();\n },\n onEditorState: (cb) => {\n stateListeners.current.add(cb);\n cb(snapshot());\n return () => stateListeners.current.delete(cb);\n },\n });\n };\n\n return (\n <GjsEditor\n grapesjs={grapesjs}\n className=\"h-full\"\n options={{\n height: \"100%\",\n storageManager: false,\n fromElement: false,\n panels: { defaults: [] },\n blockManager: { appendTo: \"#gjs-blocks\" },\n styleManager: { appendTo: \"#gjs-styles\" },\n traitManager: { appendTo: \"#gjs-traits\" },\n layerManager: { appendTo: \"#gjs-layers\" },\n assetManager: {\n // Never inline files: mail clients block `data:` images (see ImageLibrary).\n embedAsBase64: false,\n // Our own dialog instead of GrapesJS's modal. GrapesJS still decides when it opens\n // and what a choice applies to; `select(url, true)` sets the image and closes.\n custom: {\n open: (props: {\n select: (asset: string, complete?: boolean) => void;\n // `current` is ours, passed by style properties that have no target component.\n options?: { target?: Component; current?: string };\n }) =>\n setPicker({\n current:\n props.options?.current ??\n (String(props.options?.target?.get(\"src\") ?? \"\") || undefined),\n select: (url) => props.select(url, true),\n }),\n close: () => setPicker(null),\n },\n },\n deviceManager: {\n devices: [\n { id: \"desktop\", name: \"Desktop\", width: \"\" },\n { id: \"mobile\", name: \"Mobile\", width: \"375px\", widthMedia: \"480px\" },\n ],\n },\n plugins: [grapesjsMJML],\n }}\n onEditor={onEditor}\n >\n {/* Inert as well as covered, so the keyboard cannot reach the controls either. */}\n <div className=\"relative flex h-full min-h-0 flex-1\" inert={readOnly}>\n {readOnly && (\n <div className=\"absolute inset-0 z-30 flex cursor-not-allowed items-start justify-center bg-white/20 pt-3\">\n <span className=\"flex items-center gap-2 rounded-full bg-panel px-3 py-1 text-xs text-panel-fg shadow\">\n <Spinner /> {labels.agentWorking}\n </span>\n </div>\n )}\n <LeftSidebar\n view={sidebarView}\n onViewChange={setSidebarView}\n markLayersSource={() => (selectionSource.current = \"layers\")}\n selectedImageSrc={selectedImageSrc}\n onChooseImage={() => editorRef.current?.runCommand(\"open-image-picker\")}\n />\n <div className=\"flex min-w-0 flex-1 flex-col bg-surface-muted\">\n <div className=\"relative min-h-0 flex-1\">\n <Canvas className=\"h-full\" />\n {/* Canvas comment layer (pins + thread popovers) overlays the canvas. */}\n <CanvasComments\n docId={docId}\n composeTarget={composeTarget}\n onComposeConsumed={() => setComposeTarget(null)}\n refreshSignal={commentsRefresh}\n onOpenCountChange={onOpenCountChange}\n />\n <ImagePicker\n open={picker !== null}\n currentUrl={picker?.current}\n onSelect={(url) => {\n // An upload that finished after a turn locked the canvas is not applied.\n if (!readOnlyRef.current) picker?.select(url);\n setPicker(null);\n }}\n // Closed here as well as through GrapesJS: its close() is a no-op whenever it\n // does not consider its asset command active, which left the dialog stuck open.\n onClose={() => {\n setPicker(null);\n editorRef.current?.AssetManager.close();\n }}\n />\n {loading && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center gap-2 bg-white/70 text-sm text-zinc-500\">\n <Spinner /> Loading editor…\n </div>\n )}\n </div>\n <Breadcrumb crumbs={crumbs} onSelect={(c) => editorRef.current?.select(c)} />\n </div>\n </div>\n </GjsEditor>\n );\n}\n\ntype SidebarView = \"blocks\" | \"settings\" | \"layers\";\n\n// Collapsible section — content ALWAYS mounted (appendTo target), hidden via `hidden`.\nfunction CollapseSection({\n title,\n defaultOpen = true,\n children,\n}: {\n title: string;\n defaultOpen?: boolean;\n children: ReactNode;\n}) {\n const [open, setOpen] = useState(defaultOpen);\n return (\n <div className=\"border-b border-panel-border\">\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-1.5 px-3 py-2 text-xs font-semibold tracking-wide text-panel-muted-fg uppercase hover:text-panel-fg\"\n >\n <ChevronRight className={cn(\"size-3.5 transition-transform\", open && \"rotate-90\")} />\n {title}\n </button>\n <div className={cn(!open && \"hidden\")}>{children}</div>\n </div>\n );\n}\n\n// Left sidebar (dark): Blocks / Settings / Layers. All managers are always\n// mounted (appendTo targets for GrapesJS) — visibility toggled via `hidden`.\nfunction LeftSidebar({\n view,\n onViewChange,\n markLayersSource,\n selectedImageSrc,\n onChooseImage,\n}: {\n view: SidebarView;\n onViewChange: (v: SidebarView) => void;\n markLayersSource: () => void;\n /** `src` of the selected image, or null when no image is selected. */\n selectedImageSrc: string | null;\n onChooseImage: () => void;\n}) {\n const editor = useEditorMaybe();\n const labels = useLabels();\n const [layersExpanded, setLayersExpanded] = useState(false);\n const [blockSearch, setBlockSearch] = useState(\"\");\n\n const changeView = (v: SidebarView) => {\n if (v === \"settings\" && editor && !editor.getSelected()) {\n const wrapper = editor.getWrapper();\n const body = wrapper?.find(\"mj-body\")[0] ?? wrapper;\n if (body) editor.select(body);\n }\n onViewChange(v);\n };\n\n const toggleLayers = (expand: boolean) => {\n setLayersExpanded(expand);\n editor?.getWrapper()?.onAll((c) => c.set(\"open\", expand));\n };\n\n // Filter the GrapesJS-rendered blocks (DOM lives outside React) by label;\n // hide category groups that end up empty.\n const filterBlocks = (q: string) => {\n setBlockSearch(q);\n const root = document.getElementById(\"gjs-blocks\");\n if (!root) return;\n const term = q.trim().toLowerCase();\n root.querySelectorAll<HTMLElement>(\".gjs-block\").forEach((b) => {\n const label = (b.querySelector(\".gjs-block-label\")?.textContent ?? b.textContent ?? \"\")\n .toLowerCase()\n .trim();\n b.style.display = !term || label.includes(term) ? \"\" : \"none\";\n });\n root.querySelectorAll<HTMLElement>(\".gjs-block-category\").forEach((cat) => {\n const anyVisible = [...cat.querySelectorAll<HTMLElement>(\".gjs-block\")].some(\n (b) => b.style.display !== \"none\",\n );\n cat.style.display = anyVisible ? \"\" : \"none\";\n });\n };\n\n return (\n <div className=\"editor-dark flex w-64 shrink-0 flex-col border-r border-panel-border bg-panel text-panel-fg\">\n <div className=\"flex border-b border-panel-border\">\n {(\n [\n [\"blocks\", \"Blocks\"],\n [\"settings\", \"Settings\"],\n [\"layers\", \"Layers\"],\n ] as const\n ).map(([value, label]) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => changeView(value)}\n className={cn(\n \"relative flex-1 px-3 py-2.5 text-sm transition-colors\",\n \"after:absolute after:inset-x-0 after:-bottom-px after:h-0.5 after:transition-colors\",\n view === value\n ? \"font-medium text-panel-fg after:bg-brand\"\n : \"text-panel-muted-fg after:bg-transparent hover:text-panel-fg\",\n )}\n >\n {label}\n </button>\n ))}\n </div>\n\n {/* Blocks: search + categorized block palette */}\n <div className={cn(\"flex min-h-0 flex-1 flex-col\", view !== \"blocks\" && \"hidden\")}>\n <div className=\"relative border-b border-panel-border p-2\">\n <Search className=\"pointer-events-none absolute top-1/2 left-4 size-3.5 -translate-y-1/2 text-panel-muted-fg\" />\n <Input\n value={blockSearch}\n onChange={(e) => filterBlocks(e.target.value)}\n placeholder={labels.searchBlocks}\n className=\"h-8 border-panel-border bg-panel-elevated pr-10 pl-7 text-sm text-panel-fg placeholder:text-panel-muted-fg\"\n />\n <kbd className=\"pointer-events-none absolute top-1/2 right-4 -translate-y-1/2 rounded border border-panel-border bg-panel px-1 py-0.5 font-mono text-[10px] text-panel-muted-fg\">\n ⌘K\n </kbd>\n </div>\n <div id=\"gjs-blocks\" className=\"min-h-0 flex-1 overflow-y-auto\" />\n </div>\n\n {/* Settings: Attributes + Style (collapsible, stacked) */}\n <div className={cn(\"min-h-0 flex-1 overflow-y-auto\", view !== \"settings\" && \"hidden\")}>\n {selectedImageSrc !== null && (\n <div className=\"border-b border-panel-border p-3\">\n <button\n type=\"button\"\n onClick={onChooseImage}\n title={labels.chooseImage}\n className=\"group relative block w-full overflow-hidden rounded-lg border border-panel-border bg-panel-elevated\"\n >\n {selectedImageSrc ? (\n <img\n src={selectedImageSrc}\n alt=\"\"\n className=\"h-32 w-full bg-black/25 object-contain\"\n />\n ) : (\n <span className=\"flex h-32 items-center justify-center text-panel-muted-fg\">\n <ImageIcon className=\"size-8\" />\n </span>\n )}\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/50 text-sm font-medium text-white opacity-0 transition group-hover:opacity-100\">\n {labels.chooseImage}\n </span>\n </button>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"mt-2 w-full bg-brand text-brand-fg hover:bg-brand/90\"\n onClick={onChooseImage}\n >\n <ImageUp />\n {labels.chooseImage}\n </Button>\n </div>\n )}\n <CollapseSection title={labels.attributes}>\n <div id=\"gjs-traits\" />\n </CollapseSection>\n <CollapseSection title={labels.style}>\n <div id=\"gjs-styles\" />\n </CollapseSection>\n </div>\n\n {/* Layers — with an expand/collapse-all switch. A pointer-down here marks\n the next selection as coming from the tree (→ do NOT open Settings). */}\n <div\n className={cn(\"flex min-h-0 flex-1 flex-col\", view !== \"layers\" && \"hidden\")}\n onMouseDownCapture={markLayersSource}\n >\n <label className=\"flex items-center justify-between gap-2 border-b border-panel-border px-3 py-2 text-xs text-panel-muted-fg\">\n <span>{labels.expandAll}</span>\n <Switch\n checked={layersExpanded}\n onCheckedChange={toggleLayers}\n className=\"data-[state=checked]:bg-brand data-[state=unchecked]:bg-panel-border\"\n />\n </label>\n <div id=\"gjs-layers\" className=\"min-h-0 flex-1 overflow-y-auto\" />\n </div>\n </div>\n );\n}\n\n// Breadcrumb under the canvas: the selected component's ancestry (clickable).\nfunction Breadcrumb({\n crumbs,\n onSelect,\n}: {\n crumbs: Component[];\n onSelect: (c: Component) => void;\n}) {\n const labels = useLabels();\n return (\n <div className=\"flex h-9 shrink-0 items-center gap-0.5 border-t border-border bg-surface px-3 text-xs text-muted-foreground\">\n {crumbs.length === 0 ? (\n <span className=\"text-muted-foreground/60\">{labels.selectAnElement}</span>\n ) : (\n crumbs.map((c, i) => (\n <span key={i} className=\"flex items-center gap-0.5\">\n {i > 0 && <ChevronRight className=\"size-3 opacity-40\" />}\n <button\n type=\"button\"\n onClick={() => onSelect(c)}\n className={cn(\n \"rounded px-1.5 py-0.5 hover:bg-muted hover:text-foreground\",\n i === crumbs.length - 1 && \"font-medium text-brand\",\n )}\n >\n {crumbName(c)}\n </button>\n </span>\n ))\n )}\n </div>\n );\n}\n","\"use client\";\n\n/**\n * How the editor reaches its data.\n *\n * In the spike every component imported `@/lib/documents`, which imported a Supabase\n * client — so adopting the editor meant adopting Supabase, and the components could not\n * be tested without a database. They now read whatever the host provides through this\n * context, and the host is free to back it with Supabase, plain Postgres, an HTTP API or\n * an in-memory object.\n *\n * The shapes are the same `DocumentStore` and `CommentStore` the agent uses\n * (`@mjml-agent-editor/core`), so one adapter serves both sides.\n */\n\nimport type { CommentStore, DocumentStore } from \"@mjml-agent-editor/core\";\nimport { createContext, useContext, useMemo, useState, type ReactNode } from \"react\";\n\nimport { mergeLabels, type EditorLabels } from \"./labels.js\";\n\n/** An image the host has stored on the user's behalf. */\nexport interface ImageAsset {\n readonly id: string;\n readonly url: string;\n readonly name?: string;\n}\n\n/**\n * The user's own images: uploaded once, then picked from a gallery in any email.\n *\n * Without one the picker accepts only URLs. GrapesJS would otherwise inline a dropped file\n * as base64: most mail clients block `data:` images, and every agent turn pays for the bytes.\n */\nexport interface ImageLibrary {\n /** Newest first. `quota` is shown in the picker when given. */\n list(): Promise<{\n readonly images: readonly ImageAsset[];\n readonly quota?: { readonly used: number; readonly limit: number };\n }>;\n upload(file: File): Promise<ImageAsset>;\n /**\n * What `upload` takes, as an `<input accept>` value (\"image/png,image/jpeg\"). The picker\n * refuses anything else before sending it. Defaults to any image.\n */\n readonly accept?: string;\n /**\n * Omit to make the gallery read-only — for a host that caps uploads and cannot reclaim\n * the storage, deleting would only be a way round the cap.\n */\n remove?(id: string): Promise<void>;\n}\n\nexport interface EditorStores {\n readonly documents: DocumentStore;\n readonly comments: CommentStore;\n readonly images?: ImageLibrary;\n}\n\nconst StoreContext = createContext<EditorStores | null>(null);\nconst LabelContext = createContext<EditorLabels | null>(null);\ntype AgentTurn = {\n /** Set by the chat panel, from its pre-turn save to the turn's end. */\n running: boolean;\n setRunning: (running: boolean) => void;\n /**\n * Set by the canvas, from a turn's start until the document it left is loaded. The chat\n * waits for it before the next turn, so a turn never starts on a stale canvas.\n */\n locked: boolean;\n setLocked: (locked: boolean) => void;\n};\n\nconst TurnContext = createContext<AgentTurn>({\n running: false,\n setRunning: () => {},\n locked: false,\n setLocked: () => {},\n});\n\nexport function EditorStoreProvider({\n stores,\n labels,\n children,\n}: {\n stores: EditorStores;\n /**\n * Overrides for the copy the editor renders. Omitted, everything is English.\n *\n * It rides on this provider rather than a second one because the host already has to\n * wrap the editor in exactly one place, and a component that needs a store almost always\n * needs a word too.\n */\n labels?: Partial<EditorLabels>;\n children: ReactNode;\n}) {\n const merged = useMemo(() => mergeLabels(labels), [labels]);\n const [running, setRunning] = useState(false);\n const [locked, setLocked] = useState(false);\n const turn = useMemo(() => ({ running, setRunning, locked, setLocked }), [running, locked]);\n return (\n <StoreContext.Provider value={stores}>\n <LabelContext.Provider value={merged}>\n <TurnContext.Provider value={turn}>{children}</TurnContext.Provider>\n </LabelContext.Provider>\n </StoreContext.Provider>\n );\n}\n\n/**\n * The agent turn, shared by the chat and the canvas. The canvas is read only meanwhile: the\n * agent and the visitor would otherwise both write the document.\n */\nexport function useAgentTurn(): AgentTurn {\n return useContext(TurnContext);\n}\n\n/**\n * Falls back to the defaults instead of throwing, unlike the stores.\n *\n * A missing store means the editor cannot work at all and should say so loudly; missing copy\n * means English, which is a perfectly good outcome and keeps the components renderable in\n * isolation.\n */\nexport function useLabels(): EditorLabels {\n return useContext(LabelContext) ?? mergeLabels();\n}\n\nfunction useStores(): EditorStores {\n const stores = useContext(StoreContext);\n if (!stores) {\n throw new Error(\n \"The editor needs an <EditorStoreProvider>. Wrap the editor and pass a DocumentStore \" +\n \"and a CommentStore — see packages/editor/README.md.\",\n );\n }\n return stores;\n}\n\nexport function useDocumentStore(): DocumentStore {\n return useStores().documents;\n}\n\nexport function useCommentStore(): CommentStore {\n return useStores().comments;\n}\n\nexport function useImageLibrary(): ImageLibrary | undefined {\n return useStores().images;\n}\n","/**\n * Every word the editor puts on screen.\n *\n * It was all inline English, which quietly made the package unusable to anyone building in\n * another language: adopting it meant forking it to translate a button. The defaults below\n * keep existing hosts working unchanged, and a host overrides only the keys it cares about.\n *\n * Flat and shallow-merged on purpose. A nested dictionary reads better and merges worse —\n * overriding one key inside a group would silently drop its siblings. `toolLabels` is the\n * single nested map, and it is merged key by key for exactly that reason.\n *\n * Values that interpolate are functions rather than templates with placeholders, so the\n * translator controls word order instead of the format string.\n */\n\nexport interface EditorLabels {\n // Header\n readonly appName: string;\n readonly emailNameAria: string;\n readonly renameFailed: string;\n readonly saving: string;\n readonly saved: string;\n readonly saveFailed: string;\n readonly undo: string;\n readonly redo: string;\n readonly contentWidth: string;\n readonly preview: string;\n readonly closePreview: string;\n readonly previewFrameTitle: string;\n readonly sourceCode: string;\n readonly exportHtml: string;\n readonly exportMjml: string;\n /** `kind` is already upper-cased (\"MJML\", \"HTML\"). */\n readonly copied: (kind: string) => string;\n\n // Canvas\n readonly searchBlocks: string;\n readonly expandAll: string;\n readonly selectAnElement: string;\n readonly attributes: string;\n readonly style: string;\n readonly rteFont: string;\n readonly rteSize: string;\n readonly rteTextColor: string;\n readonly rteBackgroundColor: string;\n readonly documentLoadFailed: string;\n readonly documentSaveFailed: string;\n readonly retry: string;\n\n // Image picker\n readonly chooseImage: string;\n readonly imagePickerTitle: string;\n readonly imagePickerDrop: string;\n readonly imagePickerBrowse: string;\n readonly imagePickerUrlPlaceholder: string;\n readonly imagePickerUseUrl: string;\n readonly imagePickerGallery: string;\n readonly imagePickerEmpty: string;\n readonly imagePickerQuota: (used: number, limit: number) => string;\n readonly imagePickerQuotaFull: string;\n readonly imagePickerDelete: string;\n readonly imagePickerConfirmDelete: string;\n readonly imagePickerLoadFailed: string;\n readonly imageUploadFailed: string;\n readonly imageDeleteFailed: string;\n readonly imagePickerUnsupported: (fileName: string) => string;\n readonly imageFieldRemove: string;\n\n // Color picker\n readonly colorPickerOpen: string;\n readonly colorPickerEyedropper: string;\n readonly colorPickerDocument: string;\n readonly colorPickerPresets: string;\n readonly colorPickerTransparent: string;\n\n // Comments\n readonly commentOnSection: string;\n readonly closeComments: string;\n readonly noCommentsYet: string;\n readonly resolveComment: string;\n readonly addComment: string;\n readonly addCommentPlaceholder: string;\n\n // Chat\n readonly chatEmptyTitle: string;\n readonly chatEmptyDescription: string;\n readonly agentWorking: string;\n readonly reasoning: string;\n readonly completedSteps: (count: number, failed: number) => string;\n /** Under a turn that hit the backend's step ceiling before it could reply. */\n readonly stepLimitReached: string;\n readonly applyComments: string;\n readonly chatPlaceholder: string;\n readonly stop: string;\n readonly send: string;\n readonly chatError: string;\n /** Keyed by tool name; merged key by key rather than replaced wholesale. */\n readonly toolLabels: Readonly<Record<string, string>>;\n}\n\nexport const DEFAULT_LABELS: EditorLabels = {\n appName: \"MJML Editor\",\n emailNameAria: \"Email name\",\n renameFailed: \"Failed to rename.\",\n saving: \"Saving…\",\n saved: \"Saved\",\n saveFailed: \"Save failed\",\n undo: \"Undo\",\n redo: \"Redo\",\n contentWidth: \"Content width\",\n preview: \"Preview\",\n closePreview: \"Close preview\",\n previewFrameTitle: \"Email preview\",\n sourceCode: \"Source code\",\n exportHtml: \"Export HTML\",\n exportMjml: \"Export MJML\",\n copied: (kind) => `${kind} copied.`,\n\n searchBlocks: \"Search blocks…\",\n expandAll: \"Expand all\",\n selectAnElement: \"Select an element…\",\n attributes: \"Attributes\",\n style: \"Style\",\n rteFont: \"Font\",\n rteSize: \"Size\",\n rteTextColor: \"Text color\",\n rteBackgroundColor: \"Background color\",\n documentLoadFailed: \"Failed to load the document.\",\n documentSaveFailed: \"Failed to save changes.\",\n retry: \"Retry\",\n\n chooseImage: \"Choose image\",\n imagePickerTitle: \"Choose an image\",\n imagePickerDrop: \"Drag images here, or\",\n imagePickerBrowse: \"Browse files\",\n imagePickerUrlPlaceholder: \"…or paste an image URL\",\n imagePickerUseUrl: \"Use URL\",\n imagePickerGallery: \"Your images\",\n imagePickerEmpty: \"No images yet — the ones you upload will be kept here.\",\n imagePickerQuota: (used, limit) => `${used} of ${limit} uploads used`,\n imagePickerQuotaFull: \"Upload limit reached — pick an image below or paste a URL.\",\n imagePickerDelete: \"Delete image\",\n imagePickerConfirmDelete: \"Click again to delete\",\n imagePickerLoadFailed: \"Failed to load your images.\",\n imageUploadFailed: \"Failed to upload the image.\",\n imageDeleteFailed: \"Failed to delete the image.\",\n imagePickerUnsupported: (fileName) => `${fileName} is not a supported image type.`,\n imageFieldRemove: \"Remove image\",\n\n colorPickerOpen: \"Pick a color\",\n colorPickerEyedropper: \"Pick from the screen\",\n colorPickerDocument: \"In this email\",\n colorPickerPresets: \"Presets\",\n colorPickerTransparent: \"Transparent\",\n\n commentOnSection: \"Comment on the whole section\",\n closeComments: \"Close\",\n noCommentsYet: \"No comments yet — add the first one.\",\n resolveComment: \"Resolve\",\n addComment: \"Add\",\n addCommentPlaceholder: \"Add a comment… (⌘/Ctrl+Enter to send)\",\n\n chatEmptyTitle: \"Start a conversation with the agent\",\n chatEmptyDescription:\n \"Describe the email (goal, tone, content) and paste your data — the agent will design the sections and images.\",\n agentWorking: \"Agent is working…\",\n reasoning: \"Reasoning\",\n completedSteps: (count, failed) =>\n failed > 0 ? `Completed steps (${count}, ${failed} failed)` : `Completed steps (${count})`,\n stepLimitReached: \"The agent ran out of steps for this turn. Ask it to continue.\",\n applyComments: \"Apply changes from comments\",\n chatPlaceholder: \"Describe what you want to change… (Enter = send, Shift+Enter = new line)\",\n stop: \"Stop\",\n send: \"Send\",\n chatError: \"Agent chat error.\",\n\n toolLabels: {\n get_document: \"Reading document\",\n get_section: \"Reading section\",\n set_document: \"Saving whole email\",\n set_section: \"Replacing section\",\n insert_section: \"Adding section\",\n remove_section: \"Removing section\",\n generate_image: \"Generating image\",\n list_open_comments: \"Reading comments\",\n resolve_comment: \"Resolving comment\",\n },\n};\n\n/**\n * Overrides on top of the defaults. `toolLabels` merges rather than replaces, so a host\n * renaming one step does not lose the other eight.\n */\nexport function mergeLabels(overrides?: Partial<EditorLabels>): EditorLabels {\n if (!overrides) return DEFAULT_LABELS;\n return {\n ...DEFAULT_LABELS,\n ...overrides,\n toolLabels: { ...DEFAULT_LABELS.toolLabels, ...overrides.toolLabels },\n };\n}\n","export {\n EditorStoreProvider,\n useCommentStore,\n useDocumentStore,\n useImageLibrary,\n useLabels,\n type EditorStores,\n type ImageAsset,\n type ImageLibrary,\n} from \"./stores.js\";\n\nexport { DEFAULT_LABELS, mergeLabels, type EditorLabels } from \"./labels.js\";\n\n/**\n * Re-exported from `core`, where it moved so a server could import it. Kept here because\n * removing an export is a breaking change for no gain — but a route handler should reach\n * for `@mjml-agent-editor/core`, since everything in this entry point is client-only.\n */\nexport { STARTER_MJML } from \"@mjml-agent-editor/core\";\n\nexport { default as EditorHeader } from \"./components/editor/EditorHeader.js\";\nexport { default as ChatPanel } from \"./components/chat/ChatPanel.js\";\nexport { default as CanvasComments } from \"./components/comments/CanvasComments.js\";\nexport { Markdown } from \"./components/chat/Markdown.js\";\n\n// Types only — erased at build time, so this does not pull the canvas in.\nexport type { EditorApi, EditorState, SaveStatus } from \"./components/editor/EmailEditor.js\";\n\nexport type {\n CommentStore,\n CommentTarget,\n DocumentPatch,\n DocumentStore,\n EmailDocument,\n SectionComment,\n} from \"@mjml-agent-editor/core\";\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/80\",\n outline:\n \"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground\",\n ghost:\n \"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50\",\n destructive:\n \"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default:\n \"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n xs: \"h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5\",\n lg: \"h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n icon: \"size-8\",\n \"icon-xs\":\n \"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\":\n \"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg\",\n \"icon-lg\": \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","import * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Input({ className, type, ...props }: React.ComponentProps<\"input\">) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n \"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Input };\n","import { cn } from \"../../lib/utils\";\nimport { Loader2Icon } from \"lucide-react\";\n\nfunction Spinner({ className, ...props }: React.ComponentProps<\"svg\">) {\n return (\n <Loader2Icon\n data-slot=\"spinner\"\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(\"size-4 animate-spin\", className)}\n {...props}\n />\n );\n}\n\nexport { Spinner };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />;\n}\n\nfunction PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />;\n}\n\nfunction PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />;\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden\",\n className,\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n );\n}\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Dialog as DialogPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"./button\";\nimport { XIcon } from \"lucide-react\";\n\nfunction Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {\n return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n}) {\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close data-slot=\"dialog-close\" asChild>\n <Button variant=\"ghost\" className=\"absolute top-2 right-2\" size=\"icon-sm\">\n <XIcon />\n <span className=\"sr-only\">Close</span>\n </Button>\n </DialogPrimitive.Close>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div data-slot=\"dialog-header\" className={cn(\"flex flex-col gap-2\", className)} {...props} />\n );\n}\n\nfunction DialogFooter({\n className,\n showCloseButton = false,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showCloseButton?: boolean;\n}) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <DialogPrimitive.Close asChild>\n <Button variant=\"outline\">Close</Button>\n </DialogPrimitive.Close>\n )}\n </div>\n );\n}\n\nfunction DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"font-heading text-base leading-none font-medium\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\n \"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n","/**\n * The host's own sentence when a save or a turn was refused, or null for anything else.\n *\n * A turn refusal is a `{\"error\": \"...\"}` body (the agent contract); a store refusal is the\n * host's exception with a 4xx `status`. A non-4xx status rules the message out first, so a\n * 500 whose body happens to be `{\"error\": ...}` is never shown verbatim.\n */\nexport function refusal(error: unknown): string | null {\n if (!(error instanceof Error)) return null;\n\n const status = (error as { status?: unknown }).status;\n // null: no status at all, which is every turn rejection.\n const deliberate = typeof status === \"number\" ? status >= 400 && status < 500 : null;\n if (deliberate === false) return null;\n\n if (error.message.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(error.message) as { error?: unknown };\n return typeof parsed.error === \"string\" && parsed.error.trim() !== \"\" ? parsed.error : null;\n } catch {\n return null;\n }\n }\n\n // A proxy answering for the host sends an HTML page, not a sentence.\n const message = error.message.trim();\n return deliberate && message !== \"\" && !message.startsWith(\"<\") ? error.message : null;\n}\n\n/** Shared by autosave and the pre-turn flush, so one failing save shows one toast. */\nexport function saveToastId(docId: string): string {\n return `document-save-${docId}`;\n}\n","import * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Textarea({ className, ...props }: React.ComponentProps<\"textarea\">) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(\n \"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Textarea };\n","\"use client\";\n\nimport { useEditorMaybe } from \"@grapesjs/react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Check, MessageSquarePlus, X } from \"lucide-react\";\n\nimport {\n useLabels,\n useCommentStore,\n type CommentTarget,\n type SectionComment,\n} from \"../../index.js\";\nimport { Popover, PopoverAnchor, PopoverContent } from \"../ui/popover\";\nimport { Button } from \"../ui/button\";\nimport { Textarea } from \"../ui/textarea\";\n\ntype Props = {\n docId: string;\n /** Sparkles-button target → opens the compose popover for that element. */\n composeTarget: CommentTarget | null;\n onComposeConsumed: () => void;\n /** Bump to refetch comments (after agent turns). */\n refreshSignal: number;\n onOpenCountChange: (n: number) => void;\n};\n\ntype Pos = { top: number; left: number };\n\n// Anchor key = the stable css-class the comment is attached to.\nconst keyOf = (c: Pick<SectionComment, \"objectId\" | \"sectionId\">) =>\n c.objectId ? `obj-${c.objectId}` : `sec-${c.sectionId}`;\nconst keyOfTarget = (t: CommentTarget) => (t.objectId ? `obj-${t.objectId}` : `sec-${t.sectionId}`);\n\nexport default function CanvasComments({\n docId,\n composeTarget,\n onComposeConsumed,\n refreshSignal,\n onOpenCountChange,\n}: Props) {\n // Injected by the host rather than imported, so comments carry no database.\n const commentStore = useCommentStore();\n const labels = useLabels();\n const editor = useEditorMaybe();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [comments, setComments] = useState<SectionComment[]>([]);\n const [positions, setPositions] = useState<Record<string, Pos>>({});\n const [activeKey, setActiveKey] = useState<string | null>(null);\n const [composeInfo, setComposeInfo] = useState<CommentTarget | null>(null);\n const [body, setBody] = useState(\"\");\n\n const reload = useCallback(() => {\n commentStore.list(docId).then(setComments).catch(console.error);\n }, [docId]);\n\n useEffect(() => {\n reload();\n }, [reload, refreshSignal]);\n\n const open = useMemo(() => comments.filter((c) => c.status === \"open\"), [comments]);\n\n useEffect(() => {\n onOpenCountChange(open.length);\n }, [open.length, onOpenCountChange]);\n\n // One pin per commented anchor (element or section), with its open comments.\n const pins = useMemo(() => {\n const map = new Map<string, SectionComment[]>();\n for (const c of open) {\n const k = keyOf(c);\n const arr = map.get(k) ?? [];\n arr.push(c);\n map.set(k, arr);\n }\n return [...map.entries()].map(([key, list]) => ({ key, list }));\n }, [open]);\n\n // Refs so the event-driven recompute reads fresh values without re-subscribing.\n const openRef = useRef(open);\n openRef.current = open;\n const activeRef = useRef(activeKey);\n activeRef.current = activeKey;\n const positionsRef = useRef<Record<string, Pos>>({});\n\n // Positions of pins/popover, computed from the elements' rects in the canvas iframe.\n const recompute = useCallback(() => {\n const ed = editor;\n const container = containerRef.current;\n if (!ed || !container) return;\n const frame = ed.Canvas.getFrameEl?.();\n const cdoc = ed.Canvas.getDocument?.();\n if (!frame || !cdoc) return;\n const cRect = container.getBoundingClientRect();\n const fRect = frame.getBoundingClientRect();\n // The iframe is scaled by GrapesJS zoom (transform), so its on-screen rect\n // (fRect) is scaled while elements INSIDE it report unscaled coordinates.\n // Map an inner coordinate to screen by this zoom factor.\n const zoom = frame.offsetWidth ? fRect.width / frame.offsetWidth : 1;\n const keys = new Set<string>(openRef.current.map(keyOf));\n if (activeRef.current) keys.add(activeRef.current);\n const next: Record<string, Pos> = {};\n keys.forEach((k) => {\n const el = cdoc.querySelector(`.${k}`) as HTMLElement | null;\n if (!el) return;\n const r = el.getBoundingClientRect();\n // Element's top-right corner mapped from iframe coords to screen (× zoom),\n // then a small constant offset so the pin sits just past the right edge.\n next[k] = {\n top: fRect.top - cRect.top + r.top * zoom + 4,\n left: fRect.left - cRect.left + (r.left + r.width) * zoom + 8,\n };\n });\n if (JSON.stringify(positionsRef.current) !== JSON.stringify(next)) {\n positionsRef.current = next;\n setPositions(next);\n }\n }, [editor]);\n\n // Recompute on canvas activity (edits, scroll, device change, resize) + a\n // low-frequency safety net for layout shifts the events miss.\n useEffect(() => {\n const ed = editor;\n if (!ed) return;\n const evts = [\n \"canvas:update\",\n \"update\",\n \"change:device\",\n \"load\",\n \"component:update\",\n \"component:mount\",\n ];\n evts.forEach((e) => ed.on(e, recompute));\n const win = ed.Canvas.getWindow?.();\n win?.addEventListener(\"scroll\", recompute, true);\n window.addEventListener(\"resize\", recompute);\n const id = window.setInterval(recompute, 500);\n return () => {\n evts.forEach((e) => ed.off(e, recompute));\n win?.removeEventListener(\"scroll\", recompute, true);\n window.removeEventListener(\"resize\", recompute);\n window.clearInterval(id);\n };\n }, [editor, recompute]);\n\n useEffect(() => {\n recompute();\n }, [comments, activeKey, recompute]);\n\n // Sparkles button → open the compose popover anchored to that element.\n useEffect(() => {\n if (!composeTarget) return;\n setComposeInfo(composeTarget);\n setActiveKey(keyOfTarget(composeTarget));\n setBody(\"\");\n requestAnimationFrame(recompute);\n }, [composeTarget, recompute]);\n\n const activeComments = activeKey ? open.filter((c) => keyOf(c) === activeKey) : [];\n const activeTarget: CommentTarget | null = (() => {\n if (!activeKey) return null;\n const c = activeComments[0];\n if (c) return { sectionId: c.sectionId, objectId: c.objectId, objectLabel: c.objectLabel };\n if (composeInfo && keyOfTarget(composeInfo) === activeKey) return composeInfo;\n return null;\n })();\n const activePos = activeKey ? positions[activeKey] : undefined;\n // Keep the last real anchor position through the popover's close animation.\n // On close activeKey→null makes activePos undefined; without this the anchor\n // would snap to (0,0) and the still-animating popover would flash there.\n const lastAnchorPos = useRef<Pos>({ top: 0, left: 0 });\n if (activePos) lastAnchorPos.current = activePos;\n const anchorPos = activePos ?? lastAnchorPos.current;\n\n const closePopover = useCallback(() => {\n setActiveKey(null);\n setBody(\"\");\n if (composeInfo) {\n setComposeInfo(null);\n onComposeConsumed();\n }\n }, [composeInfo, onComposeConsumed]);\n\n // Close the popover on a click inside the canvas iframe (Radix's outside-click\n // detection lives in the top document and misses iframe clicks).\n useEffect(() => {\n if (!activeKey || !editor) return;\n const doc = editor.Canvas.getDocument?.();\n if (!doc) return;\n const onDown = () => closePopover();\n doc.addEventListener(\"mousedown\", onDown, true);\n return () => doc.removeEventListener(\"mousedown\", onDown, true);\n }, [activeKey, editor, closePopover]);\n\n const submit = async () => {\n if (!activeTarget || !body.trim()) return;\n try {\n await commentStore.add(docId, activeTarget, body.trim());\n setBody(\"\");\n if (composeInfo) {\n setComposeInfo(null);\n onComposeConsumed();\n }\n reload();\n } catch (e) {\n console.error(e);\n }\n };\n\n const resolve = async (id: string) => {\n try {\n await commentStore.resolve(id);\n reload();\n } catch (e) {\n console.error(e);\n }\n };\n\n return (\n <div ref={containerRef} className=\"pointer-events-none absolute inset-0 z-20 overflow-hidden\">\n {pins.map(({ key, list }) => {\n const pos = positions[key];\n if (!pos) return null;\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => {\n setActiveKey((k) => (k === key ? null : key));\n setBody(\"\");\n }}\n style={{ top: pos.top, left: pos.left }}\n className=\"pointer-events-auto absolute flex h-6 min-w-6 items-center justify-center rounded-full rounded-bl-none bg-brand px-1.5 text-xs font-semibold text-brand-fg shadow-md ring-2 ring-white transition-transform hover:scale-110\"\n title={`${list.length} comment(s)`}\n >\n {list.length}\n </button>\n );\n })}\n\n <Popover open={!!activeKey && !!activePos} onOpenChange={(o) => !o && closePopover()}>\n <PopoverAnchor asChild>\n <div\n style={{\n position: \"absolute\",\n top: anchorPos.top,\n left: anchorPos.left,\n width: 1,\n height: 1,\n }}\n />\n </PopoverAnchor>\n <PopoverContent\n side=\"right\"\n align=\"start\"\n // Disable the close animation: while it plays, Radix keeps the content\n // mounted but floating-ui drops its positioning for a frame, flashing\n // the popover at (0,0). No exit animation → instant unmount, no flash.\n className=\"pointer-events-auto w-80 p-0 data-[state=closed]:!animate-none data-[state=closed]:!duration-0\"\n >\n <div className=\"flex items-start justify-between gap-2 border-b px-3 py-2 text-xs text-muted-foreground\">\n <span className=\"min-w-0\">\n {activeTarget?.objectId ? (\n <>\n Comment on{\" \"}\n <span className=\"font-medium text-foreground\">{activeTarget.objectLabel}</span>\n </>\n ) : (\n <>\n {labels.commentOnSection}{\" \"}\n <code className=\"font-mono\">{activeTarget?.sectionId}</code>\n </>\n )}\n </span>\n <button\n type=\"button\"\n onClick={closePopover}\n aria-label={labels.closeComments}\n className=\"-mr-1 -mt-0.5 shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n <div className=\"max-h-64 space-y-2 overflow-y-auto p-3\">\n {activeComments.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">{labels.noCommentsYet}</p>\n )}\n {activeComments.map((c) => (\n <div key={c.id} className=\"rounded-md border bg-muted/40 p-2 text-sm\">\n <p className=\"whitespace-pre-wrap\">{c.body}</p>\n <button\n onClick={() => void resolve(c.id)}\n className=\"mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <Check className=\"size-3\" /> {labels.resolveComment}\n </button>\n </div>\n ))}\n </div>\n <div className=\"border-t p-3\">\n <Textarea\n autoFocus\n value={body}\n onChange={(e) => setBody(e.target.value)}\n placeholder={labels.addCommentPlaceholder}\n className=\"min-h-14 resize-none text-sm\"\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n void submit();\n }\n }}\n />\n <div className=\"mt-2 flex justify-end\">\n <Button size=\"sm\" disabled={!body.trim()} onClick={() => void submit()}>\n <MessageSquarePlus /> {labels.addComment}\n </Button>\n </div>\n </div>\n </PopoverContent>\n </Popover>\n </div>\n );\n}\n","/** Debounced autosave, and the flush a turn waits on. Whether to write is `save`'s call. */\nexport interface SaveQueue {\n /** Restarts the debounce. */\n schedule(): void;\n /** Drops a scheduled autosave. */\n cancel(): void;\n /** Runs `save` after any work under way; rejects if it fails. */\n flush(): Promise<void>;\n /** Runs `task` after any work under way, such as a reload that must not race a save. */\n run<T>(task: () => Promise<T>): Promise<T>;\n}\n\nexport function createSaveQueue(save: () => Promise<void>, delayMs: number): SaveQueue {\n let timer: ReturnType<typeof setTimeout> | null = null;\n // Work runs one after another, so an older write or read can never land after a newer one.\n let tail: Promise<unknown> = Promise.resolve();\n\n const cancel = () => {\n if (timer) clearTimeout(timer);\n timer = null;\n };\n\n const run = <T>(task: () => Promise<T>): Promise<T> => {\n const next = tail.then(task);\n tail = next.catch(() => {});\n return next;\n };\n\n return {\n schedule() {\n cancel();\n timer = setTimeout(() => void run(save).catch(() => {}), delayMs);\n },\n cancel,\n flush() {\n cancel();\n return run(save);\n },\n run,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Switch as SwitchPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Switch({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof SwitchPrimitive.Root> & {\n size?: \"sm\" | \"default\";\n}) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n data-size={size}\n className={cn(\n \"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className=\"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] dark:data-checked:bg-primary-foreground group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 dark:data-unchecked:bg-foreground\"\n />\n </SwitchPrimitive.Root>\n );\n}\n\nexport { Switch };\n","\"use client\";\n\n/**\n * The image picker, replacing GrapesJS's asset modal.\n *\n * GrapesJS's own modal could not be styled into the editor, and its \"click to upload\" area\n * was a transparent file input that did not open the file browser at all. This one is\n * opened by GrapesJS (`assetManager.custom`), so the canvas double-click, the toolbar button\n * and the sidebar all land here, and selecting goes back through GrapesJS to the image.\n */\n\nimport { useCallback, useEffect, useRef, useState, type DragEvent } from \"react\";\nimport { Check, ImageUp, Link2, Trash2 } from \"lucide-react\";\nimport { toast } from \"sonner\";\n\nimport { useImageLibrary, useLabels, type ImageAsset } from \"../../index.js\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../ui/button\";\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from \"../ui/dialog\";\nimport { Input } from \"../ui/input\";\nimport { Spinner } from \"../ui/spinner\";\n\ntype Quota = { readonly used: number; readonly limit: number };\n\nexport default function ImagePicker({\n open,\n currentUrl,\n onSelect,\n onClose,\n}: {\n open: boolean;\n /** The image being replaced, marked in the gallery. */\n currentUrl?: string;\n onSelect: (url: string) => void;\n onClose: () => void;\n}) {\n const library = useImageLibrary();\n const labels = useLabels();\n const [images, setImages] = useState<readonly ImageAsset[]>([]);\n const [quota, setQuota] = useState<Quota>();\n const [loading, setLoading] = useState(false);\n const [pending, setPending] = useState(0);\n const [dragging, setDragging] = useState(false);\n const [url, setUrl] = useState(\"\");\n const [confirmId, setConfirmId] = useState<string | null>(null);\n const fileInput = useRef<HTMLInputElement>(null);\n // Bumped on every open and close. An upload still running when the picker closes, or\n // reopens for another image, must not apply its result to whatever is open now.\n const session = useRef(0);\n // Read through refs so neither a new `labels` object nor a new `onSelect` from a parent\n // render re-runs the open effect — that would wipe a half-typed URL.\n const latest = useRef({ labels, onSelect });\n latest.current = { labels, onSelect };\n // Opening, uploading and deleting each list the gallery; only the newest answer is shown.\n const lists = useRef(0);\n\n const refresh = useCallback(async () => {\n if (!library) return;\n const mine = ++lists.current;\n setLoading(true);\n try {\n const result = await library.list();\n if (mine !== lists.current) return;\n setImages(result.images);\n setQuota(result.quota);\n } catch (err) {\n console.error(err);\n if (mine === lists.current) toast.error(latest.current.labels.imagePickerLoadFailed);\n } finally {\n if (mine === lists.current) setLoading(false);\n }\n }, [library]);\n\n useEffect(() => {\n session.current += 1;\n if (!open) return;\n setUrl(\"\");\n setConfirmId(null);\n void refresh();\n // Once per opening; `refresh` reads the latest props through `latest`.\n }, [open]);\n // Nor after unmount, when the editor it would select into is gone.\n useEffect(() => () => void (session.current += 1), []);\n\n const accepts = (file: File) => matchesAccept(file, library?.accept);\n\n const full = quota !== undefined && quota.used >= quota.limit;\n\n const upload = async (files: FileList | null) => {\n if (!library || !files?.length) return;\n const mine = session.current;\n // The input's `accept` covers browsing; a drop bypasses it, so check here too rather\n // than send a whole file only to have the host refuse it.\n const list = Array.from(files).filter((f) => {\n if (accepts(f)) return true;\n toast.error(labels.imagePickerUnsupported(f.name));\n return false;\n });\n if (!list.length) return;\n setPending((n) => n + list.length);\n // Settled rather than all: an upload that succeeded has already been stored and\n // counted by the host, so one failure must not hide it.\n // Async, so an adapter that throws rather than rejects still settles.\n const results = await Promise.allSettled(list.map(async (f) => library.upload(f)));\n setPending((n) => n - list.length);\n for (const r of results) {\n if (r.status === \"rejected\") {\n console.error(r.reason);\n // The host's message is usually the actionable part (\"limit reached\", \"too large\").\n toast.error(r.reason instanceof Error ? r.reason.message : labels.imageUploadFailed);\n }\n }\n if (session.current !== mine) return;\n await refresh();\n // One file dropped while editing an image is meant for that image.\n const [only] = results;\n if (results.length === 1 && only?.status === \"fulfilled\" && session.current === mine) {\n latest.current.onSelect(only.value.url);\n }\n };\n\n const remove = async (image: ImageAsset) => {\n if (!library?.remove) return;\n // Two clicks rather than a confirm(): an image in use breaks every email showing it.\n if (confirmId !== image.id) {\n setConfirmId(image.id);\n return;\n }\n setConfirmId(null);\n try {\n await library.remove(image.id);\n await refresh();\n } catch (err) {\n console.error(err);\n toast.error(err instanceof Error ? err.message : labels.imageDeleteFailed);\n }\n };\n\n const onDrop = (e: DragEvent) => {\n e.preventDefault();\n setDragging(false);\n if (!full) void upload(e.dataTransfer.files);\n };\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && onClose()}>\n <DialogContent className=\"editor-dark gap-0 overflow-hidden border border-panel-border bg-panel p-0 text-panel-fg ring-0 sm:max-w-3xl\">\n <DialogHeader className=\"border-b border-panel-border px-5 py-4\">\n <DialogTitle>{labels.imagePickerTitle}</DialogTitle>\n {quota && (\n <DialogDescription className=\"text-panel-muted-fg\">\n {labels.imagePickerQuota(quota.used, quota.limit)}\n </DialogDescription>\n )}\n </DialogHeader>\n\n <div className=\"flex max-h-[70vh] flex-col gap-4 overflow-y-auto p-5\">\n {library &&\n (full ? (\n <p className=\"rounded-lg border border-panel-border bg-panel-elevated px-4 py-3 text-sm text-panel-muted-fg\">\n {labels.imagePickerQuotaFull}\n </p>\n ) : (\n <div\n onDragOver={(e) => {\n e.preventDefault();\n setDragging(true);\n }}\n onDragLeave={() => setDragging(false)}\n onDrop={onDrop}\n className={cn(\n \"flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed px-6 py-8 text-center transition-colors\",\n dragging ? \"border-brand bg-brand-soft\" : \"border-panel-border bg-panel-elevated\",\n )}\n >\n <ImageUp className=\"size-8 text-panel-muted-fg\" />\n <div className=\"flex flex-wrap items-center justify-center gap-2 text-sm text-panel-muted-fg\">\n <span>{labels.imagePickerDrop}</span>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"bg-brand text-brand-fg hover:bg-brand/90\"\n onClick={() => fileInput.current?.click()}\n >\n {labels.imagePickerBrowse}\n </Button>\n </div>\n <input\n ref={fileInput}\n type=\"file\"\n accept={library.accept ?? \"image/*\"}\n multiple\n hidden\n onChange={(e) => {\n void upload(e.target.files);\n e.target.value = \"\";\n }}\n />\n </div>\n ))}\n\n <form\n className=\"flex gap-2\"\n onSubmit={(e) => {\n e.preventDefault();\n const value = url.trim();\n if (value) onSelect(value);\n }}\n >\n <div className=\"relative flex-1\">\n <Link2 className=\"pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-panel-muted-fg\" />\n <Input\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n placeholder={labels.imagePickerUrlPlaceholder}\n className=\"h-9 border-panel-border bg-panel-elevated pl-8 text-panel-fg placeholder:text-panel-muted-fg\"\n />\n </div>\n <Button\n type=\"submit\"\n size=\"lg\"\n variant=\"outline\"\n disabled={!url.trim()}\n className=\"border-panel-border bg-panel-elevated text-panel-fg hover:bg-panel-hover hover:text-panel-fg\"\n >\n {labels.imagePickerUseUrl}\n </Button>\n </form>\n\n {library && (\n <section>\n <h3 className=\"mb-2 text-xs font-medium tracking-wide text-panel-muted-fg uppercase\">\n {labels.imagePickerGallery}\n </h3>\n {loading && images.length === 0 && pending === 0 ? (\n <div className=\"flex justify-center py-8\">\n <Spinner />\n </div>\n ) : images.length === 0 && pending === 0 ? (\n <p className=\"py-6 text-center text-sm text-panel-muted-fg\">\n {labels.imagePickerEmpty}\n </p>\n ) : (\n <ul className=\"grid grid-cols-3 gap-3 sm:grid-cols-4\">\n {Array.from({ length: pending }, (_, i) => (\n <li\n key={`pending-${i}`}\n className=\"flex aspect-[4/3] items-center justify-center rounded-lg border border-panel-border bg-panel-elevated\"\n >\n <Spinner />\n </li>\n ))}\n {images.map((image) => {\n const selected = image.url === currentUrl;\n const confirming = confirmId === image.id;\n return (\n <li key={image.id} className=\"group relative\">\n <button\n type=\"button\"\n onClick={() => onSelect(image.url)}\n title={image.name}\n className={cn(\n \"block aspect-[4/3] w-full overflow-hidden rounded-lg border bg-black/25 transition outline-none focus-visible:ring-2 focus-visible:ring-brand\",\n selected\n ? \"border-brand ring-2 ring-brand\"\n : \"border-panel-border hover:border-panel-muted-fg\",\n )}\n >\n {/* Contained, not cropped: email images are mostly wide banners, and\n choosing one means seeing its whole composition. */}\n <img\n src={image.url}\n alt={image.name ?? \"\"}\n loading=\"lazy\"\n className=\"size-full object-contain\"\n />\n </button>\n {selected && (\n <span className=\"pointer-events-none absolute top-1.5 left-1.5 flex size-5 items-center justify-center rounded-full bg-brand text-brand-fg\">\n <Check className=\"size-3\" />\n </span>\n )}\n {image.name && (\n <span className=\"pointer-events-none absolute inset-x-0 bottom-0 truncate rounded-b-lg bg-black/60 px-2 py-1 text-[11px] text-white opacity-0 transition group-hover:opacity-100\">\n {image.name}\n </span>\n )}\n {library.remove && (\n <button\n type=\"button\"\n onClick={() => void remove(image)}\n onBlur={() => confirming && setConfirmId(null)}\n title={\n confirming\n ? labels.imagePickerConfirmDelete\n : labels.imagePickerDelete\n }\n aria-label={labels.imagePickerDelete}\n className={cn(\n \"absolute top-1.5 right-1.5 flex h-6 items-center gap-1 rounded-md px-1.5 text-xs text-white transition focus-visible:opacity-100\",\n confirming\n ? \"bg-red-600 opacity-100\"\n : \"bg-black/60 opacity-0 group-hover:opacity-100 hover:bg-black/80\",\n )}\n >\n <Trash2 className=\"size-3.5\" />\n {confirming && labels.imagePickerConfirmDelete}\n </button>\n )}\n </li>\n );\n })}\n </ul>\n )}\n </section>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n/** The subset of `<input accept>` a picker needs: MIME types, `type/*`, and `.ext`. */\nfunction matchesAccept(file: File, accept: string | undefined): boolean {\n if (!accept) return file.type.startsWith(\"image/\");\n return accept.split(\",\").some((raw) => {\n const rule = raw.trim().toLowerCase();\n if (rule.startsWith(\".\")) return file.name.toLowerCase().endsWith(rule);\n if (rule.endsWith(\"/*\")) return file.type.startsWith(rule.slice(0, -1));\n return file.type === rule;\n });\n}\n","\"use client\";\n\n/**\n * The color control for every color property in the Style Manager.\n *\n * GrapesJS's own is a text box and a swatch that opens Spectrum, a jQuery picker that\n * cannot be styled into the editor. This keeps the text box — typing a hex is often the\n * fastest way — and opens a picker with the colors the email already uses, which is what\n * keeps a design consistent.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { HexAlphaColorPicker, HexColorPicker } from \"react-colorful\";\nimport { Pipette } from \"lucide-react\";\n\nimport type { EditorLabels } from \"../../labels.js\";\nimport { cn } from \"../../lib/utils\";\nimport { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from \"../ui/popover\";\n\n/** Web-safe neutrals and a few saturated hues; emails rarely want more than this. */\nconst PRESETS = [\n \"#000000\",\n \"#374151\",\n \"#6b7280\",\n \"#d1d5db\",\n \"#f3f4f6\",\n \"#ffffff\",\n \"#dc2626\",\n \"#ea580c\",\n \"#ca8a04\",\n \"#16a34a\",\n \"#0284c7\",\n \"#4f46e5\",\n \"#9333ea\",\n \"#db2777\",\n];\n\n// Chrome's EyeDropper; absent elsewhere, and so is the button.\ntype EyeDropperCtor = new () => { open: () => Promise<{ sRGBHex: string }> };\nconst eyeDropper = (): EyeDropperCtor | undefined =>\n (globalThis as { EyeDropper?: EyeDropperCtor }).EyeDropper;\n\nexport default function ColorField({\n value,\n placeholder,\n documentColors,\n labels,\n onChange,\n}: {\n value: string;\n /** The property's default, shown when no value is set. */\n placeholder?: string;\n /** Read when the picker opens, so it reflects the email as it is now. */\n documentColors: () => string[];\n /** Passed in rather than read from context: this renders in its own React root. */\n labels: EditorLabels;\n /** `partial` while dragging, so GrapesJS records one undo step per change, not per pixel. */\n onChange: (value: string, partial: boolean) => void;\n}) {\n const [text, setText] = useState(value);\n const [open, setOpen] = useState(false);\n const [used, setUsed] = useState<string[]>([]);\n // Whether a pointer is down on the picker, and the value its drag left as a partial update.\n const pressed = useRef(false);\n const dragged = useRef<string | null>(null);\n const fieldRef = useRef<HTMLDivElement>(null);\n // The picker opens beside the panel the field sits in, level with the field. Anchored to\n // the field itself it covered the panel, since a property can be half the panel wide.\n const anchor = useRef({\n getBoundingClientRect: () => {\n const field = fieldRef.current?.getBoundingClientRect() ?? new DOMRect();\n const panel = fieldRef.current?.closest(\".editor-dark\")?.getBoundingClientRect();\n return new DOMRect(panel?.right ?? field.right, field.top, 0, field.height);\n },\n });\n\n useEffect(() => setText(value), [value]);\n const latestOnChange = useRef(onChange);\n latestOnChange.current = onChange;\n // A drag ends as a real change, with an undo step, when the pointer is released: GrapesJS\n // writes to whatever is selected at that moment, and nothing else can be selected while\n // the pointer is captured. The view going away mid-drag ends it too.\n const release = useCallback(() => {\n pressed.current = false;\n if (dragged.current !== null) latestOnChange.current(dragged.current, false);\n dragged.current = null;\n }, []);\n // On the document: when the captured element is removed mid-drag (the popover closing, or\n // the alpha slider appearing), the browser sends the release there rather than to it.\n useEffect(() => {\n document.addEventListener(\"lostpointercapture\", release);\n return () => {\n document.removeEventListener(\"lostpointercapture\", release);\n release();\n };\n }, [release]);\n\n const hex = toHex(value) ?? toHex(placeholder ?? \"\") ?? \"#000000\";\n // Translucent colors keep their alpha, and get the slider to change it.\n const translucent = hex.length === 9;\n const empty = !value;\n\n const commit = (next: string) => {\n dragged.current = null;\n onChange(next, false);\n };\n\n // react-colorful reports no drag end, and moves by arrow keys as well; those are whole changes.\n const drag = (next: string) => {\n setText(next);\n if (!pressed.current) return commit(next);\n dragged.current = next;\n onChange(next, true);\n };\n\n const pickFromScreen = async () => {\n const Ctor = eyeDropper();\n if (!Ctor) return;\n try {\n const { sRGBHex } = await new Ctor().open();\n commit(sRGBHex);\n } catch {\n // Escape cancels the eyedropper; nothing to do.\n }\n };\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n if (next) setUsed(documentColors());\n // Closing by Escape or a click outside unmounts the hex input without a blur, so\n // a value typed there is committed here rather than lost.\n else if (text.trim() !== value) commit(text.trim());\n setOpen(next);\n }}\n >\n <PopoverAnchor virtualRef={anchor} />\n <div\n ref={fieldRef}\n className=\"flex h-8 w-full items-center gap-1.5 rounded-md border border-panel-border bg-panel-elevated pr-1.5 pl-2 focus-within:border-brand\"\n >\n <input\n value={text}\n placeholder={placeholder}\n spellCheck={false}\n onChange={(e) => setText(e.target.value)}\n onBlur={() => text !== value && commit(text.trim())}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") commit(text.trim());\n if (e.key === \"Escape\") setText(value);\n }}\n className=\"min-w-0 flex-1 bg-transparent font-mono text-xs text-panel-fg outline-none placeholder:text-panel-muted-fg\"\n />\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n title={labels.colorPickerOpen}\n aria-label={labels.colorPickerOpen}\n className=\"relative size-5 shrink-0 overflow-hidden rounded border border-white/20 outline-none focus-visible:ring-2 focus-visible:ring-brand\"\n >\n <Checkerboard />\n {!empty && <span className=\"absolute inset-0\" style={{ background: value }} />}\n </button>\n </PopoverTrigger>\n </div>\n <PopoverContent\n side=\"right\"\n align=\"start\"\n sideOffset={8}\n className=\"editor-dark flex w-52 flex-col gap-2.5 border border-panel-border bg-panel p-2.5 text-panel-fg\"\n >\n <div\n onPointerDownCapture={(e) => {\n pressed.current = true;\n // Captured, so a release over the canvas iframe still reaches this document. On the\n // target, not this wrapper: the mousedown react-colorful handles follows the capture.\n (e.target as Element).setPointerCapture(e.pointerId);\n }}\n >\n {translucent ? (\n <HexAlphaColorPicker color={hex} onChange={drag} className=\"color-field-picker\" />\n ) : (\n <HexColorPicker color={hex} onChange={drag} className=\"color-field-picker\" />\n )}\n </div>\n\n <div className=\"flex items-center gap-1.5\">\n <span\n className=\"size-6 shrink-0 rounded border border-white/20\"\n style={{ background: hex }}\n />\n <input\n value={text}\n spellCheck={false}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={(e) => e.key === \"Enter\" && commit(text.trim())}\n onBlur={() => text !== value && commit(text.trim())}\n className=\"h-6 min-w-0 flex-1 rounded border border-panel-border bg-panel-elevated px-1.5 font-mono text-[11px] outline-none focus:border-brand\"\n />\n {eyeDropper() && (\n <button\n type=\"button\"\n onClick={() => void pickFromScreen()}\n title={labels.colorPickerEyedropper}\n aria-label={labels.colorPickerEyedropper}\n className=\"flex size-6 shrink-0 items-center justify-center rounded border border-panel-border bg-panel-elevated text-panel-muted-fg hover:text-panel-fg\"\n >\n <Pipette className=\"size-3\" />\n </button>\n )}\n </div>\n\n {used.length > 0 && (\n <Swatches\n title={labels.colorPickerDocument}\n colors={used}\n current={hex}\n onPick={commit}\n />\n )}\n <Swatches\n title={labels.colorPickerPresets}\n colors={PRESETS}\n current={hex}\n onPick={commit}\n transparentLabel={labels.colorPickerTransparent}\n />\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction Swatches({\n title,\n colors,\n current,\n onPick,\n transparentLabel,\n}: {\n title: string;\n colors: string[];\n current: string;\n onPick: (color: string) => void;\n /** Adds a \"transparent\" swatch at the end when given. */\n transparentLabel?: string;\n}) {\n return (\n <div>\n <p className=\"mb-1 text-[10px] font-medium tracking-wide text-panel-muted-fg uppercase\">\n {title}\n </p>\n <div className=\"grid grid-cols-8 gap-1\">\n {colors.map((color) => (\n <button\n key={color}\n type=\"button\"\n title={color}\n onClick={() => onPick(color)}\n className={cn(\n \"aspect-square rounded border border-white/15 outline-none hover:scale-110 focus-visible:ring-2 focus-visible:ring-brand\",\n color.toLowerCase() === current.toLowerCase() && \"ring-2 ring-brand\",\n )}\n style={{ background: color }}\n />\n ))}\n {transparentLabel && (\n <button\n type=\"button\"\n title={transparentLabel}\n onClick={() => onPick(\"transparent\")}\n className=\"relative aspect-square overflow-hidden rounded border border-white/15 outline-none hover:scale-110 focus-visible:ring-2 focus-visible:ring-brand\"\n >\n <Checkerboard />\n </button>\n )}\n </div>\n </div>\n );\n}\n\nfunction Checkerboard() {\n return (\n <span\n className=\"absolute inset-0\"\n style={{\n backgroundImage: \"repeating-conic-gradient(#9ca3af 0% 25%, #e5e7eb 0% 50%)\",\n backgroundSize: \"8px 8px\",\n }}\n />\n );\n}\n\n/**\n * `#rgb`, `#rrggbb(aa)` and `rgb()/rgba()` to `#rrggbb`, or `#rrggbbaa` when translucent\n * unless `opaque` is set; anything else (names, `transparent`) to null.\n */\nexport function toHex(value: string, { opaque = false } = {}): string | null {\n const v = value.trim().toLowerCase();\n const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/.exec(v);\n if (short) return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`;\n if (/^#[0-9a-f]{6}$/.test(v)) return v;\n if (/^#[0-9a-f]{8}$/.test(v)) return opaque || v.endsWith(\"ff\") ? v.slice(0, 7) : v;\n const rgb = /^rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)(?:[\\s,/]+([\\d.]+%?))?/.exec(v);\n if (rgb) {\n const channels = [rgb[1], rgb[2], rgb[3]].map((n) => Math.min(255, Number(n)));\n const alphaText = rgb[4];\n const alpha = alphaText?.endsWith(\"%\") ? parseFloat(alphaText) / 100 : Number(alphaText ?? 1);\n if (!opaque && alpha < 1) channels.push(Math.round(Math.max(0, alpha) * 255));\n return `#${channels.map((n) => n.toString(16).padStart(2, \"0\")).join(\"\")}`;\n }\n return null;\n}\n","\"use client\";\n\n/**\n * The control for image properties in the Style Manager, such as a section's\n * `background-url`.\n *\n * GrapesJS's own set the preview's `background-image` to the raw value. MJML stores a bare\n * URL there, not `url(...)`, so the preview was invalid CSS and stayed empty. This one shows\n * the image itself and opens the same picker, with the gallery, as an `mj-image` does.\n */\n\nimport { ImageIcon, X } from \"lucide-react\";\n\nimport type { EditorLabels } from \"../../labels.js\";\n\nexport default function ImageField({\n value,\n labels,\n onChoose,\n onClear,\n}: {\n value: string;\n labels: EditorLabels;\n onChoose: () => void;\n /** The label's own clear button is missing on sub-properties, such as a background layer. */\n onClear: () => void;\n}) {\n const url = imageUrl(value);\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={onChoose}\n title={labels.chooseImage}\n className=\"group relative block w-full overflow-hidden rounded-md border border-panel-border bg-panel-elevated outline-none focus-visible:ring-2 focus-visible:ring-brand\"\n >\n {url ? (\n <img src={url} alt=\"\" className=\"h-24 w-full bg-black/25 object-contain\" />\n ) : (\n <span className=\"flex h-16 flex-col items-center justify-center gap-1 text-xs text-panel-muted-fg\">\n <ImageIcon className=\"size-5\" />\n {labels.chooseImage}\n </span>\n )}\n {url && (\n <span className=\"absolute inset-0 flex items-center justify-center bg-black/50 text-xs font-medium text-white opacity-0 transition group-hover:opacity-100\">\n {labels.chooseImage}\n </span>\n )}\n </button>\n {value && (\n <button\n type=\"button\"\n onClick={onClear}\n title={labels.imageFieldRemove}\n aria-label={labels.imageFieldRemove}\n className=\"absolute top-1 right-1 flex size-5 items-center justify-center rounded bg-black/60 text-white hover:bg-black/80\"\n >\n <X className=\"size-3\" />\n </button>\n )}\n </div>\n );\n}\n\n/**\n * A bare URL (MJML attributes) or `url(\"…\")` (CSS) to the URL. Anything else — `none`, a\n * gradient or another CSS image function — to null, as there is no image to show.\n */\nexport function imageUrl(value: string): string | null {\n const v = value.trim();\n if (!v || v === \"none\") return null;\n const css = /^url\\(\\s*(['\"]?)(.*?)\\1\\s*\\)$/i.exec(v);\n if (css) return css[2] || null;\n return /^[a-z-]+\\(/i.test(v) ? null : v;\n}\n"],"mappings":"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","names":["import_react","import_core","import_jsx_runtime","React","import_jsx_runtime","import_jsx_runtime","React","import_radix_ui","import_jsx_runtime","PopoverPrimitive","React","import_radix_ui","import_lucide_react","import_jsx_runtime","DialogPrimitive","DialogPrimitive","DialogPrimitive","DialogPrimitive","React","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_sonner","import_lucide_react","React","import_radix_ui","import_jsx_runtime","SwitchPrimitive","import_lucide_react","import_react","import_lucide_react","import_jsx_runtime","import_react","import_lucide_react","import_jsx_runtime","import_lucide_react","import_jsx_runtime","import_jsx_runtime","GjsEditor","grapesjs","grapesjsMJML"]}