@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/README.md +25 -0
- package/dist/canvas.cjs +97 -19
- package/dist/canvas.cjs.map +1 -1
- package/dist/canvas.js +19 -15
- package/dist/canvas.js.map +1 -1
- package/dist/{chunk-YAKRVIM2.js → chunk-XAEUCHL4.js} +152 -58
- package/dist/chunk-XAEUCHL4.js.map +1 -0
- package/dist/index.cjs +152 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +87 -2
- package/dist/index.d.ts +87 -2
- package/dist/index.js +9 -3
- package/package.json +2 -2
- package/dist/chunk-YAKRVIM2.js.map +0 -1
package/README.md
CHANGED
|
@@ -86,6 +86,31 @@ satisfying the two interfaces works, including an in-memory object.
|
|
|
86
86
|
|
|
87
87
|
Forgetting the provider throws with an explanation rather than failing on a null read.
|
|
88
88
|
|
|
89
|
+
## Copy
|
|
90
|
+
|
|
91
|
+
Every word the editor renders comes from a dictionary with English defaults, so a host
|
|
92
|
+
building in another language overrides rather than forks:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<EditorStoreProvider
|
|
96
|
+
stores={stores}
|
|
97
|
+
labels={{
|
|
98
|
+
appName: "Edytor MJML",
|
|
99
|
+
send: "Wyślij",
|
|
100
|
+
agentWorking: "Agent pracuje…",
|
|
101
|
+
toolLabels: { set_section: "Podmieniam sekcję" },
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Overrides are shallow-merged, so anything omitted stays English. `toolLabels` is the one
|
|
107
|
+
exception and merges key by key — renaming one step would otherwise silently drop the other
|
|
108
|
+
eight. `DEFAULT_LABELS` and the `EditorLabels` type are exported if you want to see the full
|
|
109
|
+
list or build a complete translation.
|
|
110
|
+
|
|
111
|
+
Values that interpolate are functions rather than format strings — `copied(kind)`,
|
|
112
|
+
`completedSteps(count, failed)` — so word order belongs to the translator.
|
|
113
|
+
|
|
89
114
|
## Reaching the agent
|
|
90
115
|
|
|
91
116
|
`ChatPanel` posts to `/api/chat` on the same origin by default, which is what
|
package/dist/canvas.cjs
CHANGED
|
@@ -44,8 +44,80 @@ var import_core2 = require("@mjml-agent-editor/core");
|
|
|
44
44
|
|
|
45
45
|
// src/stores.tsx
|
|
46
46
|
var import_react = require("react");
|
|
47
|
+
|
|
48
|
+
// src/labels.ts
|
|
49
|
+
var DEFAULT_LABELS = {
|
|
50
|
+
appName: "MJML Editor",
|
|
51
|
+
emailNameAria: "Email name",
|
|
52
|
+
renameFailed: "Failed to rename.",
|
|
53
|
+
saving: "Saving\u2026",
|
|
54
|
+
saved: "Saved",
|
|
55
|
+
saveFailed: "Save failed",
|
|
56
|
+
undo: "Undo",
|
|
57
|
+
redo: "Redo",
|
|
58
|
+
contentWidth: "Content width",
|
|
59
|
+
preview: "Preview",
|
|
60
|
+
closePreview: "Close preview",
|
|
61
|
+
previewFrameTitle: "Email preview",
|
|
62
|
+
sourceCode: "Source code",
|
|
63
|
+
exportHtml: "Export HTML",
|
|
64
|
+
exportMjml: "Export MJML",
|
|
65
|
+
copied: (kind) => `${kind} copied.`,
|
|
66
|
+
searchBlocks: "Search blocks\u2026",
|
|
67
|
+
expandAll: "Expand all",
|
|
68
|
+
selectAnElement: "Select an element\u2026",
|
|
69
|
+
attributes: "Attributes",
|
|
70
|
+
style: "Style",
|
|
71
|
+
rteFont: "Font",
|
|
72
|
+
rteSize: "Size",
|
|
73
|
+
rteTextColor: "Text color",
|
|
74
|
+
rteBackgroundColor: "Background color",
|
|
75
|
+
documentLoadFailed: "Failed to load the document.",
|
|
76
|
+
documentSaveFailed: "Failed to save changes.",
|
|
77
|
+
commentOnSection: "Comment on the whole section",
|
|
78
|
+
closeComments: "Close",
|
|
79
|
+
noCommentsYet: "No comments yet \u2014 add the first one.",
|
|
80
|
+
resolveComment: "Resolve",
|
|
81
|
+
addComment: "Add",
|
|
82
|
+
addCommentPlaceholder: "Add a comment\u2026 (\u2318/Ctrl+Enter to send)",
|
|
83
|
+
chatEmptyTitle: "Start a conversation with the agent",
|
|
84
|
+
chatEmptyDescription: "Describe the email (goal, tone, content) and paste your data \u2014 the agent will design the sections and images.",
|
|
85
|
+
agentWorking: "Agent is working\u2026",
|
|
86
|
+
reasoning: "Reasoning",
|
|
87
|
+
completedSteps: (count, failed) => failed > 0 ? `Completed steps (${count}, ${failed} failed)` : `Completed steps (${count})`,
|
|
88
|
+
applyComments: "Apply changes from comments",
|
|
89
|
+
chatPlaceholder: "Describe what you want to change\u2026 (Enter = send, Shift+Enter = new line)",
|
|
90
|
+
stop: "Stop",
|
|
91
|
+
send: "Send",
|
|
92
|
+
chatError: "Agent chat error.",
|
|
93
|
+
toolLabels: {
|
|
94
|
+
get_document: "Reading document",
|
|
95
|
+
get_section: "Reading section",
|
|
96
|
+
set_document: "Saving whole email",
|
|
97
|
+
set_section: "Replacing section",
|
|
98
|
+
insert_section: "Adding section",
|
|
99
|
+
remove_section: "Removing section",
|
|
100
|
+
generate_image: "Generating image",
|
|
101
|
+
list_open_comments: "Reading comments",
|
|
102
|
+
resolve_comment: "Resolving comment"
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function mergeLabels(overrides) {
|
|
106
|
+
if (!overrides) return DEFAULT_LABELS;
|
|
107
|
+
return {
|
|
108
|
+
...DEFAULT_LABELS,
|
|
109
|
+
...overrides,
|
|
110
|
+
toolLabels: { ...DEFAULT_LABELS.toolLabels, ...overrides.toolLabels }
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/stores.tsx
|
|
47
115
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
48
116
|
var StoreContext = (0, import_react.createContext)(null);
|
|
117
|
+
var LabelContext = (0, import_react.createContext)(null);
|
|
118
|
+
function useLabels() {
|
|
119
|
+
return (0, import_react.useContext)(LabelContext) ?? mergeLabels();
|
|
120
|
+
}
|
|
49
121
|
function useStores() {
|
|
50
122
|
const stores = (0, import_react.useContext)(StoreContext);
|
|
51
123
|
if (!stores) {
|
|
@@ -223,6 +295,7 @@ function CanvasComments({
|
|
|
223
295
|
onOpenCountChange
|
|
224
296
|
}) {
|
|
225
297
|
const commentStore = useCommentStore();
|
|
298
|
+
const labels = useLabels();
|
|
226
299
|
const editor = (0, import_react2.useEditorMaybe)();
|
|
227
300
|
const containerRef = (0, import_react3.useRef)(null);
|
|
228
301
|
const [comments, setComments] = (0, import_react3.useState)([]);
|
|
@@ -411,7 +484,7 @@ function CanvasComments({
|
|
|
411
484
|
" ",
|
|
412
485
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-medium text-foreground", children: activeTarget.objectLabel })
|
|
413
486
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
414
|
-
|
|
487
|
+
labels.commentOnSection,
|
|
415
488
|
" ",
|
|
416
489
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { className: "font-mono", children: activeTarget?.sectionId })
|
|
417
490
|
] }) }),
|
|
@@ -420,14 +493,14 @@ function CanvasComments({
|
|
|
420
493
|
{
|
|
421
494
|
type: "button",
|
|
422
495
|
onClick: closePopover,
|
|
423
|
-
"aria-label":
|
|
496
|
+
"aria-label": labels.closeComments,
|
|
424
497
|
className: "-mr-1 -mt-0.5 shrink-0 rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
425
498
|
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.X, { className: "size-4" })
|
|
426
499
|
}
|
|
427
500
|
)
|
|
428
501
|
] }),
|
|
429
502
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "max-h-64 space-y-2 overflow-y-auto p-3", children: [
|
|
430
|
-
activeComments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-xs text-muted-foreground", children:
|
|
503
|
+
activeComments.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-xs text-muted-foreground", children: labels.noCommentsYet }),
|
|
431
504
|
activeComments.map((c) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "rounded-md border bg-muted/40 p-2 text-sm", children: [
|
|
432
505
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "whitespace-pre-wrap", children: c.body }),
|
|
433
506
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
@@ -437,7 +510,8 @@ function CanvasComments({
|
|
|
437
510
|
className: "mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground",
|
|
438
511
|
children: [
|
|
439
512
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.Check, { className: "size-3" }),
|
|
440
|
-
"
|
|
513
|
+
" ",
|
|
514
|
+
labels.resolveComment
|
|
441
515
|
]
|
|
442
516
|
}
|
|
443
517
|
)
|
|
@@ -450,7 +524,7 @@ function CanvasComments({
|
|
|
450
524
|
autoFocus: true,
|
|
451
525
|
value: body,
|
|
452
526
|
onChange: (e) => setBody(e.target.value),
|
|
453
|
-
placeholder:
|
|
527
|
+
placeholder: labels.addCommentPlaceholder,
|
|
454
528
|
className: "min-h-14 resize-none text-sm",
|
|
455
529
|
onKeyDown: (e) => {
|
|
456
530
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
@@ -462,7 +536,8 @@ function CanvasComments({
|
|
|
462
536
|
),
|
|
463
537
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mt-2 flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Button, { size: "sm", disabled: !body.trim(), onClick: () => void submit(), children: [
|
|
464
538
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react2.MessageSquarePlus, {}),
|
|
465
|
-
"
|
|
539
|
+
" ",
|
|
540
|
+
labels.addComment
|
|
466
541
|
] }) })
|
|
467
542
|
] })
|
|
468
543
|
]
|
|
@@ -603,13 +678,13 @@ function wrapSelectionStyle(el, style) {
|
|
|
603
678
|
} catch {
|
|
604
679
|
}
|
|
605
680
|
}
|
|
606
|
-
function setupRichText(editor) {
|
|
681
|
+
function setupRichText(editor, labels) {
|
|
607
682
|
const rte = editor.RichTextEditor;
|
|
608
683
|
if (!rte || rte.__customActions) return;
|
|
609
684
|
rte.__customActions = true;
|
|
610
685
|
const val = (action, sel) => action.btn?.querySelector(sel)?.value ?? "";
|
|
611
686
|
rte.add("fontName", {
|
|
612
|
-
icon: `<select class="gjs-rte-select" title="
|
|
687
|
+
icon: `<select class="gjs-rte-select" title="${labels.rteFont}"><option value="">${labels.rteFont}</option>${RTE_FONTS.map(
|
|
613
688
|
(f) => `<option value="${f}">${f}</option>`
|
|
614
689
|
).join("")}</select>`,
|
|
615
690
|
event: "change",
|
|
@@ -619,7 +694,7 @@ function setupRichText(editor) {
|
|
|
619
694
|
}
|
|
620
695
|
});
|
|
621
696
|
rte.add("fontSize", {
|
|
622
|
-
icon: `<select class="gjs-rte-select" title="
|
|
697
|
+
icon: `<select class="gjs-rte-select" title="${labels.rteSize}"><option value="">${labels.rteSize}</option>${RTE_SIZES.map(
|
|
623
698
|
(s) => `<option value="${s}px">${s}</option>`
|
|
624
699
|
).join("")}</select>`,
|
|
625
700
|
event: "change",
|
|
@@ -629,18 +704,19 @@ function setupRichText(editor) {
|
|
|
629
704
|
}
|
|
630
705
|
});
|
|
631
706
|
rte.add("forecolor", {
|
|
632
|
-
icon: `<input type="color" class="gjs-rte-color" title="
|
|
707
|
+
icon: `<input type="color" class="gjs-rte-color" title="${labels.rteTextColor}" value="#000000">`,
|
|
633
708
|
event: "change",
|
|
634
709
|
result: (r, action) => r.exec("foreColor", val(action, "input"))
|
|
635
710
|
});
|
|
636
711
|
rte.add("hilitecolor", {
|
|
637
|
-
icon: `<input type="color" class="gjs-rte-color" title="
|
|
712
|
+
icon: `<input type="color" class="gjs-rte-color" title="${labels.rteBackgroundColor}" value="#ffff00">`,
|
|
638
713
|
event: "change",
|
|
639
714
|
result: (r, action) => r.exec("hiliteColor", val(action, "input"))
|
|
640
715
|
});
|
|
641
716
|
}
|
|
642
717
|
function EmailEditor({ docId, onReady, commentsRefresh, onOpenCountChange }) {
|
|
643
718
|
const documents = useDocumentStore();
|
|
719
|
+
const labels = useLabels();
|
|
644
720
|
const loadingRef = (0, import_react5.useRef)(false);
|
|
645
721
|
const saveTimer = (0, import_react5.useRef)(null);
|
|
646
722
|
const saveStatusRef = (0, import_react5.useRef)("idle");
|
|
@@ -720,7 +796,7 @@ function EmailEditor({ docId, onReady, commentsRefresh, onOpenCountChange }) {
|
|
|
720
796
|
if (target) setComposeTarget(target);
|
|
721
797
|
}
|
|
722
798
|
});
|
|
723
|
-
setupRichText(editor);
|
|
799
|
+
setupRichText(editor, labels);
|
|
724
800
|
const blockCategory = (label) => {
|
|
725
801
|
const l = label.toLowerCase();
|
|
726
802
|
if (/(column|section)/.test(l)) return "Layout";
|
|
@@ -774,7 +850,7 @@ function EmailEditor({ docId, onReady, commentsRefresh, onOpenCountChange }) {
|
|
|
774
850
|
setSave("saved");
|
|
775
851
|
} catch (e) {
|
|
776
852
|
setSave("error");
|
|
777
|
-
import_sonner.toast.error(
|
|
853
|
+
import_sonner.toast.error(labels.documentSaveFailed);
|
|
778
854
|
throw e;
|
|
779
855
|
}
|
|
780
856
|
};
|
|
@@ -832,7 +908,7 @@ function EmailEditor({ docId, onReady, commentsRefresh, onOpenCountChange }) {
|
|
|
832
908
|
const doc = await documents.get(docId);
|
|
833
909
|
loadMjml(doc.mjml);
|
|
834
910
|
} catch (e) {
|
|
835
|
-
import_sonner.toast.error(
|
|
911
|
+
import_sonner.toast.error(labels.documentLoadFailed);
|
|
836
912
|
console.error(e);
|
|
837
913
|
} finally {
|
|
838
914
|
setLoading(false);
|
|
@@ -969,6 +1045,7 @@ function LeftSidebar({
|
|
|
969
1045
|
markLayersSource
|
|
970
1046
|
}) {
|
|
971
1047
|
const editor = (0, import_react4.useEditorMaybe)();
|
|
1048
|
+
const labels = useLabels();
|
|
972
1049
|
const [layersExpanded, setLayersExpanded] = (0, import_react5.useState)(false);
|
|
973
1050
|
const [blockSearch, setBlockSearch] = (0, import_react5.useState)("");
|
|
974
1051
|
const changeView = (v) => {
|
|
@@ -1026,7 +1103,7 @@ function LeftSidebar({
|
|
|
1026
1103
|
{
|
|
1027
1104
|
value: blockSearch,
|
|
1028
1105
|
onChange: (e) => filterBlocks(e.target.value),
|
|
1029
|
-
placeholder:
|
|
1106
|
+
placeholder: labels.searchBlocks,
|
|
1030
1107
|
className: "h-8 border-panel-border bg-panel-elevated pr-10 pl-7 text-sm text-panel-fg placeholder:text-panel-muted-fg"
|
|
1031
1108
|
}
|
|
1032
1109
|
),
|
|
@@ -1035,8 +1112,8 @@ function LeftSidebar({
|
|
|
1035
1112
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { id: "gjs-blocks", className: "min-h-0 flex-1 overflow-y-auto" })
|
|
1036
1113
|
] }),
|
|
1037
1114
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cn("min-h-0 flex-1 overflow-y-auto", view !== "settings" && "hidden"), children: [
|
|
1038
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CollapseSection, { title:
|
|
1039
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CollapseSection, { title:
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CollapseSection, { title: labels.attributes, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { id: "gjs-traits" }) }),
|
|
1116
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(CollapseSection, { title: labels.style, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { id: "gjs-styles" }) })
|
|
1040
1117
|
] }),
|
|
1041
1118
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1042
1119
|
"div",
|
|
@@ -1045,7 +1122,7 @@ function LeftSidebar({
|
|
|
1045
1122
|
onMouseDownCapture: markLayersSource,
|
|
1046
1123
|
children: [
|
|
1047
1124
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { className: "flex items-center justify-between gap-2 border-b border-panel-border px-3 py-2 text-xs text-panel-muted-fg", children: [
|
|
1048
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children:
|
|
1125
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: labels.expandAll }),
|
|
1049
1126
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1050
1127
|
Switch,
|
|
1051
1128
|
{
|
|
@@ -1065,7 +1142,8 @@ function Breadcrumb({
|
|
|
1065
1142
|
crumbs,
|
|
1066
1143
|
onSelect
|
|
1067
1144
|
}) {
|
|
1068
|
-
|
|
1145
|
+
const labels = useLabels();
|
|
1146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex h-9 shrink-0 items-center gap-0.5 border-t border-border bg-surface px-3 text-xs text-muted-foreground", children: crumbs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-muted-foreground/60", children: labels.selectAnElement }) : crumbs.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex items-center gap-0.5", children: [
|
|
1069
1147
|
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.ChevronRight, { className: "size-3 opacity-40" }),
|
|
1070
1148
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1071
1149
|
"button",
|