@mjml-agent-editor/editor 0.1.14 → 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/README.md +42 -0
- package/dist/canvas.cjs +2 -0
- package/dist/canvas.cjs.map +1 -1
- package/dist/canvas.js +1 -1
- package/dist/{chunk-SR3NM6VC.js → chunk-W3HCLTFJ.js} +58 -16
- package/dist/chunk-W3HCLTFJ.js.map +1 -0
- package/dist/index.cjs +54 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +1 -1
- package/package.json +4 -2
- package/dist/chunk-SR3NM6VC.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -112,11 +112,13 @@ var DEFAULT_LABELS = {
|
|
|
112
112
|
agentWorking: "Agent is working\u2026",
|
|
113
113
|
reasoning: "Reasoning",
|
|
114
114
|
completedSteps: (count, failed) => failed > 0 ? `Completed steps (${count}, ${failed} failed)` : `Completed steps (${count})`,
|
|
115
|
+
stepLimitReached: "The agent ran out of steps for this turn. Ask it to continue.",
|
|
115
116
|
applyComments: "Apply changes from comments",
|
|
116
117
|
chatPlaceholder: "Describe what you want to change\u2026 (Enter = send, Shift+Enter = new line)",
|
|
117
118
|
stop: "Stop",
|
|
118
119
|
send: "Send",
|
|
119
120
|
chatError: "Agent chat error.",
|
|
121
|
+
agentUnavailable: "The agent is unavailable right now. You can still edit the email and leave comments.",
|
|
120
122
|
toolLabels: {
|
|
121
123
|
get_document: "Reading document",
|
|
122
124
|
get_section: "Reading section",
|
|
@@ -1076,6 +1078,11 @@ function saveToastId(docId) {
|
|
|
1076
1078
|
return `document-save-${docId}`;
|
|
1077
1079
|
}
|
|
1078
1080
|
|
|
1081
|
+
// src/lib/turn-gate.ts
|
|
1082
|
+
function canStartTurn(text, gate) {
|
|
1083
|
+
return !gate.unavailable && !gate.busy && !gate.flushing && !gate.locked && text.trim() !== "";
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1079
1086
|
// src/components/ui/textarea.tsx
|
|
1080
1087
|
var React11 = require("react");
|
|
1081
1088
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
@@ -1605,6 +1612,7 @@ function ChatPanel({
|
|
|
1605
1612
|
initialMessages,
|
|
1606
1613
|
transport,
|
|
1607
1614
|
resume,
|
|
1615
|
+
agentUnavailable = false,
|
|
1608
1616
|
onBeforeSend,
|
|
1609
1617
|
onAgentFinish,
|
|
1610
1618
|
onLiveUpdate,
|
|
@@ -1612,6 +1620,9 @@ function ChatPanel({
|
|
|
1612
1620
|
onSectionEditEnd
|
|
1613
1621
|
}) {
|
|
1614
1622
|
const labels = useLabels();
|
|
1623
|
+
const unavailableId = (0, import_react5.useId)();
|
|
1624
|
+
const footerRef = (0, import_react5.useRef)(null);
|
|
1625
|
+
const statusRef = (0, import_react5.useRef)(null);
|
|
1615
1626
|
const [input, setInput] = (0, import_react5.useState)("");
|
|
1616
1627
|
const taRef = (0, import_react5.useRef)(null);
|
|
1617
1628
|
const sending = (0, import_react5.useRef)(false);
|
|
@@ -1680,6 +1691,7 @@ function ChatPanel({
|
|
|
1680
1691
|
}, [messages, onLiveUpdate, onSectionEditStart, onSectionEditEnd]);
|
|
1681
1692
|
const busy = status === "submitted" || status === "streaming";
|
|
1682
1693
|
const { setRunning, locked } = useAgentTurn();
|
|
1694
|
+
const gate = { unavailable: agentUnavailable, busy, flushing, locked };
|
|
1683
1695
|
(0, import_react5.useEffect)(() => setRunning(busy || flushing), [busy, flushing, setRunning]);
|
|
1684
1696
|
(0, import_react5.useEffect)(() => () => setRunning(false), [setRunning]);
|
|
1685
1697
|
const lastMsg = messages[messages.length - 1];
|
|
@@ -1694,24 +1706,40 @@ function ChatPanel({
|
|
|
1694
1706
|
el.style.height = "auto";
|
|
1695
1707
|
el.style.height = `${Math.min(el.scrollHeight, 160)}px`;
|
|
1696
1708
|
};
|
|
1709
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
1710
|
+
const active = document.activeElement;
|
|
1711
|
+
if (agentUnavailable && active?.matches(":disabled") && footerRef.current?.contains(active)) {
|
|
1712
|
+
statusRef.current?.focus();
|
|
1713
|
+
} else if (!agentUnavailable && active === statusRef.current) {
|
|
1714
|
+
taRef.current?.focus();
|
|
1715
|
+
}
|
|
1716
|
+
}, [agentUnavailable]);
|
|
1717
|
+
const [pending, setPending] = (0, import_react5.useState)(null);
|
|
1718
|
+
(0, import_react5.useEffect)(() => {
|
|
1719
|
+
if (pending === null) return;
|
|
1720
|
+
setPending(null);
|
|
1721
|
+
sending.current = false;
|
|
1722
|
+
setFlushing(false);
|
|
1723
|
+
if (agentUnavailable || pending.docId !== docId) return;
|
|
1724
|
+
void sendMessage({ text: pending.text });
|
|
1725
|
+
setInput("");
|
|
1726
|
+
if (taRef.current) taRef.current.style.height = "auto";
|
|
1727
|
+
}, [pending, agentUnavailable, docId, sendMessage]);
|
|
1697
1728
|
const send = async (text) => {
|
|
1729
|
+
if (!canStartTurn(text, gate) || sending.current) return;
|
|
1698
1730
|
const trimmed = text.trim();
|
|
1699
|
-
if (!trimmed || busy || locked || sending.current) return;
|
|
1700
1731
|
sending.current = true;
|
|
1701
1732
|
setFlushing(true);
|
|
1702
1733
|
try {
|
|
1703
1734
|
await onBeforeSend();
|
|
1704
1735
|
} catch (error) {
|
|
1736
|
+
sending.current = false;
|
|
1737
|
+
setFlushing(false);
|
|
1705
1738
|
console.error(error);
|
|
1706
1739
|
import_sonner2.toast.error(refusal(error) ?? labels.documentSaveFailed, { id: saveToastId(docId) });
|
|
1707
1740
|
return;
|
|
1708
|
-
} finally {
|
|
1709
|
-
sending.current = false;
|
|
1710
|
-
setFlushing(false);
|
|
1711
1741
|
}
|
|
1712
|
-
|
|
1713
|
-
setInput("");
|
|
1714
|
-
if (taRef.current) taRef.current.style.height = "auto";
|
|
1742
|
+
setPending({ text: trimmed, docId });
|
|
1715
1743
|
};
|
|
1716
1744
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex h-full flex-col", children: [
|
|
1717
1745
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageScrollerProvider, { autoScroll: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(MessageScroller, { className: "flex-1", children: [
|
|
@@ -1725,6 +1753,7 @@ function ChatPanel({
|
|
|
1725
1753
|
const isLast = mi === messages.length - 1;
|
|
1726
1754
|
const blocks = toBlocks(message.parts);
|
|
1727
1755
|
const hasText = blocks.some((block) => block.kind === "text");
|
|
1756
|
+
const metadata = message.metadata;
|
|
1728
1757
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageScrollerItem, { messageId: message.id, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn("flex w-full gap-2", isUser && "flex-row-reverse"), children: [
|
|
1729
1758
|
!isUser && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-brand/15 text-brand", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Sparkles, { className: "size-3.5" }) }),
|
|
1730
1759
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
@@ -1773,7 +1802,11 @@ function ChatPanel({
|
|
|
1773
1802
|
i
|
|
1774
1803
|
);
|
|
1775
1804
|
}),
|
|
1776
|
-
isLast && !isUser && busy && !toolRunning && !hasText && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(BusyMarker, {})
|
|
1805
|
+
isLast && !isUser && busy && !toolRunning && !hasText && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(BusyMarker, {}),
|
|
1806
|
+
metadata?.stepLimitReached && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Marker, { className: "text-panel-muted-fg", children: [
|
|
1807
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkerIcon, { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.TriangleAlert, {}) }),
|
|
1808
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MarkerContent, { children: labels.stepLimitReached })
|
|
1809
|
+
] })
|
|
1777
1810
|
] }) }),
|
|
1778
1811
|
blocks.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "px-1 text-[10px] text-panel-muted-fg", children: timeFor(message.id) })
|
|
1779
1812
|
]
|
|
@@ -1785,12 +1818,17 @@ function ChatPanel({
|
|
|
1785
1818
|
] }) }),
|
|
1786
1819
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MessageScrollerButton, { direction: "end" })
|
|
1787
1820
|
] }) }),
|
|
1788
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "border-t border-panel-border p-3", children: [
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { ref: footerRef, className: "border-t border-panel-border p-3", children: [
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: statusRef, id: unavailableId, role: "status", tabIndex: -1, className: "rounded-lg", children: agentUnavailable && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("p", { className: "mb-2 flex items-start gap-2 rounded-lg border border-panel-border bg-panel-elevated/60 px-3 py-2 text-xs text-panel-fg", children: [
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.CirclePause, { className: "mt-px size-3.5 shrink-0" }),
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: labels.agentUnavailable })
|
|
1825
|
+
] }) }),
|
|
1789
1826
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1790
1827
|
Button,
|
|
1791
1828
|
{
|
|
1792
1829
|
className: "mb-2 w-full bg-brand text-brand-fg hover:bg-brand/90",
|
|
1793
|
-
disabled:
|
|
1830
|
+
disabled: !canStartTurn(APPLY_COMMENTS_PROMPT, gate),
|
|
1831
|
+
"aria-describedby": agentUnavailable ? unavailableId : void 0,
|
|
1794
1832
|
onClick: () => void send(APPLY_COMMENTS_PROMPT),
|
|
1795
1833
|
children: [
|
|
1796
1834
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Sparkles, {}),
|
|
@@ -1813,8 +1851,10 @@ function ChatPanel({
|
|
|
1813
1851
|
{
|
|
1814
1852
|
ref: taRef,
|
|
1815
1853
|
rows: 2,
|
|
1816
|
-
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",
|
|
1817
|
-
placeholder: labels.chatPlaceholder,
|
|
1854
|
+
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 disabled:bg-transparent",
|
|
1855
|
+
placeholder: agentUnavailable ? void 0 : labels.chatPlaceholder,
|
|
1856
|
+
disabled: agentUnavailable,
|
|
1857
|
+
"aria-describedby": agentUnavailable ? unavailableId : void 0,
|
|
1818
1858
|
value: input,
|
|
1819
1859
|
onChange: (e) => {
|
|
1820
1860
|
setInput(e.target.value);
|
|
@@ -1849,7 +1889,8 @@ function ChatPanel({
|
|
|
1849
1889
|
{
|
|
1850
1890
|
type: "submit",
|
|
1851
1891
|
size: "sm",
|
|
1852
|
-
disabled:
|
|
1892
|
+
disabled: !canStartTurn(input, gate),
|
|
1893
|
+
"aria-describedby": agentUnavailable ? unavailableId : void 0,
|
|
1853
1894
|
className: "bg-brand text-brand-fg hover:bg-brand/90",
|
|
1854
1895
|
children: [
|
|
1855
1896
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react5.Send, {}),
|