@savvycal/mjml-editor 0.5.0 → 0.7.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 +48 -52
- package/dist/components/editor/EditorCanvas.d.ts +1 -2
- package/dist/components/editor/EditorCanvas.d.ts.map +1 -1
- package/dist/components/editor/EditorCanvas.js +54 -116
- package/dist/components/editor/MjmlEditor.d.ts +1 -6
- package/dist/components/editor/MjmlEditor.d.ts.map +1 -1
- package/dist/components/editor/MjmlEditor.js +80 -89
- package/dist/components/editor/SourceEditor.d.ts +1 -6
- package/dist/components/editor/SourceEditor.d.ts.map +1 -1
- package/dist/components/editor/SourceEditor.js +96 -83
- package/dist/components.css +102 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,15 +15,15 @@ A React-based visual editor for MJML email templates. Built for embedding in app
|
|
|
15
15
|
|
|
16
16
|
## Supported Components
|
|
17
17
|
|
|
18
|
-
| Component
|
|
19
|
-
|
|
20
|
-
| `mj-section` | Row containers with background color/image
|
|
21
|
-
| `mj-column`
|
|
22
|
-
| `mj-text`
|
|
23
|
-
| `mj-image`
|
|
24
|
-
| `mj-button`
|
|
25
|
-
| `mj-divider` | Horizontal separators
|
|
26
|
-
| `mj-spacer`
|
|
18
|
+
| Component | Description |
|
|
19
|
+
| ------------ | ------------------------------------------- |
|
|
20
|
+
| `mj-section` | Row containers with background color/image |
|
|
21
|
+
| `mj-column` | Responsive columns within sections |
|
|
22
|
+
| `mj-text` | Text content with typography settings |
|
|
23
|
+
| `mj-image` | Images with dimensions, alt text, and links |
|
|
24
|
+
| `mj-button` | Call-to-action buttons with styling |
|
|
25
|
+
| `mj-divider` | Horizontal separators |
|
|
26
|
+
| `mj-spacer` | Vertical spacing |
|
|
27
27
|
|
|
28
28
|
## Installation
|
|
29
29
|
|
|
@@ -48,20 +48,22 @@ This library is designed to work with Tailwind CSS v4. Instead of bundling all s
|
|
|
48
48
|
Add the following imports to your app's main CSS file:
|
|
49
49
|
|
|
50
50
|
```css
|
|
51
|
-
@import
|
|
52
|
-
@import
|
|
53
|
-
@import
|
|
54
|
-
@import
|
|
51
|
+
@import '@savvycal/mjml-editor/preset.css';
|
|
52
|
+
@import 'tailwindcss';
|
|
53
|
+
@import 'tw-animate-css';
|
|
54
|
+
@import '@savvycal/mjml-editor/components.css';
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
**Note:** `preset.css` must come before `tailwindcss` so that `@theme` tokens are registered before Tailwind generates its utilities.
|
|
58
58
|
|
|
59
59
|
The `preset.css` file includes:
|
|
60
|
+
|
|
60
61
|
- `@source` directive that tells Tailwind to scan the library's dist files for utility classes (works with npm, yarn, and pnpm)
|
|
61
62
|
- `@theme` tokens that map CSS variables to Tailwind utilities
|
|
62
63
|
- Custom utilities (`bg-checkered`, `shadow-framer`, etc.)
|
|
63
64
|
|
|
64
65
|
The `components.css` file includes:
|
|
66
|
+
|
|
65
67
|
- Scoped CSS variables for the editor theme (light/dark mode)
|
|
66
68
|
- Tiptap/ProseMirror editor styles
|
|
67
69
|
|
|
@@ -74,27 +76,23 @@ import { MjmlEditor } from '@savvycal/mjml-editor';
|
|
|
74
76
|
function App() {
|
|
75
77
|
const [mjml, setMjml] = useState(initialMjml);
|
|
76
78
|
|
|
77
|
-
return
|
|
78
|
-
<MjmlEditor
|
|
79
|
-
value={mjml}
|
|
80
|
-
onChange={setMjml}
|
|
81
|
-
/>
|
|
82
|
-
);
|
|
79
|
+
return <MjmlEditor value={mjml} onChange={setMjml} />;
|
|
83
80
|
}
|
|
84
81
|
```
|
|
85
82
|
|
|
86
83
|
### Props
|
|
87
84
|
|
|
88
|
-
| Prop
|
|
89
|
-
|
|
90
|
-
| `value`
|
|
91
|
-
| `onChange`
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
|
|
85
|
+
| Prop | Type | Description |
|
|
86
|
+
| ---------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `value` | `string` | MJML markup string (required) |
|
|
88
|
+
| `onChange` | `(mjml: string) => void` | Called when the document changes (required) |
|
|
89
|
+
| `className` | `string` | Optional CSS class for the container |
|
|
90
|
+
| `defaultTheme` | `'light' \| 'dark' \| 'system'` | Theme preference (default: `'system'`) |
|
|
91
|
+
| `liquidSchema` | `LiquidSchema` | Optional schema for Liquid template autocomplete |
|
|
92
|
+
| `extensions` | `EditorExtensions` | Optional extensions for custom features beyond standard MJML |
|
|
93
|
+
| `applyThemeToDocument` | `boolean` | Whether to apply theme class to `document.documentElement`. Needed for dropdown/popover theming. Set to `false` if your app manages document-level theme classes. (default: `true`) |
|
|
94
|
+
|
|
95
|
+
When using the Source tab, valid MJML edits are applied automatically as you type.
|
|
98
96
|
|
|
99
97
|
## Liquid Template Support
|
|
100
98
|
|
|
@@ -120,11 +118,7 @@ function App() {
|
|
|
120
118
|
const [mjml, setMjml] = useState(initialMjml);
|
|
121
119
|
|
|
122
120
|
return (
|
|
123
|
-
<MjmlEditor
|
|
124
|
-
value={mjml}
|
|
125
|
-
onChange={setMjml}
|
|
126
|
-
liquidSchema={liquidSchema}
|
|
127
|
-
/>
|
|
121
|
+
<MjmlEditor value={mjml} onChange={setMjml} liquidSchema={liquidSchema} />
|
|
128
122
|
);
|
|
129
123
|
}
|
|
130
124
|
```
|
|
@@ -160,11 +154,13 @@ function App() {
|
|
|
160
154
|
Enables the `sc-if` attribute for server-side conditional rendering using Liquid expressions.
|
|
161
155
|
|
|
162
156
|
When enabled:
|
|
157
|
+
|
|
163
158
|
- A "Condition (Liquid)" field appears in the Advanced section of the inspector for all block types
|
|
164
159
|
- Blocks with conditions display an "if" badge indicator in both the canvas and outline tree
|
|
165
160
|
- The Advanced section auto-expands when a block has a condition
|
|
166
161
|
|
|
167
162
|
**How it works:**
|
|
163
|
+
|
|
168
164
|
- The `sc-if` attribute is preserved in the MJML output for server-side processing
|
|
169
165
|
- The attribute is stripped from preview rendering to avoid MJML validation warnings
|
|
170
166
|
- Your server processes the Liquid condition and conditionally renders the block
|
|
@@ -196,12 +192,12 @@ The library exports TypeScript types for integration:
|
|
|
196
192
|
|
|
197
193
|
```tsx
|
|
198
194
|
import type {
|
|
199
|
-
MjmlNode,
|
|
200
|
-
MjmlTagName,
|
|
201
|
-
ContentBlockType,
|
|
202
|
-
EditorExtensions,
|
|
203
|
-
LiquidSchema,
|
|
204
|
-
LiquidSchemaItem,
|
|
195
|
+
MjmlNode, // MJML document node structure
|
|
196
|
+
MjmlTagName, // Union of supported MJML tag names
|
|
197
|
+
ContentBlockType, // Union of content block types
|
|
198
|
+
EditorExtensions, // Extensions configuration
|
|
199
|
+
LiquidSchema, // Schema for Liquid autocomplete
|
|
200
|
+
LiquidSchemaItem, // Individual variable/tag definition
|
|
205
201
|
} from '@savvycal/mjml-editor';
|
|
206
202
|
```
|
|
207
203
|
|
|
@@ -217,13 +213,13 @@ interface EditorExtensions {
|
|
|
217
213
|
|
|
218
214
|
```typescript
|
|
219
215
|
interface LiquidSchemaItem {
|
|
220
|
-
name: string;
|
|
216
|
+
name: string; // Variable or tag name (e.g., "user.name")
|
|
221
217
|
description?: string; // Description shown in autocomplete
|
|
222
218
|
}
|
|
223
219
|
|
|
224
220
|
interface LiquidSchema {
|
|
225
221
|
variables: LiquidSchemaItem[]; // {{ variable }} syntax
|
|
226
|
-
tags: LiquidSchemaItem[];
|
|
222
|
+
tags: LiquidSchemaItem[]; // {% tag %} syntax
|
|
227
223
|
}
|
|
228
224
|
```
|
|
229
225
|
|
|
@@ -235,22 +231,22 @@ The library exports theme utilities if you need to integrate with or control the
|
|
|
235
231
|
import { ThemeProvider, useTheme, ThemeToggle } from '@savvycal/mjml-editor';
|
|
236
232
|
```
|
|
237
233
|
|
|
238
|
-
| Export
|
|
239
|
-
|
|
240
|
-
| `ThemeProvider` | Context provider for theme management
|
|
241
|
-
| `useTheme()`
|
|
242
|
-
| `ThemeToggle`
|
|
234
|
+
| Export | Description |
|
|
235
|
+
| --------------- | ------------------------------------------ |
|
|
236
|
+
| `ThemeProvider` | Context provider for theme management |
|
|
237
|
+
| `useTheme()` | Hook returning `{ theme, setTheme }` |
|
|
238
|
+
| `ThemeToggle` | Pre-built UI component for theme switching |
|
|
243
239
|
|
|
244
240
|
Note: `MjmlEditor` includes its own `ThemeProvider`, so you don't need to wrap it. These exports are for advanced use cases where you need theme access outside the editor.
|
|
245
241
|
|
|
246
242
|
## Keyboard Shortcuts
|
|
247
243
|
|
|
248
|
-
| Shortcut
|
|
249
|
-
|
|
250
|
-
| `Cmd/Ctrl + Z`
|
|
251
|
-
| `Cmd/Ctrl + Shift + Z` | Redo
|
|
244
|
+
| Shortcut | Action |
|
|
245
|
+
| ---------------------- | --------------------- |
|
|
246
|
+
| `Cmd/Ctrl + Z` | Undo |
|
|
247
|
+
| `Cmd/Ctrl + Shift + Z` | Redo |
|
|
252
248
|
| `Delete` / `Backspace` | Delete selected block |
|
|
253
|
-
| `Escape`
|
|
249
|
+
| `Escape` | Deselect block |
|
|
254
250
|
|
|
255
251
|
## Contributing
|
|
256
252
|
|
|
@@ -6,8 +6,7 @@ interface EditorCanvasProps {
|
|
|
6
6
|
leftPanelOpen?: boolean;
|
|
7
7
|
rightPanelOpen?: boolean;
|
|
8
8
|
showThemeToggle?: boolean;
|
|
9
|
-
onSourceApply?: (mjml: string) => void;
|
|
10
9
|
}
|
|
11
|
-
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, showThemeToggle,
|
|
10
|
+
export declare function EditorCanvas({ activeTab, onTabChange, leftPanelOpen, rightPanelOpen, showThemeToggle, }: EditorCanvasProps): import("react/jsx-runtime").JSX.Element;
|
|
12
11
|
export {};
|
|
13
12
|
//# 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;CAC3B;AAED,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAsB,GACvB,EAAE,iBAAiB,2CA0GnB"}
|
|
@@ -1,47 +1,31 @@
|
|
|
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 r, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { useState as x } from "react";
|
|
3
|
+
import { Monitor as v, Smartphone as N, Undo2 as b, Redo2 as w } from "lucide-react";
|
|
4
|
+
import { VisualEditor as k } from "./VisualEditor.js";
|
|
5
|
+
import { InteractivePreview as C } from "./InteractivePreview.js";
|
|
6
|
+
import { SourceEditor as y } from "./SourceEditor.js";
|
|
7
|
+
import { useEditor as E } 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:
|
|
18
|
-
onSourceApply: N
|
|
9
|
+
import { Tabs as S, TabsList as M, TabsTrigger as s } from "../ui/tabs.js";
|
|
10
|
+
import { ThemeToggle as P } from "../ui/theme-toggle.js";
|
|
11
|
+
import { cn as d } from "../../lib/utils.js";
|
|
12
|
+
function q({
|
|
13
|
+
activeTab: o,
|
|
14
|
+
onTabChange: t,
|
|
15
|
+
leftPanelOpen: a,
|
|
16
|
+
rightPanelOpen: l,
|
|
17
|
+
showThemeToggle: c = !0
|
|
19
18
|
}) {
|
|
20
|
-
const { undo:
|
|
21
|
-
|
|
22
|
-
|
|
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: [
|
|
19
|
+
const { undo: m, redo: u, canUndo: h, canRedo: f } = E(), [n, p] = x("desktop");
|
|
20
|
+
return /* @__PURE__ */ r("div", { className: "relative flex flex-col h-full", children: [
|
|
21
|
+
/* @__PURE__ */ r("div", { className: "h-11 px-4 flex items-center gap-1 border-b border-border bg-background relative", children: [
|
|
38
22
|
/* @__PURE__ */ e(
|
|
39
23
|
"button",
|
|
40
24
|
{
|
|
41
|
-
onClick: () =>
|
|
42
|
-
className:
|
|
25
|
+
onClick: () => t("edit"),
|
|
26
|
+
className: d(
|
|
43
27
|
"px-3 py-1.5 text-sm font-medium rounded-md transition-colors",
|
|
44
|
-
|
|
28
|
+
o === "edit" ? "bg-accent text-foreground" : "text-foreground-muted hover:text-foreground hover:bg-accent/50"
|
|
45
29
|
),
|
|
46
30
|
children: "Edit"
|
|
47
31
|
}
|
|
@@ -49,10 +33,10 @@ function _({
|
|
|
49
33
|
/* @__PURE__ */ e(
|
|
50
34
|
"button",
|
|
51
35
|
{
|
|
52
|
-
onClick: () =>
|
|
53
|
-
className:
|
|
36
|
+
onClick: () => t("preview"),
|
|
37
|
+
className: d(
|
|
54
38
|
"px-3 py-1.5 text-sm font-medium rounded-md transition-colors",
|
|
55
|
-
|
|
39
|
+
o === "preview" ? "bg-accent text-foreground" : "text-foreground-muted hover:text-foreground hover:bg-accent/50"
|
|
56
40
|
),
|
|
57
41
|
children: "Preview"
|
|
58
42
|
}
|
|
@@ -60,44 +44,44 @@ function _({
|
|
|
60
44
|
/* @__PURE__ */ e(
|
|
61
45
|
"button",
|
|
62
46
|
{
|
|
63
|
-
onClick: () =>
|
|
64
|
-
className:
|
|
47
|
+
onClick: () => t("source"),
|
|
48
|
+
className: d(
|
|
65
49
|
"px-3 py-1.5 text-sm font-medium rounded-md transition-colors",
|
|
66
|
-
|
|
50
|
+
o === "source" ? "bg-accent text-foreground" : "text-foreground-muted hover:text-foreground hover:bg-accent/50"
|
|
67
51
|
),
|
|
68
52
|
children: "Source"
|
|
69
53
|
}
|
|
70
54
|
),
|
|
71
|
-
|
|
72
|
-
|
|
55
|
+
o === "preview" && /* @__PURE__ */ e(
|
|
56
|
+
S,
|
|
73
57
|
{
|
|
74
|
-
value:
|
|
75
|
-
onValueChange: (
|
|
58
|
+
value: n,
|
|
59
|
+
onValueChange: (g) => p(g),
|
|
76
60
|
className: "absolute left-1/2 -translate-x-1/2",
|
|
77
|
-
children: /* @__PURE__ */
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
/* @__PURE__ */ e(
|
|
61
|
+
children: /* @__PURE__ */ r(M, { className: "h-8", children: [
|
|
62
|
+
/* @__PURE__ */ r(s, { value: "desktop", className: "h-7 px-2 gap-1.5", children: [
|
|
63
|
+
/* @__PURE__ */ e(v, { className: "h-3.5 w-3.5" }),
|
|
80
64
|
/* @__PURE__ */ e("span", { className: "hidden md:inline", children: "Desktop" })
|
|
81
65
|
] }),
|
|
82
|
-
/* @__PURE__ */
|
|
83
|
-
/* @__PURE__ */ e(
|
|
66
|
+
/* @__PURE__ */ r(s, { value: "mobile", className: "h-7 px-2 gap-1.5", children: [
|
|
67
|
+
/* @__PURE__ */ e(N, { className: "h-3.5 w-3.5" }),
|
|
84
68
|
/* @__PURE__ */ e("span", { className: "hidden md:inline", children: "Mobile" })
|
|
85
69
|
] })
|
|
86
70
|
] })
|
|
87
71
|
}
|
|
88
72
|
),
|
|
89
73
|
/* @__PURE__ */ e("div", { className: "flex-1" }),
|
|
90
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ r("div", { className: "flex items-center gap-0.5", children: [
|
|
91
75
|
/* @__PURE__ */ e(
|
|
92
76
|
i,
|
|
93
77
|
{
|
|
94
78
|
variant: "ghost",
|
|
95
79
|
size: "icon-sm",
|
|
96
|
-
onClick:
|
|
97
|
-
disabled: !
|
|
80
|
+
onClick: m,
|
|
81
|
+
disabled: !h,
|
|
98
82
|
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent disabled:opacity-40",
|
|
99
83
|
title: "Undo (Cmd+Z)",
|
|
100
|
-
children: /* @__PURE__ */ e(
|
|
84
|
+
children: /* @__PURE__ */ e(b, { className: "h-4 w-4" })
|
|
101
85
|
}
|
|
102
86
|
),
|
|
103
87
|
/* @__PURE__ */ e(
|
|
@@ -105,75 +89,29 @@ function _({
|
|
|
105
89
|
{
|
|
106
90
|
variant: "ghost",
|
|
107
91
|
size: "icon-sm",
|
|
108
|
-
onClick:
|
|
109
|
-
disabled: !
|
|
92
|
+
onClick: u,
|
|
93
|
+
disabled: !f,
|
|
110
94
|
className: "h-7 w-7 rounded-md text-foreground-muted hover:text-foreground hover:bg-accent disabled:opacity-40",
|
|
111
95
|
title: "Redo (Cmd+Shift+Z)",
|
|
112
|
-
children: /* @__PURE__ */ e(
|
|
96
|
+
children: /* @__PURE__ */ e(w, { className: "h-4 w-4" })
|
|
113
97
|
}
|
|
114
98
|
),
|
|
115
|
-
|
|
99
|
+
c && /* @__PURE__ */ e(P, {})
|
|
116
100
|
] })
|
|
117
101
|
] }),
|
|
118
|
-
/* @__PURE__ */
|
|
119
|
-
|
|
120
|
-
|
|
102
|
+
/* @__PURE__ */ r("div", { className: "flex-1 min-h-0 overflow-hidden", children: [
|
|
103
|
+
o === "edit" && /* @__PURE__ */ e(
|
|
104
|
+
k,
|
|
121
105
|
{
|
|
122
|
-
leftPanelOpen:
|
|
123
|
-
rightPanelOpen:
|
|
106
|
+
leftPanelOpen: a,
|
|
107
|
+
rightPanelOpen: l
|
|
124
108
|
}
|
|
125
109
|
),
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
) })
|
|
110
|
+
o === "preview" && /* @__PURE__ */ e(C, { showHeader: !1, previewMode: n }),
|
|
111
|
+
o === "source" && /* @__PURE__ */ e(y, {})
|
|
112
|
+
] })
|
|
175
113
|
] });
|
|
176
114
|
}
|
|
177
115
|
export {
|
|
178
|
-
|
|
116
|
+
q as EditorCanvas
|
|
179
117
|
};
|
|
@@ -3,11 +3,6 @@ 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;
|
|
11
6
|
className?: string;
|
|
12
7
|
defaultTheme?: 'light' | 'dark' | 'system';
|
|
13
8
|
liquidSchema?: LiquidSchema;
|
|
@@ -54,6 +49,6 @@ interface MjmlEditorProps {
|
|
|
54
49
|
*/
|
|
55
50
|
defaultRightPanelOpen?: boolean;
|
|
56
51
|
}
|
|
57
|
-
export declare function MjmlEditor({ value, onChange,
|
|
52
|
+
export declare function MjmlEditor({ value, onChange, className, defaultTheme, liquidSchema, extensions, applyThemeToDocument, showThemeToggle, defaultLeftPanelOpen, defaultRightPanelOpen, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
58
53
|
export {};
|
|
59
54
|
//# 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
|
|
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;AAoKD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAuB,EACvB,YAAY,EACZ,UAAU,EACV,oBAA2B,EAC3B,eAAsB,EACtB,oBAA2B,EAC3B,qBAA6B,GAC9B,EAAE,eAAe,2CA8CjB"}
|
|
@@ -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 w, Fragment as L } from "react/jsx-runtime";
|
|
2
|
+
import { useState as a, useEffect as d, useCallback as D, useRef as M } from "react";
|
|
3
|
+
import { EditorProvider as O, useEditor as j } from "../../context/EditorContext.js";
|
|
4
|
+
import { ThemeProvider as x, useTheme as C } from "../../context/ThemeContext.js";
|
|
5
|
+
import { LiquidSchemaProvider as S } 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 B } from "./OutlineTree.js";
|
|
8
|
+
import { EditorCanvas as A } from "./EditorCanvas.js";
|
|
9
|
+
import { BlockInspector as F } from "./BlockInspector.js";
|
|
10
|
+
import { GlobalStylesPanel as N } from "./GlobalStylesPanel.js";
|
|
11
|
+
import { FloatingPanel as I } from "../ui/floating-panel.js";
|
|
12
|
+
import { createEmptyDocument as P, parseMjml as $, serializeMjml as z } from "../../lib/mjml/parser.js";
|
|
13
|
+
function G(n) {
|
|
14
14
|
if (!n || n.trim() === "")
|
|
15
|
-
return
|
|
15
|
+
return P();
|
|
16
16
|
try {
|
|
17
|
-
return
|
|
17
|
+
return $(n);
|
|
18
18
|
} catch (o) {
|
|
19
|
-
return console.error("Failed to parse MJML:", o),
|
|
19
|
+
return console.error("Failed to parse MJML:", o), P();
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function H({
|
|
23
23
|
className: n,
|
|
24
24
|
children: o
|
|
25
25
|
}) {
|
|
@@ -32,134 +32,125 @@ function _({
|
|
|
32
32
|
}
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
|
-
function
|
|
35
|
+
function _({
|
|
36
36
|
onChange: n,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
defaultRightPanelOpen: E = !1
|
|
37
|
+
showThemeToggle: o = !0,
|
|
38
|
+
defaultLeftPanelOpen: l = !0,
|
|
39
|
+
defaultRightPanelOpen: T = !1
|
|
41
40
|
}) {
|
|
42
|
-
const { state: r, undo:
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
}, [n]),
|
|
41
|
+
const { state: r, undo: u, redo: f, canUndo: m, canRedo: p, deleteBlock: h, selectBlock: g } = j(), [s, k] = a(l), [c, i] = a(T), [E, b] = a("edit"), y = M(n);
|
|
42
|
+
return d(() => {
|
|
43
|
+
y.current = n;
|
|
44
|
+
}, [n]), d(() => {
|
|
46
45
|
r.selectedBlockId && i(!0);
|
|
47
|
-
}, [r.selectedBlockId]),
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
}, [r.document]),
|
|
51
|
-
const
|
|
46
|
+
}, [r.selectedBlockId]), d(() => {
|
|
47
|
+
const v = z(r.document);
|
|
48
|
+
y.current(v);
|
|
49
|
+
}, [r.document]), d(() => {
|
|
50
|
+
const v = (t) => {
|
|
52
51
|
if (!(t.target instanceof HTMLInputElement || t.target instanceof HTMLTextAreaElement || t.target?.isContentEditable)) {
|
|
53
52
|
if ((t.metaKey || t.ctrlKey) && t.key === "z") {
|
|
54
|
-
t.preventDefault(), t.shiftKey ?
|
|
53
|
+
t.preventDefault(), t.shiftKey ? p && f() : m && u();
|
|
55
54
|
return;
|
|
56
55
|
}
|
|
57
|
-
if ((t.key === "Delete" || t.key === "Backspace") && r.selectedBlockId && r.selectedBlockId !==
|
|
58
|
-
t.preventDefault(),
|
|
56
|
+
if ((t.key === "Delete" || t.key === "Backspace") && r.selectedBlockId && r.selectedBlockId !== B) {
|
|
57
|
+
t.preventDefault(), h(r.selectedBlockId);
|
|
59
58
|
return;
|
|
60
59
|
}
|
|
61
60
|
if (t.key === "Escape" && r.selectedBlockId) {
|
|
62
|
-
t.preventDefault(),
|
|
61
|
+
t.preventDefault(), g(null);
|
|
63
62
|
return;
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
};
|
|
67
|
-
return window.addEventListener("keydown",
|
|
66
|
+
return window.addEventListener("keydown", v), () => window.removeEventListener("keydown", v);
|
|
68
67
|
}, [
|
|
68
|
+
u,
|
|
69
|
+
f,
|
|
69
70
|
m,
|
|
70
71
|
p,
|
|
71
72
|
h,
|
|
72
73
|
g,
|
|
73
|
-
k,
|
|
74
|
-
v,
|
|
75
74
|
r.selectedBlockId
|
|
76
|
-
]), /* @__PURE__ */
|
|
75
|
+
]), /* @__PURE__ */ w("div", { className: "relative h-full overflow-hidden", children: [
|
|
77
76
|
/* @__PURE__ */ e("div", { className: "absolute inset-0 bg-canvas", children: /* @__PURE__ */ e(
|
|
78
|
-
|
|
77
|
+
A,
|
|
79
78
|
{
|
|
80
|
-
activeTab:
|
|
81
|
-
onTabChange:
|
|
82
|
-
leftPanelOpen:
|
|
83
|
-
rightPanelOpen:
|
|
84
|
-
showThemeToggle:
|
|
85
|
-
onSourceApply: o
|
|
79
|
+
activeTab: E,
|
|
80
|
+
onTabChange: b,
|
|
81
|
+
leftPanelOpen: s,
|
|
82
|
+
rightPanelOpen: c,
|
|
83
|
+
showThemeToggle: o
|
|
86
84
|
}
|
|
87
85
|
) }),
|
|
88
|
-
|
|
86
|
+
E === "edit" && /* @__PURE__ */ w(L, { children: [
|
|
89
87
|
/* @__PURE__ */ e(
|
|
90
|
-
|
|
88
|
+
I,
|
|
91
89
|
{
|
|
92
90
|
side: "left",
|
|
93
|
-
isOpen:
|
|
94
|
-
onToggle: () =>
|
|
91
|
+
isOpen: s,
|
|
92
|
+
onToggle: () => k(!s),
|
|
95
93
|
width: 256,
|
|
96
|
-
children: /* @__PURE__ */ e(R, { onTogglePanel: () =>
|
|
94
|
+
children: /* @__PURE__ */ e(R, { onTogglePanel: () => k(!1) })
|
|
97
95
|
}
|
|
98
96
|
),
|
|
99
97
|
/* @__PURE__ */ e(
|
|
100
|
-
|
|
98
|
+
I,
|
|
101
99
|
{
|
|
102
100
|
side: "right",
|
|
103
|
-
isOpen:
|
|
104
|
-
onToggle: () => i(!
|
|
101
|
+
isOpen: c,
|
|
102
|
+
onToggle: () => i(!c),
|
|
105
103
|
width: 300,
|
|
106
|
-
children: r.selectedBlockId ===
|
|
107
|
-
|
|
104
|
+
children: r.selectedBlockId === B ? /* @__PURE__ */ e(
|
|
105
|
+
N,
|
|
108
106
|
{
|
|
109
107
|
onTogglePanel: () => i(!1)
|
|
110
108
|
}
|
|
111
|
-
) : /* @__PURE__ */ e(
|
|
109
|
+
) : /* @__PURE__ */ e(F, { onTogglePanel: () => i(!1) })
|
|
112
110
|
}
|
|
113
111
|
)
|
|
114
112
|
] })
|
|
115
113
|
] });
|
|
116
114
|
}
|
|
117
|
-
function
|
|
115
|
+
function ne({
|
|
118
116
|
value: n,
|
|
119
117
|
onChange: o,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
defaultTheme: E = "system",
|
|
118
|
+
className: l,
|
|
119
|
+
defaultTheme: T = "system",
|
|
123
120
|
liquidSchema: r,
|
|
124
|
-
extensions:
|
|
125
|
-
applyThemeToDocument:
|
|
126
|
-
showThemeToggle:
|
|
127
|
-
defaultLeftPanelOpen:
|
|
128
|
-
defaultRightPanelOpen:
|
|
121
|
+
extensions: u,
|
|
122
|
+
applyThemeToDocument: f = !0,
|
|
123
|
+
showThemeToggle: m = !0,
|
|
124
|
+
defaultLeftPanelOpen: p = !0,
|
|
125
|
+
defaultRightPanelOpen: h = !1
|
|
129
126
|
}) {
|
|
130
|
-
const [
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
const [g, s] = a(!1);
|
|
128
|
+
d(() => {
|
|
129
|
+
s(!0);
|
|
133
130
|
}, []);
|
|
134
|
-
const [
|
|
135
|
-
(
|
|
136
|
-
o(
|
|
131
|
+
const [k] = a(() => G(n)), c = D(
|
|
132
|
+
(i) => {
|
|
133
|
+
o(i);
|
|
137
134
|
},
|
|
138
135
|
[o]
|
|
139
|
-
), i = I(
|
|
140
|
-
(s) => {
|
|
141
|
-
l?.(s);
|
|
142
|
-
},
|
|
143
|
-
[l]
|
|
144
136
|
);
|
|
145
|
-
return
|
|
146
|
-
|
|
137
|
+
return g ? /* @__PURE__ */ e(
|
|
138
|
+
x,
|
|
147
139
|
{
|
|
148
|
-
defaultTheme:
|
|
149
|
-
applyToDocument:
|
|
150
|
-
children: /* @__PURE__ */ e(K, { extensions:
|
|
151
|
-
|
|
140
|
+
defaultTheme: T,
|
|
141
|
+
applyToDocument: f,
|
|
142
|
+
children: /* @__PURE__ */ e(K, { extensions: u, children: /* @__PURE__ */ e(S, { schema: r, children: /* @__PURE__ */ e(H, { className: l, children: /* @__PURE__ */ e(O, { initialDocument: k, children: /* @__PURE__ */ e(
|
|
143
|
+
_,
|
|
152
144
|
{
|
|
153
|
-
onChange:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
defaultRightPanelOpen: k
|
|
145
|
+
onChange: c,
|
|
146
|
+
showThemeToggle: m,
|
|
147
|
+
defaultLeftPanelOpen: p,
|
|
148
|
+
defaultRightPanelOpen: h
|
|
158
149
|
}
|
|
159
150
|
) }) }) }) })
|
|
160
151
|
}
|
|
161
|
-
) : /* @__PURE__ */ e("div", { className: `h-full w-full bg-background ${
|
|
152
|
+
) : /* @__PURE__ */ e("div", { className: `h-full w-full bg-background ${l || ""}` });
|
|
162
153
|
}
|
|
163
154
|
export {
|
|
164
|
-
|
|
155
|
+
ne as MjmlEditor
|
|
165
156
|
};
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
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 {};
|
|
1
|
+
export declare function SourceEditor(): import("react/jsx-runtime").JSX.Element;
|
|
7
2
|
//# 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":"AA0DA,wBAAgB,YAAY,4CAkH3B"}
|
|
@@ -1,88 +1,110 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useState as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { history as J, indentWithTab as P, defaultKeymap as H, historyKeymap as I } from "@codemirror/commands";
|
|
1
|
+
import { jsxs as m, jsx as n } from "react/jsx-runtime";
|
|
2
|
+
import { useState as f, useRef as g, useEffect as p, useCallback as v, useMemo as y } from "react";
|
|
3
|
+
import k from "@uiw/react-codemirror";
|
|
4
|
+
import { EditorState as N } from "@codemirror/state";
|
|
5
|
+
import { indentUnit as M, bracketMatching as E, syntaxHighlighting as j, HighlightStyle as C } from "@codemirror/language";
|
|
6
|
+
import { lineNumbers as w, highlightActiveLineGutter as L, highlightActiveLine as D, EditorView as W, keymap as R } from "@codemirror/view";
|
|
7
|
+
import { history as z, indentWithTab as A, defaultKeymap as H, historyKeymap as K } from "@codemirror/commands";
|
|
9
8
|
import { highlightSelectionMatches as T, searchKeymap as U } from "@codemirror/search";
|
|
10
|
-
import { xml as
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
import { xml as _ } from "@codemirror/lang-xml";
|
|
10
|
+
import { tags as t } from "@lezer/highlight";
|
|
11
|
+
import { useEditor as B } from "../../context/EditorContext.js";
|
|
12
|
+
import { serializeMjml as b, parseMjml as I } from "../../lib/mjml/parser.js";
|
|
13
|
+
import { ResizableSplitPane as J } from "../ui/resizable-split-pane.js";
|
|
14
|
+
import { SourcePreview as O } from "./SourcePreview.js";
|
|
15
|
+
const P = 350, V = C.define([
|
|
16
|
+
{
|
|
17
|
+
tag: t.tagName,
|
|
18
|
+
color: "var(--cm-tag, oklch(0.74 0.1 158))",
|
|
19
|
+
fontWeight: "600"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
tag: t.attributeName,
|
|
23
|
+
color: "var(--cm-attribute-name, oklch(0.76 0.09 80))"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
tag: [t.attributeValue, t.string],
|
|
27
|
+
color: "var(--cm-attribute-value, oklch(0.77 0.1 255))"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
tag: [t.angleBracket, t.bracket],
|
|
31
|
+
color: "var(--cm-punctuation, oklch(0.7 0.02 250))"
|
|
32
|
+
},
|
|
33
|
+
{ tag: t.comment, color: "var(--cm-comment, oklch(0.63 0.01 250))" },
|
|
34
|
+
{
|
|
35
|
+
tag: t.invalid,
|
|
36
|
+
color: "var(--cm-invalid, oklch(0.7 0.16 25))",
|
|
37
|
+
textDecoration: "underline"
|
|
38
|
+
}
|
|
39
|
+
]);
|
|
40
|
+
function ae() {
|
|
41
|
+
const { state: s, setDocument: u } = B(), [o, d] = f(""), [a, i] = f(null), c = g(!1), l = g("");
|
|
42
|
+
p(() => {
|
|
43
|
+
if (c.current) {
|
|
44
|
+
c.current = !1;
|
|
45
|
+
return;
|
|
32
46
|
}
|
|
33
|
-
|
|
47
|
+
const e = b(s.document);
|
|
48
|
+
l.current = e, d(e), i(null);
|
|
49
|
+
}, [s.document]);
|
|
50
|
+
const h = v(
|
|
51
|
+
(e) => {
|
|
52
|
+
try {
|
|
53
|
+
const r = I(e);
|
|
54
|
+
if (r.tagName !== "mjml") {
|
|
55
|
+
i("Invalid MJML: Document must have an <mjml> root element");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
l.current = b(r), c.current = !0, u(r), i(null);
|
|
59
|
+
} catch (r) {
|
|
60
|
+
i(r instanceof Error ? r.message : "Failed to parse MJML");
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[u]
|
|
64
|
+
), x = y(
|
|
34
65
|
() => [
|
|
35
|
-
W(),
|
|
36
|
-
A(),
|
|
37
|
-
C(),
|
|
38
|
-
J(),
|
|
39
|
-
E.tabSize.of(2),
|
|
40
|
-
k.of(" "),
|
|
41
66
|
w(),
|
|
67
|
+
L(),
|
|
68
|
+
D(),
|
|
69
|
+
z(),
|
|
70
|
+
N.tabSize.of(2),
|
|
71
|
+
M.of(" "),
|
|
72
|
+
E(),
|
|
42
73
|
T(),
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
74
|
+
j(V),
|
|
75
|
+
W.lineWrapping,
|
|
76
|
+
R.of([
|
|
77
|
+
A,
|
|
47
78
|
...H,
|
|
48
|
-
...
|
|
79
|
+
...K,
|
|
49
80
|
...U
|
|
50
81
|
]),
|
|
51
|
-
|
|
82
|
+
_()
|
|
52
83
|
],
|
|
53
84
|
[]
|
|
54
|
-
),
|
|
55
|
-
|
|
56
|
-
}, [])
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}, [
|
|
65
|
-
|
|
66
|
-
o?.(!1);
|
|
67
|
-
},
|
|
68
|
-
[o]
|
|
69
|
-
), /* @__PURE__ */ s(
|
|
70
|
-
V,
|
|
85
|
+
), S = v((e) => {
|
|
86
|
+
d(e), i(null);
|
|
87
|
+
}, []);
|
|
88
|
+
return p(() => {
|
|
89
|
+
if (o === l.current)
|
|
90
|
+
return;
|
|
91
|
+
const e = window.setTimeout(() => {
|
|
92
|
+
h(o);
|
|
93
|
+
}, P);
|
|
94
|
+
return () => window.clearTimeout(e);
|
|
95
|
+
}, [o, h]), /* @__PURE__ */ m(
|
|
96
|
+
J,
|
|
71
97
|
{
|
|
72
98
|
defaultLeftWidth: 50,
|
|
73
99
|
minLeftWidth: 30,
|
|
74
100
|
maxLeftWidth: 70,
|
|
75
101
|
children: [
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
|
|
79
|
-
/* @__PURE__ */ t("p", { className: "text-sm", children: "You are editing the raw MJML source. Changes may affect the visual editor." })
|
|
80
|
-
] }) }),
|
|
81
|
-
/* @__PURE__ */ t("div", { className: "flex-1 min-h-0 p-4", onKeyDown: v, children: /* @__PURE__ */ t(
|
|
82
|
-
j,
|
|
102
|
+
/* @__PURE__ */ m("div", { className: "flex flex-col h-full bg-background", children: [
|
|
103
|
+
/* @__PURE__ */ n("div", { className: "flex-1 min-h-0 p-4", children: /* @__PURE__ */ n(
|
|
104
|
+
k,
|
|
83
105
|
{
|
|
84
|
-
value:
|
|
85
|
-
onChange:
|
|
106
|
+
value: o,
|
|
107
|
+
onChange: S,
|
|
86
108
|
extensions: x,
|
|
87
109
|
basicSetup: !1,
|
|
88
110
|
theme: "none",
|
|
@@ -91,25 +113,16 @@ function se({ onApply: f, onDirtyChange: o }) {
|
|
|
91
113
|
spellCheck: !1
|
|
92
114
|
}
|
|
93
115
|
) }),
|
|
94
|
-
/* @__PURE__ */
|
|
95
|
-
/* @__PURE__ */
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
onClick: n,
|
|
99
|
-
disabled: !a,
|
|
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",
|
|
101
|
-
children: "Apply"
|
|
102
|
-
}
|
|
103
|
-
),
|
|
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 })
|
|
116
|
+
/* @__PURE__ */ m("div", { className: "px-4 pb-4 flex items-center gap-3", children: [
|
|
117
|
+
!a && /* @__PURE__ */ n("span", { className: "text-sm text-foreground-muted", children: "Changes sync automatically" }),
|
|
118
|
+
a && /* @__PURE__ */ n("span", { className: "text-sm text-destructive", children: a })
|
|
106
119
|
] })
|
|
107
120
|
] }),
|
|
108
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ n(O, { mjmlSource: o, debounceMs: 300 })
|
|
109
122
|
]
|
|
110
123
|
}
|
|
111
124
|
);
|
|
112
125
|
}
|
|
113
126
|
export {
|
|
114
|
-
|
|
127
|
+
ae as SourceEditor
|
|
115
128
|
};
|
package/dist/components.css
CHANGED
|
@@ -70,9 +70,21 @@
|
|
|
70
70
|
|
|
71
71
|
/* Block states */
|
|
72
72
|
--block-hover: oklch(0.97 0.02 250);
|
|
73
|
-
--block-selected: oklch(0.
|
|
73
|
+
--block-selected: oklch(0.4 0.15 250);
|
|
74
74
|
--block-selected-bg: oklch(0.96 0.02 250);
|
|
75
75
|
|
|
76
|
+
/* Source editor syntax colors */
|
|
77
|
+
--cm-tag: oklch(0.56 0.11 158);
|
|
78
|
+
--cm-attribute-name: oklch(0.53 0.11 78);
|
|
79
|
+
--cm-attribute-value: oklch(0.53 0.11 255);
|
|
80
|
+
--cm-punctuation: oklch(0.46 0.02 250);
|
|
81
|
+
--cm-comment: oklch(0.57 0.01 250);
|
|
82
|
+
--cm-invalid: oklch(0.56 0.2 25);
|
|
83
|
+
--cm-caret: oklch(0.48 0.14 250);
|
|
84
|
+
--cm-selection-bg: color-mix(in oklab, var(--ring) 24%, transparent);
|
|
85
|
+
--cm-selection-fg: var(--foreground);
|
|
86
|
+
--cm-active-line-bg: color-mix(in oklab, var(--ring) 12%, transparent);
|
|
87
|
+
|
|
76
88
|
/* Chart colors */
|
|
77
89
|
--chart-1: oklch(0.646 0.222 41.116);
|
|
78
90
|
--chart-2: oklch(0.6 0.118 184.704);
|
|
@@ -92,10 +104,11 @@
|
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
/* Dark mode at root level (for Radix UI portals that render outside .mjml-editor) */
|
|
95
|
-
:root.dark,
|
|
107
|
+
:root.dark,
|
|
108
|
+
.dark {
|
|
96
109
|
--background: var(--color-zinc-950);
|
|
97
110
|
--background-subtle: var(--color-zinc-900);
|
|
98
|
-
--canvas: oklch(0.
|
|
111
|
+
--canvas: oklch(0.1 0.005 250);
|
|
99
112
|
--surface: var(--color-zinc-800);
|
|
100
113
|
--foreground: var(--color-zinc-50);
|
|
101
114
|
--foreground-muted: var(--color-zinc-400);
|
|
@@ -115,22 +128,32 @@
|
|
|
115
128
|
--border: var(--color-zinc-700);
|
|
116
129
|
--border-subtle: var(--color-zinc-800);
|
|
117
130
|
--border-strong: var(--color-zinc-600);
|
|
118
|
-
--ring: oklch(0.
|
|
131
|
+
--ring: oklch(0.6 0.15 250);
|
|
119
132
|
--selection: oklch(0.25 0.06 250);
|
|
120
133
|
--selection-foreground: oklch(0.75 0.12 250);
|
|
121
134
|
--input: var(--color-zinc-800);
|
|
122
|
-
--destructive: oklch(0.
|
|
135
|
+
--destructive: oklch(0.6 0.22 25);
|
|
123
136
|
--inspector: var(--color-zinc-950);
|
|
124
137
|
--inspector-header: var(--color-zinc-900);
|
|
125
138
|
--toolbar: var(--color-zinc-950);
|
|
126
139
|
--toolbar-border: var(--color-zinc-800);
|
|
127
|
-
--block-hover: oklch(0.
|
|
128
|
-
--block-selected: oklch(0.
|
|
140
|
+
--block-hover: oklch(0.2 0.03 250);
|
|
141
|
+
--block-selected: oklch(0.5 0.15 250);
|
|
129
142
|
--block-selected-bg: oklch(0.22 0.04 250);
|
|
130
|
-
--
|
|
143
|
+
--cm-tag: oklch(0.78 0.11 158);
|
|
144
|
+
--cm-attribute-name: oklch(0.81 0.1 80);
|
|
145
|
+
--cm-attribute-value: oklch(0.79 0.11 255);
|
|
146
|
+
--cm-punctuation: oklch(0.72 0.02 250);
|
|
147
|
+
--cm-comment: oklch(0.64 0.01 250);
|
|
148
|
+
--cm-invalid: oklch(0.72 0.17 25);
|
|
149
|
+
--cm-caret: oklch(0.82 0.11 250);
|
|
150
|
+
--cm-selection-bg: color-mix(in oklab, var(--ring) 44%, transparent);
|
|
151
|
+
--cm-selection-fg: var(--foreground);
|
|
152
|
+
--cm-active-line-bg: color-mix(in oklab, var(--ring) 24%, transparent);
|
|
153
|
+
--chart-1: oklch(0.7 0.2 41);
|
|
131
154
|
--chart-2: oklch(0.65 0.12 185);
|
|
132
|
-
--chart-3: oklch(0.
|
|
133
|
-
--chart-4: oklch(0.
|
|
155
|
+
--chart-3: oklch(0.5 0.08 227);
|
|
156
|
+
--chart-4: oklch(0.8 0.17 84);
|
|
134
157
|
--chart-5: oklch(0.75 0.17 70);
|
|
135
158
|
--sidebar: var(--color-zinc-950);
|
|
136
159
|
--sidebar-foreground: var(--color-zinc-50);
|
|
@@ -139,7 +162,7 @@
|
|
|
139
162
|
--sidebar-accent: var(--color-zinc-800);
|
|
140
163
|
--sidebar-accent-foreground: var(--color-zinc-200);
|
|
141
164
|
--sidebar-border: var(--color-zinc-800);
|
|
142
|
-
--sidebar-ring: oklch(0.
|
|
165
|
+
--sidebar-ring: oklch(0.6 0.15 250);
|
|
143
166
|
}
|
|
144
167
|
|
|
145
168
|
/* Scoped CSS variable defaults - applied to .mjml-editor container */
|
|
@@ -203,9 +226,21 @@
|
|
|
203
226
|
|
|
204
227
|
/* Block states */
|
|
205
228
|
--block-hover: oklch(0.97 0.02 250);
|
|
206
|
-
--block-selected: oklch(0.
|
|
229
|
+
--block-selected: oklch(0.4 0.15 250);
|
|
207
230
|
--block-selected-bg: oklch(0.96 0.02 250);
|
|
208
231
|
|
|
232
|
+
/* Source editor syntax colors */
|
|
233
|
+
--cm-tag: oklch(0.56 0.11 158);
|
|
234
|
+
--cm-attribute-name: oklch(0.53 0.11 78);
|
|
235
|
+
--cm-attribute-value: oklch(0.53 0.11 255);
|
|
236
|
+
--cm-punctuation: oklch(0.46 0.02 250);
|
|
237
|
+
--cm-comment: oklch(0.57 0.01 250);
|
|
238
|
+
--cm-invalid: oklch(0.56 0.2 25);
|
|
239
|
+
--cm-caret: oklch(0.48 0.14 250);
|
|
240
|
+
--cm-selection-bg: color-mix(in oklab, var(--ring) 24%, transparent);
|
|
241
|
+
--cm-selection-fg: var(--foreground);
|
|
242
|
+
--cm-active-line-bg: color-mix(in oklab, var(--ring) 12%, transparent);
|
|
243
|
+
|
|
209
244
|
/* Chart colors */
|
|
210
245
|
--chart-1: oklch(0.646 0.222 41.116);
|
|
211
246
|
--chart-2: oklch(0.6 0.118 184.704);
|
|
@@ -230,7 +265,7 @@
|
|
|
230
265
|
/* Core backgrounds */
|
|
231
266
|
--background: var(--color-zinc-950);
|
|
232
267
|
--background-subtle: var(--color-zinc-900);
|
|
233
|
-
--canvas: oklch(0.
|
|
268
|
+
--canvas: oklch(0.1 0.005 250);
|
|
234
269
|
--surface: var(--color-zinc-800);
|
|
235
270
|
|
|
236
271
|
/* Foregrounds */
|
|
@@ -266,13 +301,13 @@
|
|
|
266
301
|
--border-strong: var(--color-zinc-600);
|
|
267
302
|
|
|
268
303
|
/* Focus/Selection */
|
|
269
|
-
--ring: oklch(0.
|
|
304
|
+
--ring: oklch(0.6 0.15 250);
|
|
270
305
|
--selection: oklch(0.25 0.06 250);
|
|
271
306
|
--selection-foreground: oklch(0.75 0.12 250);
|
|
272
307
|
--input: var(--color-zinc-800);
|
|
273
308
|
|
|
274
309
|
/* Destructive */
|
|
275
|
-
--destructive: oklch(0.
|
|
310
|
+
--destructive: oklch(0.6 0.22 25);
|
|
276
311
|
|
|
277
312
|
/* Inspector */
|
|
278
313
|
--inspector: var(--color-zinc-950);
|
|
@@ -283,15 +318,27 @@
|
|
|
283
318
|
--toolbar-border: var(--color-zinc-800);
|
|
284
319
|
|
|
285
320
|
/* Block states */
|
|
286
|
-
--block-hover: oklch(0.
|
|
287
|
-
--block-selected: oklch(0.
|
|
321
|
+
--block-hover: oklch(0.2 0.03 250);
|
|
322
|
+
--block-selected: oklch(0.5 0.15 250);
|
|
288
323
|
--block-selected-bg: oklch(0.22 0.04 250);
|
|
289
324
|
|
|
325
|
+
/* Source editor syntax colors */
|
|
326
|
+
--cm-tag: oklch(0.78 0.11 158);
|
|
327
|
+
--cm-attribute-name: oklch(0.81 0.1 80);
|
|
328
|
+
--cm-attribute-value: oklch(0.79 0.11 255);
|
|
329
|
+
--cm-punctuation: oklch(0.72 0.02 250);
|
|
330
|
+
--cm-comment: oklch(0.64 0.01 250);
|
|
331
|
+
--cm-invalid: oklch(0.72 0.17 25);
|
|
332
|
+
--cm-caret: oklch(0.82 0.11 250);
|
|
333
|
+
--cm-selection-bg: color-mix(in oklab, var(--ring) 44%, transparent);
|
|
334
|
+
--cm-selection-fg: var(--foreground);
|
|
335
|
+
--cm-active-line-bg: color-mix(in oklab, var(--ring) 24%, transparent);
|
|
336
|
+
|
|
290
337
|
/* Chart colors - adjusted for dark bg */
|
|
291
|
-
--chart-1: oklch(0.
|
|
338
|
+
--chart-1: oklch(0.7 0.2 41);
|
|
292
339
|
--chart-2: oklch(0.65 0.12 185);
|
|
293
|
-
--chart-3: oklch(0.
|
|
294
|
-
--chart-4: oklch(0.
|
|
340
|
+
--chart-3: oklch(0.5 0.08 227);
|
|
341
|
+
--chart-4: oklch(0.8 0.17 84);
|
|
295
342
|
--chart-5: oklch(0.75 0.17 70);
|
|
296
343
|
|
|
297
344
|
/* Sidebar */
|
|
@@ -302,7 +349,7 @@
|
|
|
302
349
|
--sidebar-accent: var(--color-zinc-800);
|
|
303
350
|
--sidebar-accent-foreground: var(--color-zinc-200);
|
|
304
351
|
--sidebar-border: var(--color-zinc-800);
|
|
305
|
-
--sidebar-ring: oklch(0.
|
|
352
|
+
--sidebar-ring: oklch(0.6 0.15 250);
|
|
306
353
|
}
|
|
307
354
|
|
|
308
355
|
/* Force light mode within .light scope (e.g., email canvas preview) */
|
|
@@ -336,23 +383,42 @@
|
|
|
336
383
|
--input: var(--color-zinc-300);
|
|
337
384
|
--destructive: oklch(0.55 0.22 25);
|
|
338
385
|
--block-hover: oklch(0.97 0.02 250);
|
|
339
|
-
--block-selected: oklch(0.
|
|
386
|
+
--block-selected: oklch(0.4 0.15 250);
|
|
340
387
|
--block-selected-bg: oklch(0.96 0.02 250);
|
|
341
388
|
}
|
|
342
389
|
|
|
343
390
|
/* Source editor (CodeMirror) */
|
|
344
391
|
.mjml-editor .source-editor .cm-editor {
|
|
345
392
|
height: 100%;
|
|
346
|
-
background: var(--muted);
|
|
393
|
+
background: color-mix(in oklab, var(--muted) 78%, var(--background) 22%);
|
|
347
394
|
color: var(--foreground);
|
|
348
|
-
font-family:
|
|
349
|
-
|
|
350
|
-
|
|
395
|
+
font-family:
|
|
396
|
+
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
|
397
|
+
'Courier New', monospace;
|
|
398
|
+
font-size: 14px;
|
|
399
|
+
line-height: 1.6;
|
|
351
400
|
}
|
|
352
401
|
|
|
353
402
|
.mjml-editor .source-editor .cm-scroller {
|
|
354
403
|
font-family: inherit;
|
|
355
|
-
line-height:
|
|
404
|
+
line-height: inherit;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.mjml-editor .source-editor .cm-content,
|
|
408
|
+
.mjml-editor .source-editor .cm-gutterElement {
|
|
409
|
+
font-family: inherit;
|
|
410
|
+
font-size: inherit;
|
|
411
|
+
letter-spacing: normal;
|
|
412
|
+
font-variant-ligatures: none;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.mjml-editor .source-editor .cm-content {
|
|
416
|
+
caret-color: var(--cm-caret);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.mjml-editor .source-editor .cm-line {
|
|
420
|
+
overflow-wrap: normal;
|
|
421
|
+
word-break: normal;
|
|
356
422
|
}
|
|
357
423
|
|
|
358
424
|
.mjml-editor .source-editor .cm-focused {
|
|
@@ -367,12 +433,16 @@
|
|
|
367
433
|
|
|
368
434
|
.mjml-editor .source-editor .cm-activeLine,
|
|
369
435
|
.mjml-editor .source-editor .cm-activeLineGutter {
|
|
370
|
-
background:
|
|
436
|
+
background: var(--cm-active-line-bg);
|
|
371
437
|
}
|
|
372
438
|
|
|
373
439
|
.mjml-editor .source-editor .cm-selectionBackground,
|
|
374
440
|
.mjml-editor .source-editor .cm-content ::selection {
|
|
375
|
-
background: var(--selection);
|
|
441
|
+
background: var(--cm-selection-bg);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.mjml-editor .source-editor .cm-content ::selection {
|
|
445
|
+
color: var(--cm-selection-fg);
|
|
376
446
|
}
|
|
377
447
|
|
|
378
448
|
.mjml-editor .source-editor .cm-searchMatch {
|
|
@@ -389,8 +459,9 @@
|
|
|
389
459
|
outline: 1px solid var(--border-strong);
|
|
390
460
|
}
|
|
391
461
|
|
|
392
|
-
.mjml-editor .source-editor .cm-cursor
|
|
393
|
-
|
|
462
|
+
.mjml-editor .source-editor .cm-cursor,
|
|
463
|
+
.mjml-editor .source-editor .cm-dropCursor {
|
|
464
|
+
border-left-color: var(--cm-caret) !important;
|
|
394
465
|
}
|
|
395
466
|
|
|
396
467
|
/* Tiptap Editor Styles */
|