@mjml-agent-editor/editor 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -90,6 +90,8 @@ interface EditorLabels {
90
90
  readonly stop: string;
91
91
  readonly send: string;
92
92
  readonly chatError: string;
93
+ /** Above the disabled composer while the host has the agent switched off. */
94
+ readonly agentUnavailable: string;
93
95
  /** Keyed by tool name; merged key by key rather than replaced wholesale. */
94
96
  readonly toolLabels: Readonly<Record<string, string>>;
95
97
  }
@@ -218,6 +220,17 @@ type Props$1 = {
218
220
  * means there was nothing to re-attach to — which is the ordinary case and not an error.
219
221
  */
220
222
  resume?: boolean;
223
+ /**
224
+ * The host has switched the agent off — a spending cap, a kill switch. The message box,
225
+ * Send and "Apply changes from comments" are disabled and `labels.agentUnavailable` says
226
+ * why; the conversation stays readable and the rest of the editor keeps working.
227
+ *
228
+ * It stops new turns only, including one still in its pre-turn save, whose prompt stays in
229
+ * the box. One already running when it turns on is left to finish, and Stop still ends it:
230
+ * aborting mid-turn can leave half its edits written, and whether work the server has
231
+ * already accepted should be cut short is the server's decision.
232
+ */
233
+ agentUnavailable?: boolean;
221
234
  /**
222
235
  * Flushes unsaved editor changes before the agent starts. A rejection stops the turn and
223
236
  * keeps the prompt; its message is shown when it carries a 4xx `status` or an
@@ -233,7 +246,7 @@ type Props$1 = {
233
246
  /** Agent finished editing a section (remove the highlight). */
234
247
  onSectionEditEnd: (sectionId: string) => void;
235
248
  };
236
- declare function ChatPanel({ docId, initialMessages, transport, resume, onBeforeSend, onAgentFinish, onLiveUpdate, onSectionEditStart, onSectionEditEnd, }: Props$1): React.JSX.Element;
249
+ declare function ChatPanel({ docId, initialMessages, transport, resume, agentUnavailable, onBeforeSend, onAgentFinish, onLiveUpdate, onSectionEditStart, onSectionEditEnd, }: Props$1): React.JSX.Element;
237
250
 
238
251
  type Props = {
239
252
  docId: string;
package/dist/index.d.ts CHANGED
@@ -90,6 +90,8 @@ interface EditorLabels {
90
90
  readonly stop: string;
91
91
  readonly send: string;
92
92
  readonly chatError: string;
93
+ /** Above the disabled composer while the host has the agent switched off. */
94
+ readonly agentUnavailable: string;
93
95
  /** Keyed by tool name; merged key by key rather than replaced wholesale. */
94
96
  readonly toolLabels: Readonly<Record<string, string>>;
95
97
  }
@@ -218,6 +220,17 @@ type Props$1 = {
218
220
  * means there was nothing to re-attach to — which is the ordinary case and not an error.
219
221
  */
220
222
  resume?: boolean;
223
+ /**
224
+ * The host has switched the agent off — a spending cap, a kill switch. The message box,
225
+ * Send and "Apply changes from comments" are disabled and `labels.agentUnavailable` says
226
+ * why; the conversation stays readable and the rest of the editor keeps working.
227
+ *
228
+ * It stops new turns only, including one still in its pre-turn save, whose prompt stays in
229
+ * the box. One already running when it turns on is left to finish, and Stop still ends it:
230
+ * aborting mid-turn can leave half its edits written, and whether work the server has
231
+ * already accepted should be cut short is the server's decision.
232
+ */
233
+ agentUnavailable?: boolean;
221
234
  /**
222
235
  * Flushes unsaved editor changes before the agent starts. A rejection stops the turn and
223
236
  * keeps the prompt; its message is shown when it carries a 4xx `status` or an
@@ -233,7 +246,7 @@ type Props$1 = {
233
246
  /** Agent finished editing a section (remove the highlight). */
234
247
  onSectionEditEnd: (sectionId: string) => void;
235
248
  };
236
- declare function ChatPanel({ docId, initialMessages, transport, resume, onBeforeSend, onAgentFinish, onLiveUpdate, onSectionEditStart, onSectionEditEnd, }: Props$1): React.JSX.Element;
249
+ declare function ChatPanel({ docId, initialMessages, transport, resume, agentUnavailable, onBeforeSend, onAgentFinish, onLiveUpdate, onSectionEditStart, onSectionEditEnd, }: Props$1): React.JSX.Element;
237
250
 
238
251
  type Props = {
239
252
  docId: string;
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  useDocumentStore,
13
13
  useImageLibrary,
14
14
  useLabels
15
- } from "./chunk-3RCKZYM4.js";
15
+ } from "./chunk-W3HCLTFJ.js";
16
16
  import "./chunk-V6RR4VRD.js";
17
17
  export {
18
18
  CanvasComments,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjml-agent-editor/editor",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "React MJML editor with an agent chat panel and canvas comments.",
5
5
  "keywords": [
6
6
  "mjml",
@@ -82,7 +82,9 @@
82
82
  "@types/react": "^19",
83
83
  "@types/react-dom": "^19",
84
84
  "ai": "^6.0.230",
85
+ "jsdom": "^26.1.0",
85
86
  "react": "19.2.4",
87
+ "react-dom": "19.2.4",
86
88
  "tsup": "^8.3.5",
87
89
  "typescript": "^5.7.2",
88
90
  "vitest": "^2.1.8"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/stores.tsx","../src/labels.ts","../src/index.ts","../src/components/editor/EditorHeader.tsx","../src/components/ui/toggle-group.tsx","../src/components/ui/toggle.tsx","../src/components/ui/popover.tsx","../src/components/ui/tabs.tsx","../src/components/ui/dialog.tsx","../src/components/chat/ChatPanel.tsx","../src/lib/refusal.ts","../src/components/ui/message.tsx","../src/components/ui/bubble.tsx","../src/components/chat/Markdown.tsx","../src/components/ui/marker.tsx","../src/components/ui/collapsible.tsx","../src/components/ui/message-scroller.tsx","../src/components/comments/CanvasComments.tsx"],"sourcesContent":["\"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","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n Undo2,\n Redo2,\n Code2,\n Copy,\n Eye,\n Download,\n MessageSquare,\n Mail,\n ChevronDown,\n Minus,\n Plus,\n Monitor,\n Smartphone,\n MoreVertical,\n X,\n} from \"lucide-react\";\nimport type { ReactNode } from \"react\";\n\nimport { useDocumentStore } from \"../../index.js\";\nimport type { EditorApi, EditorState, SaveStatus } from \"../editor/EmailEditor.js\";\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../ui/button\";\nimport { Input } from \"../ui/input\";\nimport { ToggleGroup, ToggleGroupItem } from \"../ui/toggle-group\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"../ui/tooltip\";\nimport { Badge } from \"../ui/badge\";\nimport { useLabels } from \"../../stores.js\";\nimport { Spinner } from \"../ui/spinner\";\nimport { Separator } from \"../ui/separator\";\nimport { Popover, PopoverTrigger, PopoverContent } from \"../ui/popover\";\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from \"../ui/tabs\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"../ui/dialog\";\n\nconst WIDTHS = [\"600px\", \"640px\", \"680px\", \"720px\"];\n\nconst DEVICE_ICON: Record<string, ReactNode> = {\n Desktop: <Monitor className=\"size-3.5\" />,\n Mobile: <Smartphone className=\"size-3.5\" />,\n};\n\nfunction SaveBadge({ status }: { status: SaveStatus }) {\n const labels = useLabels();\n if (status === \"idle\") return null;\n if (status === \"saving\")\n return (\n <span className=\"flex items-center gap-1 text-xs text-panel-muted-fg\">\n <Spinner className=\"size-3\" /> {labels.saving}\n </span>\n );\n if (status === \"error\") return <Badge variant=\"destructive\">{labels.saveFailed}</Badge>;\n return <span className=\"text-xs text-emerald-400\">✓ {labels.saved}</span>;\n}\n\nfunction slugify(name: string): string {\n return (\n name\n .trim()\n .replace(/[^\\p{L}\\p{N}]+/gu, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase() || \"email\"\n );\n}\n\nfunction download(filename: string, content: string, mime: string) {\n const blob = new Blob([content], { type: mime });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = filename;\n a.click();\n URL.revokeObjectURL(url);\n}\n\nconst darkGhost = \"text-panel-muted-fg hover:bg-panel-hover hover:text-panel-fg\";\n\nexport default function EditorHeader({\n docId,\n api,\n openCount,\n homeLink,\n logo,\n}: {\n docId: string;\n api: EditorApi | null;\n openCount: number;\n /**\n * Rendered in the overflow menu as the way back out of the editor. Routing belongs to\n * the host — a Next.js app passes `<Link href=\"/\">`, anything else passes an anchor —\n * so the package does not depend on a router.\n */\n homeLink?: ReactNode;\n /**\n * The mark shown before the application name. Defaults to a generic envelope.\n *\n * A brand asset is the host's, not the package's — the same reasoning as `homeLink`. The\n * default exists so the header does not look broken out of the box, not as a suggestion\n * that anyone ship it.\n */\n logo?: ReactNode;\n}) {\n // Injected by the host rather than imported, so the header carries no database.\n const documents = useDocumentStore();\n const labels = useLabels();\n const [name, setName] = useState(\"\");\n const [state, setState] = useState<EditorState>({\n device: \"Desktop\",\n canUndo: false,\n canRedo: false,\n saveStatus: \"idle\",\n zoom: 100,\n contentWidth: \"600px\",\n readOnly: false,\n });\n const [code, setCode] = useState({ mjml: \"\", html: \"\" });\n const [preview, setPreview] = useState(\"\");\n\n useEffect(() => {\n documents\n .get(docId)\n .then((d) => setName(d.name))\n .catch(() => {});\n }, [docId, documents]);\n\n useEffect(() => {\n if (!api) return;\n return api.onEditorState(setState);\n }, [api]);\n\n const commitName = async () => {\n const n = name.trim() || \"Untitled\";\n setName(n);\n try {\n await documents.save(docId, { name: n });\n } catch {\n toast.error(labels.renameFailed);\n }\n };\n\n const devices = api?.getDevices() ?? [];\n const base = slugify(name);\n\n const openCode = () =>\n setCode({ mjml: api?.getMjml() ?? \"\", html: api?.getCompiledHtml() ?? \"\" });\n\n return (\n <header className=\"flex h-14 shrink-0 items-center gap-2 border-b border-panel-border bg-panel px-3 text-panel-fg\">\n {/* Logo + document name */}\n {logo ?? (\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-brand to-indigo-400 text-brand-fg shadow-sm\">\n <Mail className=\"size-4\" />\n </span>\n )}\n <span className=\"hidden text-sm font-semibold sm:inline\">{labels.appName}</span>\n <Separator orientation=\"vertical\" className=\"mx-1 !h-5 bg-panel-border\" />\n <Input\n value={name}\n onChange={(e) => setName(e.target.value)}\n onBlur={() => void commitName()}\n onKeyDown={(e) => e.key === \"Enter\" && e.currentTarget.blur()}\n className=\"h-8 w-44 border-transparent bg-transparent text-sm font-medium text-panel-fg hover:border-panel-border focus-visible:border-panel-border\"\n aria-label={labels.emailNameAria}\n />\n\n <Separator orientation=\"vertical\" className=\"mx-1 !h-5 bg-panel-border\" />\n\n {devices.length > 0 && (\n <ToggleGroup\n type=\"single\"\n size=\"sm\"\n value={state.device}\n onValueChange={(v) => v && api?.setDevice(v)}\n className=\"rounded-md border border-panel-border\"\n >\n {devices.map((d) => (\n <ToggleGroupItem\n key={d.id}\n value={d.name}\n className=\"gap-1.5 px-2.5 text-panel-muted-fg data-[state=on]:bg-panel-elevated data-[state=on]:text-panel-fg\"\n >\n {DEVICE_ICON[d.name]}\n {d.name}\n </ToggleGroupItem>\n ))}\n </ToggleGroup>\n )}\n\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={darkGhost}\n disabled={!state.canUndo}\n onClick={() => api?.undo()}\n >\n <Undo2 />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{labels.undo}</TooltipContent>\n </Tooltip>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={darkGhost}\n disabled={!state.canRedo}\n onClick={() => api?.redo()}\n >\n <Redo2 />\n </Button>\n </TooltipTrigger>\n <TooltipContent>{labels.redo}</TooltipContent>\n </Tooltip>\n\n {/* Content width */}\n <Separator orientation=\"vertical\" className=\"mx-1 !h-5 bg-panel-border max-xl:hidden\" />\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={state.readOnly}\n className={cn(darkGhost, \"gap-1.5 rounded-md border border-panel-border max-xl:hidden\")}\n >\n <span className=\"text-panel-muted-fg\">{labels.contentWidth}</span>\n {state.contentWidth}\n <ChevronDown className=\"size-3.5\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-40 p-1\">\n {WIDTHS.map((w) => (\n <button\n key={w}\n onClick={() => api?.setContentWidth(w)}\n className={cn(\n \"flex w-full items-center justify-between rounded px-2 py-1.5 text-sm hover:bg-muted\",\n state.contentWidth === w && \"font-medium text-brand\",\n )}\n >\n {w}\n </button>\n ))}\n </PopoverContent>\n </Popover>\n\n {/* Zoom */}\n <div className=\"flex items-center gap-0.5 rounded-md border border-panel-border px-0.5 max-xl:hidden\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(darkGhost, \"size-7\")}\n onClick={() => api?.setZoom(state.zoom - 10)}\n >\n <Minus className=\"size-3.5\" />\n </Button>\n <span className=\"w-10 text-center text-xs tabular-nums text-panel-muted-fg\">\n {state.zoom}%\n </span>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn(darkGhost, \"size-7\")}\n onClick={() => api?.setZoom(state.zoom + 10)}\n >\n <Plus className=\"size-3.5\" />\n </Button>\n </div>\n\n {/* Right group */}\n <div className=\"ml-auto flex items-center gap-2\">\n {openCount > 0 && (\n <span className=\"flex items-center gap-1 text-xs text-panel-muted-fg\">\n <MessageSquare className=\"size-3.5\" /> {openCount}\n </span>\n )}\n <SaveBadge status={state.saveStatus} />\n\n {/* Full-screen preview */}\n <Dialog>\n <DialogTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className={cn(darkGhost, \"gap-1.5\")}\n onClick={() => setPreview(api?.getCompiledHtml() ?? \"\")}\n >\n <Eye /> {labels.preview}\n </Button>\n </DialogTrigger>\n <DialogContent\n // The dialog's own close button is absolutely positioned top-right, where it\n // sits on top of \"Export HTML\". Replaced by one inside the toolbar flow.\n showCloseButton={false}\n className=\"flex !max-w-none h-screen w-screen flex-col gap-0 rounded-none border-0 bg-zinc-100 p-0 sm:rounded-none\"\n >\n <div className=\"flex h-12 shrink-0 items-center justify-between border-b bg-white px-4\">\n <DialogTitle className=\"text-sm\">\n {labels.preview} — {name}\n </DialogTitle>\n <div className=\"flex items-center gap-2\">\n {devices.length > 0 && (\n <ToggleGroup\n type=\"single\"\n size=\"sm\"\n value={state.device}\n onValueChange={(v) => v && api?.setDevice(v)}\n variant=\"outline\"\n >\n {devices.map((d) => (\n <ToggleGroupItem key={d.id} value={d.name} className=\"px-2.5\">\n {d.name}\n </ToggleGroupItem>\n ))}\n </ToggleGroup>\n )}\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => download(`${base}.html`, preview, \"text/html\")}\n >\n <Download /> Export HTML\n </Button>\n <DialogClose asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={labels.closePreview}>\n <X />\n </Button>\n </DialogClose>\n </div>\n </div>\n <div className=\"min-h-0 flex-1 overflow-auto p-6\">\n <iframe\n title={labels.previewFrameTitle}\n srcDoc={preview}\n className={cn(\n \"mx-auto block h-full min-h-[80vh] rounded-md border bg-white shadow-sm\",\n state.device === \"Mobile\" ? \"w-[375px]\" : \"w-full max-w-[720px]\",\n )}\n />\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Code (MJML + HTML) */}\n <Dialog>\n <DialogTrigger asChild>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className={cn(darkGhost, \"gap-1.5\")}\n onClick={openCode}\n >\n <Code2 /> Code\n </Button>\n </DialogTrigger>\n <DialogContent className=\"flex !max-w-4xl flex-col\">\n <DialogHeader>\n <DialogTitle>{labels.sourceCode}</DialogTitle>\n </DialogHeader>\n <Tabs defaultValue=\"mjml\" className=\"min-h-0 flex-1\">\n <TabsList>\n <TabsTrigger value=\"mjml\">MJML</TabsTrigger>\n <TabsTrigger value=\"html\">HTML</TabsTrigger>\n </TabsList>\n {([\"mjml\", \"html\"] as const).map((kind) => (\n <TabsContent key={kind} value={kind} className=\"mt-2\">\n <pre className=\"max-h-[62vh] overflow-auto rounded-md border bg-muted/40 p-3 text-xs leading-relaxed break-all whitespace-pre-wrap\">\n {code[kind]}\n </pre>\n <div className=\"mt-2 flex justify-end gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() =>\n download(\n `${base}.${kind}`,\n code[kind],\n kind === \"html\" ? \"text/html\" : \"text/plain\",\n )\n }\n >\n <Download /> Download .{kind}\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => {\n void navigator.clipboard?.writeText(code[kind]);\n toast.success(labels.copied(kind.toUpperCase()));\n }}\n >\n <Copy /> Copy\n </Button>\n </div>\n </TabsContent>\n ))}\n </Tabs>\n </DialogContent>\n </Dialog>\n\n {/* Export split-button (HTML / MJML), file named from the project */}\n <Popover>\n <PopoverTrigger asChild>\n <Button size=\"sm\" className=\"gap-1.5 bg-brand text-brand-fg hover:bg-brand/90\">\n <Download /> Export\n <ChevronDown className=\"size-3.5\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-52 p-1\">\n <button\n onClick={() => download(`${base}.html`, api?.getCompiledHtml() ?? \"\", \"text/html\")}\n className=\"flex w-full flex-col rounded px-2 py-1.5 text-left text-sm hover:bg-muted\"\n >\n <span className=\"font-medium\">{labels.exportHtml}</span>\n <span className=\"text-xs text-muted-foreground\">{base}.html</span>\n </button>\n <button\n onClick={() => download(`${base}.mjml`, api?.getMjml() ?? \"\", \"text/plain\")}\n className=\"flex w-full flex-col rounded px-2 py-1.5 text-left text-sm hover:bg-muted\"\n >\n <span className=\"font-medium\">{labels.exportMjml}</span>\n <span className=\"text-xs text-muted-foreground\">{base}.mjml</span>\n </button>\n </PopoverContent>\n </Popover>\n\n {/* Overflow menu */}\n <Popover>\n <PopoverTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className={darkGhost}>\n <MoreVertical />\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"end\" className=\"w-44 p-1\">\n {homeLink}\n </PopoverContent>\n </Popover>\n </div>\n </header>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { type VariantProps } from \"class-variance-authority\";\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\nimport { toggleVariants } from \"./toggle\";\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number;\n orientation?: \"horizontal\" | \"vertical\";\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n});\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number;\n orientation?: \"horizontal\" | \"vertical\";\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch\",\n className,\n )}\n {...props}\n >\n <ToggleGroupContext.Provider value={{ variant, size, spacing, orientation }}>\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n );\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext);\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className,\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n );\n}\n\nexport { ToggleGroup, ToggleGroupItem };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Toggle as TogglePrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst toggleVariants = cva(\n \"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-muted data-[state=on]:bg-muted 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-transparent\",\n outline: \"border border-input bg-transparent hover:bg-muted\",\n },\n size: {\n default:\n \"h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n sm: \"h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] 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 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Toggle({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>) {\n return (\n <TogglePrimitive.Root\n data-slot=\"toggle\"\n className={cn(toggleVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Toggle, toggleVariants };\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 { cva, type VariantProps } from \"class-variance-authority\";\nimport { Tabs as TabsPrimitive } from \"radix-ui\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction Tabs({\n className,\n orientation = \"horizontal\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.Root>) {\n return (\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n data-orientation={orientation}\n className={cn(\"group/tabs flex gap-2 data-horizontal:flex-col\", className)}\n {...props}\n />\n );\n}\n\nconst tabsListVariants = cva(\n \"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none\",\n {\n variants: {\n variant: {\n default: \"bg-muted\",\n line: \"gap-1 bg-transparent\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\nfunction TabsList({\n className,\n variant = \"default\",\n ...props\n}: React.ComponentProps<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>) {\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n data-variant={variant}\n className={cn(tabsListVariants({ variant }), className)}\n {...props}\n />\n );\n}\n\nfunction TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {\n return (\n <TabsPrimitive.Trigger\n data-slot=\"tabs-trigger\"\n className={cn(\n \"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n \"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent\",\n \"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground\",\n \"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {\n return (\n <TabsPrimitive.Content\n data-slot=\"tabs-content\"\n className={cn(\"flex-1 text-sm outline-none\", className)}\n {...props}\n />\n );\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };\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","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport type { AgentMessageMetadata } from \"@mjml-agent-editor/core\";\nimport { DefaultChatTransport, type ChatTransport, type ToolUIPart, type UIMessage } from \"ai\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport {\n Brain,\n Check,\n Loader2,\n TriangleAlert,\n Send,\n Square,\n Sparkles,\n MessageSquare,\n ChevronRight,\n} from \"lucide-react\";\n\nimport { refusal, saveToastId } from \"../../lib/refusal.js\";\nimport { cn } from \"../../lib/utils\";\nimport { useAgentTurn, useLabels } from \"../../stores.js\";\nimport { Button } from \"../ui/button\";\nimport { Textarea } from \"../ui/textarea\";\nimport { Empty, EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription } from \"../ui/empty\";\nimport { Message, MessageContent } from \"../ui/message\";\nimport { Bubble, BubbleContent } from \"../ui/bubble\";\nimport { Markdown } from \"../chat/Markdown.js\";\nimport { Marker, MarkerIcon, MarkerContent } from \"../ui/marker\";\nimport { Collapsible, CollapsibleTrigger, CollapsibleContent } from \"../ui/collapsible\";\nimport {\n MessageScrollerProvider,\n MessageScroller,\n MessageScrollerViewport,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerButton,\n} from \"../ui/message-scroller\";\n\nconst APPLY_COMMENTS_PROMPT =\n \"Apply changes based on all open section comments. \" +\n \"After each successful change mark the comment as resolved and summarize what you changed.\";\n\n// Tools that mutate the document or comments — after each of them we refresh\n// the preview/panel live, without waiting for the agent's turn to finish.\nconst MUTATING_TOOLS = new Set([\n \"set_document\",\n \"set_section\",\n \"insert_section\",\n \"remove_section\",\n \"resolve_comment\",\n]);\n\n// Tools that edit a specific section → name of the argument holding its section_id.\n// Used to highlight that section while it is being edited.\nconst SECTION_ARG: Record<string, string> = {\n set_section: \"section_id\",\n remove_section: \"section_id\",\n insert_section: \"after_section_id\",\n};\n\n/**\n * One rendered unit of a message, in the order it happened.\n *\n * A multi-step turn interleaves narration and tool calls — \"I'll check the document\",\n * `get_document`, \"now I'll write the hero\", `set_section`. `message.parts` records that\n * order faithfully; this preserves it.\n *\n * The panel used to filter the same array twice, once for tools and once for text, and\n * render the two results as separate blocks. Every tool call therefore appeared above every\n * sentence, so a reader saw \"Saving whole email\" before the sentence that preceded it by\n * three steps — effects before their causes, in a product whose whole promise is watching\n * the agent work. With one text part and a couple of tools that reads as a deliberate\n * summary, which is why it survived this long.\n */\ntype MessageBlock =\n | { readonly kind: \"tools\"; readonly tools: ToolUIPart[] }\n | { readonly kind: \"text\"; readonly text: string; readonly narration: boolean }\n | { readonly kind: \"reasoning\"; readonly text: string };\n\n/**\n * Groups a message's parts into consecutive runs.\n *\n * Adjacent tool calls collapse into one activity block — a turn that generates three images\n * back to back should not produce three boxes — but a run that is interrupted by text\n * starts a new one, which is what restores the sequence.\n *\n * Reasoning parts were previously dropped on the floor. No model in the template's default\n * configuration emits them, so nothing was visibly wrong; point the host at a reasoning\n * model and half of every answer would vanish with no error. Silence is the worst way for a\n * UI to be wrong.\n *\n * Text is also marked narration or answer. The SDK offers nothing to distinguish them —\n * `TextUIPart` carries `state?: \"streaming\" | \"done\"`, which is a lifecycle and not a role,\n * and `step-start` is a marker with no fields — but the structure decides it: a sentence with\n * another tool call after it was introducing that call, and the last one is the reply. Four\n * identical bubbles per turn buried the answer among the commentary.\n */\nfunction toBlocks(parts: UIMessage[\"parts\"]): MessageBlock[] {\n const blocks: MessageBlock[] = [];\n\n for (const part of parts) {\n if (part.type.startsWith(\"tool-\")) {\n const last = blocks[blocks.length - 1];\n if (last?.kind === \"tools\") last.tools.push(part as ToolUIPart);\n else blocks.push({ kind: \"tools\", tools: [part as ToolUIPart] });\n continue;\n }\n\n if (part.type === \"reasoning\") {\n const text = part.text;\n if (!text.trim()) continue;\n const last = blocks[blocks.length - 1];\n if (last?.kind === \"reasoning\")\n blocks[blocks.length - 1] = { kind: \"reasoning\", text: `${last.text}${text}` };\n else blocks.push({ kind: \"reasoning\", text });\n continue;\n }\n\n if (part.type === \"text\") {\n // While streaming, the SDK appends an empty text part; rendering it produced an empty\n // bubble above \"Agent is working…\".\n if (!part.text.trim()) continue;\n blocks.push({ kind: \"text\", text: part.text, narration: false });\n }\n }\n\n // One backward pass: everything before the last tool call was leading up to it.\n let seenTools = false;\n for (let i = blocks.length - 1; i >= 0; i--) {\n const block = blocks[i]!;\n if (block.kind === \"tools\") seenTools = true;\n else if (block.kind === \"text\" && seenTools) {\n blocks[i] = { kind: \"text\", text: block.text, narration: true };\n }\n }\n\n return blocks;\n}\n\ntype Props = {\n docId: string;\n /**\n * Conversation to start from, oldest first.\n *\n * Only useful to a host that stores the conversation server-side. Without it the panel\n * mounts empty, which is right when the browser is the only place the history ever\n * lived — and wrong, invisibly, when it is not: the agent still receives the stored\n * history on the next turn, so the model remembers a conversation the visitor is\n * looking at an empty panel for.\n */\n initialMessages?: UIMessage[];\n /**\n * Where the panel sends turns. Defaults to `POST /api/chat` in this application.\n *\n * Supplying one replaces the endpoint entirely, which is how a host reaches an agent that\n * is not a same-origin route — and how the scripted demo drives this panel from a\n * recorded transcript without making any request at all.\n *\n * Memoize it. A transport carrying state — a playback position, an open connection —\n * restarts if it is rebuilt on every render.\n */\n transport?: ChatTransport<UIMessage>;\n /**\n * Re-attach to a turn that is already running for this document.\n *\n * Only meaningful to a host whose server keeps streaming after the browser lets go — one\n * that drains the response itself rather than leaving it to the tab that asked. There the\n * visitor can leave mid-turn and come back to it still running, and without this the\n * panel mounts showing the stored conversation as it was before the turn started, with no\n * sign that anything is happening.\n *\n * The panel asks on mount only. `GET {api}/{docId}/stream` is what answers, and `204`\n * means there was nothing to re-attach to — which is the ordinary case and not an error.\n */\n resume?: boolean;\n /**\n * Flushes unsaved editor changes before the agent starts. A rejection stops the turn and\n * keeps the prompt; its message is shown when it carries a 4xx `status` or an\n * `{\"error\": \"...\"}` body, otherwise `labels.documentSaveFailed`.\n */\n onBeforeSend: () => Promise<void>;\n /** After the agent's turn finishes (refresh the editor and comments). */\n onAgentFinish: () => void;\n /** After each single agent edit (mutating tool call) — live reload. */\n onLiveUpdate: () => void;\n /** Agent started editing a section (highlight it). */\n onSectionEditStart: (sectionId: string) => void;\n /** Agent finished editing a section (remove the highlight). */\n onSectionEditEnd: (sectionId: string) => void;\n};\n\nfunction ToolMarker({ tool }: { tool: ToolUIPart }) {\n const labels = useLabels();\n const name = tool.type.slice(5);\n const done = tool.state === \"output-available\";\n const failed = tool.state === \"output-error\";\n return (\n <Marker className={cn(failed ? \"text-destructive\" : \"text-panel-fg\")}>\n <MarkerIcon>\n {failed ? (\n <TriangleAlert />\n ) : done ? (\n <Check className=\"text-emerald-400\" />\n ) : (\n <Loader2 className=\"animate-spin\" />\n )}\n </MarkerIcon>\n <MarkerContent>\n {labels.toolLabels[name] ?? name}\n {failed && tool.errorText ? `: ${tool.errorText}` : \"\"}\n </MarkerContent>\n </Marker>\n );\n}\n\n// \"Agent is working…\" indicator — rendered either inside the current assistant\n// message (tight under the steps) or standalone when the assistant hasn't started writing yet.\nfunction BusyMarker() {\n const labels = useLabels();\n return (\n <Marker className=\"text-panel-fg\">\n <MarkerIcon>\n <Loader2 className=\"animate-spin\" />\n </MarkerIcon>\n <MarkerContent>{labels.agentWorking}</MarkerContent>\n </Marker>\n );\n}\n\n// Tool activity: in-progress tasks visible; finished and failed ones collapsed underneath.\nfunction ToolActivity({ tools }: { tools: ToolUIPart[] }) {\n const labels = useLabels();\n const running = tools.filter((t) => t.state !== \"output-available\" && t.state !== \"output-error\");\n const finished = tools.filter(\n (t) => t.state === \"output-available\" || t.state === \"output-error\",\n );\n const failedCount = finished.filter((t) => t.state === \"output-error\").length;\n\n return (\n <div className=\"space-y-1 rounded-lg border border-panel-border bg-panel-elevated/60 p-2\">\n {/* Collapsed by default. Finished steps are history — worth being able to check, not\n worth occupying the panel above every answer. What is still running stays visible\n below, outside this. */}\n {finished.length > 0 && (\n <Collapsible>\n <CollapsibleTrigger className=\"group/col flex w-full items-center gap-1.5 text-left text-xs font-medium text-panel-fg hover:text-panel-fg\">\n <ChevronRight className=\"size-3.5 shrink-0 transition-transform group-data-[state=open]/col:rotate-90\" />\n <span>{labels.completedSteps(finished.length, failedCount)}</span>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"mt-1 space-y-0.5 pl-1\">\n {finished.map((t, i) => (\n <ToolMarker key={`f${i}`} tool={t} />\n ))}\n </CollapsibleContent>\n </Collapsible>\n )}\n {running.map((t, i) => (\n <ToolMarker key={`r${i}`} tool={t} />\n ))}\n </div>\n );\n}\n\n/**\n * The model's own reasoning, collapsed.\n *\n * Shown rather than hidden, because a turn that spends thirty seconds thinking and says\n * nothing looks broken. Collapsed rather than expanded, because it is not the answer.\n */\nfunction ReasoningBlock({ text }: { text: string }) {\n const labels = useLabels();\n return (\n <div className=\"rounded-lg border border-panel-border bg-panel-elevated/60 p-2\">\n <Collapsible>\n <CollapsibleTrigger className=\"group/col flex w-full items-center gap-1.5 text-left text-xs font-medium text-panel-muted-fg hover:text-panel-fg\">\n <ChevronRight className=\"size-3.5 shrink-0 transition-transform group-data-[state=open]/col:rotate-90\" />\n <Brain className=\"size-3.5 shrink-0\" />\n <span>{labels.reasoning}</span>\n </CollapsibleTrigger>\n <CollapsibleContent className=\"mt-1.5 pl-1 text-xs whitespace-pre-wrap text-panel-muted-fg\">\n {text}\n </CollapsibleContent>\n </Collapsible>\n </div>\n );\n}\n\nexport default function ChatPanel({\n docId,\n initialMessages,\n transport,\n resume,\n onBeforeSend,\n onAgentFinish,\n onLiveUpdate,\n onSectionEditStart,\n onSectionEditEnd,\n}: Props) {\n const labels = useLabels();\n const [input, setInput] = useState(\"\");\n const taRef = useRef<HTMLTextAreaElement>(null);\n // The ref blocks a second send at once; the state disables the buttons meanwhile.\n const sending = useRef(false);\n const [flushing, setFlushing] = useState(false);\n // Per-message timestamp, stamped when the message first renders.\n const times = useRef<Map<string, string>>(new Map());\n const timeFor = (id: string) => {\n if (!times.current.has(id)) {\n times.current.set(\n id,\n new Date().toLocaleTimeString([], { hour: \"2-digit\", minute: \"2-digit\" }),\n );\n }\n return times.current.get(id)!;\n };\n const { messages, sendMessage, status, stop } = useChat({\n // The document is the conversation, so it is also the chat's identity. Left to itself\n // the SDK invents a fresh id per mount, which is fine until something has to be found\n // again across one: the reconnect below is addressed as `{api}/{id}/stream`, and an id\n // the server never saw addresses nothing.\n id: docId,\n messages: initialMessages,\n resume,\n transport:\n transport ??\n new DefaultChatTransport({\n // Same-origin: the agent is a route handler in this app, not a separate service.\n api: \"/api/chat\",\n body: { docId },\n }),\n onFinish: onAgentFinish,\n onError: (e) => {\n console.error(\"chat error:\", e);\n toast.error(refusal(e) ?? labels.chatError);\n },\n });\n\n // Reactions to the agent's tool calls (deduped by message:part key):\n // - section edit started (section_id known, tool not finished yet) → highlight\n // - mutating tool call finished → live-reload the preview + remove the highlight\n const firedTools = useRef<Set<string>>(new Set());\n const highlightStarted = useRef<Map<string, string>>(new Map());\n const highlightEnded = useRef<Set<string>>(new Set());\n useEffect(() => {\n for (const message of messages) {\n if (message.role !== \"assistant\") continue;\n message.parts.forEach((part, i) => {\n if (!part.type.startsWith(\"tool-\")) return;\n const tool = part as ToolUIPart;\n const name = tool.type.slice(5);\n const key = `${message.id}:${i}`;\n const done = tool.state === \"output-available\" || tool.state === \"output-error\";\n\n // start the highlight — when we know the section_id and the tool is still running\n const argName = SECTION_ARG[name];\n if (argName && !done && !highlightStarted.current.has(key)) {\n const sid = (tool.input as Record<string, unknown> | undefined)?.[argName];\n if (typeof sid === \"string\" && sid) {\n highlightStarted.current.set(key, sid);\n onSectionEditStart(sid);\n }\n }\n\n if (!done) return;\n\n // remove the section highlight (before the reload, which rebuilds the canvas anyway)\n const startedSid = highlightStarted.current.get(key);\n if (startedSid && !highlightEnded.current.has(key)) {\n highlightEnded.current.add(key);\n onSectionEditEnd(startedSid);\n }\n // mutating tool call finished → live reload\n if (MUTATING_TOOLS.has(name) && !firedTools.current.has(key)) {\n firedTools.current.add(key);\n onLiveUpdate();\n }\n });\n }\n }, [messages, onLiveUpdate, onSectionEditStart, onSectionEditEnd]);\n\n const busy = status === \"submitted\" || status === \"streaming\";\n\n // The canvas is read only from the pre-turn save until the turn ends.\n const { setRunning, locked } = useAgentTurn();\n useEffect(() => setRunning(busy || flushing), [busy, flushing, setRunning]);\n useEffect(() => () => setRunning(false), [setRunning]);\n\n // Whether any tool is in progress (to avoid duplicating the global \"Agent is working…\").\n const lastMsg = messages[messages.length - 1];\n const toolRunning =\n lastMsg?.role === \"assistant\" &&\n lastMsg.parts.some((p) => {\n if (!p.type.startsWith(\"tool-\")) return false;\n const s = (p as ToolUIPart).state;\n return s !== \"output-available\" && s !== \"output-error\";\n });\n\n const grow = () => {\n const el = taRef.current;\n if (!el) return;\n el.style.height = \"auto\";\n el.style.height = `${Math.min(el.scrollHeight, 160)}px`;\n };\n\n const send = async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed || busy || locked || sending.current) return;\n sending.current = true;\n setFlushing(true);\n try {\n await onBeforeSend();\n } catch (error) {\n console.error(error);\n toast.error(refusal(error) ?? labels.documentSaveFailed, { id: saveToastId(docId) });\n return;\n } finally {\n sending.current = false;\n setFlushing(false);\n }\n void sendMessage({ text: trimmed });\n setInput(\"\");\n if (taRef.current) taRef.current.style.height = \"auto\";\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n <MessageScrollerProvider autoScroll>\n <MessageScroller className=\"flex-1\">\n <MessageScrollerViewport>\n <MessageScrollerContent className=\"px-5 py-3\">\n {messages.length === 0 ? (\n <Empty className=\"h-full\">\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <MessageSquare />\n </EmptyMedia>\n <EmptyTitle>{labels.chatEmptyTitle}</EmptyTitle>\n <EmptyDescription>{labels.chatEmptyDescription}</EmptyDescription>\n </EmptyHeader>\n </Empty>\n ) : (\n messages.map((message, mi) => {\n const isUser = message.role === \"user\";\n const isLast = mi === messages.length - 1;\n const blocks = toBlocks(message.parts);\n const hasText = blocks.some((block) => block.kind === \"text\");\n const metadata = message.metadata as AgentMessageMetadata | undefined;\n return (\n <MessageScrollerItem key={message.id} messageId={message.id}>\n <div className={cn(\"flex w-full gap-2\", isUser && \"flex-row-reverse\")}>\n {!isUser && (\n <span className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-brand/15 text-brand\">\n <Sparkles className=\"size-3.5\" />\n </span>\n )}\n <div\n className={cn(\n \"flex min-w-0 flex-1 flex-col gap-1\",\n isUser ? \"items-end\" : \"items-start\",\n )}\n >\n <Message align={isUser ? \"end\" : \"start\"}>\n <MessageContent>\n {/* In the order they happened, so narration stays attached to\n the step it introduced. */}\n {blocks.map((block, i) => {\n if (block.kind === \"tools\") {\n return <ToolActivity key={i} tools={block.tools} />;\n }\n if (block.kind === \"reasoning\") {\n return <ReasoningBlock key={i} text={block.text} />;\n }\n // Narration gets no bubble. It is the trail of what the agent\n // was doing, and giving it the same weight as the reply is\n // what made the reply hard to find.\n if (block.narration && !isUser) {\n return (\n <div\n key={i}\n data-slot=\"narration\"\n className=\"px-1 text-xs leading-relaxed text-panel-muted-fg [&_a]:text-brand [&_code]:!bg-white/10 [&_p]:my-0.5\"\n >\n <Markdown>{block.text}</Markdown>\n </div>\n );\n }\n return (\n <Bubble\n key={i}\n variant={isUser ? \"default\" : \"muted\"}\n align={isUser ? \"end\" : \"start\"}\n className={cn(\n isUser\n ? \"[&_[data-slot=bubble-content]]:bg-brand [&_[data-slot=bubble-content]]:text-brand-fg\"\n : \"max-w-full [&_[data-slot=bubble-content]]:border [&_[data-slot=bubble-content]]:border-panel-border [&_[data-slot=bubble-content]]:bg-panel-elevated [&_[data-slot=bubble-content]]:text-panel-fg [&_code]:!bg-white/12 [&_pre]:!bg-white/10 [&_a]:text-brand\",\n )}\n >\n <BubbleContent\n className={isUser ? \"whitespace-pre-wrap\" : undefined}\n >\n {isUser ? block.text : <Markdown>{block.text}</Markdown>}\n </BubbleContent>\n </Bubble>\n );\n })}\n {/* Busy indicator as part of the assistant's current turn —\n tight spacing (gap-2.5). Hidden once text is already\n streaming — keeps things clean. */}\n {isLast && !isUser && busy && !toolRunning && !hasText && (\n <BusyMarker />\n )}\n {metadata?.stepLimitReached && (\n <Marker className=\"text-panel-muted-fg\">\n <MarkerIcon>\n <TriangleAlert />\n </MarkerIcon>\n <MarkerContent>{labels.stepLimitReached}</MarkerContent>\n </Marker>\n )}\n </MessageContent>\n </Message>\n {blocks.length > 0 && (\n <span className=\"px-1 text-[10px] text-panel-muted-fg\">\n {timeFor(message.id)}\n </span>\n )}\n </div>\n </div>\n </MessageScrollerItem>\n );\n })\n )}\n {/* Fallback: the agent hasn't created a message yet (last one is the user's). */}\n {busy && !toolRunning && lastMsg?.role !== \"assistant\" && <BusyMarker />}\n </MessageScrollerContent>\n </MessageScrollerViewport>\n <MessageScrollerButton direction=\"end\" />\n </MessageScroller>\n </MessageScrollerProvider>\n\n <div className=\"border-t border-panel-border p-3\">\n <Button\n className=\"mb-2 w-full bg-brand text-brand-fg hover:bg-brand/90\"\n disabled={busy || flushing || locked}\n onClick={() => void send(APPLY_COMMENTS_PROMPT)}\n >\n <Sparkles /> {labels.applyComments}\n </Button>\n <form\n onSubmit={(e) => {\n e.preventDefault();\n void send(input);\n }}\n className=\"rounded-xl border border-panel-border bg-panel-elevated transition-colors focus-within:border-brand/60\"\n >\n <Textarea\n ref={taRef}\n rows={2}\n className=\"max-h-40 min-h-14 resize-none border-0 bg-transparent px-3 pt-2.5 text-panel-fg shadow-none placeholder:text-panel-muted-fg focus-visible:ring-0\"\n placeholder={labels.chatPlaceholder}\n value={input}\n onChange={(e) => {\n setInput(e.target.value);\n grow();\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n void send(input);\n }\n }}\n />\n <div className=\"flex items-center justify-end gap-2 px-2 pb-2\">\n {busy && (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"border-panel-border bg-transparent text-panel-fg hover:bg-panel-hover hover:text-panel-fg\"\n onClick={() => void stop()}\n >\n <Square /> {labels.stop}\n </Button>\n )}\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={busy || flushing || locked || !input.trim()}\n className=\"bg-brand text-brand-fg hover:bg-brand/90\"\n >\n <Send /> {labels.send}\n </Button>\n </div>\n </form>\n </div>\n </div>\n );\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 MessageGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-group\"\n className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n {...props}\n />\n );\n}\n\nfunction Message({\n className,\n align = \"start\",\n ...props\n}: React.ComponentProps<\"div\"> & { align?: \"start\" | \"end\" }) {\n return (\n <div\n data-slot=\"message\"\n data-align={align}\n className={cn(\n \"group/message relative flex w-full min-w-0 gap-2 text-sm data-[align=end]:flex-row-reverse\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageAvatar({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-avatar\"\n className={cn(\n \"flex w-fit min-w-8 shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-muted group-has-data-[slot=message-footer]/message:-translate-y-8\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-content\"\n className={cn(\n \"flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-header\"\n className={cn(\n \"flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-footer\"\n className={cn(\n \"flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { MessageGroup, Message, MessageAvatar, MessageContent, MessageFooter, MessageHeader };\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\nfunction BubbleGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"bubble-group\"\n className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n {...props}\n />\n );\n}\n\nconst bubbleVariants = cva(\n \"group/bubble relative flex w-fit max-w-[80%] min-w-0 flex-col gap-1 group-data-[align=end]/message:self-end data-[align=end]:self-end data-[variant=ghost]:max-w-full\",\n {\n variants: {\n variant: {\n default:\n \"*:data-[slot=bubble-content]:bg-primary *:data-[slot=bubble-content]:text-primary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-primary/80\",\n secondary:\n \"*:data-[slot=bubble-content]:bg-secondary *:data-[slot=bubble-content]:text-secondary-foreground [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)]\",\n muted:\n \"*:data-[slot=bubble-content]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[color-mix(in_oklch,var(--muted),var(--foreground)_5%)]\",\n tinted:\n \"*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.93_calc(c*0.4)_h)] *:data-[slot=bubble-content]:text-foreground dark:*:data-[slot=bubble-content]:bg-[oklch(from_var(--primary)_0.3_calc(c*0.4)_h)] [&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.88_calc(c*0.5)_h)] dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-[oklch(from_var(--primary)_0.35_calc(c*0.5)_h)]\",\n outline:\n \"*:data-[slot=bubble-content]:border-border *:data-[slot=bubble-content]:bg-background [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-input/30\",\n ghost:\n \"border-none *:data-[slot=bubble-content]:rounded-none *:data-[slot=bubble-content]:bg-transparent *:data-[slot=bubble-content]:p-0 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted [&>[data-slot=bubble-content]:is(button,a):hover]:text-foreground dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-muted/50\",\n destructive:\n \"*:data-[slot=bubble-content]:bg-destructive/10 *:data-[slot=bubble-content]:text-destructive dark:*:data-[slot=bubble-content]:bg-destructive/20 [&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/20 dark:[&>[data-slot=bubble-content]:is(button,a):hover]:bg-destructive/30\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\nfunction Bubble({\n variant = \"default\",\n align = \"start\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> &\n VariantProps<typeof bubbleVariants> & {\n align?: \"start\" | \"end\";\n }) {\n return (\n <div\n data-slot=\"bubble\"\n data-variant={variant}\n data-align={align}\n className={cn(bubbleVariants({ variant }), className)}\n {...props}\n />\n );\n}\n\nfunction BubbleContent({\n asChild = false,\n className,\n ...props\n}: React.ComponentProps<\"div\"> & {\n asChild?: boolean;\n}) {\n const Comp = asChild ? Slot.Root : \"div\";\n\n return (\n <Comp\n data-slot=\"bubble-content\"\n className={cn(\n \"w-fit max-w-full min-w-0 overflow-hidden rounded-xl border border-transparent px-3 py-2 text-sm leading-relaxed wrap-break-word group-data-[align=end]/bubble:self-end [button]:text-left [button,a]:transition-colors [button,a]:outline-none [button,a]:focus-visible:border-ring [button,a]:focus-visible:ring-3 [button,a]:focus-visible:ring-ring/50\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst bubbleReactionsVariants = cva(\n \"absolute z-10 flex w-fit shrink-0 items-center justify-center gap-1 rounded-full bg-muted px-1.5 py-0.5 text-sm ring-3 ring-card has-[button]:p-0\",\n {\n variants: {\n side: {\n top: \"top-0 -translate-y-3/4\",\n bottom: \"bottom-0 translate-y-3/4\",\n },\n align: {\n start: \"left-3\",\n end: \"right-3\",\n },\n },\n defaultVariants: {\n side: \"bottom\",\n align: \"end\",\n },\n },\n);\n\nfunction BubbleReactions({\n side = \"bottom\",\n align = \"end\",\n className,\n ...props\n}: React.ComponentProps<\"div\"> & {\n align?: \"start\" | \"end\";\n side?: \"top\" | \"bottom\";\n}) {\n return (\n <div\n data-slot=\"bubble-reactions\"\n data-align={align}\n data-side={side}\n className={cn(bubbleReactionsVariants({ side, align }), className)}\n {...props}\n />\n );\n}\n\nexport { BubbleGroup, Bubble, BubbleContent, BubbleReactions };\n","\"use client\";\n\nimport { memo } from \"react\";\nimport ReactMarkdown, { type Components } from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\nimport { cn } from \"../../lib/utils\";\n\n// Agent links open in a new tab (don't navigate away from the editor).\nconst components: Components = {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n a: ({ node, ...props }) => <a target=\"_blank\" rel=\"noopener noreferrer\" {...props} />,\n};\n\nfunction MarkdownImpl({ children }: { children: string }) {\n return (\n <div\n className={cn(\n \"text-sm leading-relaxed break-words\",\n \"[&>:first-child]:mt-0 [&>:last-child]:mb-0\",\n \"[&_p]:my-1.5\",\n \"[&_ul]:my-1.5 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-1.5 [&_ol]:list-decimal [&_ol]:pl-5\",\n \"[&_li]:my-0.5 [&_li>ul]:my-0.5 [&_li>ol]:my-0.5\",\n \"[&_strong]:font-semibold [&_em]:italic\",\n \"[&_a]:underline [&_a]:underline-offset-2\",\n \"[&_h1]:mt-2 [&_h1]:mb-1 [&_h1]:text-base [&_h1]:font-semibold\",\n \"[&_h2]:mt-2 [&_h2]:mb-1 [&_h2]:text-sm [&_h2]:font-semibold\",\n \"[&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-semibold\",\n \"[&_code]:rounded [&_code]:bg-black/5 [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.85em]\",\n \"[&_pre]:my-1.5 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-black/5 [&_pre]:p-2 [&_pre]:text-xs\",\n \"[&_pre_code]:bg-transparent [&_pre_code]:p-0\",\n \"[&_blockquote]:my-1.5 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground\",\n \"[&_hr]:my-2 [&_hr]:border-border\",\n \"[&_table]:my-1.5 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs\",\n \"[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold\",\n \"[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1\",\n )}\n >\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={components}>\n {children}\n </ReactMarkdown>\n </div>\n );\n}\n\nexport const Markdown = memo(MarkdownImpl);\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 markerVariants = cva(\n \"group/marker relative flex min-h-4 w-full items-center gap-2 text-left text-sm text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [a]:underline [a]:underline-offset-3 [a]:hover:text-foreground\",\n {\n variants: {\n variant: {\n default: \"\",\n separator:\n \"before:mr-1 before:h-px before:min-w-0 before:flex-1 before:bg-border after:ml-1 after:h-px after:min-w-0 after:flex-1 after:bg-border\",\n border: \"border-b border-border pb-2\",\n },\n },\n },\n);\n\nfunction Marker({\n className,\n variant = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"div\"> &\n VariantProps<typeof markerVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot.Root : \"div\";\n\n return (\n <Comp\n data-slot=\"marker\"\n data-variant={variant}\n className={cn(markerVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nfunction MarkerIcon({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"marker-icon\"\n aria-hidden=\"true\"\n className={cn(\"size-4 shrink-0 [&_svg:not([class*='size-'])]:size-4\", className)}\n {...props}\n />\n );\n}\n\nfunction MarkerContent({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"marker-content\"\n className={cn(\n \"min-w-0 wrap-break-word group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Marker, MarkerIcon, MarkerContent, markerVariants };\n","\"use client\";\n\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\";\n\nfunction Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n return <CollapsiblePrimitive.CollapsibleTrigger data-slot=\"collapsible-trigger\" {...props} />;\n}\n\nfunction CollapsibleContent({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n return <CollapsiblePrimitive.CollapsibleContent data-slot=\"collapsible-content\" {...props} />;\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\n","\"use client\";\n\nimport * as React from \"react\";\nimport {\n MessageScroller as MessageScrollerPrimitive,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n} from \"@shadcn/react/message-scroller\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"./button\";\nimport { ArrowDownIcon } from \"lucide-react\";\n\nfunction MessageScrollerProvider(\n props: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>,\n) {\n return <MessageScrollerPrimitive.Provider {...props} />;\n}\n\nfunction MessageScroller({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Root>) {\n return (\n <MessageScrollerPrimitive.Root\n data-slot=\"message-scroller\"\n className={cn(\n \"group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerViewport({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>) {\n return (\n <MessageScrollerPrimitive.Viewport\n data-slot=\"message-scroller-viewport\"\n className={cn(\n \"size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerContent({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Content>) {\n return (\n <MessageScrollerPrimitive.Content\n data-slot=\"message-scroller-content\"\n className={cn(\"flex h-max min-h-full flex-col gap-6\", className)}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerItem({\n className,\n scrollAnchor = false,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Item>) {\n return (\n <MessageScrollerPrimitive.Item\n data-slot=\"message-scroller-item\"\n scrollAnchor={scrollAnchor}\n className={cn(\n \"min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction MessageScrollerButton({\n direction = \"end\",\n className,\n children,\n render,\n variant = \"secondary\",\n size = \"icon-sm\",\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Button> &\n Pick<React.ComponentProps<typeof Button>, \"variant\" | \"size\">) {\n return (\n <MessageScrollerPrimitive.Button\n data-slot=\"message-scroller-button\"\n data-direction={direction}\n data-variant={variant}\n data-size={size}\n direction={direction}\n className={cn(\n \"absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180\",\n className,\n )}\n render={render ?? <Button variant={variant} size={size} />}\n {...props}\n >\n {children ?? (\n <>\n <ArrowDownIcon />\n <span className=\"sr-only\">\n {direction === \"end\" ? \"Scroll to end\" : \"Scroll to start\"}\n </span>\n </>\n )}\n </MessageScrollerPrimitive.Button>\n );\n}\n\nexport {\n MessageScrollerProvider,\n MessageScroller,\n MessageScrollerViewport,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerButton,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n};\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"],"mappings":"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","names":["useState","React","jsx","jsx","jsx","cva","jsx","cva","jsx","jsx","jsx","jsxs","useState","useEffect","useState","toast","MessageSquare","jsx","jsx","jsx","cva","jsx","cva","jsx","jsx","cva","Slot","jsx","cva","Slot","jsx","jsx","jsxs","jsx","jsxs","useState","toast","useEffect","MessageSquare","useEffect","useMemo","useRef","useState","Check","X","Fragment","jsx","jsxs","useRef","useState","useEffect","useMemo","X","Check"]}