@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.
@@ -18,14 +18,88 @@ import {
18
18
  } from "./chunk-V6RR4VRD.js";
19
19
 
20
20
  // src/stores.tsx
21
- import { createContext, useContext } from "react";
21
+ import { createContext, useContext, useMemo } from "react";
22
+
23
+ // src/labels.ts
24
+ var DEFAULT_LABELS = {
25
+ appName: "MJML Editor",
26
+ emailNameAria: "Email name",
27
+ renameFailed: "Failed to rename.",
28
+ saving: "Saving\u2026",
29
+ saved: "Saved",
30
+ saveFailed: "Save failed",
31
+ undo: "Undo",
32
+ redo: "Redo",
33
+ contentWidth: "Content width",
34
+ preview: "Preview",
35
+ closePreview: "Close preview",
36
+ previewFrameTitle: "Email preview",
37
+ sourceCode: "Source code",
38
+ exportHtml: "Export HTML",
39
+ exportMjml: "Export MJML",
40
+ copied: (kind) => `${kind} copied.`,
41
+ searchBlocks: "Search blocks\u2026",
42
+ expandAll: "Expand all",
43
+ selectAnElement: "Select an element\u2026",
44
+ attributes: "Attributes",
45
+ style: "Style",
46
+ rteFont: "Font",
47
+ rteSize: "Size",
48
+ rteTextColor: "Text color",
49
+ rteBackgroundColor: "Background color",
50
+ documentLoadFailed: "Failed to load the document.",
51
+ documentSaveFailed: "Failed to save changes.",
52
+ commentOnSection: "Comment on the whole section",
53
+ closeComments: "Close",
54
+ noCommentsYet: "No comments yet \u2014 add the first one.",
55
+ resolveComment: "Resolve",
56
+ addComment: "Add",
57
+ addCommentPlaceholder: "Add a comment\u2026 (\u2318/Ctrl+Enter to send)",
58
+ chatEmptyTitle: "Start a conversation with the agent",
59
+ chatEmptyDescription: "Describe the email (goal, tone, content) and paste your data \u2014 the agent will design the sections and images.",
60
+ agentWorking: "Agent is working\u2026",
61
+ reasoning: "Reasoning",
62
+ completedSteps: (count, failed) => failed > 0 ? `Completed steps (${count}, ${failed} failed)` : `Completed steps (${count})`,
63
+ applyComments: "Apply changes from comments",
64
+ chatPlaceholder: "Describe what you want to change\u2026 (Enter = send, Shift+Enter = new line)",
65
+ stop: "Stop",
66
+ send: "Send",
67
+ chatError: "Agent chat error.",
68
+ toolLabels: {
69
+ get_document: "Reading document",
70
+ get_section: "Reading section",
71
+ set_document: "Saving whole email",
72
+ set_section: "Replacing section",
73
+ insert_section: "Adding section",
74
+ remove_section: "Removing section",
75
+ generate_image: "Generating image",
76
+ list_open_comments: "Reading comments",
77
+ resolve_comment: "Resolving comment"
78
+ }
79
+ };
80
+ function mergeLabels(overrides) {
81
+ if (!overrides) return DEFAULT_LABELS;
82
+ return {
83
+ ...DEFAULT_LABELS,
84
+ ...overrides,
85
+ toolLabels: { ...DEFAULT_LABELS.toolLabels, ...overrides.toolLabels }
86
+ };
87
+ }
88
+
89
+ // src/stores.tsx
22
90
  import { jsx } from "react/jsx-runtime";
23
91
  var StoreContext = createContext(null);
92
+ var LabelContext = createContext(null);
24
93
  function EditorStoreProvider({
25
94
  stores,
95
+ labels,
26
96
  children
27
97
  }) {
28
- return /* @__PURE__ */ jsx(StoreContext.Provider, { value: stores, children });
98
+ const merged = useMemo(() => mergeLabels(labels), [labels]);
99
+ return /* @__PURE__ */ jsx(StoreContext.Provider, { value: stores, children: /* @__PURE__ */ jsx(LabelContext.Provider, { value: merged, children }) });
100
+ }
101
+ function useLabels() {
102
+ return useContext(LabelContext) ?? mergeLabels();
29
103
  }
30
104
  function useStores() {
31
105
  const stores = useContext(StoreContext);
@@ -349,29 +423,24 @@ function DialogTitle({ className, ...props }) {
349
423
 
350
424
  // src/components/editor/EditorHeader.tsx
351
425
  import { jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
352
- var SAVE_LABEL = {
353
- idle: "",
354
- saving: "Saving\u2026",
355
- saved: "Saved",
356
- error: "Save failed"
357
- };
358
426
  var WIDTHS = ["600px", "640px", "680px", "720px"];
359
427
  var DEVICE_ICON = {
360
428
  Desktop: /* @__PURE__ */ jsx7(Monitor, { className: "size-3.5" }),
361
429
  Mobile: /* @__PURE__ */ jsx7(Smartphone, { className: "size-3.5" })
362
430
  };
363
431
  function SaveBadge({ status }) {
432
+ const labels = useLabels();
364
433
  if (status === "idle") return null;
365
434
  if (status === "saving")
366
435
  return /* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-1 text-xs text-panel-muted-fg", children: [
367
436
  /* @__PURE__ */ jsx7(Spinner, { className: "size-3" }),
368
437
  " ",
369
- SAVE_LABEL.saving
438
+ labels.saving
370
439
  ] });
371
- if (status === "error") return /* @__PURE__ */ jsx7(Badge, { variant: "destructive", children: SAVE_LABEL.error });
440
+ if (status === "error") return /* @__PURE__ */ jsx7(Badge, { variant: "destructive", children: labels.saveFailed });
372
441
  return /* @__PURE__ */ jsxs2("span", { className: "text-xs text-emerald-400", children: [
373
442
  "\u2713 ",
374
- SAVE_LABEL.saved
443
+ labels.saved
375
444
  ] });
376
445
  }
377
446
  function slugify(name) {
@@ -394,6 +463,7 @@ function EditorHeader({
394
463
  homeLink
395
464
  }) {
396
465
  const documents = useDocumentStore();
466
+ const labels = useLabels();
397
467
  const [name, setName] = useState("");
398
468
  const [state, setState] = useState({
399
469
  device: "Desktop",
@@ -419,7 +489,7 @@ function EditorHeader({
419
489
  try {
420
490
  await documents.save(docId, { name: n });
421
491
  } catch {
422
- toast.error("Failed to rename.");
492
+ toast.error(labels.renameFailed);
423
493
  }
424
494
  };
425
495
  const devices = api?.getDevices() ?? [];
@@ -427,7 +497,7 @@ function EditorHeader({
427
497
  const openCode = () => setCode({ mjml: api?.getMjml() ?? "", html: api?.getCompiledHtml() ?? "" });
428
498
  return /* @__PURE__ */ jsxs2("header", { className: "flex h-14 shrink-0 items-center gap-2 border-b border-panel-border bg-panel px-3 text-panel-fg", children: [
429
499
  /* @__PURE__ */ jsx7("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__ */ jsx7(Mail, { className: "size-4" }) }),
430
- /* @__PURE__ */ jsx7("span", { className: "hidden text-sm font-semibold sm:inline", children: "MJML Editor" }),
500
+ /* @__PURE__ */ jsx7("span", { className: "hidden text-sm font-semibold sm:inline", children: labels.appName }),
431
501
  /* @__PURE__ */ jsx7(Separator, { orientation: "vertical", className: "mx-1 !h-5 bg-panel-border" }),
432
502
  /* @__PURE__ */ jsx7(
433
503
  Input,
@@ -437,7 +507,7 @@ function EditorHeader({
437
507
  onBlur: () => void commitName(),
438
508
  onKeyDown: (e) => e.key === "Enter" && e.currentTarget.blur(),
439
509
  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",
440
- "aria-label": "Email name"
510
+ "aria-label": labels.emailNameAria
441
511
  }
442
512
  ),
443
513
  /* @__PURE__ */ jsx7(Separator, { orientation: "vertical", className: "mx-1 !h-5 bg-panel-border" }),
@@ -475,7 +545,7 @@ function EditorHeader({
475
545
  children: /* @__PURE__ */ jsx7(Undo2, {})
476
546
  }
477
547
  ) }),
478
- /* @__PURE__ */ jsx7(TooltipContent, { children: "Undo" })
548
+ /* @__PURE__ */ jsx7(TooltipContent, { children: labels.undo })
479
549
  ] }),
480
550
  /* @__PURE__ */ jsxs2(Tooltip, { children: [
481
551
  /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
@@ -489,7 +559,7 @@ function EditorHeader({
489
559
  children: /* @__PURE__ */ jsx7(Redo2, {})
490
560
  }
491
561
  ) }),
492
- /* @__PURE__ */ jsx7(TooltipContent, { children: "Redo" })
562
+ /* @__PURE__ */ jsx7(TooltipContent, { children: labels.redo })
493
563
  ] }),
494
564
  /* @__PURE__ */ jsx7(Separator, { orientation: "vertical", className: "mx-1 !h-5 bg-panel-border max-xl:hidden" }),
495
565
  /* @__PURE__ */ jsxs2(Popover, { children: [
@@ -500,7 +570,7 @@ function EditorHeader({
500
570
  size: "sm",
501
571
  className: cn(darkGhost, "gap-1.5 rounded-md border border-panel-border max-xl:hidden"),
502
572
  children: [
503
- /* @__PURE__ */ jsx7("span", { className: "text-panel-muted-fg", children: "Content width" }),
573
+ /* @__PURE__ */ jsx7("span", { className: "text-panel-muted-fg", children: labels.contentWidth }),
504
574
  state.contentWidth,
505
575
  /* @__PURE__ */ jsx7(ChevronDown, { className: "size-3.5" })
506
576
  ]
@@ -562,7 +632,8 @@ function EditorHeader({
562
632
  onClick: () => setPreview(api?.getCompiledHtml() ?? ""),
563
633
  children: [
564
634
  /* @__PURE__ */ jsx7(Eye, {}),
565
- " Preview"
635
+ " ",
636
+ labels.preview
566
637
  ]
567
638
  }
568
639
  ) }),
@@ -574,7 +645,8 @@ function EditorHeader({
574
645
  children: [
575
646
  /* @__PURE__ */ jsxs2("div", { className: "flex h-12 shrink-0 items-center justify-between border-b bg-white px-4", children: [
576
647
  /* @__PURE__ */ jsxs2(DialogTitle, { className: "text-sm", children: [
577
- "Preview \u2014 ",
648
+ labels.preview,
649
+ " \u2014 ",
578
650
  name
579
651
  ] }),
580
652
  /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
@@ -601,13 +673,13 @@ function EditorHeader({
601
673
  ]
602
674
  }
603
675
  ),
604
- /* @__PURE__ */ jsx7(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx7(Button, { variant: "ghost", size: "icon", "aria-label": "Close preview", children: /* @__PURE__ */ jsx7(X, {}) }) })
676
+ /* @__PURE__ */ jsx7(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx7(Button, { variant: "ghost", size: "icon", "aria-label": labels.closePreview, children: /* @__PURE__ */ jsx7(X, {}) }) })
605
677
  ] })
606
678
  ] }),
607
679
  /* @__PURE__ */ jsx7("div", { className: "min-h-0 flex-1 overflow-auto p-6", children: /* @__PURE__ */ jsx7(
608
680
  "iframe",
609
681
  {
610
- title: "Email preview",
682
+ title: labels.previewFrameTitle,
611
683
  srcDoc: preview,
612
684
  className: cn(
613
685
  "mx-auto block h-full min-h-[80vh] rounded-md border bg-white shadow-sm",
@@ -634,7 +706,7 @@ function EditorHeader({
634
706
  }
635
707
  ) }),
636
708
  /* @__PURE__ */ jsxs2(DialogContent, { className: "flex !max-w-4xl flex-col", children: [
637
- /* @__PURE__ */ jsx7(DialogHeader, { children: /* @__PURE__ */ jsx7(DialogTitle, { children: "Source code" }) }),
709
+ /* @__PURE__ */ jsx7(DialogHeader, { children: /* @__PURE__ */ jsx7(DialogTitle, { children: labels.sourceCode }) }),
638
710
  /* @__PURE__ */ jsxs2(Tabs, { defaultValue: "mjml", className: "min-h-0 flex-1", children: [
639
711
  /* @__PURE__ */ jsxs2(TabsList, { children: [
640
712
  /* @__PURE__ */ jsx7(TabsTrigger, { value: "mjml", children: "MJML" }),
@@ -667,7 +739,7 @@ function EditorHeader({
667
739
  size: "sm",
668
740
  onClick: () => {
669
741
  void navigator.clipboard?.writeText(code[kind]);
670
- toast.success(`${kind.toUpperCase()} copied.`);
742
+ toast.success(labels.copied(kind.toUpperCase()));
671
743
  },
672
744
  children: [
673
745
  /* @__PURE__ */ jsx7(Copy, {}),
@@ -693,7 +765,7 @@ function EditorHeader({
693
765
  onClick: () => download(`${base}.html`, api?.getCompiledHtml() ?? "", "text/html"),
694
766
  className: "flex w-full flex-col rounded px-2 py-1.5 text-left text-sm hover:bg-muted",
695
767
  children: [
696
- /* @__PURE__ */ jsx7("span", { className: "font-medium", children: "Export HTML" }),
768
+ /* @__PURE__ */ jsx7("span", { className: "font-medium", children: labels.exportHtml }),
697
769
  /* @__PURE__ */ jsxs2("span", { className: "text-xs text-muted-foreground", children: [
698
770
  base,
699
771
  ".html"
@@ -707,7 +779,7 @@ function EditorHeader({
707
779
  onClick: () => download(`${base}.mjml`, api?.getMjml() ?? "", "text/plain"),
708
780
  className: "flex w-full flex-col rounded px-2 py-1.5 text-left text-sm hover:bg-muted",
709
781
  children: [
710
- /* @__PURE__ */ jsx7("span", { className: "font-medium", children: "Export MJML" }),
782
+ /* @__PURE__ */ jsx7("span", { className: "font-medium", children: labels.exportMjml }),
711
783
  /* @__PURE__ */ jsxs2("span", { className: "text-xs text-muted-foreground", children: [
712
784
  base,
713
785
  ".mjml"
@@ -1080,17 +1152,6 @@ function MessageScrollerButton({
1080
1152
 
1081
1153
  // src/components/chat/ChatPanel.tsx
1082
1154
  import { jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
1083
- var TOOL_LABELS = {
1084
- get_document: "Reading document",
1085
- get_section: "Reading section",
1086
- set_document: "Saving whole email",
1087
- set_section: "Replacing section",
1088
- insert_section: "Adding section",
1089
- remove_section: "Removing section",
1090
- generate_image: "Generating image",
1091
- list_open_comments: "Reading comments",
1092
- resolve_comment: "Resolving comment"
1093
- };
1094
1155
  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.";
1095
1156
  var MUTATING_TOOLS = /* @__PURE__ */ new Set([
1096
1157
  "set_document",
@@ -1124,30 +1185,41 @@ function toBlocks(parts) {
1124
1185
  }
1125
1186
  if (part.type === "text") {
1126
1187
  if (!part.text.trim()) continue;
1127
- blocks.push({ kind: "text", text: part.text });
1188
+ blocks.push({ kind: "text", text: part.text, narration: false });
1189
+ }
1190
+ }
1191
+ let seenTools = false;
1192
+ for (let i = blocks.length - 1; i >= 0; i--) {
1193
+ const block = blocks[i];
1194
+ if (block.kind === "tools") seenTools = true;
1195
+ else if (block.kind === "text" && seenTools) {
1196
+ blocks[i] = { kind: "text", text: block.text, narration: true };
1128
1197
  }
1129
1198
  }
1130
1199
  return blocks;
1131
1200
  }
1132
1201
  function ToolMarker({ tool }) {
1202
+ const labels = useLabels();
1133
1203
  const name = tool.type.slice(5);
1134
1204
  const done = tool.state === "output-available";
1135
1205
  const failed = tool.state === "output-error";
1136
1206
  return /* @__PURE__ */ jsxs4(Marker, { className: cn(failed ? "text-destructive" : "text-panel-fg"), children: [
1137
1207
  /* @__PURE__ */ jsx14(MarkerIcon, { children: failed ? /* @__PURE__ */ jsx14(TriangleAlert, {}) : done ? /* @__PURE__ */ jsx14(Check, { className: "text-emerald-400" }) : /* @__PURE__ */ jsx14(Loader2, { className: "animate-spin" }) }),
1138
1208
  /* @__PURE__ */ jsxs4(MarkerContent, { children: [
1139
- TOOL_LABELS[name] ?? name,
1209
+ labels.toolLabels[name] ?? name,
1140
1210
  failed && tool.errorText ? `: ${tool.errorText}` : ""
1141
1211
  ] })
1142
1212
  ] });
1143
1213
  }
1144
1214
  function BusyMarker() {
1215
+ const labels = useLabels();
1145
1216
  return /* @__PURE__ */ jsxs4(Marker, { className: "text-panel-fg", children: [
1146
1217
  /* @__PURE__ */ jsx14(MarkerIcon, { children: /* @__PURE__ */ jsx14(Loader2, { className: "animate-spin" }) }),
1147
- /* @__PURE__ */ jsx14(MarkerContent, { children: "Agent is working\u2026" })
1218
+ /* @__PURE__ */ jsx14(MarkerContent, { children: labels.agentWorking })
1148
1219
  ] });
1149
1220
  }
1150
1221
  function ToolActivity({ tools }) {
1222
+ const labels = useLabels();
1151
1223
  const running = tools.filter((t) => t.state !== "output-available" && t.state !== "output-error");
1152
1224
  const finished = tools.filter(
1153
1225
  (t) => t.state === "output-available" || t.state === "output-error"
@@ -1157,7 +1229,7 @@ function ToolActivity({ tools }) {
1157
1229
  finished.length > 0 && /* @__PURE__ */ jsxs4(Collapsible, { children: [
1158
1230
  /* @__PURE__ */ jsxs4(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: [
1159
1231
  /* @__PURE__ */ jsx14(ChevronRight, { className: "size-3.5 shrink-0 transition-transform group-data-[state=open]/col:rotate-90" }),
1160
- /* @__PURE__ */ jsx14("span", { children: failedCount > 0 ? `Completed steps (${finished.length}, ${failedCount} failed)` : `Completed steps (${finished.length})` })
1232
+ /* @__PURE__ */ jsx14("span", { children: labels.completedSteps(finished.length, failedCount) })
1161
1233
  ] }),
1162
1234
  /* @__PURE__ */ jsx14(CollapsibleContent, { className: "mt-1 space-y-0.5 pl-1", children: finished.map((t, i) => /* @__PURE__ */ jsx14(ToolMarker, { tool: t }, `f${i}`)) })
1163
1235
  ] }),
@@ -1165,11 +1237,12 @@ function ToolActivity({ tools }) {
1165
1237
  ] });
1166
1238
  }
1167
1239
  function ReasoningBlock({ text }) {
1240
+ const labels = useLabels();
1168
1241
  return /* @__PURE__ */ jsx14("div", { className: "rounded-lg border border-panel-border bg-panel-elevated/60 p-2", children: /* @__PURE__ */ jsxs4(Collapsible, { children: [
1169
1242
  /* @__PURE__ */ jsxs4(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: [
1170
1243
  /* @__PURE__ */ jsx14(ChevronRight, { className: "size-3.5 shrink-0 transition-transform group-data-[state=open]/col:rotate-90" }),
1171
1244
  /* @__PURE__ */ jsx14(Brain, { className: "size-3.5 shrink-0" }),
1172
- /* @__PURE__ */ jsx14("span", { children: "Reasoning" })
1245
+ /* @__PURE__ */ jsx14("span", { children: labels.reasoning })
1173
1246
  ] }),
1174
1247
  /* @__PURE__ */ jsx14(CollapsibleContent, { className: "mt-1.5 pl-1 text-xs whitespace-pre-wrap text-panel-muted-fg", children: text })
1175
1248
  ] }) });
@@ -1184,6 +1257,7 @@ function ChatPanel({
1184
1257
  onSectionEditStart,
1185
1258
  onSectionEditEnd
1186
1259
  }) {
1260
+ const labels = useLabels();
1187
1261
  const [input, setInput] = useState2("");
1188
1262
  const taRef = useRef(null);
1189
1263
  const times = useRef(/* @__PURE__ */ new Map());
@@ -1206,7 +1280,7 @@ function ChatPanel({
1206
1280
  onFinish: onAgentFinish,
1207
1281
  onError: (e) => {
1208
1282
  console.error("chat error:", e);
1209
- toast2.error("Agent chat error.");
1283
+ toast2.error(labels.chatError);
1210
1284
  }
1211
1285
  });
1212
1286
  const firedTools = useRef(/* @__PURE__ */ new Set());
@@ -1268,8 +1342,8 @@ function ChatPanel({
1268
1342
  /* @__PURE__ */ jsx14(MessageScrollerViewport, { children: /* @__PURE__ */ jsxs4(MessageScrollerContent, { className: "p-3", children: [
1269
1343
  messages.length === 0 ? /* @__PURE__ */ jsx14(Empty, { className: "h-full", children: /* @__PURE__ */ jsxs4(EmptyHeader, { children: [
1270
1344
  /* @__PURE__ */ jsx14(EmptyMedia, { variant: "icon", children: /* @__PURE__ */ jsx14(MessageSquare2, {}) }),
1271
- /* @__PURE__ */ jsx14(EmptyTitle, { children: "Start a conversation with the agent" }),
1272
- /* @__PURE__ */ jsx14(EmptyDescription, { children: "Describe the email (goal, tone, content) and paste your data \u2014 the agent will design the sections and images." })
1345
+ /* @__PURE__ */ jsx14(EmptyTitle, { children: labels.chatEmptyTitle }),
1346
+ /* @__PURE__ */ jsx14(EmptyDescription, { children: labels.chatEmptyDescription })
1273
1347
  ] }) }) : messages.map((message, mi) => {
1274
1348
  const isUser = message.role === "user";
1275
1349
  const isLast = mi === messages.length - 1;
@@ -1293,6 +1367,17 @@ function ChatPanel({
1293
1367
  if (block.kind === "reasoning") {
1294
1368
  return /* @__PURE__ */ jsx14(ReasoningBlock, { text: block.text }, i);
1295
1369
  }
1370
+ if (block.narration && !isUser) {
1371
+ return /* @__PURE__ */ jsx14(
1372
+ "div",
1373
+ {
1374
+ "data-slot": "narration",
1375
+ className: "px-1 text-xs leading-relaxed text-panel-muted-fg [&_a]:text-brand [&_code]:!bg-white/10 [&_p]:my-0.5",
1376
+ children: /* @__PURE__ */ jsx14(Markdown, { children: block.text })
1377
+ },
1378
+ i
1379
+ );
1380
+ }
1296
1381
  return /* @__PURE__ */ jsx14(
1297
1382
  Bubble,
1298
1383
  {
@@ -1333,7 +1418,8 @@ function ChatPanel({
1333
1418
  onClick: () => void send(APPLY_COMMENTS_PROMPT),
1334
1419
  children: [
1335
1420
  /* @__PURE__ */ jsx14(Sparkles, {}),
1336
- " Apply changes from comments"
1421
+ " ",
1422
+ labels.applyComments
1337
1423
  ]
1338
1424
  }
1339
1425
  ),
@@ -1352,7 +1438,7 @@ function ChatPanel({
1352
1438
  ref: taRef,
1353
1439
  rows: 2,
1354
1440
  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",
1355
- placeholder: "Describe what you want to change\u2026 (Enter = send, Shift+Enter = new line)",
1441
+ placeholder: labels.chatPlaceholder,
1356
1442
  value: input,
1357
1443
  onChange: (e) => {
1358
1444
  setInput(e.target.value);
@@ -1377,7 +1463,8 @@ function ChatPanel({
1377
1463
  onClick: () => void stop(),
1378
1464
  children: [
1379
1465
  /* @__PURE__ */ jsx14(Square, {}),
1380
- " Stop"
1466
+ " ",
1467
+ labels.stop
1381
1468
  ]
1382
1469
  }
1383
1470
  ),
@@ -1390,7 +1477,8 @@ function ChatPanel({
1390
1477
  className: "bg-brand text-brand-fg hover:bg-brand/90",
1391
1478
  children: [
1392
1479
  /* @__PURE__ */ jsx14(Send, {}),
1393
- " Send"
1480
+ " ",
1481
+ labels.send
1394
1482
  ]
1395
1483
  }
1396
1484
  )
@@ -1404,7 +1492,7 @@ function ChatPanel({
1404
1492
 
1405
1493
  // src/components/comments/CanvasComments.tsx
1406
1494
  import { useEditorMaybe } from "@grapesjs/react";
1407
- import { useCallback, useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState3 } from "react";
1495
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
1408
1496
  import { Check as Check2, MessageSquarePlus, X as X2 } from "lucide-react";
1409
1497
  import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs5 } from "react/jsx-runtime";
1410
1498
  var keyOf = (c) => c.objectId ? `obj-${c.objectId}` : `sec-${c.sectionId}`;
@@ -1417,6 +1505,7 @@ function CanvasComments({
1417
1505
  onOpenCountChange
1418
1506
  }) {
1419
1507
  const commentStore = useCommentStore();
1508
+ const labels = useLabels();
1420
1509
  const editor = useEditorMaybe();
1421
1510
  const containerRef = useRef2(null);
1422
1511
  const [comments, setComments] = useState3([]);
@@ -1430,11 +1519,11 @@ function CanvasComments({
1430
1519
  useEffect3(() => {
1431
1520
  reload();
1432
1521
  }, [reload, refreshSignal]);
1433
- const open = useMemo(() => comments.filter((c) => c.status === "open"), [comments]);
1522
+ const open = useMemo2(() => comments.filter((c) => c.status === "open"), [comments]);
1434
1523
  useEffect3(() => {
1435
1524
  onOpenCountChange(open.length);
1436
1525
  }, [open.length, onOpenCountChange]);
1437
- const pins = useMemo(() => {
1526
+ const pins = useMemo2(() => {
1438
1527
  const map = /* @__PURE__ */ new Map();
1439
1528
  for (const c of open) {
1440
1529
  const k = keyOf(c);
@@ -1605,7 +1694,7 @@ function CanvasComments({
1605
1694
  " ",
1606
1695
  /* @__PURE__ */ jsx15("span", { className: "font-medium text-foreground", children: activeTarget.objectLabel })
1607
1696
  ] }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
1608
- "Comment on the whole section",
1697
+ labels.commentOnSection,
1609
1698
  " ",
1610
1699
  /* @__PURE__ */ jsx15("code", { className: "font-mono", children: activeTarget?.sectionId })
1611
1700
  ] }) }),
@@ -1614,14 +1703,14 @@ function CanvasComments({
1614
1703
  {
1615
1704
  type: "button",
1616
1705
  onClick: closePopover,
1617
- "aria-label": "Close",
1706
+ "aria-label": labels.closeComments,
1618
1707
  className: "-mr-1 -mt-0.5 shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
1619
1708
  children: /* @__PURE__ */ jsx15(X2, { className: "size-4" })
1620
1709
  }
1621
1710
  )
1622
1711
  ] }),
1623
1712
  /* @__PURE__ */ jsxs5("div", { className: "max-h-64 space-y-2 overflow-y-auto p-3", children: [
1624
- activeComments.length === 0 && /* @__PURE__ */ jsx15("p", { className: "text-xs text-muted-foreground", children: "No comments yet \u2014 add the first one." }),
1713
+ activeComments.length === 0 && /* @__PURE__ */ jsx15("p", { className: "text-xs text-muted-foreground", children: labels.noCommentsYet }),
1625
1714
  activeComments.map((c) => /* @__PURE__ */ jsxs5("div", { className: "rounded-md border bg-muted/40 p-2 text-sm", children: [
1626
1715
  /* @__PURE__ */ jsx15("p", { className: "whitespace-pre-wrap", children: c.body }),
1627
1716
  /* @__PURE__ */ jsxs5(
@@ -1631,7 +1720,8 @@ function CanvasComments({
1631
1720
  className: "mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground",
1632
1721
  children: [
1633
1722
  /* @__PURE__ */ jsx15(Check2, { className: "size-3" }),
1634
- " Resolve"
1723
+ " ",
1724
+ labels.resolveComment
1635
1725
  ]
1636
1726
  }
1637
1727
  )
@@ -1644,7 +1734,7 @@ function CanvasComments({
1644
1734
  autoFocus: true,
1645
1735
  value: body,
1646
1736
  onChange: (e) => setBody(e.target.value),
1647
- placeholder: "Add a comment\u2026 (\u2318/Ctrl+Enter to send)",
1737
+ placeholder: labels.addCommentPlaceholder,
1648
1738
  className: "min-h-14 resize-none text-sm",
1649
1739
  onKeyDown: (e) => {
1650
1740
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
@@ -1656,7 +1746,8 @@ function CanvasComments({
1656
1746
  ),
1657
1747
  /* @__PURE__ */ jsx15("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ jsxs5(Button, { size: "sm", disabled: !body.trim(), onClick: () => void submit(), children: [
1658
1748
  /* @__PURE__ */ jsx15(MessageSquarePlus, {}),
1659
- " Add"
1749
+ " ",
1750
+ labels.addComment
1660
1751
  ] }) })
1661
1752
  ] })
1662
1753
  ]
@@ -1667,7 +1758,10 @@ function CanvasComments({
1667
1758
  }
1668
1759
 
1669
1760
  export {
1761
+ DEFAULT_LABELS,
1762
+ mergeLabels,
1670
1763
  EditorStoreProvider,
1764
+ useLabels,
1671
1765
  useDocumentStore,
1672
1766
  useCommentStore,
1673
1767
  EditorHeader,
@@ -1676,4 +1770,4 @@ export {
1676
1770
  CanvasComments,
1677
1771
  STARTER_MJML
1678
1772
  };
1679
- //# sourceMappingURL=chunk-YAKRVIM2.js.map
1773
+ //# sourceMappingURL=chunk-XAEUCHL4.js.map