@savvycal/mjml-editor 0.0.2 → 0.0.3

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 CHANGED
@@ -35,26 +35,41 @@ pnpm add @savvycal/mjml-editor
35
35
 
36
36
  ### Peer Dependencies
37
37
 
38
- This library requires React 18 or 19:
38
+ This library requires React 18+ and Tailwind CSS v4:
39
39
 
40
40
  ```bash
41
- npm install react react-dom
41
+ npm install react react-dom tailwindcss @tailwindcss/vite tw-animate-css
42
42
  ```
43
43
 
44
44
  ### Styles
45
45
 
46
- You must import the library's CSS in your application:
46
+ This library is designed to work with Tailwind CSS v4. Instead of bundling all styles, the library exports CSS files that integrate with your app's Tailwind build, ensuring no style conflicts and minimal CSS overhead.
47
47
 
48
- ```tsx
49
- import '@savvycal/mjml-editor/styles.css';
48
+ Add the following imports to your app's main CSS file:
49
+
50
+ ```css
51
+ @import "@savvycal/mjml-editor/preset.css";
52
+ @import "tailwindcss";
53
+ @import "tw-animate-css";
54
+ @import "@savvycal/mjml-editor/components.css";
50
55
  ```
51
56
 
57
+ **Note:** `preset.css` must come before `tailwindcss` so that `@theme` tokens are registered before Tailwind generates its utilities.
58
+
59
+ The `preset.css` file includes:
60
+ - `@source` directive that tells Tailwind to scan the library's dist files for utility classes (works with npm, yarn, and pnpm)
61
+ - `@theme` tokens that map CSS variables to Tailwind utilities
62
+ - Custom utilities (`bg-checkered`, `shadow-framer`, etc.)
63
+
64
+ The `components.css` file includes:
65
+ - Scoped CSS variables for the editor theme (light/dark mode)
66
+ - Tiptap/ProseMirror editor styles
67
+
52
68
  ## Usage
53
69
 
54
70
  ```tsx
55
71
  import { useState } from 'react';
56
72
  import { MjmlEditor } from '@savvycal/mjml-editor';
57
- import '@savvycal/mjml-editor/styles.css';
58
73
 
59
74
  function App() {
60
75
  const [mjml, setMjml] = useState(initialMjml);
@@ -77,6 +92,7 @@ function App() {
77
92
  | `className` | `string` | Optional CSS class for the container |
78
93
  | `defaultTheme` | `'light' \| 'dark' \| 'system'` | Theme preference (default: `'system'`) |
79
94
  | `liquidSchema` | `LiquidSchema` | Optional schema for Liquid template autocomplete |
95
+ | `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`) |
80
96
 
81
97
  ## Liquid Template Support
82
98
 
@@ -84,7 +100,6 @@ The editor provides autocomplete for Liquid template variables and tags. Pass a
84
100
 
85
101
  ```tsx
86
102
  import { MjmlEditor, type LiquidSchema } from '@savvycal/mjml-editor';
87
- import '@savvycal/mjml-editor/styles.css';
88
103
 
89
104
  const liquidSchema: LiquidSchema = {
90
105
  variables: [
@@ -1 +1 @@
1
- {"version":3,"file":"InteractivePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/InteractivePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,uBAAuB;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAID,wBAAgB,kBAAkB,CAAC,EACjC,UAAiB,EACjB,WAAuB,GACxB,EAAE,uBAAuB,2CAoKzB"}
1
+ {"version":3,"file":"InteractivePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/InteractivePreview.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,UAAU,uBAAuB;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAID,wBAAgB,kBAAkB,CAAC,EACjC,UAAiB,EACjB,WAAuB,GACxB,EAAE,uBAAuB,2CAkLzB"}
@@ -1,27 +1,37 @@
1
- import { jsxs as o, jsx as l } from "react/jsx-runtime";
2
- import { useRef as x, useState as v, useEffect as i, useMemo as k, useCallback as w } from "react";
3
- import { useEditor as E } from "../../context/EditorContext.js";
4
- import { renderMjmlInteractive as y } from "../../lib/mjml/renderer.js";
5
- const C = 375;
6
- function B({
7
- showHeader: h = !0,
8
- previewMode: p = "desktop"
1
+ import { jsxs as r, jsx as s } from "react/jsx-runtime";
2
+ import { useRef as w, useState as b, useEffect as c, useCallback as E } from "react";
3
+ import { useEditor as y } from "../../context/EditorContext.js";
4
+ import { renderMjmlInteractive as C } from "../../lib/mjml/renderer.js";
5
+ const I = 375;
6
+ function S({
7
+ showHeader: p = !0,
8
+ previewMode: g = "desktop"
9
9
  }) {
10
- const { state: n, selectBlock: d } = E(), r = x(null), [m, g] = v(n.document);
11
- i(() => {
10
+ const { state: n, selectBlock: d } = y(), o = w(null), [m, x] = b(n.document), [v, k] = b({
11
+ html: "",
12
+ errors: []
13
+ });
14
+ c(() => {
12
15
  const e = setTimeout(() => {
13
- g(n.document);
16
+ x(n.document);
14
17
  }, 300);
15
18
  return () => clearTimeout(e);
16
- }, [n.document]);
17
- const { html: u, errors: s } = k(() => y(m), [m]), a = w(
19
+ }, [n.document]), c(() => {
20
+ let e = !1;
21
+ return C(m).then((t) => {
22
+ e || k(t);
23
+ }), () => {
24
+ e = !0;
25
+ };
26
+ }, [m]);
27
+ const { html: u, errors: l } = v, i = E(
18
28
  (e) => {
19
29
  e.data?.type === "BLOCK_SELECTED" && d(e.data.blockId);
20
30
  },
21
31
  [d]
22
32
  );
23
- i(() => (window.addEventListener("message", a), () => window.removeEventListener("message", a)), [a]);
24
- const c = n.selectedBlockId ? `.block-${n.selectedBlockId} { outline: 2px solid #6366f1 !important; outline-offset: -2px; position: relative; }` : "", f = `
33
+ c(() => (window.addEventListener("message", i), () => window.removeEventListener("message", i)), [i]);
34
+ const a = n.selectedBlockId ? `.block-${n.selectedBlockId} { outline: 2px solid #6366f1 !important; outline-offset: -2px; position: relative; }` : "", f = `
25
35
  (function() {
26
36
  document.addEventListener('click', function(e) {
27
37
  e.preventDefault();
@@ -50,15 +60,15 @@ function B({
50
60
  }, true);
51
61
  })();
52
62
  `;
53
- return i(() => {
54
- if (r.current) {
55
- const e = r.current.contentDocument;
63
+ return c(() => {
64
+ if (o.current) {
65
+ const e = o.current.contentDocument;
56
66
  if (e) {
57
67
  e.open(), e.write(u), e.close();
58
68
  const t = e.createElement("script");
59
69
  t.textContent = f, e.body.appendChild(t);
60
- const b = e.createElement("style");
61
- b.textContent = `
70
+ const h = e.createElement("style");
71
+ h.textContent = `
62
72
  [class*="block-"] {
63
73
  cursor: pointer;
64
74
  transition: outline 0.15s ease;
@@ -67,44 +77,44 @@ function B({
67
77
  outline: 1px dashed #94a3b8 !important;
68
78
  outline-offset: -1px;
69
79
  }
70
- ${c}
71
- `, e.head.appendChild(b);
80
+ ${a}
81
+ `, e.head.appendChild(h);
72
82
  }
73
83
  }
74
- }, [u, c, f]), i(() => {
75
- if (r.current) {
76
- const e = r.current.contentDocument;
84
+ }, [u, a, f]), c(() => {
85
+ if (o.current) {
86
+ const e = o.current.contentDocument;
77
87
  if (e) {
78
88
  let t = e.getElementById("selection-highlight");
79
- t || (t = e.createElement("style"), t.id = "selection-highlight", e.head.appendChild(t)), t.textContent = c;
89
+ t || (t = e.createElement("style"), t.id = "selection-highlight", e.head.appendChild(t)), t.textContent = a;
80
90
  }
81
91
  }
82
- }, [n.selectedBlockId, c]), /* @__PURE__ */ o("div", { className: "flex flex-col h-full", children: [
83
- h && /* @__PURE__ */ o("div", { className: "h-11 px-4 flex items-center justify-between border-b border-border bg-background", children: [
84
- /* @__PURE__ */ l("span", { className: "text-sm font-semibold text-foreground", children: "Preview" }),
85
- s.length > 0 && /* @__PURE__ */ o("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-0.5 rounded-md", children: [
86
- s.length,
92
+ }, [n.selectedBlockId, a]), /* @__PURE__ */ r("div", { className: "flex flex-col h-full", children: [
93
+ p && /* @__PURE__ */ r("div", { className: "h-11 px-4 flex items-center justify-between border-b border-border bg-background", children: [
94
+ /* @__PURE__ */ s("span", { className: "text-sm font-semibold text-foreground", children: "Preview" }),
95
+ l.length > 0 && /* @__PURE__ */ r("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-0.5 rounded-md", children: [
96
+ l.length,
87
97
  " warning",
88
- s.length !== 1 ? "s" : ""
98
+ l.length !== 1 ? "s" : ""
89
99
  ] })
90
100
  ] }),
91
- /* @__PURE__ */ l("div", { className: "flex-1 overflow-auto bg-muted flex justify-center", children: /* @__PURE__ */ l(
101
+ /* @__PURE__ */ s("div", { className: "flex-1 overflow-auto bg-muted flex justify-center", children: /* @__PURE__ */ s(
92
102
  "iframe",
93
103
  {
94
- ref: r,
104
+ ref: o,
95
105
  title: "Email Preview",
96
106
  className: "h-full border-0 bg-white transition-all duration-200",
97
107
  style: {
98
- width: p === "mobile" ? C : "100%",
108
+ width: g === "mobile" ? I : "100%",
99
109
  maxWidth: "100%"
100
110
  },
101
111
  sandbox: "allow-same-origin allow-scripts"
102
112
  }
103
113
  ) }),
104
- s.length > 0 && /* @__PURE__ */ o("div", { className: "max-h-28 overflow-auto border-t border-border bg-amber-50/50 p-3", children: [
105
- /* @__PURE__ */ l("div", { className: "text-xs font-semibold text-amber-700 mb-2", children: "Warnings" }),
106
- /* @__PURE__ */ l("div", { className: "space-y-1", children: s.map((e, t) => /* @__PURE__ */ o("div", { className: "text-xs text-amber-600", children: [
107
- /* @__PURE__ */ o("span", { className: "font-mono", children: [
114
+ l.length > 0 && /* @__PURE__ */ r("div", { className: "max-h-28 overflow-auto border-t border-border bg-amber-50/50 p-3", children: [
115
+ /* @__PURE__ */ s("div", { className: "text-xs font-semibold text-amber-700 mb-2", children: "Warnings" }),
116
+ /* @__PURE__ */ s("div", { className: "space-y-1", children: l.map((e, t) => /* @__PURE__ */ r("div", { className: "text-xs text-amber-600", children: [
117
+ /* @__PURE__ */ r("span", { className: "font-mono", children: [
108
118
  "Line ",
109
119
  e.line,
110
120
  ":"
@@ -116,5 +126,5 @@ function B({
116
126
  ] });
117
127
  }
118
128
  export {
119
- B as InteractivePreview
129
+ S as InteractivePreview
120
130
  };
@@ -5,7 +5,18 @@ interface MjmlEditorProps {
5
5
  className?: string;
6
6
  defaultTheme?: 'light' | 'dark' | 'system';
7
7
  liquidSchema?: LiquidSchema;
8
+ /**
9
+ * Whether to apply the theme class to document.documentElement.
10
+ * This is needed for Radix UI portals (popovers, menus, etc.) which
11
+ * render outside the editor container.
12
+ *
13
+ * Set to false if your app manages document-level theme classes
14
+ * and you want to prevent conflicts.
15
+ *
16
+ * @default true
17
+ */
18
+ applyThemeToDocument?: boolean;
8
19
  }
9
- export declare function MjmlEditor({ value, onChange, className, defaultTheme, liquidSchema, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function MjmlEditor({ value, onChange, className, defaultTheme, liquidSchema, applyThemeToDocument, }: MjmlEditorProps): import("react/jsx-runtime").JSX.Element;
10
21
  export {};
11
22
  //# 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,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;CAC7B;AA4HD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAuB,EACvB,YAAY,GACb,EAAE,eAAe,2CAuBjB"}
1
+ {"version":3,"file":"MjmlEditor.d.ts","sourceRoot":"","sources":["../../../src/components/editor/MjmlEditor.tsx"],"names":[],"mappings":"AAeA,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;;;;;;;;;OASG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAuJD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,SAAS,EACT,YAAuB,EACvB,YAAY,EACZ,oBAA2B,GAC5B,EAAE,eAAe,2CAuCjB"}
@@ -1,111 +1,137 @@
1
- import { jsx as n, jsxs as v, Fragment as I } from "react/jsx-runtime";
2
- import { useState as f, useCallback as P, useEffect as h } from "react";
3
- import { EditorProvider as w, useEditor as L } from "../../context/EditorContext.js";
4
- import { ThemeProvider as b } from "../../context/ThemeContext.js";
5
- import { LiquidSchemaProvider as M } from "../../context/LiquidSchemaContext.js";
6
- import { OutlineTree as D, GLOBAL_STYLES_ID as y } from "./OutlineTree.js";
7
- import { EditorCanvas as O } from "./EditorCanvas.js";
8
- import { BlockInspector as j } from "./BlockInspector.js";
9
- import { GlobalStylesPanel as C } from "./GlobalStylesPanel.js";
1
+ import { jsx as t, jsxs as T, Fragment as I } from "react/jsx-runtime";
2
+ import { useState as d, useEffect as c, useCallback as b, useRef as P } from "react";
3
+ import { EditorProvider as L, useEditor as D } from "../../context/EditorContext.js";
4
+ import { ThemeProvider as M, useTheme as O } from "../../context/ThemeContext.js";
5
+ import { LiquidSchemaProvider as j } from "../../context/LiquidSchemaContext.js";
6
+ import { OutlineTree as x, GLOBAL_STYLES_ID as y } from "./OutlineTree.js";
7
+ import { EditorCanvas as C } from "./EditorCanvas.js";
8
+ import { BlockInspector as S } from "./BlockInspector.js";
9
+ import { GlobalStylesPanel as K } from "./GlobalStylesPanel.js";
10
10
  import { FloatingPanel as E } from "../ui/floating-panel.js";
11
- import { createEmptyDocument as T, parseMjml as S, serializeMjml as x } from "../../lib/mjml/parser.js";
12
- function K(r) {
13
- if (!r || r.trim() === "")
14
- return T();
11
+ import { createEmptyDocument as w, parseMjml as R, serializeMjml as A } from "../../lib/mjml/parser.js";
12
+ function F(n) {
13
+ if (!n || n.trim() === "")
14
+ return w();
15
15
  try {
16
- return S(r);
16
+ return R(n);
17
17
  } catch (e) {
18
- return console.error("Failed to parse MJML:", e), T();
18
+ return console.error("Failed to parse MJML:", e), w();
19
19
  }
20
20
  }
21
- function A({ onChange: r }) {
22
- const { state: e, undo: l, redo: i, canUndo: c, canRedo: a, deleteBlock: d, selectBlock: s } = L(), [u, g] = f(!0), [p, m] = f(!0), [k, B] = f("edit");
23
- return h(() => {
24
- e.selectedBlockId && m(!0);
25
- }, [e.selectedBlockId]), h(() => {
26
- const o = x(e.document);
27
- console.log(`MJML markup updated:
28
- `, o), r(o);
29
- }, [e.document, r]), h(() => {
30
- const o = (t) => {
31
- if (!(t.target instanceof HTMLInputElement || t.target instanceof HTMLTextAreaElement || t.target?.isContentEditable)) {
32
- if ((t.metaKey || t.ctrlKey) && t.key === "z") {
33
- t.preventDefault(), t.shiftKey ? a && i() : c && l();
21
+ function N({
22
+ className: n,
23
+ children: e
24
+ }) {
25
+ const { resolvedTheme: o } = O();
26
+ return /* @__PURE__ */ t(
27
+ "div",
28
+ {
29
+ className: `mjml-editor ${o} relative h-full w-full overflow-hidden bg-background text-foreground antialiased ${n || ""}`,
30
+ children: e
31
+ }
32
+ );
33
+ }
34
+ function $({ onChange: n }) {
35
+ const { state: e, undo: o, redo: s, canUndo: a, canRedo: u, deleteBlock: m, selectBlock: f } = D(), [l, p] = d(!0), [i, h] = d(!0), [k, B] = d("edit"), v = P(n);
36
+ return c(() => {
37
+ v.current = n;
38
+ }, [n]), c(() => {
39
+ e.selectedBlockId && h(!0);
40
+ }, [e.selectedBlockId]), c(() => {
41
+ const g = A(e.document);
42
+ v.current(g);
43
+ }, [e.document]), c(() => {
44
+ const g = (r) => {
45
+ if (!(r.target instanceof HTMLInputElement || r.target instanceof HTMLTextAreaElement || r.target?.isContentEditable)) {
46
+ if ((r.metaKey || r.ctrlKey) && r.key === "z") {
47
+ r.preventDefault(), r.shiftKey ? u && s() : a && o();
34
48
  return;
35
49
  }
36
- if ((t.key === "Delete" || t.key === "Backspace") && e.selectedBlockId && e.selectedBlockId !== y) {
37
- t.preventDefault(), d(e.selectedBlockId);
50
+ if ((r.key === "Delete" || r.key === "Backspace") && e.selectedBlockId && e.selectedBlockId !== y) {
51
+ r.preventDefault(), m(e.selectedBlockId);
38
52
  return;
39
53
  }
40
- if (t.key === "Escape" && e.selectedBlockId) {
41
- t.preventDefault(), s(null);
54
+ if (r.key === "Escape" && e.selectedBlockId) {
55
+ r.preventDefault(), f(null);
42
56
  return;
43
57
  }
44
58
  }
45
59
  };
46
- return window.addEventListener("keydown", o), () => window.removeEventListener("keydown", o);
60
+ return window.addEventListener("keydown", g), () => window.removeEventListener("keydown", g);
47
61
  }, [
48
- l,
49
- i,
50
- c,
51
- a,
52
- d,
62
+ o,
53
63
  s,
64
+ a,
65
+ u,
66
+ m,
67
+ f,
54
68
  e.selectedBlockId
55
- ]), /* @__PURE__ */ v("div", { className: "relative h-full overflow-hidden", children: [
56
- /* @__PURE__ */ n("div", { className: "absolute inset-0 bg-canvas", children: /* @__PURE__ */ n(
57
- O,
69
+ ]), /* @__PURE__ */ T("div", { className: "relative h-full overflow-hidden", children: [
70
+ /* @__PURE__ */ t("div", { className: "absolute inset-0 bg-canvas", children: /* @__PURE__ */ t(
71
+ C,
58
72
  {
59
73
  activeTab: k,
60
74
  onTabChange: B,
61
- leftPanelOpen: u,
62
- rightPanelOpen: p
75
+ leftPanelOpen: l,
76
+ rightPanelOpen: i
63
77
  }
64
78
  ) }),
65
- k === "edit" && /* @__PURE__ */ v(I, { children: [
66
- /* @__PURE__ */ n(
79
+ k === "edit" && /* @__PURE__ */ T(I, { children: [
80
+ /* @__PURE__ */ t(
67
81
  E,
68
82
  {
69
83
  side: "left",
70
- isOpen: u,
71
- onToggle: () => g(!u),
84
+ isOpen: l,
85
+ onToggle: () => p(!l),
72
86
  width: 256,
73
- children: /* @__PURE__ */ n(D, { onTogglePanel: () => g(!1) })
87
+ children: /* @__PURE__ */ t(x, { onTogglePanel: () => p(!1) })
74
88
  }
75
89
  ),
76
- /* @__PURE__ */ n(
90
+ /* @__PURE__ */ t(
77
91
  E,
78
92
  {
79
93
  side: "right",
80
- isOpen: p,
81
- onToggle: () => m(!p),
94
+ isOpen: i,
95
+ onToggle: () => h(!i),
82
96
  width: 300,
83
- children: e.selectedBlockId === y ? /* @__PURE__ */ n(
84
- C,
97
+ children: e.selectedBlockId === y ? /* @__PURE__ */ t(
98
+ K,
85
99
  {
86
- onTogglePanel: () => m(!1)
100
+ onTogglePanel: () => h(!1)
87
101
  }
88
- ) : /* @__PURE__ */ n(j, { onTogglePanel: () => m(!1) })
102
+ ) : /* @__PURE__ */ t(S, { onTogglePanel: () => h(!1) })
89
103
  }
90
104
  )
91
105
  ] })
92
106
  ] });
93
107
  }
94
- function Y({
95
- value: r,
108
+ function X({
109
+ value: n,
96
110
  onChange: e,
97
- className: l,
98
- defaultTheme: i = "system",
99
- liquidSchema: c
111
+ className: o,
112
+ defaultTheme: s = "system",
113
+ liquidSchema: a,
114
+ applyThemeToDocument: u = !0
100
115
  }) {
101
- const [a] = f(() => K(r)), d = P(
102
- (s) => {
103
- e(s);
116
+ const [m, f] = d(!1);
117
+ c(() => {
118
+ f(!0);
119
+ }, []);
120
+ const [l] = d(() => F(n)), p = b(
121
+ (i) => {
122
+ e(i);
104
123
  },
105
124
  [e]
106
125
  );
107
- return /* @__PURE__ */ n(b, { defaultTheme: i, children: /* @__PURE__ */ n(M, { schema: c, children: /* @__PURE__ */ n("div", { className: `h-full w-full bg-background ${l || ""}`, children: /* @__PURE__ */ n(w, { initialDocument: a, children: /* @__PURE__ */ n(A, { onChange: d }) }) }) }) });
126
+ return m ? /* @__PURE__ */ t(
127
+ M,
128
+ {
129
+ defaultTheme: s,
130
+ applyToDocument: u,
131
+ children: /* @__PURE__ */ t(j, { schema: a, children: /* @__PURE__ */ t(N, { className: o, children: /* @__PURE__ */ t(L, { initialDocument: l, children: /* @__PURE__ */ t($, { onChange: p }) }) }) })
132
+ }
133
+ ) : /* @__PURE__ */ t("div", { className: `h-full w-full bg-background ${o || ""}` });
108
134
  }
109
135
  export {
110
- Y as MjmlEditor
136
+ X as MjmlEditor
111
137
  };
@@ -42,7 +42,7 @@ function E() {
42
42
  {
43
43
  value: a,
44
44
  onChange: (e) => f(e.target.value),
45
- className: "w-full h-full p-4 font-mono text-sm bg-muted border border-border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-ring",
45
+ className: "w-full h-full p-4 font-mono text-sm bg-muted text-foreground border border-border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-ring",
46
46
  spellCheck: !1
47
47
  }
48
48
  ) }),
@@ -1 +1 @@
1
- {"version":3,"file":"SourcePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourcePreview.tsx"],"names":[],"mappings":"AAGA,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAgB,GACjB,EAAE,kBAAkB,2CAkEpB"}
1
+ {"version":3,"file":"SourcePreview.d.ts","sourceRoot":"","sources":["../../../src/components/editor/SourcePreview.tsx"],"names":[],"mappings":"AAGA,UAAU,kBAAkB;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAC5B,UAAU,EACV,UAAgB,GACjB,EAAE,kBAAkB,2CAkFpB"}
@@ -1,24 +1,38 @@
1
1
  import { jsxs as r, jsx as n } from "react/jsx-runtime";
2
- import { useRef as f, useState as u, useEffect as m, useMemo as b } from "react";
2
+ import { useRef as h, useState as f, useEffect as a } from "react";
3
3
  import { renderMjmlString as x } from "../../lib/mjml/renderer.js";
4
- function v({
4
+ function w({
5
5
  mjmlSource: s,
6
- debounceMs: i = 300
6
+ debounceMs: c = 300
7
7
  }) {
8
- const o = f(null), [l, c] = u(s);
9
- m(() => {
8
+ const l = h(null), [o, u] = f(s), [b, m] = f({
9
+ html: "",
10
+ errors: []
11
+ });
12
+ a(() => {
10
13
  const e = setTimeout(() => {
11
- c(s);
12
- }, i);
14
+ u(s);
15
+ }, c);
13
16
  return () => clearTimeout(e);
14
- }, [s, i]);
15
- const { html: a, errors: t } = b(() => l.trim() ? x(l) : { html: "", errors: [] }, [l]);
16
- return m(() => {
17
- if (o.current) {
18
- const e = o.current.contentDocument;
19
- e && (e.open(), e.write(a), e.close());
17
+ }, [s, c]), a(() => {
18
+ if (!o.trim()) {
19
+ m({ html: "", errors: [] });
20
+ return;
20
21
  }
21
- }, [a]), /* @__PURE__ */ r("div", { className: "flex flex-col h-full", children: [
22
+ let e = !1;
23
+ return x(o).then((i) => {
24
+ e || m(i);
25
+ }), () => {
26
+ e = !0;
27
+ };
28
+ }, [o]);
29
+ const { html: d, errors: t } = b;
30
+ return a(() => {
31
+ if (l.current) {
32
+ const e = l.current.contentDocument;
33
+ e && (e.open(), e.write(d), e.close());
34
+ }
35
+ }, [d]), /* @__PURE__ */ r("div", { className: "flex flex-col h-full", children: [
22
36
  /* @__PURE__ */ r("div", { className: "h-11 px-4 flex items-center justify-between border-b border-border bg-background", children: [
23
37
  /* @__PURE__ */ n("span", { className: "text-sm font-semibold text-foreground", children: "Preview" }),
24
38
  t.length > 0 && /* @__PURE__ */ r("span", { className: "text-xs font-medium text-amber-600 bg-amber-50 px-2 py-0.5 rounded-md", children: [
@@ -30,7 +44,7 @@ function v({
30
44
  /* @__PURE__ */ n("div", { className: "flex-1 overflow-auto bg-muted", children: /* @__PURE__ */ n(
31
45
  "iframe",
32
46
  {
33
- ref: o,
47
+ ref: l,
34
48
  title: "Source Preview",
35
49
  className: "w-full h-full border-0 bg-white",
36
50
  sandbox: "allow-same-origin"
@@ -38,7 +52,7 @@ function v({
38
52
  ) }),
39
53
  t.length > 0 && /* @__PURE__ */ r("div", { className: "max-h-28 overflow-auto border-t border-border bg-amber-50/50 p-3", children: [
40
54
  /* @__PURE__ */ n("div", { className: "text-xs font-semibold text-amber-700 mb-2", children: "Warnings" }),
41
- /* @__PURE__ */ n("div", { className: "space-y-1", children: t.map((e, d) => /* @__PURE__ */ r("div", { className: "text-xs text-amber-600", children: [
55
+ /* @__PURE__ */ n("div", { className: "space-y-1", children: t.map((e, i) => /* @__PURE__ */ r("div", { className: "text-xs text-amber-600", children: [
42
56
  /* @__PURE__ */ r("span", { className: "font-mono", children: [
43
57
  "Line ",
44
58
  e.line,
@@ -46,10 +60,10 @@ function v({
46
60
  ] }),
47
61
  " ",
48
62
  e.message
49
- ] }, d)) })
63
+ ] }, i)) })
50
64
  ] })
51
65
  ] });
52
66
  }
53
67
  export {
54
- v as SourcePreview
68
+ w as SourcePreview
55
69
  };
@@ -10,7 +10,7 @@ const i = n(
10
10
  default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
11
11
  secondary: "border-transparent bg-muted text-foreground hover:bg-muted/80",
12
12
  destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
13
- outline: "text-foreground"
13
+ outline: "border-border text-foreground"
14
14
  }
15
15
  },
16
16
  defaultVariants: {
@@ -18,8 +18,8 @@ const i = n(
18
18
  }
19
19
  }
20
20
  );
21
- function m({ className: r, variant: t, ...e }) {
22
- return /* @__PURE__ */ o("div", { className: a(i({ variant: t }), r), ...e });
21
+ function m({ className: r, variant: e, ...t }) {
22
+ return /* @__PURE__ */ o("div", { className: a(i({ variant: e }), r), ...t });
23
23
  }
24
24
  export {
25
25
  m as Badge
@@ -10,9 +10,9 @@ const u = a(
10
10
  variant: {
11
11
  default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
12
  destructive: "bg-destructive text-white hover:bg-destructive/90",
13
- outline: "border border-border bg-background hover:bg-accent hover:border-border",
13
+ outline: "border border-border bg-background text-foreground hover:bg-accent hover:border-border",
14
14
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
- ghost: "hover:bg-accent hover:text-accent-foreground",
15
+ ghost: "text-foreground hover:bg-accent hover:text-accent-foreground",
16
16
  link: "text-primary underline-offset-4 hover:underline"
17
17
  },
18
18
  size: {
@@ -1,18 +1,18 @@
1
1
  import { jsx as o } from "react/jsx-runtime";
2
2
  import "react";
3
- import { PanelLeft as f, PanelRight as d } from "lucide-react";
3
+ import { PanelLeft as f, PanelRight as u } from "lucide-react";
4
4
  import { cn as n } from "../../lib/utils.js";
5
5
  function c({
6
6
  side: t,
7
7
  onClick: e
8
8
  }) {
9
- const r = t === "left" ? f : d;
9
+ const r = t === "left" ? f : u;
10
10
  return /* @__PURE__ */ o(
11
11
  "button",
12
12
  {
13
13
  onClick: e,
14
14
  className: n(
15
- "fixed top-14 z-40",
15
+ "absolute top-14 z-40",
16
16
  "h-8 w-8 rounded-md",
17
17
  "flex items-center justify-center",
18
18
  "text-foreground-muted hover:text-foreground hover:bg-accent/50",
@@ -24,31 +24,31 @@ function c({
24
24
  }
25
25
  );
26
26
  }
27
- function g({
27
+ function h({
28
28
  children: t,
29
29
  side: e,
30
30
  isOpen: r,
31
31
  onToggle: l,
32
- width: i,
33
- className: a
32
+ width: a,
33
+ className: i
34
34
  }) {
35
35
  return r ? /* @__PURE__ */ o(
36
36
  "div",
37
37
  {
38
38
  className: n(
39
- "fixed top-14 bottom-3 z-40",
40
- "bg-background border border-border rounded-lg",
39
+ "absolute top-14 bottom-3 z-40",
40
+ "bg-background text-foreground border border-border rounded-lg",
41
41
  "shadow-lg",
42
42
  "flex flex-col overflow-hidden",
43
43
  "transition-all duration-150 ease-out",
44
44
  e === "left" ? "left-3" : "right-3",
45
- a
45
+ i
46
46
  ),
47
- style: { width: i },
47
+ style: { width: a },
48
48
  children: t
49
49
  }
50
50
  ) : /* @__PURE__ */ o(c, { side: e, onClick: l });
51
51
  }
52
52
  export {
53
- g as FloatingPanel
53
+ h as FloatingPanel
54
54
  };
@@ -10,7 +10,7 @@ function a({ className: o, type: r, ...t }) {
10
10
  autoComplete: "off",
11
11
  "data-1p-ignore": !0,
12
12
  className: n(
13
- "h-9 w-full min-w-0 rounded-md border border-border bg-background px-3 py-1 text-sm",
13
+ "h-9 w-full min-w-0 rounded-md border border-border bg-background text-foreground px-3 py-1 text-sm",
14
14
  "placeholder:text-foreground-muted",
15
15
  "transition-smooth",
16
16
  "focus:border-ring focus:ring-2 focus:ring-ring/20 focus:outline-none",