@mjml-agent-editor/editor 0.1.17 → 0.3.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/dist/index.d.cts CHANGED
@@ -5,6 +5,7 @@ export { CommentStore, CommentTarget, DocumentPatch, DocumentStore, EmailDocumen
5
5
  import { EditorApi } from './canvas.cjs';
6
6
  export { EditorState, SaveStatus } from './canvas.cjs';
7
7
  import { UIMessage, ChatTransport } from 'ai';
8
+ import 'grapesjs';
8
9
 
9
10
  /**
10
11
  * Every word the editor puts on screen.
@@ -14,8 +15,10 @@ import { UIMessage, ChatTransport } from 'ai';
14
15
  * keep existing hosts working unchanged, and a host overrides only the keys it cares about.
15
16
  *
16
17
  * Flat and shallow-merged on purpose. A nested dictionary reads better and merges worse —
17
- * overriding one key inside a group would silently drop its siblings. `toolLabels` is the
18
- * single nested map, and it is merged key by key for exactly that reason.
18
+ * overriding one key inside a group would silently drop its siblings. The only nested maps
19
+ * are the ones keyed by an id (`toolLabels`, `blockLabels`, `elementNames`,
20
+ * `inspectorLabels`), and those are
21
+ * merged key by key for exactly that reason.
19
22
  *
20
23
  * Values that interpolate are functions rather than templates with placeholders, so the
21
24
  * translator controls word order instead of the format string.
@@ -38,10 +41,72 @@ interface EditorLabels {
38
41
  readonly exportMjml: string;
39
42
  /** `kind` is already upper-cased ("MJML", "HTML"). */
40
43
  readonly copied: (kind: string) => string;
44
+ readonly tabBlocks: string;
45
+ readonly tabSettings: string;
46
+ readonly tabLayers: string;
41
47
  readonly searchBlocks: string;
48
+ readonly noBlocksMatch: string;
49
+ /** Announced after a block is added from the keyboard, with the name on its tile. */
50
+ readonly blockAdded: (block: string) => string;
51
+ readonly blockCategoryLayout: string;
52
+ readonly blockCategoryContent: string;
53
+ /** Where the host's own sections (the `blocks` prop) are listed. */
54
+ readonly blockCategorySections: string;
55
+ /** Keyed by grapesjs-mjml block id (`mj-1-column`, `mj-social-group`, …); merged key by key. */
56
+ readonly blockLabels: Readonly<Record<string, string>>;
57
+ /** Keyed by MJML tag, for the breadcrumb; merged key by key. */
58
+ readonly elementNames: Readonly<Record<string, string>>;
42
59
  readonly expandAll: string;
43
60
  readonly selectAnElement: string;
61
+ readonly settingsEmpty: string;
62
+ readonly emailSettings: string;
63
+ readonly loadingEditor: string;
64
+ readonly commentOnElement: string;
65
+ readonly groupContent: string;
66
+ readonly groupLayout: string;
67
+ readonly groupSpacing: string;
68
+ readonly groupStyle: string;
69
+ readonly groupAdvanced: string;
70
+ readonly otherAttributes: string;
71
+ readonly removeAttribute: string;
72
+ readonly resetToDefault: string;
73
+ readonly selectParent: string;
74
+ readonly duplicate: string;
75
+ readonly deleteElement: string;
76
+ readonly multipleSelected: (count: number) => string;
77
+ readonly editEachSide: string;
78
+ readonly increase: string;
79
+ readonly decrease: string;
80
+ /** The unit a length is in, on the button that changes it. */
81
+ readonly changeUnit: (unit: string) => string;
82
+ /** How a length without a unit (a line-height multiplier) shows its unit. */
83
+ readonly noUnit: string;
84
+ readonly sidesVertical: string;
85
+ readonly sidesHorizontal: string;
86
+ readonly sideTop: string;
87
+ readonly sideRight: string;
88
+ readonly sideBottom: string;
89
+ readonly sideLeft: string;
90
+ readonly linkTypeProfile: string;
91
+ readonly linkTypeShare: string;
92
+ readonly allScreenSizes: string;
93
+ readonly columnWidths: string;
94
+ /** A section's columns, by their proportions ("1:2"). */
95
+ readonly columnRatio: (ratio: string) => string;
96
+ readonly socialNetworks: string;
97
+ readonly addNetwork: string;
98
+ readonly navbarLinks: string;
99
+ readonly addLink: string;
100
+ /** The text a new navigation link starts with. */
101
+ readonly newLinkText: string;
102
+ /**
103
+ * Field names, help (`<key>.help`) and option names for the Settings tab, keyed as the
104
+ * schema in `inspector/schema.ts` names them; merged key by key.
105
+ */
106
+ readonly inspectorLabels: Readonly<Record<string, string>>;
107
+ /** @deprecated The Settings tab no longer splits attributes from style. */
44
108
  readonly attributes: string;
109
+ /** @deprecated The Settings tab no longer splits attributes from style. */
45
110
  readonly style: string;
46
111
  readonly rteFont: string;
47
112
  readonly rteSize: string;
@@ -97,8 +162,8 @@ interface EditorLabels {
97
162
  }
98
163
  declare const DEFAULT_LABELS: EditorLabels;
99
164
  /**
100
- * Overrides on top of the defaults. `toolLabels` merges rather than replaces, so a host
101
- * renaming one step does not lose the other eight.
165
+ * Overrides on top of the defaults. The maps merge rather than replace, so a host renaming
166
+ * one step does not lose the other eight.
102
167
  */
103
168
  declare function mergeLabels(overrides?: Partial<EditorLabels>): EditorLabels;
104
169
 
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { CommentStore, CommentTarget, DocumentPatch, DocumentStore, EmailDocumen
5
5
  import { EditorApi } from './canvas.js';
6
6
  export { EditorState, SaveStatus } from './canvas.js';
7
7
  import { UIMessage, ChatTransport } from 'ai';
8
+ import 'grapesjs';
8
9
 
9
10
  /**
10
11
  * Every word the editor puts on screen.
@@ -14,8 +15,10 @@ import { UIMessage, ChatTransport } from 'ai';
14
15
  * keep existing hosts working unchanged, and a host overrides only the keys it cares about.
15
16
  *
16
17
  * Flat and shallow-merged on purpose. A nested dictionary reads better and merges worse —
17
- * overriding one key inside a group would silently drop its siblings. `toolLabels` is the
18
- * single nested map, and it is merged key by key for exactly that reason.
18
+ * overriding one key inside a group would silently drop its siblings. The only nested maps
19
+ * are the ones keyed by an id (`toolLabels`, `blockLabels`, `elementNames`,
20
+ * `inspectorLabels`), and those are
21
+ * merged key by key for exactly that reason.
19
22
  *
20
23
  * Values that interpolate are functions rather than templates with placeholders, so the
21
24
  * translator controls word order instead of the format string.
@@ -38,10 +41,72 @@ interface EditorLabels {
38
41
  readonly exportMjml: string;
39
42
  /** `kind` is already upper-cased ("MJML", "HTML"). */
40
43
  readonly copied: (kind: string) => string;
44
+ readonly tabBlocks: string;
45
+ readonly tabSettings: string;
46
+ readonly tabLayers: string;
41
47
  readonly searchBlocks: string;
48
+ readonly noBlocksMatch: string;
49
+ /** Announced after a block is added from the keyboard, with the name on its tile. */
50
+ readonly blockAdded: (block: string) => string;
51
+ readonly blockCategoryLayout: string;
52
+ readonly blockCategoryContent: string;
53
+ /** Where the host's own sections (the `blocks` prop) are listed. */
54
+ readonly blockCategorySections: string;
55
+ /** Keyed by grapesjs-mjml block id (`mj-1-column`, `mj-social-group`, …); merged key by key. */
56
+ readonly blockLabels: Readonly<Record<string, string>>;
57
+ /** Keyed by MJML tag, for the breadcrumb; merged key by key. */
58
+ readonly elementNames: Readonly<Record<string, string>>;
42
59
  readonly expandAll: string;
43
60
  readonly selectAnElement: string;
61
+ readonly settingsEmpty: string;
62
+ readonly emailSettings: string;
63
+ readonly loadingEditor: string;
64
+ readonly commentOnElement: string;
65
+ readonly groupContent: string;
66
+ readonly groupLayout: string;
67
+ readonly groupSpacing: string;
68
+ readonly groupStyle: string;
69
+ readonly groupAdvanced: string;
70
+ readonly otherAttributes: string;
71
+ readonly removeAttribute: string;
72
+ readonly resetToDefault: string;
73
+ readonly selectParent: string;
74
+ readonly duplicate: string;
75
+ readonly deleteElement: string;
76
+ readonly multipleSelected: (count: number) => string;
77
+ readonly editEachSide: string;
78
+ readonly increase: string;
79
+ readonly decrease: string;
80
+ /** The unit a length is in, on the button that changes it. */
81
+ readonly changeUnit: (unit: string) => string;
82
+ /** How a length without a unit (a line-height multiplier) shows its unit. */
83
+ readonly noUnit: string;
84
+ readonly sidesVertical: string;
85
+ readonly sidesHorizontal: string;
86
+ readonly sideTop: string;
87
+ readonly sideRight: string;
88
+ readonly sideBottom: string;
89
+ readonly sideLeft: string;
90
+ readonly linkTypeProfile: string;
91
+ readonly linkTypeShare: string;
92
+ readonly allScreenSizes: string;
93
+ readonly columnWidths: string;
94
+ /** A section's columns, by their proportions ("1:2"). */
95
+ readonly columnRatio: (ratio: string) => string;
96
+ readonly socialNetworks: string;
97
+ readonly addNetwork: string;
98
+ readonly navbarLinks: string;
99
+ readonly addLink: string;
100
+ /** The text a new navigation link starts with. */
101
+ readonly newLinkText: string;
102
+ /**
103
+ * Field names, help (`<key>.help`) and option names for the Settings tab, keyed as the
104
+ * schema in `inspector/schema.ts` names them; merged key by key.
105
+ */
106
+ readonly inspectorLabels: Readonly<Record<string, string>>;
107
+ /** @deprecated The Settings tab no longer splits attributes from style. */
44
108
  readonly attributes: string;
109
+ /** @deprecated The Settings tab no longer splits attributes from style. */
45
110
  readonly style: string;
46
111
  readonly rteFont: string;
47
112
  readonly rteSize: string;
@@ -97,8 +162,8 @@ interface EditorLabels {
97
162
  }
98
163
  declare const DEFAULT_LABELS: EditorLabels;
99
164
  /**
100
- * Overrides on top of the defaults. `toolLabels` merges rather than replaces, so a host
101
- * renaming one step does not lose the other eight.
165
+ * Overrides on top of the defaults. The maps merge rather than replace, so a host renaming
166
+ * one step does not lose the other eight.
102
167
  */
103
168
  declare function mergeLabels(overrides?: Partial<EditorLabels>): EditorLabels;
104
169
 
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  useDocumentStore,
13
13
  useImageLibrary,
14
14
  useLabels
15
- } from "./chunk-VJRKDFM4.js";
15
+ } from "./chunk-K7KQC7LX.js";
16
16
  import "./chunk-V6RR4VRD.js";
17
17
  export {
18
18
  CanvasComments,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjml-agent-editor/editor",
3
- "version": "0.1.17",
3
+ "version": "0.3.0",
4
4
  "description": "React MJML editor with an agent chat panel and canvas comments.",
5
5
  "keywords": [
6
6
  "mjml",
@@ -71,7 +71,7 @@
71
71
  "remark-gfm": "^4.0.1",
72
72
  "sonner": "^2.0.7",
73
73
  "tailwind-merge": "^3.6.0",
74
- "@mjml-agent-editor/core": "0.1.9"
74
+ "@mjml-agent-editor/core": "0.2.0"
75
75
  },
76
76
  "peerDependencies": {
77
77
  "ai": "^6.0.230",
@@ -83,6 +83,7 @@
83
83
  "@types/react-dom": "^19.2.3",
84
84
  "ai": "^6.0.238",
85
85
  "jsdom": "^26.1.0",
86
+ "mjml": "^4.18.0",
86
87
  "react": "19.2.4",
87
88
  "react-dom": "19.2.4",
88
89
  "tsup": "^8.5.1",
@@ -155,6 +155,10 @@
155
155
  box-shadow 0.12s ease,
156
156
  background-color 0.12s ease;
157
157
  }
158
+ .editor-dark .gjs-block:focus-visible {
159
+ outline: 2px solid var(--mjml-editor-accent);
160
+ outline-offset: 1px;
161
+ }
158
162
  .editor-dark .gjs-block:hover {
159
163
  border-color: var(--mjml-editor-accent);
160
164
  background-color: var(--mjml-editor-panel-raised);
@@ -189,92 +193,73 @@
189
193
  display: none;
190
194
  }
191
195
 
192
- /* ── Form fields (style/traits) ──────────────────────────────────────────── */
193
- .editor-dark .gjs-field,
194
- .editor-dark .gjs-sm-field,
195
- .editor-dark .gjs-clm-field {
196
- background-color: var(--mjml-editor-panel-elevated);
197
- border: 1px solid var(--mjml-editor-border);
198
- border-radius: 6px;
199
- color: var(--mjml-editor-fg);
196
+ /* ── Layers: rows named by content, with an icon for each kind ─────────────── */
197
+ .editor-dark .gjs-layer {
198
+ font-weight: 400;
199
+ font-size: 12px;
200
200
  }
201
- .editor-dark .gjs-field input,
202
- .editor-dark .gjs-field select,
203
- .editor-dark .gjs-field textarea,
204
- .editor-dark .gjs-sm-field input,
205
- .editor-dark .gjs-clm-field input {
206
- color: var(--mjml-editor-fg);
201
+ .editor-dark .gjs-layer-item {
202
+ padding: 4px 8px;
203
+ border-bottom: none;
204
+ background-color: transparent;
207
205
  }
208
- .editor-dark .gjs-field-arrow-u,
209
- .editor-dark .gjs-field-arrow-d {
210
- border-bottom-color: var(--mjml-editor-fg-subtle);
211
- border-top-color: var(--mjml-editor-fg-subtle);
206
+ .editor-dark .gjs-layer-title {
207
+ color: var(--mjml-editor-neutral);
212
208
  }
213
-
214
- /* Unified manager typography (traits + styles) */
215
- .editor-dark .gjs-sm-label,
216
- .editor-dark .gjs-trt-trait .gjs-label,
217
- .editor-dark .gjs-label {
218
- font-size: 11px;
219
- font-weight: 500;
220
- line-height: 1.4;
221
- letter-spacing: normal;
222
- text-transform: none;
209
+ .editor-dark .gjs-layer-title-c,
210
+ .editor-dark .gjs-layer-title-inn {
211
+ min-width: 0;
212
+ }
213
+ .editor-dark .gjs-layer__icon {
214
+ flex-shrink: 0;
215
+ width: 14px;
216
+ height: 14px;
217
+ padding-left: 0;
223
218
  color: var(--mjml-editor-fg-subtle);
224
219
  }
225
- .editor-dark .gjs-sm-properties,
226
- .editor-dark .gjs-traits-c {
227
- padding: 6px 12px;
220
+ /* GrapesJS fills its icons; these are drawn with strokes. */
221
+ .editor-dark .gjs-layer__icon svg {
222
+ display: block;
223
+ width: 14px;
224
+ height: 14px;
225
+ fill: none;
226
+ stroke: currentColor;
228
227
  }
229
- .editor-dark .gjs-trt-trait {
230
- padding: 4px 0;
228
+ .editor-dark .gjs-layer-name {
229
+ max-width: none;
230
+ min-width: 0;
231
+ overflow: hidden;
232
+ text-overflow: ellipsis;
231
233
  }
232
- .editor-dark .gjs-sm-property {
233
- margin-bottom: 6px;
234
- padding: 0;
234
+ .editor-dark .gjs-layer-count {
235
+ font-size: 10px;
236
+ color: var(--mjml-editor-fg-faint);
235
237
  }
236
- .editor-dark .gjs-field,
237
- .editor-dark .gjs-sm-field {
238
- min-height: 28px;
239
- font-size: 12px;
238
+ /* How many it holds matters while they are out of sight. */
239
+ .editor-dark .gjs-layer.open > .gjs-layer-item .gjs-layer-count {
240
+ visibility: hidden;
240
241
  }
241
- .editor-dark .gjs-field input,
242
- .editor-dark .gjs-field select,
243
- .editor-dark .gjs-field textarea,
244
- .editor-dark .gjs-sm-field input,
245
- .editor-dark .gjs-sm-field select {
246
- font-size: 12px;
247
- }
248
-
249
- /* Sector headers */
250
- .editor-dark .gjs-sm-sector-title,
251
- .editor-dark .gjs-trt-header {
252
- padding: 8px 10px;
253
- background-color: var(--mjml-editor-panel-sunken);
254
- color: var(--mjml-editor-neutral);
255
- border-bottom: 1px solid var(--mjml-editor-border);
256
- font-size: 11px;
257
- font-weight: 600;
258
- text-transform: uppercase;
259
- letter-spacing: 0.04em;
242
+ .editor-dark .gjs-layer-move {
243
+ opacity: 0;
244
+ color: var(--mjml-editor-fg-subtle);
260
245
  }
261
- .editor-dark .gjs-sm-sector,
262
- .editor-dark .gjs-trt-trait {
263
- border-bottom: 1px solid var(--mjml-editor-panel-elevated);
246
+ .editor-dark .gjs-layer-item:hover .gjs-layer-move {
247
+ opacity: 1;
264
248
  }
265
-
266
- /* Layers */
267
- .editor-dark .gjs-layer-title {
268
- color: var(--mjml-editor-neutral);
249
+ .editor-dark .gjs-layer.gjs-hovered > .gjs-layer-item {
250
+ background-color: var(--mjml-editor-panel-elevated);
269
251
  }
270
252
  .editor-dark .gjs-layer.gjs-selected > .gjs-layer-item {
271
253
  background-color: color-mix(in srgb, var(--mjml-editor-accent) 22%, transparent);
272
254
  }
255
+ .editor-dark .gjs-layer.gjs-selected > .gjs-layer-item .gjs-layer__icon {
256
+ color: var(--mjml-editor-fg);
257
+ }
273
258
 
274
- /* Selector bar (component classes) */
275
- .editor-dark .gjs-clm-tags {
276
- background-color: var(--mjml-editor-panel-elevated);
277
- color: var(--mjml-editor-neutral);
259
+ /* The canvas badge's icon is one of the same stroked icons. */
260
+ :root .gjs-badge__icon svg {
261
+ fill: none;
262
+ stroke: currentColor;
278
263
  }
279
264
 
280
265
  /* ── Drop placeholder (was green) → violet ───────────────────────────────── */
@@ -428,13 +413,3 @@
428
413
  height: 12px;
429
414
  border-width: 2px;
430
415
  }
431
-
432
- /* Image properties take the full row: a thumbnail at half the sidebar is too small to judge. */
433
- .editor-dark .gjs-sm-property.gjs-sm-file {
434
- width: 100%;
435
- flex-basis: 100%;
436
- }
437
- .editor-dark .gjs-sm-property.gjs-sm-file .gjs-fields > * {
438
- flex: 1 1 auto;
439
- width: 100%;
440
- }