@savvycal/mjml-editor 0.4.0 → 0.5.0
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 +1 -0
- package/dist/components/editor/EditorCanvas.d.ts +2 -1
- package/dist/components/editor/EditorCanvas.d.ts.map +1 -1
- package/dist/components/editor/EditorCanvas.js +116 -54
- package/dist/components/editor/MjmlEditor.d.ts +6 -1
- package/dist/components/editor/MjmlEditor.d.ts.map +1 -1
- package/dist/components/editor/MjmlEditor.js +89 -80
- package/dist/components/editor/SourceEditor.d.ts +6 -1
- package/dist/components/editor/SourceEditor.d.ts.map +1 -1
- package/dist/components/editor/SourceEditor.js +83 -38
- package/dist/components/editor/SourceEditor.test.d.ts +2 -0
- package/dist/components/editor/SourceEditor.test.d.ts.map +1 -0
- package/dist/components.css +53 -0
- package/dist/lib/html-utils.d.ts.map +1 -1
- package/dist/lib/html-utils.js +49 -19
- package/dist/lib/html-utils.test.d.ts +2 -0
- package/dist/lib/html-utils.test.d.ts.map +1 -0
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -89,6 +89,7 @@ function App() {
|
|
|
89
89
|
|------|------|-------------|
|
|
90
90
|
| `value` | `string` | MJML markup string (required) |
|
|
91
91
|
| `onChange` | `(mjml: string) => void` | Called when the document changes (required) |
|
|
92
|
+
| `onSourceApply` | `(mjml: string) => void` | Called when Source tab **Apply** succeeds with valid MJML (optional) |
|
|
92
93
|
| `className` | `string` | Optional CSS class for the container |
|
|
93
94
|
| `defaultTheme` | `'light' \| 'dark' \| 'system'` | Theme preference (default: `'system'`) |
|
|
94
95
|
| `liquidSchema` | `LiquidSchema` | Optional schema for Liquid template autocomplete |
|
|
@@ -6,7 +6,8 @@ interface EditorCanvasProps {
|
|
|
6
6
|
leftPanelOpen?: boolean;
|
|
7
7
|
rightPanelOpen?: boolean;
|
|
8
8
|
showThemeToggle?: boolean;
|
|
9
|
+
onSourceApply?: (mjml: string) => void;
|
|
9
10
|
}
|
|
10
|
-
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, showThemeToggle, }: EditorCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, showThemeToggle, onSourceApply, }: EditorCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
11
12
|
export {};
|
|
12
13
|
//# sourceMappingURL=EditorCanvas.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorCanvas.d.ts","sourceRoot":"","sources":["../../../src/components/editor/EditorCanvas.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D,UAAU,iBAAiB;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorCanvas.d.ts","sourceRoot":"","sources":["../../../src/components/editor/EditorCanvas.tsx"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE1D,UAAU,iBAAiB;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,WAAW,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAsB,EACtB,aAAa,GACd,EAAE,iBAAiB,2CAsLnB"}
|
|
@@ -1,31 +1,47 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Monitor as
|
|
4
|
-
import { VisualEditor as
|
|
5
|
-
import { InteractivePreview as
|
|
6
|
-
import { SourceEditor as
|
|
7
|
-
import { useEditor as
|
|
1
|
+
import { jsxs as o, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useState as s, useCallback as m } from "react";
|
|
3
|
+
import { Monitor as z, Smartphone as j, Undo2 as P, Redo2 as R } from "lucide-react";
|
|
4
|
+
import { VisualEditor as U } from "./VisualEditor.js";
|
|
5
|
+
import { InteractivePreview as L } from "./InteractivePreview.js";
|
|
6
|
+
import { SourceEditor as V } from "./SourceEditor.js";
|
|
7
|
+
import { useEditor as Z } from "../../context/EditorContext.js";
|
|
8
8
|
import { Button as i } from "../ui/button.js";
|
|
9
|
-
import { Tabs as
|
|
10
|
-
import { ThemeToggle as
|
|
11
|
-
import { cn as
|
|
12
|
-
function
|
|
13
|
-
activeTab:
|
|
14
|
-
onTabChange:
|
|
15
|
-
leftPanelOpen:
|
|
16
|
-
rightPanelOpen:
|
|
17
|
-
showThemeToggle:
|
|
9
|
+
import { Tabs as A, TabsList as B, TabsTrigger as g } from "../ui/tabs.js";
|
|
10
|
+
import { ThemeToggle as H } from "../ui/theme-toggle.js";
|
|
11
|
+
import { cn as u } from "../../lib/utils.js";
|
|
12
|
+
function _({
|
|
13
|
+
activeTab: r,
|
|
14
|
+
onTabChange: d,
|
|
15
|
+
leftPanelOpen: x,
|
|
16
|
+
rightPanelOpen: v,
|
|
17
|
+
showThemeToggle: b = !0,
|
|
18
|
+
onSourceApply: N
|
|
18
19
|
}) {
|
|
19
|
-
const { undo:
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const { undo: w, redo: k, canUndo: C, canRedo: y } = Z(), [h, S] = s("desktop"), [f, p] = s(!1), [n, l] = s(null), [D, a] = s(!1), c = m(
|
|
21
|
+
(t) => {
|
|
22
|
+
if (t !== r) {
|
|
23
|
+
if (r === "source" && f) {
|
|
24
|
+
l(t), a(!0);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
d(t);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
[r, f, d]
|
|
31
|
+
), E = m(() => {
|
|
32
|
+
l(null), a(!1);
|
|
33
|
+
}, []), M = m(() => {
|
|
34
|
+
n && (p(!1), d(n)), l(null), a(!1);
|
|
35
|
+
}, [n, d]);
|
|
36
|
+
return /* @__PURE__ */ o("div", { className: "relative flex flex-col h-full", children: [
|
|
37
|
+
/* @__PURE__ */ o("div", { className: "h-11 px-4 flex items-center gap-1 border-b border-border bg-background relative", children: [
|
|
22
38
|
/* @__PURE__ */ e(
|
|
23
39
|
"button",
|
|
24
40
|
{
|
|
25
|
-
onClick: () =>
|
|
26
|
-
className:
|
|
41
|
+
onClick: () => c("edit"),
|
|
42
|
+
className: u(
|
|
27
43
|
"px-3 py-1.5 text-sm font-medium rounded-md transition-colors",
|
|
28
|
-
|
|
44
|
+
r === "edit" ? "bg-accent text-foreground" : "text-foreground-muted hover:text-foreground hover:bg-accent/50"
|
|
29
45
|
),
|
|
30
46
|
children: "Edit"
|
|
31
47
|
}
|
|
@@ -33,10 +49,10 @@ function q({
|
|
|
33
49
|
/* @__PURE__ */ e(
|
|
34
50
|
"button",
|
|
35
51
|
{
|
|
36
|
-
onClick: () =>
|
|
37
|
-
className:
|
|
52
|
+
onClick: () => c("preview"),
|
|
53
|
+
className: u(
|
|
38
54
|
"px-3 py-1.5 text-sm font-medium rounded-md transition-colors",
|
|
39
|
-
|
|
55
|
+
r === "preview" ? "bg-accent text-foreground" : "text-foreground-muted hover:text-foreground hover:bg-accent/50"
|
|
40
56
|
),
|
|
41
57
|
children: "Preview"
|
|
42
58
|
}
|
|
@@ -44,44 +60,44 @@ function q({
|
|
|
44
60
|
/* @__PURE__ */ e(
|
|
45
61
|
"button",
|
|
46
62
|
{
|
|
47
|
-
onClick: () =>
|
|
48
|
-
className:
|
|
63
|
+
onClick: () => c("source"),
|
|
64
|
+
className: u(
|
|
49
65
|
"px-3 py-1.5 text-sm font-medium rounded-md transition-colors",
|
|
50
|
-
|
|
66
|
+
r === "source" ? "bg-accent text-foreground" : "text-foreground-muted hover:text-foreground hover:bg-accent/50"
|
|
51
67
|
),
|
|
52
68
|
children: "Source"
|
|
53
69
|
}
|
|
54
70
|
),
|
|
55
|
-
|
|
56
|
-
|
|
71
|
+
r === "preview" && /* @__PURE__ */ e(
|
|
72
|
+
A,
|
|
57
73
|
{
|
|
58
|
-
value:
|
|
59
|
-
onValueChange: (
|
|
74
|
+
value: h,
|
|
75
|
+
onValueChange: (t) => S(t),
|
|
60
76
|
className: "absolute left-1/2 -translate-x-1/2",
|
|
61
|
-
children: /* @__PURE__ */
|
|
62
|
-
/* @__PURE__ */
|
|
63
|
-
/* @__PURE__ */ e(
|
|
77
|
+
children: /* @__PURE__ */ o(B, { className: "h-8", children: [
|
|
78
|
+
/* @__PURE__ */ o(g, { value: "desktop", className: "h-7 px-2 gap-1.5", children: [
|
|
79
|
+
/* @__PURE__ */ e(z, { className: "h-3.5 w-3.5" }),
|
|
64
80
|
/* @__PURE__ */ e("span", { className: "hidden md:inline", children: "Desktop" })
|
|
65
81
|
] }),
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
/* @__PURE__ */ e(
|
|
82
|
+
/* @__PURE__ */ o(g, { value: "mobile", className: "h-7 px-2 gap-1.5", children: [
|
|
83
|
+
/* @__PURE__ */ e(j, { className: "h-3.5 w-3.5" }),
|
|
68
84
|
/* @__PURE__ */ e("span", { className: "hidden md:inline", children: "Mobile" })
|
|
69
85
|
] })
|
|
70
86
|
] })
|
|
71
87
|
}
|
|
72
88
|
),
|
|
73
89
|
/* @__PURE__ */ e("div", { className: "flex-1" }),
|
|
74
|
-
/* @__PURE__ */
|
|
90
|
+
/* @__PURE__ */ o("div", { className: "flex items-center gap-0.5", children: [
|
|
75
91
|
/* @__PURE__ */ e(
|
|
76
92
|
i,
|
|
77
93
|
{
|
|
78
94
|
variant: "ghost",
|
|
79
95
|
size: "icon-sm",
|
|
80
|
-
onClick:
|
|
81
|
-
disabled: !
|
|
96
|
+
onClick: w,
|
|
97
|
+
disabled: !C,
|
|
82
98
|
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent disabled:opacity-40",
|
|
83
99
|
title: "Undo (Cmd+Z)",
|
|
84
|
-
children: /* @__PURE__ */ e(
|
|
100
|
+
children: /* @__PURE__ */ e(P, { className: "h-4 w-4" })
|
|
85
101
|
}
|
|
86
102
|
),
|
|
87
103
|
/* @__PURE__ */ e(
|
|
@@ -89,29 +105,75 @@ function q({
|
|
|
89
105
|
{
|
|
90
106
|
variant: "ghost",
|
|
91
107
|
size: "icon-sm",
|
|
92
|
-
onClick:
|
|
93
|
-
disabled: !
|
|
108
|
+
onClick: k,
|
|
109
|
+
disabled: !y,
|
|
94
110
|
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent disabled:opacity-40",
|
|
95
111
|
title: "Redo (Cmd+Shift+Z)",
|
|
96
|
-
children: /* @__PURE__ */ e(
|
|
112
|
+
children: /* @__PURE__ */ e(R, { className: "h-4 w-4" })
|
|
97
113
|
}
|
|
98
114
|
),
|
|
99
|
-
|
|
115
|
+
b && /* @__PURE__ */ e(H, {})
|
|
100
116
|
] })
|
|
101
117
|
] }),
|
|
102
|
-
/* @__PURE__ */
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
/* @__PURE__ */ o("div", { className: "flex-1 min-h-0 overflow-hidden", children: [
|
|
119
|
+
r === "edit" && /* @__PURE__ */ e(
|
|
120
|
+
U,
|
|
105
121
|
{
|
|
106
|
-
leftPanelOpen:
|
|
107
|
-
rightPanelOpen:
|
|
122
|
+
leftPanelOpen: x,
|
|
123
|
+
rightPanelOpen: v
|
|
108
124
|
}
|
|
109
125
|
),
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
126
|
+
r === "preview" && /* @__PURE__ */ e(L, { showHeader: !1, previewMode: h }),
|
|
127
|
+
r === "source" && /* @__PURE__ */ e(
|
|
128
|
+
V,
|
|
129
|
+
{
|
|
130
|
+
onApply: N,
|
|
131
|
+
onDirtyChange: p
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }),
|
|
135
|
+
D && /* @__PURE__ */ e("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/40 p-4", children: /* @__PURE__ */ o(
|
|
136
|
+
"div",
|
|
137
|
+
{
|
|
138
|
+
role: "dialog",
|
|
139
|
+
"aria-modal": "true",
|
|
140
|
+
"aria-labelledby": "discard-source-title",
|
|
141
|
+
className: "w-full max-w-md rounded-lg border border-border bg-popover p-5 shadow-lg",
|
|
142
|
+
children: [
|
|
143
|
+
/* @__PURE__ */ e(
|
|
144
|
+
"h2",
|
|
145
|
+
{
|
|
146
|
+
id: "discard-source-title",
|
|
147
|
+
className: "text-base font-semibold text-foreground",
|
|
148
|
+
children: "Discard un-applied source changes?"
|
|
149
|
+
}
|
|
150
|
+
),
|
|
151
|
+
/* @__PURE__ */ e("p", { className: "mt-2 text-sm text-foreground-muted", children: "You have unsaved edits in Source. Leaving this tab will discard those changes." }),
|
|
152
|
+
/* @__PURE__ */ o("div", { className: "mt-5 flex justify-end gap-2", children: [
|
|
153
|
+
/* @__PURE__ */ e(
|
|
154
|
+
i,
|
|
155
|
+
{
|
|
156
|
+
variant: "outline",
|
|
157
|
+
size: "sm",
|
|
158
|
+
onClick: E,
|
|
159
|
+
children: "Keep Editing"
|
|
160
|
+
}
|
|
161
|
+
),
|
|
162
|
+
/* @__PURE__ */ e(
|
|
163
|
+
i,
|
|
164
|
+
{
|
|
165
|
+
variant: "destructive",
|
|
166
|
+
size: "sm",
|
|
167
|
+
onClick: M,
|
|
168
|
+
children: "Discard Changes"
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
] })
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
) })
|
|
113
175
|
] });
|
|
114
176
|
}
|
|
115
177
|
export {
|
|
116
|
-
|
|
178
|
+
_ as EditorCanvas
|
|
117
179
|
};
|
|
@@ -3,6 +3,11 @@ import { LiquidSchema } from '../../types/liquid';
|
|
|
3
3
|
interface MjmlEditorProps {
|
|
4
4
|
value: string;
|
|
5
5
|
onChange: (mjml: string) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Called when Source tab "Apply" succeeds with valid MJML.
|
|
8
|
+
* Useful for triggering explicit save/persist actions.
|
|
9
|
+
*/
|
|
10
|
+
onSourceApply?: (mjml: string) => void;
|
|
6
11
|
className?: string;
|
|
7
12
|
defaultTheme?: 'light' | 'dark' | 'system';
|
|
8
13
|
liquidSchema?: LiquidSchema;
|
|
@@ -49,6 +54,6 @@ interface MjmlEditorProps {
|
|
|
49
54
|
*/
|
|
50
55
|
defaultRightPanelOpen?: boolean;
|
|
51
56
|
}
|
|
52
|
-
export declare function MjmlEditor({ value, onChange, className, defaultTheme, liquidSchema, extensions, applyThemeToDocument, showThemeToggle, defaultLeftPanelOpen, defaultRightPanelOpen, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export declare function MjmlEditor({ value, onChange, onSourceApply, className, defaultTheme, liquidSchema, extensions, applyThemeToDocument, showThemeToggle, defaultLeftPanelOpen, defaultRightPanelOpen, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
53
58
|
export {};
|
|
54
59
|
//# sourceMappingURL=MjmlEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MjmlEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/MjmlEditor.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAcnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"MjmlEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/MjmlEditor.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAY,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAcnD,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3C,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAuKD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,aAAa,EACb,SAAS,EACT,YAAuB,EACvB,YAAY,EACZ,UAAU,EACV,oBAA2B,EAC3B,eAAsB,EACtB,oBAA2B,EAC3B,qBAA6B,GAC9B,EAAE,eAAe,2CAsDjB"}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { jsx as e, jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { EditorProvider as
|
|
4
|
-
import { ThemeProvider as
|
|
5
|
-
import { LiquidSchemaProvider as
|
|
1
|
+
import { jsx as e, jsxs as B, Fragment as M } from "react/jsx-runtime";
|
|
2
|
+
import { useState as u, useEffect as a, useCallback as I, useRef as O } from "react";
|
|
3
|
+
import { EditorProvider as j, useEditor as x } from "../../context/EditorContext.js";
|
|
4
|
+
import { ThemeProvider as S, useTheme as C } from "../../context/ThemeContext.js";
|
|
5
|
+
import { LiquidSchemaProvider as A } from "../../context/LiquidSchemaContext.js";
|
|
6
6
|
import { ExtensionsProvider as K } from "../../context/ExtensionsContext.js";
|
|
7
|
-
import { OutlineTree as R, GLOBAL_STYLES_ID as
|
|
8
|
-
import { EditorCanvas as
|
|
9
|
-
import { BlockInspector as
|
|
10
|
-
import { GlobalStylesPanel as
|
|
11
|
-
import { FloatingPanel as
|
|
12
|
-
import { createEmptyDocument as
|
|
13
|
-
function
|
|
7
|
+
import { OutlineTree as R, GLOBAL_STYLES_ID as P } from "./OutlineTree.js";
|
|
8
|
+
import { EditorCanvas as F } from "./EditorCanvas.js";
|
|
9
|
+
import { BlockInspector as N } from "./BlockInspector.js";
|
|
10
|
+
import { GlobalStylesPanel as $ } from "./GlobalStylesPanel.js";
|
|
11
|
+
import { FloatingPanel as b } from "../ui/floating-panel.js";
|
|
12
|
+
import { createEmptyDocument as L, parseMjml as z, serializeMjml as G } from "../../lib/mjml/parser.js";
|
|
13
|
+
function H(n) {
|
|
14
14
|
if (!n || n.trim() === "")
|
|
15
|
-
return
|
|
15
|
+
return L();
|
|
16
16
|
try {
|
|
17
|
-
return
|
|
17
|
+
return z(n);
|
|
18
18
|
} catch (o) {
|
|
19
|
-
return console.error("Failed to parse MJML:", o),
|
|
19
|
+
return console.error("Failed to parse MJML:", o), L();
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function _({
|
|
23
23
|
className: n,
|
|
24
24
|
children: o
|
|
25
25
|
}) {
|
|
@@ -32,125 +32,134 @@ function H({
|
|
|
32
32
|
}
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function q({
|
|
36
36
|
onChange: n,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
onSourceApply: o,
|
|
38
|
+
showThemeToggle: l = !0,
|
|
39
|
+
defaultLeftPanelOpen: f = !0,
|
|
40
|
+
defaultRightPanelOpen: E = !1
|
|
40
41
|
}) {
|
|
41
|
-
const { state: r, undo:
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
}, [n]),
|
|
42
|
+
const { state: r, undo: m, redo: p, canUndo: h, canRedo: g, deleteBlock: k, selectBlock: v } = x(), [c, T] = u(f), [d, i] = u(E), [s, D] = u("edit"), w = O(n);
|
|
43
|
+
return a(() => {
|
|
44
|
+
w.current = n;
|
|
45
|
+
}, [n]), a(() => {
|
|
45
46
|
r.selectedBlockId && i(!0);
|
|
46
|
-
}, [r.selectedBlockId]),
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
}, [r.document]),
|
|
50
|
-
const
|
|
47
|
+
}, [r.selectedBlockId]), a(() => {
|
|
48
|
+
const y = G(r.document);
|
|
49
|
+
w.current(y);
|
|
50
|
+
}, [r.document]), a(() => {
|
|
51
|
+
const y = (t) => {
|
|
51
52
|
if (!(t.target instanceof HTMLInputElement || t.target instanceof HTMLTextAreaElement || t.target?.isContentEditable)) {
|
|
52
53
|
if ((t.metaKey || t.ctrlKey) && t.key === "z") {
|
|
53
|
-
t.preventDefault(), t.shiftKey ?
|
|
54
|
+
t.preventDefault(), t.shiftKey ? g && p() : h && m();
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
|
-
if ((t.key === "Delete" || t.key === "Backspace") && r.selectedBlockId && r.selectedBlockId !==
|
|
57
|
-
t.preventDefault(),
|
|
57
|
+
if ((t.key === "Delete" || t.key === "Backspace") && r.selectedBlockId && r.selectedBlockId !== P) {
|
|
58
|
+
t.preventDefault(), k(r.selectedBlockId);
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
if (t.key === "Escape" && r.selectedBlockId) {
|
|
61
|
-
t.preventDefault(),
|
|
62
|
+
t.preventDefault(), v(null);
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
|
-
return window.addEventListener("keydown",
|
|
67
|
+
return window.addEventListener("keydown", y), () => window.removeEventListener("keydown", y);
|
|
67
68
|
}, [
|
|
68
|
-
u,
|
|
69
|
-
f,
|
|
70
69
|
m,
|
|
71
70
|
p,
|
|
72
71
|
h,
|
|
73
72
|
g,
|
|
73
|
+
k,
|
|
74
|
+
v,
|
|
74
75
|
r.selectedBlockId
|
|
75
|
-
]), /* @__PURE__ */
|
|
76
|
+
]), /* @__PURE__ */ B("div", { className: "relative h-full overflow-hidden", children: [
|
|
76
77
|
/* @__PURE__ */ e("div", { className: "absolute inset-0 bg-canvas", children: /* @__PURE__ */ e(
|
|
77
|
-
|
|
78
|
+
F,
|
|
78
79
|
{
|
|
79
|
-
activeTab:
|
|
80
|
-
onTabChange:
|
|
81
|
-
leftPanelOpen:
|
|
82
|
-
rightPanelOpen:
|
|
83
|
-
showThemeToggle:
|
|
80
|
+
activeTab: s,
|
|
81
|
+
onTabChange: D,
|
|
82
|
+
leftPanelOpen: c,
|
|
83
|
+
rightPanelOpen: d,
|
|
84
|
+
showThemeToggle: l,
|
|
85
|
+
onSourceApply: o
|
|
84
86
|
}
|
|
85
87
|
) }),
|
|
86
|
-
|
|
88
|
+
s === "edit" && /* @__PURE__ */ B(M, { children: [
|
|
87
89
|
/* @__PURE__ */ e(
|
|
88
|
-
|
|
90
|
+
b,
|
|
89
91
|
{
|
|
90
92
|
side: "left",
|
|
91
|
-
isOpen:
|
|
92
|
-
onToggle: () =>
|
|
93
|
+
isOpen: c,
|
|
94
|
+
onToggle: () => T(!c),
|
|
93
95
|
width: 256,
|
|
94
|
-
children: /* @__PURE__ */ e(R, { onTogglePanel: () =>
|
|
96
|
+
children: /* @__PURE__ */ e(R, { onTogglePanel: () => T(!1) })
|
|
95
97
|
}
|
|
96
98
|
),
|
|
97
99
|
/* @__PURE__ */ e(
|
|
98
|
-
|
|
100
|
+
b,
|
|
99
101
|
{
|
|
100
102
|
side: "right",
|
|
101
|
-
isOpen:
|
|
102
|
-
onToggle: () => i(!
|
|
103
|
+
isOpen: d,
|
|
104
|
+
onToggle: () => i(!d),
|
|
103
105
|
width: 300,
|
|
104
|
-
children: r.selectedBlockId ===
|
|
105
|
-
|
|
106
|
+
children: r.selectedBlockId === P ? /* @__PURE__ */ e(
|
|
107
|
+
$,
|
|
106
108
|
{
|
|
107
109
|
onTogglePanel: () => i(!1)
|
|
108
110
|
}
|
|
109
|
-
) : /* @__PURE__ */ e(
|
|
111
|
+
) : /* @__PURE__ */ e(N, { onTogglePanel: () => i(!1) })
|
|
110
112
|
}
|
|
111
113
|
)
|
|
112
114
|
] })
|
|
113
115
|
] });
|
|
114
116
|
}
|
|
115
|
-
function
|
|
117
|
+
function oe({
|
|
116
118
|
value: n,
|
|
117
119
|
onChange: o,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
onSourceApply: l,
|
|
121
|
+
className: f,
|
|
122
|
+
defaultTheme: E = "system",
|
|
120
123
|
liquidSchema: r,
|
|
121
|
-
extensions:
|
|
122
|
-
applyThemeToDocument:
|
|
123
|
-
showThemeToggle:
|
|
124
|
-
defaultLeftPanelOpen:
|
|
125
|
-
defaultRightPanelOpen:
|
|
124
|
+
extensions: m,
|
|
125
|
+
applyThemeToDocument: p = !0,
|
|
126
|
+
showThemeToggle: h = !0,
|
|
127
|
+
defaultLeftPanelOpen: g = !0,
|
|
128
|
+
defaultRightPanelOpen: k = !1
|
|
126
129
|
}) {
|
|
127
|
-
const [
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
const [v, c] = u(!1);
|
|
131
|
+
a(() => {
|
|
132
|
+
c(!0);
|
|
130
133
|
}, []);
|
|
131
|
-
const [
|
|
132
|
-
(
|
|
133
|
-
o(
|
|
134
|
+
const [T] = u(() => H(n)), d = I(
|
|
135
|
+
(s) => {
|
|
136
|
+
o(s);
|
|
134
137
|
},
|
|
135
138
|
[o]
|
|
139
|
+
), i = I(
|
|
140
|
+
(s) => {
|
|
141
|
+
l?.(s);
|
|
142
|
+
},
|
|
143
|
+
[l]
|
|
136
144
|
);
|
|
137
|
-
return
|
|
138
|
-
|
|
145
|
+
return v ? /* @__PURE__ */ e(
|
|
146
|
+
S,
|
|
139
147
|
{
|
|
140
|
-
defaultTheme:
|
|
141
|
-
applyToDocument:
|
|
142
|
-
children: /* @__PURE__ */ e(K, { extensions:
|
|
143
|
-
|
|
148
|
+
defaultTheme: E,
|
|
149
|
+
applyToDocument: p,
|
|
150
|
+
children: /* @__PURE__ */ e(K, { extensions: m, children: /* @__PURE__ */ e(A, { schema: r, children: /* @__PURE__ */ e(_, { className: f, children: /* @__PURE__ */ e(j, { initialDocument: T, children: /* @__PURE__ */ e(
|
|
151
|
+
q,
|
|
144
152
|
{
|
|
145
|
-
onChange:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
onChange: d,
|
|
154
|
+
onSourceApply: i,
|
|
155
|
+
showThemeToggle: h,
|
|
156
|
+
defaultLeftPanelOpen: g,
|
|
157
|
+
defaultRightPanelOpen: k
|
|
149
158
|
}
|
|
150
159
|
) }) }) }) })
|
|
151
160
|
}
|
|
152
|
-
) : /* @__PURE__ */ e("div", { className: `h-full w-full bg-background ${
|
|
161
|
+
) : /* @__PURE__ */ e("div", { className: `h-full w-full bg-background ${f || ""}` });
|
|
153
162
|
}
|
|
154
163
|
export {
|
|
155
|
-
|
|
164
|
+
oe as MjmlEditor
|
|
156
165
|
};
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
interface SourceEditorProps {
|
|
2
|
+
onApply?: (mjml: string) => void;
|
|
3
|
+
onDirtyChange?: (isDirty: boolean) => void;
|
|
4
|
+
}
|
|
5
|
+
export declare function SourceEditor({ onApply, onDirtyChange }: SourceEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export {};
|
|
2
7
|
//# sourceMappingURL=SourceEditor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SourceEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourceEditor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SourceEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourceEditor.tsx"],"names":[],"mappings":"AAoCA,UAAU,iBAAiB;IACzB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,iBAAiB,2CAwIzE"}
|
|
@@ -1,70 +1,115 @@
|
|
|
1
|
-
import { jsxs as s, jsx as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { AlertTriangle as
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { jsxs as s, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { useState as d, useEffect as c, useCallback as u, useMemo as N } from "react";
|
|
3
|
+
import { AlertTriangle as S } from "lucide-react";
|
|
4
|
+
import j from "@uiw/react-codemirror";
|
|
5
|
+
import { EditorState as E } from "@codemirror/state";
|
|
6
|
+
import { indentUnit as k, bracketMatching as w, syntaxHighlighting as L, defaultHighlightStyle as K } from "@codemirror/language";
|
|
7
|
+
import { lineNumbers as W, highlightActiveLineGutter as A, highlightActiveLine as C, EditorView as D, keymap as z } from "@codemirror/view";
|
|
8
|
+
import { history as J, indentWithTab as P, defaultKeymap as H, historyKeymap as I } from "@codemirror/commands";
|
|
9
|
+
import { highlightSelectionMatches as T, searchKeymap as U } from "@codemirror/search";
|
|
10
|
+
import { xml as F } from "@codemirror/lang-xml";
|
|
11
|
+
import { useEditor as G } from "../../context/EditorContext.js";
|
|
12
|
+
import { serializeMjml as b, parseMjml as R } from "../../lib/mjml/parser.js";
|
|
13
|
+
import { ResizableSplitPane as V } from "../ui/resizable-split-pane.js";
|
|
14
|
+
import { SourcePreview as Y } from "./SourcePreview.js";
|
|
15
|
+
function se({ onApply: f, onDirtyChange: o }) {
|
|
16
|
+
const { state: h, setDocument: p } = G(), [i, g] = d(""), [l, r] = d(null), [a, m] = d(!1);
|
|
17
|
+
c(() => {
|
|
18
|
+
const e = b(h.document);
|
|
19
|
+
g(e), m(!1), r(null);
|
|
20
|
+
}, [h.document]);
|
|
21
|
+
const n = u(() => {
|
|
15
22
|
try {
|
|
16
|
-
const e =
|
|
23
|
+
const e = R(i);
|
|
17
24
|
if (e.tagName !== "mjml") {
|
|
18
|
-
|
|
25
|
+
r("Invalid MJML: Document must have an <mjml> root element");
|
|
19
26
|
return;
|
|
20
27
|
}
|
|
21
|
-
|
|
28
|
+
const M = b(e);
|
|
29
|
+
p(e), f?.(M), r(null), m(!1);
|
|
22
30
|
} catch (e) {
|
|
23
|
-
|
|
31
|
+
r(e instanceof Error ? e.message : "Failed to parse MJML");
|
|
24
32
|
}
|
|
25
|
-
}, f = (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
}, [i, p, f]), x = N(
|
|
34
|
+
() => [
|
|
35
|
+
W(),
|
|
36
|
+
A(),
|
|
37
|
+
C(),
|
|
38
|
+
J(),
|
|
39
|
+
E.tabSize.of(2),
|
|
40
|
+
k.of(" "),
|
|
41
|
+
w(),
|
|
42
|
+
T(),
|
|
43
|
+
L(K),
|
|
44
|
+
D.lineWrapping,
|
|
45
|
+
z.of([
|
|
46
|
+
P,
|
|
47
|
+
...H,
|
|
48
|
+
...I,
|
|
49
|
+
...U
|
|
50
|
+
]),
|
|
51
|
+
F()
|
|
52
|
+
],
|
|
53
|
+
[]
|
|
54
|
+
), y = u((e) => {
|
|
55
|
+
g(e), m(!0), r(null);
|
|
56
|
+
}, []), v = u(
|
|
57
|
+
(e) => {
|
|
58
|
+
e.defaultPrevented || (e.metaKey || e.ctrlKey) && e.key === "Enter" && (e.preventDefault(), n());
|
|
59
|
+
},
|
|
60
|
+
[n]
|
|
61
|
+
);
|
|
62
|
+
return c(() => {
|
|
63
|
+
o?.(a);
|
|
64
|
+
}, [a, o]), c(
|
|
65
|
+
() => () => {
|
|
66
|
+
o?.(!1);
|
|
67
|
+
},
|
|
68
|
+
[o]
|
|
69
|
+
), /* @__PURE__ */ s(
|
|
70
|
+
V,
|
|
30
71
|
{
|
|
31
72
|
defaultLeftWidth: 50,
|
|
32
73
|
minLeftWidth: 30,
|
|
33
74
|
maxLeftWidth: 70,
|
|
34
75
|
children: [
|
|
35
76
|
/* @__PURE__ */ s("div", { className: "flex flex-col h-full bg-background", children: [
|
|
36
|
-
/* @__PURE__ */
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ t("div", { className: "px-4 pt-4", children: /* @__PURE__ */ s("div", { className: "flex items-start gap-3 p-3 rounded-md bg-amber-50 border border-amber-200 text-amber-800", children: [
|
|
78
|
+
/* @__PURE__ */ t(S, { className: "h-4 w-4 mt-0.5 flex-shrink-0" }),
|
|
79
|
+
/* @__PURE__ */ t("p", { className: "text-sm", children: "You are editing the raw MJML source. Changes may affect the visual editor." })
|
|
39
80
|
] }) }),
|
|
40
|
-
/* @__PURE__ */
|
|
41
|
-
|
|
81
|
+
/* @__PURE__ */ t("div", { className: "flex-1 min-h-0 p-4", onKeyDown: v, children: /* @__PURE__ */ t(
|
|
82
|
+
j,
|
|
42
83
|
{
|
|
43
|
-
value:
|
|
44
|
-
onChange:
|
|
45
|
-
|
|
84
|
+
value: i,
|
|
85
|
+
onChange: y,
|
|
86
|
+
extensions: x,
|
|
87
|
+
basicSetup: !1,
|
|
88
|
+
theme: "none",
|
|
89
|
+
className: "source-editor h-full overflow-hidden rounded-md border border-border bg-muted text-foreground",
|
|
90
|
+
height: "100%",
|
|
46
91
|
spellCheck: !1
|
|
47
92
|
}
|
|
48
93
|
) }),
|
|
49
94
|
/* @__PURE__ */ s("div", { className: "px-4 pb-4 flex items-center gap-3", children: [
|
|
50
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ t(
|
|
51
96
|
"button",
|
|
52
97
|
{
|
|
53
|
-
onClick:
|
|
54
|
-
disabled: !
|
|
98
|
+
onClick: n,
|
|
99
|
+
disabled: !a,
|
|
55
100
|
className: "px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
56
101
|
children: "Apply"
|
|
57
102
|
}
|
|
58
103
|
),
|
|
59
|
-
|
|
60
|
-
|
|
104
|
+
a && !l && /* @__PURE__ */ t("span", { className: "text-sm text-foreground-muted", children: "Unsaved changes" }),
|
|
105
|
+
l && /* @__PURE__ */ t("span", { className: "text-sm text-destructive", children: l })
|
|
61
106
|
] })
|
|
62
107
|
] }),
|
|
63
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ t(Y, { mjmlSource: i, debounceMs: 300 })
|
|
64
109
|
]
|
|
65
110
|
}
|
|
66
111
|
);
|
|
67
112
|
}
|
|
68
113
|
export {
|
|
69
|
-
|
|
114
|
+
se as SourceEditor
|
|
70
115
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SourceEditor.test.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourceEditor.test.tsx"],"names":[],"mappings":""}
|
package/dist/components.css
CHANGED
|
@@ -340,6 +340,59 @@
|
|
|
340
340
|
--block-selected-bg: oklch(0.96 0.02 250);
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
/* Source editor (CodeMirror) */
|
|
344
|
+
.mjml-editor .source-editor .cm-editor {
|
|
345
|
+
height: 100%;
|
|
346
|
+
background: var(--muted);
|
|
347
|
+
color: var(--foreground);
|
|
348
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
|
349
|
+
'Liberation Mono', 'Courier New', monospace;
|
|
350
|
+
font-size: 0.875rem;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.mjml-editor .source-editor .cm-scroller {
|
|
354
|
+
font-family: inherit;
|
|
355
|
+
line-height: 1.5;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.mjml-editor .source-editor .cm-focused {
|
|
359
|
+
outline: none;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.mjml-editor .source-editor .cm-gutters {
|
|
363
|
+
border-right: 1px solid var(--border);
|
|
364
|
+
background: color-mix(in oklab, var(--muted) 90%, var(--background) 10%);
|
|
365
|
+
color: var(--foreground-subtle);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.mjml-editor .source-editor .cm-activeLine,
|
|
369
|
+
.mjml-editor .source-editor .cm-activeLineGutter {
|
|
370
|
+
background: color-mix(in oklab, var(--accent) 80%, transparent);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.mjml-editor .source-editor .cm-selectionBackground,
|
|
374
|
+
.mjml-editor .source-editor .cm-content ::selection {
|
|
375
|
+
background: var(--selection);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.mjml-editor .source-editor .cm-searchMatch {
|
|
379
|
+
background: color-mix(in oklab, var(--selection) 70%, var(--accent) 30%);
|
|
380
|
+
border: 1px solid var(--border-strong);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.mjml-editor .source-editor .cm-searchMatch.cm-searchMatch-selected {
|
|
384
|
+
background: color-mix(in oklab, var(--selection) 60%, var(--accent) 40%);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.mjml-editor .source-editor .cm-matchingBracket {
|
|
388
|
+
background: color-mix(in oklab, var(--accent) 70%, transparent);
|
|
389
|
+
outline: 1px solid var(--border-strong);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.mjml-editor .source-editor .cm-cursor {
|
|
393
|
+
border-left-color: var(--foreground);
|
|
394
|
+
}
|
|
395
|
+
|
|
343
396
|
/* Tiptap Editor Styles */
|
|
344
397
|
.ProseMirror {
|
|
345
398
|
outline: none;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html-utils.d.ts","sourceRoot":"","sources":["../../src/lib/html-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBxD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAcxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"html-utils.d.ts","sourceRoot":"","sources":["../../src/lib/html-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBxD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAcxD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwExD"}
|
package/dist/lib/html-utils.js
CHANGED
|
@@ -1,25 +1,55 @@
|
|
|
1
|
-
function
|
|
2
|
-
if (!
|
|
1
|
+
function x(e) {
|
|
2
|
+
if (!e || e === "<p></p>")
|
|
3
3
|
return "";
|
|
4
|
-
let
|
|
5
|
-
return
|
|
4
|
+
let t = e.replace(/<p>/g, "");
|
|
5
|
+
return t = t.replace(/<\/p>/g, "<br />"), t = t.replace(/(<br\s*\/?>)+$/, ""), t = t.replace(/<br>/gi, "<br />"), t;
|
|
6
6
|
}
|
|
7
|
-
function
|
|
8
|
-
return !
|
|
7
|
+
function T(e) {
|
|
8
|
+
return !e || e.trim() === "" ? "<p></p>" : e.includes("<p>") ? e : e.split(/<br\s*\/?>/gi).map((i) => `<p>${i}</p>`).join("");
|
|
9
9
|
}
|
|
10
|
-
function
|
|
11
|
-
if (!
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
function N(e) {
|
|
11
|
+
if (!e || typeof document > "u")
|
|
12
|
+
return e;
|
|
13
|
+
try {
|
|
14
|
+
const t = document.createElement("div");
|
|
15
|
+
t.innerHTML = e;
|
|
16
|
+
const i = /(\{\{[^{}]*\}\}|\{%[^{}]*%\})/g, p = [], u = document.createTreeWalker(
|
|
17
|
+
t,
|
|
18
|
+
NodeFilter.SHOW_TEXT,
|
|
19
|
+
null
|
|
20
|
+
);
|
|
21
|
+
let l = u.nextNode();
|
|
22
|
+
for (; l; )
|
|
23
|
+
p.push(l), l = u.nextNode();
|
|
24
|
+
for (const r of p) {
|
|
25
|
+
const s = r.parentElement;
|
|
26
|
+
if (!s) continue;
|
|
27
|
+
const f = s.tagName.toLowerCase();
|
|
28
|
+
if (f === "script" || f === "style" || s.closest(".liquid-highlight")) continue;
|
|
29
|
+
const o = r.textContent || "";
|
|
30
|
+
i.lastIndex = 0;
|
|
31
|
+
const m = Array.from(o.matchAll(i));
|
|
32
|
+
if (m.length === 0) continue;
|
|
33
|
+
const c = document.createDocumentFragment();
|
|
34
|
+
let n = 0;
|
|
35
|
+
for (const g of m) {
|
|
36
|
+
const h = g[0], a = g.index ?? -1;
|
|
37
|
+
if (a < 0) continue;
|
|
38
|
+
a > n && c.appendChild(
|
|
39
|
+
document.createTextNode(o.slice(n, a))
|
|
40
|
+
);
|
|
41
|
+
const d = document.createElement("span");
|
|
42
|
+
d.className = "liquid-highlight", d.textContent = h, c.appendChild(d), n = a + h.length;
|
|
43
|
+
}
|
|
44
|
+
n < o.length && c.appendChild(document.createTextNode(o.slice(n))), r.parentNode?.replaceChild(c, r);
|
|
45
|
+
}
|
|
46
|
+
return t.innerHTML;
|
|
47
|
+
} catch {
|
|
48
|
+
return e;
|
|
49
|
+
}
|
|
20
50
|
}
|
|
21
51
|
export {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
52
|
+
N as highlightLiquidTags,
|
|
53
|
+
T as mjmlToTiptapHtml,
|
|
54
|
+
x as sanitizeHtmlForMjml
|
|
25
55
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-utils.test.d.ts","sourceRoot":"","sources":["../../src/lib/html-utils.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvycal/mjml-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,7 +35,14 @@
|
|
|
35
35
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"@codemirror/commands": "^6.10.2",
|
|
39
|
+
"@codemirror/lang-xml": "^6.1.0",
|
|
40
|
+
"@codemirror/language": "^6.12.1",
|
|
41
|
+
"@codemirror/search": "^6.6.0",
|
|
42
|
+
"@codemirror/state": "^6.5.4",
|
|
43
|
+
"@codemirror/view": "^6.39.14",
|
|
38
44
|
"@floating-ui/react": "^0.27.16",
|
|
45
|
+
"@lezer/highlight": "^1.2.3",
|
|
39
46
|
"@radix-ui/react-collapsible": "^1.1.11",
|
|
40
47
|
"@radix-ui/react-label": "^2.1.8",
|
|
41
48
|
"@radix-ui/react-popover": "^1.1.15",
|
|
@@ -51,6 +58,7 @@
|
|
|
51
58
|
"@tiptap/react": "^2.10.0",
|
|
52
59
|
"@tiptap/starter-kit": "^2.10.0",
|
|
53
60
|
"@tiptap/suggestion": "^2.27.2",
|
|
61
|
+
"@uiw/react-codemirror": "^4.25.4",
|
|
54
62
|
"class-variance-authority": "^0.7.1",
|
|
55
63
|
"clsx": "^2.1.1",
|
|
56
64
|
"lucide-react": "^0.562.0",
|
|
@@ -61,6 +69,7 @@
|
|
|
61
69
|
},
|
|
62
70
|
"devDependencies": {
|
|
63
71
|
"@tailwindcss/vite": "^4.1.18",
|
|
72
|
+
"@testing-library/react": "^16.3.2",
|
|
64
73
|
"@types/react": "^19.2.5",
|
|
65
74
|
"@types/react-dom": "^19.2.3",
|
|
66
75
|
"@types/uuid": "^11.0.0",
|