@mjml-agent-editor/editor 0.1.4 → 0.1.6

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.cjs CHANGED
@@ -33,24 +33,101 @@ var index_exports = {};
33
33
  __export(index_exports, {
34
34
  CanvasComments: () => CanvasComments,
35
35
  ChatPanel: () => ChatPanel,
36
+ DEFAULT_LABELS: () => DEFAULT_LABELS,
36
37
  EditorHeader: () => EditorHeader,
37
38
  EditorStoreProvider: () => EditorStoreProvider,
38
39
  Markdown: () => Markdown,
39
40
  STARTER_MJML: () => import_core.STARTER_MJML,
41
+ mergeLabels: () => mergeLabels,
40
42
  useCommentStore: () => useCommentStore,
41
- useDocumentStore: () => useDocumentStore
43
+ useDocumentStore: () => useDocumentStore,
44
+ useLabels: () => useLabels
42
45
  });
43
46
  module.exports = __toCommonJS(index_exports);
44
47
 
45
48
  // src/stores.tsx
46
49
  var import_react = require("react");
50
+
51
+ // src/labels.ts
52
+ var DEFAULT_LABELS = {
53
+ appName: "MJML Editor",
54
+ emailNameAria: "Email name",
55
+ renameFailed: "Failed to rename.",
56
+ saving: "Saving\u2026",
57
+ saved: "Saved",
58
+ saveFailed: "Save failed",
59
+ undo: "Undo",
60
+ redo: "Redo",
61
+ contentWidth: "Content width",
62
+ preview: "Preview",
63
+ closePreview: "Close preview",
64
+ previewFrameTitle: "Email preview",
65
+ sourceCode: "Source code",
66
+ exportHtml: "Export HTML",
67
+ exportMjml: "Export MJML",
68
+ copied: (kind) => `${kind} copied.`,
69
+ searchBlocks: "Search blocks\u2026",
70
+ expandAll: "Expand all",
71
+ selectAnElement: "Select an element\u2026",
72
+ attributes: "Attributes",
73
+ style: "Style",
74
+ rteFont: "Font",
75
+ rteSize: "Size",
76
+ rteTextColor: "Text color",
77
+ rteBackgroundColor: "Background color",
78
+ documentLoadFailed: "Failed to load the document.",
79
+ documentSaveFailed: "Failed to save changes.",
80
+ commentOnSection: "Comment on the whole section",
81
+ closeComments: "Close",
82
+ noCommentsYet: "No comments yet \u2014 add the first one.",
83
+ resolveComment: "Resolve",
84
+ addComment: "Add",
85
+ addCommentPlaceholder: "Add a comment\u2026 (\u2318/Ctrl+Enter to send)",
86
+ chatEmptyTitle: "Start a conversation with the agent",
87
+ chatEmptyDescription: "Describe the email (goal, tone, content) and paste your data \u2014 the agent will design the sections and images.",
88
+ agentWorking: "Agent is working\u2026",
89
+ reasoning: "Reasoning",
90
+ completedSteps: (count, failed) => failed > 0 ? `Completed steps (${count}, ${failed} failed)` : `Completed steps (${count})`,
91
+ applyComments: "Apply changes from comments",
92
+ chatPlaceholder: "Describe what you want to change\u2026 (Enter = send, Shift+Enter = new line)",
93
+ stop: "Stop",
94
+ send: "Send",
95
+ chatError: "Agent chat error.",
96
+ toolLabels: {
97
+ get_document: "Reading document",
98
+ get_section: "Reading section",
99
+ set_document: "Saving whole email",
100
+ set_section: "Replacing section",
101
+ insert_section: "Adding section",
102
+ remove_section: "Removing section",
103
+ generate_image: "Generating image",
104
+ list_open_comments: "Reading comments",
105
+ resolve_comment: "Resolving comment"
106
+ }
107
+ };
108
+ function mergeLabels(overrides) {
109
+ if (!overrides) return DEFAULT_LABELS;
110
+ return {
111
+ ...DEFAULT_LABELS,
112
+ ...overrides,
113
+ toolLabels: { ...DEFAULT_LABELS.toolLabels, ...overrides.toolLabels }
114
+ };
115
+ }
116
+
117
+ // src/stores.tsx
47
118
  var import_jsx_runtime = require("react/jsx-runtime");
48
119
  var StoreContext = (0, import_react.createContext)(null);
120
+ var LabelContext = (0, import_react.createContext)(null);
49
121
  function EditorStoreProvider({
50
122
  stores,
123
+ labels,
51
124
  children
52
125
  }) {
53
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StoreContext.Provider, { value: stores, children });
126
+ const merged = (0, import_react.useMemo)(() => mergeLabels(labels), [labels]);
127
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StoreContext.Provider, { value: stores, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LabelContext.Provider, { value: merged, children }) });
128
+ }
129
+ function useLabels() {
130
+ return (0, import_react.useContext)(LabelContext) ?? mergeLabels();
54
131
  }
55
132
  function useStores() {
56
133
  const stores = (0, import_react.useContext)(StoreContext);
@@ -553,29 +630,24 @@ function DialogTitle({ className, ...props }) {
553
630
 
554
631
  // src/components/editor/EditorHeader.tsx
555
632
  var import_jsx_runtime13 = require("react/jsx-runtime");
556
- var SAVE_LABEL = {
557
- idle: "",
558
- saving: "Saving\u2026",
559
- saved: "Saved",
560
- error: "Save failed"
561
- };
562
633
  var WIDTHS = ["600px", "640px", "680px", "720px"];
563
634
  var DEVICE_ICON = {
564
635
  Desktop: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Monitor, { className: "size-3.5" }),
565
636
  Mobile: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Smartphone, { className: "size-3.5" })
566
637
  };
567
638
  function SaveBadge({ status }) {
639
+ const labels = useLabels();
568
640
  if (status === "idle") return null;
569
641
  if (status === "saving")
570
642
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1 text-xs text-panel-muted-fg", children: [
571
643
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Spinner, { className: "size-3" }),
572
644
  " ",
573
- SAVE_LABEL.saving
645
+ labels.saving
574
646
  ] });
575
- if (status === "error") return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Badge, { variant: "destructive", children: SAVE_LABEL.error });
647
+ if (status === "error") return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Badge, { variant: "destructive", children: labels.saveFailed });
576
648
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-xs text-emerald-400", children: [
577
649
  "\u2713 ",
578
- SAVE_LABEL.saved
650
+ labels.saved
579
651
  ] });
580
652
  }
581
653
  function slugify(name) {
@@ -598,6 +670,7 @@ function EditorHeader({
598
670
  homeLink
599
671
  }) {
600
672
  const documents = useDocumentStore();
673
+ const labels = useLabels();
601
674
  const [name, setName] = (0, import_react2.useState)("");
602
675
  const [state, setState] = (0, import_react2.useState)({
603
676
  device: "Desktop",
@@ -623,7 +696,7 @@ function EditorHeader({
623
696
  try {
624
697
  await documents.save(docId, { name: n });
625
698
  } catch {
626
- import_sonner.toast.error("Failed to rename.");
699
+ import_sonner.toast.error(labels.renameFailed);
627
700
  }
628
701
  };
629
702
  const devices = api?.getDevices() ?? [];
@@ -631,7 +704,7 @@ function EditorHeader({
631
704
  const openCode = () => setCode({ mjml: api?.getMjml() ?? "", html: api?.getCompiledHtml() ?? "" });
632
705
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b border-panel-border bg-panel px-3 text-panel-fg", children: [
633
706
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Mail, { className: "size-4" }) }),
634
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "hidden text-sm font-semibold sm:inline", children: "MJML Editor" }),
707
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "hidden text-sm font-semibold sm:inline", children: labels.appName }),
635
708
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Separator, { orientation: "vertical", className: "mx-1 !h-5 bg-panel-border" }),
636
709
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
637
710
  Input,
@@ -641,7 +714,7 @@ function EditorHeader({
641
714
  onBlur: () => void commitName(),
642
715
  onKeyDown: (e) => e.key === "Enter" && e.currentTarget.blur(),
643
716
  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",
644
- "aria-label": "Email name"
717
+ "aria-label": labels.emailNameAria
645
718
  }
646
719
  ),
647
720
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Separator, { orientation: "vertical", className: "mx-1 !h-5 bg-panel-border" }),
@@ -679,7 +752,7 @@ function EditorHeader({
679
752
  children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Undo2, {})
680
753
  }
681
754
  ) }),
682
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TooltipContent, { children: "Undo" })
755
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TooltipContent, { children: labels.undo })
683
756
  ] }),
684
757
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Tooltip, { children: [
685
758
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
@@ -693,7 +766,7 @@ function EditorHeader({
693
766
  children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Redo2, {})
694
767
  }
695
768
  ) }),
696
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TooltipContent, { children: "Redo" })
769
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TooltipContent, { children: labels.redo })
697
770
  ] }),
698
771
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Separator, { orientation: "vertical", className: "mx-1 !h-5 bg-panel-border max-xl:hidden" }),
699
772
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Popover, { children: [
@@ -704,7 +777,7 @@ function EditorHeader({
704
777
  size: "sm",
705
778
  className: cn(darkGhost, "gap-1.5 rounded-md border border-panel-border max-xl:hidden"),
706
779
  children: [
707
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-panel-muted-fg", children: "Content width" }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-panel-muted-fg", children: labels.contentWidth }),
708
781
  state.contentWidth,
709
782
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.ChevronDown, { className: "size-3.5" })
710
783
  ]
@@ -766,7 +839,8 @@ function EditorHeader({
766
839
  onClick: () => setPreview(api?.getCompiledHtml() ?? ""),
767
840
  children: [
768
841
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Eye, {}),
769
- " Preview"
842
+ " ",
843
+ labels.preview
770
844
  ]
771
845
  }
772
846
  ) }),
@@ -778,7 +852,8 @@ function EditorHeader({
778
852
  children: [
779
853
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex h-12 shrink-0 items-center justify-between border-b bg-white px-4", children: [
780
854
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogTitle, { className: "text-sm", children: [
781
- "Preview \u2014 ",
855
+ labels.preview,
856
+ " \u2014 ",
782
857
  name
783
858
  ] }),
784
859
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
@@ -805,13 +880,13 @@ function EditorHeader({
805
880
  ]
806
881
  }
807
882
  ),
808
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", size: "icon", "aria-label": "Close preview", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.X, {}) }) })
883
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogClose, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", size: "icon", "aria-label": labels.closePreview, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.X, {}) }) })
809
884
  ] })
810
885
  ] }),
811
886
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "min-h-0 flex-1 overflow-auto p-6", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
812
887
  "iframe",
813
888
  {
814
- title: "Email preview",
889
+ title: labels.previewFrameTitle,
815
890
  srcDoc: preview,
816
891
  className: cn(
817
892
  "mx-auto block h-full min-h-[80vh] rounded-md border bg-white shadow-sm",
@@ -838,7 +913,7 @@ function EditorHeader({
838
913
  }
839
914
  ) }),
840
915
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(DialogContent, { className: "flex !max-w-4xl flex-col", children: [
841
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogTitle, { children: "Source code" }) }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DialogTitle, { children: labels.sourceCode }) }),
842
917
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(Tabs, { defaultValue: "mjml", className: "min-h-0 flex-1", children: [
843
918
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(TabsList, { children: [
844
919
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TabsTrigger, { value: "mjml", children: "MJML" }),
@@ -871,7 +946,7 @@ function EditorHeader({
871
946
  size: "sm",
872
947
  onClick: () => {
873
948
  void navigator.clipboard?.writeText(code[kind]);
874
- import_sonner.toast.success(`${kind.toUpperCase()} copied.`);
949
+ import_sonner.toast.success(labels.copied(kind.toUpperCase()));
875
950
  },
876
951
  children: [
877
952
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react3.Copy, {}),
@@ -897,7 +972,7 @@ function EditorHeader({
897
972
  onClick: () => download(`${base}.html`, api?.getCompiledHtml() ?? "", "text/html"),
898
973
  className: "flex w-full flex-col rounded px-2 py-1.5 text-left text-sm hover:bg-muted",
899
974
  children: [
900
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium", children: "Export HTML" }),
975
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium", children: labels.exportHtml }),
901
976
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
902
977
  base,
903
978
  ".html"
@@ -911,7 +986,7 @@ function EditorHeader({
911
986
  onClick: () => download(`${base}.mjml`, api?.getMjml() ?? "", "text/plain"),
912
987
  className: "flex w-full flex-col rounded px-2 py-1.5 text-left text-sm hover:bg-muted",
913
988
  children: [
914
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium", children: "Export MJML" }),
989
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "font-medium", children: labels.exportMjml }),
915
990
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-xs text-muted-foreground", children: [
916
991
  base,
917
992
  ".mjml"
@@ -1365,17 +1440,6 @@ function MessageScrollerButton({
1365
1440
 
1366
1441
  // src/components/chat/ChatPanel.tsx
1367
1442
  var import_jsx_runtime22 = require("react/jsx-runtime");
1368
- var TOOL_LABELS = {
1369
- get_document: "Reading document",
1370
- get_section: "Reading section",
1371
- set_document: "Saving whole email",
1372
- set_section: "Replacing section",
1373
- insert_section: "Adding section",
1374
- remove_section: "Removing section",
1375
- generate_image: "Generating image",
1376
- list_open_comments: "Reading comments",
1377
- resolve_comment: "Resolving comment"
1378
- };
1379
1443
  var APPLY_COMMENTS_PROMPT = "Apply changes based on all open section comments. After each successful change mark the comment as resolved and summarize what you changed.";
1380
1444
  var MUTATING_TOOLS = /* @__PURE__ */ new Set([
1381
1445
  "set_document",
@@ -1409,30 +1473,41 @@ function toBlocks(parts) {
1409
1473
  }
1410
1474
  if (part.type === "text") {
1411
1475
  if (!part.text.trim()) continue;
1412
- blocks.push({ kind: "text", text: part.text });
1476
+ blocks.push({ kind: "text", text: part.text, narration: false });
1477
+ }
1478
+ }
1479
+ let seenTools = false;
1480
+ for (let i = blocks.length - 1; i >= 0; i--) {
1481
+ const block = blocks[i];
1482
+ if (block.kind === "tools") seenTools = true;
1483
+ else if (block.kind === "text" && seenTools) {
1484
+ blocks[i] = { kind: "text", text: block.text, narration: true };
1413
1485
  }
1414
1486
  }
1415
1487
  return blocks;
1416
1488
  }
1417
1489
  function ToolMarker({ tool }) {
1490
+ const labels = useLabels();
1418
1491
  const name = tool.type.slice(5);
1419
1492
  const done = tool.state === "output-available";
1420
1493
  const failed = tool.state === "output-error";
1421
1494
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Marker, { className: cn(failed ? "text-destructive" : "text-panel-fg"), children: [
1422
1495
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkerIcon, { children: failed ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.TriangleAlert, {}) : done ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Check, { className: "text-emerald-400" }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Loader2, { className: "animate-spin" }) }),
1423
1496
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(MarkerContent, { children: [
1424
- TOOL_LABELS[name] ?? name,
1497
+ labels.toolLabels[name] ?? name,
1425
1498
  failed && tool.errorText ? `: ${tool.errorText}` : ""
1426
1499
  ] })
1427
1500
  ] });
1428
1501
  }
1429
1502
  function BusyMarker() {
1503
+ const labels = useLabels();
1430
1504
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Marker, { className: "text-panel-fg", children: [
1431
1505
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkerIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Loader2, { className: "animate-spin" }) }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkerContent, { children: "Agent is working\u2026" })
1506
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkerContent, { children: labels.agentWorking })
1433
1507
  ] });
1434
1508
  }
1435
1509
  function ToolActivity({ tools }) {
1510
+ const labels = useLabels();
1436
1511
  const running = tools.filter((t) => t.state !== "output-available" && t.state !== "output-error");
1437
1512
  const finished = tools.filter(
1438
1513
  (t) => t.state === "output-available" || t.state === "output-error"
@@ -1442,7 +1517,7 @@ function ToolActivity({ tools }) {
1442
1517
  finished.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Collapsible, { children: [
1443
1518
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(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", children: [
1444
1519
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.ChevronRight, { className: "size-3.5 shrink-0 transition-transform group-data-[state=open]/col:rotate-90" }),
1445
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: failedCount > 0 ? `Completed steps (${finished.length}, ${failedCount} failed)` : `Completed steps (${finished.length})` })
1520
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: labels.completedSteps(finished.length, failedCount) })
1446
1521
  ] }),
1447
1522
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CollapsibleContent, { className: "mt-1 space-y-0.5 pl-1", children: finished.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolMarker, { tool: t }, `f${i}`)) })
1448
1523
  ] }),
@@ -1450,11 +1525,12 @@ function ToolActivity({ tools }) {
1450
1525
  ] });
1451
1526
  }
1452
1527
  function ReasoningBlock({ text }) {
1528
+ const labels = useLabels();
1453
1529
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "rounded-lg border border-panel-border bg-panel-elevated/60 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Collapsible, { children: [
1454
1530
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(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", children: [
1455
1531
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.ChevronRight, { className: "size-3.5 shrink-0 transition-transform group-data-[state=open]/col:rotate-90" }),
1456
1532
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Brain, { className: "size-3.5 shrink-0" }),
1457
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: "Reasoning" })
1533
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: labels.reasoning })
1458
1534
  ] }),
1459
1535
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CollapsibleContent, { className: "mt-1.5 pl-1 text-xs whitespace-pre-wrap text-panel-muted-fg", children: text })
1460
1536
  ] }) });
@@ -1469,6 +1545,7 @@ function ChatPanel({
1469
1545
  onSectionEditStart,
1470
1546
  onSectionEditEnd
1471
1547
  }) {
1548
+ const labels = useLabels();
1472
1549
  const [input, setInput] = (0, import_react5.useState)("");
1473
1550
  const taRef = (0, import_react5.useRef)(null);
1474
1551
  const times = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
@@ -1491,7 +1568,7 @@ function ChatPanel({
1491
1568
  onFinish: onAgentFinish,
1492
1569
  onError: (e) => {
1493
1570
  console.error("chat error:", e);
1494
- import_sonner2.toast.error("Agent chat error.");
1571
+ import_sonner2.toast.error(labels.chatError);
1495
1572
  }
1496
1573
  });
1497
1574
  const firedTools = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
@@ -1553,8 +1630,8 @@ function ChatPanel({
1553
1630
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageScrollerViewport, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(MessageScrollerContent, { className: "p-3", children: [
1554
1631
  messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Empty, { className: "h-full", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(EmptyHeader, { children: [
1555
1632
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmptyMedia, { variant: "icon", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.MessageSquare, {}) }),
1556
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmptyTitle, { children: "Start a conversation with the agent" }),
1557
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmptyDescription, { children: "Describe the email (goal, tone, content) and paste your data \u2014 the agent will design the sections and images." })
1633
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmptyTitle, { children: labels.chatEmptyTitle }),
1634
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(EmptyDescription, { children: labels.chatEmptyDescription })
1558
1635
  ] }) }) : messages.map((message, mi) => {
1559
1636
  const isUser = message.role === "user";
1560
1637
  const isLast = mi === messages.length - 1;
@@ -1578,6 +1655,17 @@ function ChatPanel({
1578
1655
  if (block.kind === "reasoning") {
1579
1656
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ReasoningBlock, { text: block.text }, i);
1580
1657
  }
1658
+ if (block.narration && !isUser) {
1659
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1660
+ "div",
1661
+ {
1662
+ "data-slot": "narration",
1663
+ className: "px-1 text-xs leading-relaxed text-panel-muted-fg [&_a]:text-brand [&_code]:!bg-white/10 [&_p]:my-0.5",
1664
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Markdown, { children: block.text })
1665
+ },
1666
+ i
1667
+ );
1668
+ }
1581
1669
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1582
1670
  Bubble,
1583
1671
  {
@@ -1618,7 +1706,8 @@ function ChatPanel({
1618
1706
  onClick: () => void send(APPLY_COMMENTS_PROMPT),
1619
1707
  children: [
1620
1708
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Sparkles, {}),
1621
- " Apply changes from comments"
1709
+ " ",
1710
+ labels.applyComments
1622
1711
  ]
1623
1712
  }
1624
1713
  ),
@@ -1637,7 +1726,7 @@ function ChatPanel({
1637
1726
  ref: taRef,
1638
1727
  rows: 2,
1639
1728
  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",
1640
- placeholder: "Describe what you want to change\u2026 (Enter = send, Shift+Enter = new line)",
1729
+ placeholder: labels.chatPlaceholder,
1641
1730
  value: input,
1642
1731
  onChange: (e) => {
1643
1732
  setInput(e.target.value);
@@ -1662,7 +1751,8 @@ function ChatPanel({
1662
1751
  onClick: () => void stop(),
1663
1752
  children: [
1664
1753
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Square, {}),
1665
- " Stop"
1754
+ " ",
1755
+ labels.stop
1666
1756
  ]
1667
1757
  }
1668
1758
  ),
@@ -1675,7 +1765,8 @@ function ChatPanel({
1675
1765
  className: "bg-brand text-brand-fg hover:bg-brand/90",
1676
1766
  children: [
1677
1767
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Send, {}),
1678
- " Send"
1768
+ " ",
1769
+ labels.send
1679
1770
  ]
1680
1771
  }
1681
1772
  )
@@ -1702,6 +1793,7 @@ function CanvasComments({
1702
1793
  onOpenCountChange
1703
1794
  }) {
1704
1795
  const commentStore = useCommentStore();
1796
+ const labels = useLabels();
1705
1797
  const editor = (0, import_react6.useEditorMaybe)();
1706
1798
  const containerRef = (0, import_react7.useRef)(null);
1707
1799
  const [comments, setComments] = (0, import_react7.useState)([]);
@@ -1890,7 +1982,7 @@ function CanvasComments({
1890
1982
  " ",
1891
1983
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "font-medium text-foreground", children: activeTarget.objectLabel })
1892
1984
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
1893
- "Comment on the whole section",
1985
+ labels.commentOnSection,
1894
1986
  " ",
1895
1987
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("code", { className: "font-mono", children: activeTarget?.sectionId })
1896
1988
  ] }) }),
@@ -1899,14 +1991,14 @@ function CanvasComments({
1899
1991
  {
1900
1992
  type: "button",
1901
1993
  onClick: closePopover,
1902
- "aria-label": "Close",
1994
+ "aria-label": labels.closeComments,
1903
1995
  className: "-mr-1 -mt-0.5 shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
1904
1996
  children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react6.X, { className: "size-4" })
1905
1997
  }
1906
1998
  )
1907
1999
  ] }),
1908
2000
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "max-h-64 space-y-2 overflow-y-auto p-3", children: [
1909
- activeComments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-xs text-muted-foreground", children: "No comments yet \u2014 add the first one." }),
2001
+ activeComments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-xs text-muted-foreground", children: labels.noCommentsYet }),
1910
2002
  activeComments.map((c) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "rounded-md border bg-muted/40 p-2 text-sm", children: [
1911
2003
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "whitespace-pre-wrap", children: c.body }),
1912
2004
  /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
@@ -1916,7 +2008,8 @@ function CanvasComments({
1916
2008
  className: "mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground",
1917
2009
  children: [
1918
2010
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react6.Check, { className: "size-3" }),
1919
- " Resolve"
2011
+ " ",
2012
+ labels.resolveComment
1920
2013
  ]
1921
2014
  }
1922
2015
  )
@@ -1929,7 +2022,7 @@ function CanvasComments({
1929
2022
  autoFocus: true,
1930
2023
  value: body,
1931
2024
  onChange: (e) => setBody(e.target.value),
1932
- placeholder: "Add a comment\u2026 (\u2318/Ctrl+Enter to send)",
2025
+ placeholder: labels.addCommentPlaceholder,
1933
2026
  className: "min-h-14 resize-none text-sm",
1934
2027
  onKeyDown: (e) => {
1935
2028
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
@@ -1941,7 +2034,8 @@ function CanvasComments({
1941
2034
  ),
1942
2035
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(Button, { size: "sm", disabled: !body.trim(), onClick: () => void submit(), children: [
1943
2036
  /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react6.MessageSquarePlus, {}),
1944
- " Add"
2037
+ " ",
2038
+ labels.addComment
1945
2039
  ] }) })
1946
2040
  ] })
1947
2041
  ]
@@ -1954,11 +2048,14 @@ function CanvasComments({
1954
2048
  0 && (module.exports = {
1955
2049
  CanvasComments,
1956
2050
  ChatPanel,
2051
+ DEFAULT_LABELS,
1957
2052
  EditorHeader,
1958
2053
  EditorStoreProvider,
1959
2054
  Markdown,
1960
2055
  STARTER_MJML,
2056
+ mergeLabels,
1961
2057
  useCommentStore,
1962
- useDocumentStore
2058
+ useDocumentStore,
2059
+ useLabels
1963
2060
  });
1964
2061
  //# sourceMappingURL=index.cjs.map