@fufulog/brevomorphic-cms-sdk 1.0.2 → 1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fufulog/brevomorphic-cms-sdk",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "SDK library to bootstrap Brevo email templates and manage local mappings in Postgres, MySQL, or Firestore",
6
6
  "main": "dist/index.js",
@@ -187,7 +187,6 @@ export default function BrevoWysiwyg({
187
187
 
188
188
  return (
189
189
  <div className={wrapperClasses}>
190
- {/* ── Toolbar ── */}
191
190
  <div className="brevo-toolbar">
192
191
  <div className="brevo-toolbar__group">
193
192
  <button
@@ -26,11 +26,11 @@
26
26
  -->
27
27
 
28
28
  <script>
29
- import { onMount, createEventDispatcher, tick } from 'svelte';
29
+ import { onMount, createEventDispatcher, tick } from "svelte";
30
30
 
31
- export let value = '';
31
+ export let value = "";
32
32
  export let variables = [];
33
- export let placeholder = 'Start designing your email template...';
33
+ export let placeholder = "Start designing your email template...";
34
34
  export let disabled = false;
35
35
 
36
36
  const dispatch = createEventDispatcher();
@@ -38,7 +38,7 @@
38
38
  let editorEl;
39
39
  let sourceEl;
40
40
  let isSourceMode = false;
41
- let sourceCode = '';
41
+ let sourceCode = "";
42
42
  let showVariableMenu = false;
43
43
  let showHeadingMenu = false;
44
44
  let isFocused = false;
@@ -65,20 +65,20 @@
65
65
  const raw = editorEl.innerHTML;
66
66
  // Restore any accidentally encoded Twig brackets
67
67
  const cleaned = raw
68
- .replace(/&lbrace;/g, '{')
69
- .replace(/&rbrace;/g, '}')
70
- .replace(/&#123;/g, '{')
71
- .replace(/&#125;/g, '}')
72
- .replace(/&#37;/g, '%')
73
- .replace(/&percnt;/g, '%');
68
+ .replace(/&lbrace;/g, "{")
69
+ .replace(/&rbrace;/g, "}")
70
+ .replace(/&#123;/g, "{")
71
+ .replace(/&#125;/g, "}")
72
+ .replace(/&#37;/g, "%")
73
+ .replace(/&percnt;/g, "%");
74
74
  value = cleaned;
75
- dispatch('change', cleaned);
75
+ dispatch("change", cleaned);
76
76
  }
77
77
 
78
78
  function syncFromSource() {
79
79
  sourceCode = sourceEl?.value ?? sourceCode;
80
80
  value = sourceCode;
81
- dispatch('change', value);
81
+ dispatch("change", value);
82
82
  }
83
83
 
84
84
  async function toggleSourceMode() {
@@ -108,24 +108,24 @@
108
108
 
109
109
  function updateActiveFormats() {
110
110
  activeFormats = {
111
- bold: document.queryCommandState('bold'),
112
- italic: document.queryCommandState('italic'),
113
- underline: document.queryCommandState('underline'),
114
- strikeThrough: document.queryCommandState('strikeThrough'),
115
- insertOrderedList: document.queryCommandState('insertOrderedList'),
116
- insertUnorderedList: document.queryCommandState('insertUnorderedList'),
111
+ bold: document.queryCommandState("bold"),
112
+ italic: document.queryCommandState("italic"),
113
+ underline: document.queryCommandState("underline"),
114
+ strikeThrough: document.queryCommandState("strikeThrough"),
115
+ insertOrderedList: document.queryCommandState("insertOrderedList"),
116
+ insertUnorderedList: document.queryCommandState("insertUnorderedList"),
117
117
  };
118
118
  }
119
119
 
120
120
  function insertHeading(tag) {
121
- execCmd('formatBlock', tag);
121
+ execCmd("formatBlock", tag);
122
122
  showHeadingMenu = false;
123
123
  }
124
124
 
125
125
  function insertLink() {
126
126
  if (isSourceMode || disabled) return;
127
- const url = prompt('Enter URL:');
128
- if (url) execCmd('createLink', url);
127
+ const url = prompt("Enter URL:");
128
+ if (url) execCmd("createLink", url);
129
129
  }
130
130
 
131
131
  function insertVariable(variable) {
@@ -141,17 +141,18 @@
141
141
  sourceCode = before + twigExpr + after;
142
142
  sourceEl.value = sourceCode;
143
143
  value = sourceCode;
144
- dispatch('change', value);
144
+ dispatch("change", value);
145
145
  // Restore cursor after inserted text
146
146
  tick().then(() => {
147
- sourceEl.selectionStart = sourceEl.selectionEnd = start + twigExpr.length;
147
+ sourceEl.selectionStart = sourceEl.selectionEnd =
148
+ start + twigExpr.length;
148
149
  sourceEl.focus();
149
150
  });
150
151
  } else if (editorEl) {
151
152
  // Insert into contentEditable at cursor position
152
153
  editorEl.focus();
153
154
  // Use insertHTML to preserve Twig brackets exactly
154
- document.execCommand('insertHTML', false, twigExpr);
155
+ document.execCommand("insertHTML", false, twigExpr);
155
156
  syncFromEditor();
156
157
  }
157
158
 
@@ -172,7 +173,7 @@
172
173
  }
173
174
 
174
175
  function closeMenus(e) {
175
- if (!e.target.closest('.brevo-dropdown')) {
176
+ if (!e.target.closest(".brevo-dropdown")) {
176
177
  showVariableMenu = false;
177
178
  showHeadingMenu = false;
178
179
  }
@@ -181,7 +182,11 @@
181
182
 
182
183
  <svelte:window on:click={closeMenus} />
183
184
 
184
- <div class="brevo-wysiwyg" class:brevo-wysiwyg--disabled={disabled} class:brevo-wysiwyg--focused={isFocused}>
185
+ <div
186
+ class="brevo-wysiwyg"
187
+ class:brevo-wysiwyg--disabled={disabled}
188
+ class:brevo-wysiwyg--focused={isFocused}
189
+ >
185
190
  <!-- Toolbar -->
186
191
  <div class="brevo-toolbar">
187
192
  <div class="brevo-toolbar__group">
@@ -190,36 +195,36 @@
190
195
  class="brevo-btn"
191
196
  class:brevo-btn--active={activeFormats.bold}
192
197
  title="Bold"
193
- on:click={() => execCmd('bold')}
194
- {disabled}
195
- ><strong>B</strong></button>
198
+ on:click={() => execCmd("bold")}
199
+ {disabled}><strong>B</strong></button
200
+ >
196
201
 
197
202
  <button
198
203
  type="button"
199
204
  class="brevo-btn"
200
205
  class:brevo-btn--active={activeFormats.italic}
201
206
  title="Italic"
202
- on:click={() => execCmd('italic')}
203
- {disabled}
204
- ><em>I</em></button>
207
+ on:click={() => execCmd("italic")}
208
+ {disabled}><em>I</em></button
209
+ >
205
210
 
206
211
  <button
207
212
  type="button"
208
213
  class="brevo-btn"
209
214
  class:brevo-btn--active={activeFormats.underline}
210
215
  title="Underline"
211
- on:click={() => execCmd('underline')}
212
- {disabled}
213
- ><u>U</u></button>
216
+ on:click={() => execCmd("underline")}
217
+ {disabled}><u>U</u></button
218
+ >
214
219
 
215
220
  <button
216
221
  type="button"
217
222
  class="brevo-btn"
218
223
  class:brevo-btn--active={activeFormats.strikeThrough}
219
224
  title="Strikethrough"
220
- on:click={() => execCmd('strikeThrough')}
221
- {disabled}
222
- ><s>S</s></button>
225
+ on:click={() => execCmd("strikeThrough")}
226
+ {disabled}><s>S</s></button
227
+ >
223
228
  </div>
224
229
 
225
230
  <div class="brevo-toolbar__divider"></div>
@@ -230,16 +235,27 @@
230
235
  type="button"
231
236
  class="brevo-btn"
232
237
  title="Headings"
233
- on:click|stopPropagation={() => { showHeadingMenu = !showHeadingMenu; showVariableMenu = false; }}
234
- {disabled}
235
- >H ▾</button>
238
+ on:click|stopPropagation={() => {
239
+ showHeadingMenu = !showHeadingMenu;
240
+ showVariableMenu = false;
241
+ }}
242
+ {disabled}>H ▾</button
243
+ >
236
244
 
237
245
  {#if showHeadingMenu}
238
246
  <div class="brevo-dropdown__menu">
239
- <button type="button" on:click={() => insertHeading('h1')}>Heading 1</button>
240
- <button type="button" on:click={() => insertHeading('h2')}>Heading 2</button>
241
- <button type="button" on:click={() => insertHeading('h3')}>Heading 3</button>
242
- <button type="button" on:click={() => insertHeading('p')}>Paragraph</button>
247
+ <button type="button" on:click={() => insertHeading("h1")}
248
+ >Heading 1</button
249
+ >
250
+ <button type="button" on:click={() => insertHeading("h2")}
251
+ >Heading 2</button
252
+ >
253
+ <button type="button" on:click={() => insertHeading("h3")}
254
+ >Heading 3</button
255
+ >
256
+ <button type="button" on:click={() => insertHeading("p")}
257
+ >Paragraph</button
258
+ >
243
259
  </div>
244
260
  {/if}
245
261
  </div>
@@ -252,31 +268,55 @@
252
268
  class="brevo-btn"
253
269
  class:brevo-btn--active={activeFormats.insertUnorderedList}
254
270
  title="Bullet List"
255
- on:click={() => execCmd('insertUnorderedList')}
256
- {disabled}
257
- >• List</button>
271
+ on:click={() => execCmd("insertUnorderedList")}
272
+ {disabled}>• List</button
273
+ >
258
274
 
259
275
  <button
260
276
  type="button"
261
277
  class="brevo-btn"
262
278
  class:brevo-btn--active={activeFormats.insertOrderedList}
263
279
  title="Numbered List"
264
- on:click={() => execCmd('insertOrderedList')}
265
- {disabled}
266
- >1. List</button>
280
+ on:click={() => execCmd("insertOrderedList")}
281
+ {disabled}>1. List</button
282
+ >
267
283
  </div>
268
284
 
269
285
  <div class="brevo-toolbar__divider"></div>
270
286
 
271
287
  <div class="brevo-toolbar__group">
272
- <button type="button" class="brevo-btn" title="Align Left" on:click={() => execCmd('justifyLeft')} {disabled}>⫷</button>
273
- <button type="button" class="brevo-btn" title="Align Center" on:click={() => execCmd('justifyCenter')} {disabled}>⫿</button>
274
- <button type="button" class="brevo-btn" title="Align Right" on:click={() => execCmd('justifyRight')} {disabled}>⫸</button>
288
+ <button
289
+ type="button"
290
+ class="brevo-btn"
291
+ title="Align Left"
292
+ on:click={() => execCmd("justifyLeft")}
293
+ {disabled}>⫷</button
294
+ >
295
+ <button
296
+ type="button"
297
+ class="brevo-btn"
298
+ title="Align Center"
299
+ on:click={() => execCmd("justifyCenter")}
300
+ {disabled}>⫿</button
301
+ >
302
+ <button
303
+ type="button"
304
+ class="brevo-btn"
305
+ title="Align Right"
306
+ on:click={() => execCmd("justifyRight")}
307
+ {disabled}>⫸</button
308
+ >
275
309
  </div>
276
310
 
277
311
  <div class="brevo-toolbar__divider"></div>
278
312
 
279
- <button type="button" class="brevo-btn" title="Insert Link" on:click={insertLink} {disabled}>🔗</button>
313
+ <button
314
+ type="button"
315
+ class="brevo-btn"
316
+ title="Insert Link"
317
+ on:click={insertLink}
318
+ {disabled}>🔗</button
319
+ >
280
320
 
281
321
  <!-- Variable injector -->
282
322
  {#if variables.length > 0}
@@ -286,16 +326,20 @@
286
326
  type="button"
287
327
  class="brevo-btn brevo-btn--accent"
288
328
  title="Insert Variable"
289
- on:click|stopPropagation={() => { showVariableMenu = !showVariableMenu; showHeadingMenu = false; }}
290
- {disabled}
291
- >&#123;&#123; &#125;&#125; Insert Variable ▾</button>
329
+ on:click|stopPropagation={() => {
330
+ showVariableMenu = !showVariableMenu;
331
+ showHeadingMenu = false;
332
+ }}
333
+ {disabled}>&#123;&#123; &#125;&#125; Insert Variable ▾</button
334
+ >
292
335
 
293
336
  {#if showVariableMenu}
294
337
  <div class="brevo-dropdown__menu brevo-dropdown__menu--variables">
295
338
  {#each variables as variable}
296
339
  <button type="button" on:click={() => insertVariable(variable)}>
297
340
  <span class="brevo-var-label">{variable.label}</span>
298
- <code class="brevo-var-key">{'{{ ' + variable.key + ' }}'}</code>
341
+ <code class="brevo-var-key">{"{{ " + variable.key + " }}"}</code
342
+ >
299
343
  </button>
300
344
  {/each}
301
345
  </div>
@@ -311,8 +355,8 @@
311
355
  class:brevo-btn--active={isSourceMode}
312
356
  title="Toggle Source View"
313
357
  on:click={toggleSourceMode}
314
- {disabled}
315
- >{isSourceMode ? '✎ Visual' : '&lt;/&gt; Source'}</button>
358
+ {disabled}>{isSourceMode ? "✎ Visual" : "</> Source"}</button
359
+ >
316
360
  </div>
317
361
 
318
362
  <!-- Editor Area -->
@@ -364,7 +408,7 @@
364
408
  --brevo-btn-hover: #2a2a4a;
365
409
  --brevo-btn-active: #3730a3;
366
410
  --brevo-radius: 8px;
367
- --brevo-font: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
411
+ --brevo-font: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
368
412
 
369
413
  font-family: var(--brevo-font);
370
414
  border: 1.5px solid var(--brevo-border);
@@ -479,8 +523,14 @@
479
523
  }
480
524
 
481
525
  @keyframes brevo-dropdown-in {
482
- from { opacity: 0; transform: translateY(-4px); }
483
- to { opacity: 1; transform: translateY(0); }
526
+ from {
527
+ opacity: 0;
528
+ transform: translateY(-4px);
529
+ }
530
+ to {
531
+ opacity: 1;
532
+ transform: translateY(0);
533
+ }
484
534
  }
485
535
 
486
536
  .brevo-dropdown__menu button {
@@ -541,12 +591,29 @@
541
591
  pointer-events: none;
542
592
  }
543
593
 
544
- .brevo-editor :global(h1) { font-size: 1.8em; font-weight: 700; margin: 0.4em 0; }
545
- .brevo-editor :global(h2) { font-size: 1.4em; font-weight: 600; margin: 0.4em 0; }
546
- .brevo-editor :global(h3) { font-size: 1.15em; font-weight: 600; margin: 0.4em 0; }
547
- .brevo-editor :global(a) { color: var(--brevo-accent-hover); text-decoration: underline; }
594
+ .brevo-editor :global(h1) {
595
+ font-size: 1.8em;
596
+ font-weight: 700;
597
+ margin: 0.4em 0;
598
+ }
599
+ .brevo-editor :global(h2) {
600
+ font-size: 1.4em;
601
+ font-weight: 600;
602
+ margin: 0.4em 0;
603
+ }
604
+ .brevo-editor :global(h3) {
605
+ font-size: 1.15em;
606
+ font-weight: 600;
607
+ margin: 0.4em 0;
608
+ }
609
+ .brevo-editor :global(a) {
610
+ color: var(--brevo-accent-hover);
611
+ text-decoration: underline;
612
+ }
548
613
  .brevo-editor :global(ul),
549
- .brevo-editor :global(ol) { padding-left: 1.6em; }
614
+ .brevo-editor :global(ol) {
615
+ padding-left: 1.6em;
616
+ }
550
617
 
551
618
  /* ── Source ── */
552
619
  .brevo-source {
@@ -557,7 +624,7 @@
557
624
  border: none;
558
625
  background: #0d1117;
559
626
  color: #c9d1d9;
560
- font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
627
+ font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace;
561
628
  font-size: 13px;
562
629
  line-height: 1.65;
563
630
  resize: vertical;