mail-editor-pancake 0.2.8 → 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.
Files changed (60) hide show
  1. package/README.md +37 -4
  2. package/package.json +1 -1
  3. package/packages/blocks/dist/index.js +275 -263
  4. package/packages/blocks/dist/index.js.map +1 -1
  5. package/packages/blocks/package.json +1 -3
  6. package/packages/blocks/src/button.ts +1 -1
  7. package/packages/blocks/src/hero.ts +6 -3
  8. package/packages/blocks/src/i18n.ts +10 -11
  9. package/packages/blocks/src/image.ts +2 -1
  10. package/packages/blocks/src/index.ts +5 -3
  11. package/packages/blocks/src/social.ts +3 -3
  12. package/packages/blocks/src/socialGroup.ts +4 -4
  13. package/packages/blocks/src/socialShared.ts +23 -3
  14. package/packages/blocks/src/text.ts +1 -2
  15. package/packages/blocks/vite.config.ts +1 -1
  16. package/packages/core/dist/index.d.ts +96 -1
  17. package/packages/core/dist/index.js +4346 -3591
  18. package/packages/core/dist/index.js.map +1 -1
  19. package/packages/core/dist/style.css +1 -1
  20. package/packages/core/package.json +1 -3
  21. package/packages/core/src/editor/BlockCodeModal.ts +6 -7
  22. package/packages/core/src/editor/Canvas.ts +558 -20
  23. package/packages/core/src/editor/ColorPickerPopover.ts +2 -2
  24. package/packages/core/src/editor/Editor.ts +379 -39
  25. package/packages/core/src/editor/ExportModal.ts +7 -9
  26. package/packages/core/src/editor/FocusBreadcrumb.ts +3 -3
  27. package/packages/core/src/editor/ImportDocModal.ts +50 -7
  28. package/packages/core/src/editor/InlineEditor.ts +43 -39
  29. package/packages/core/src/editor/LeftPanel.ts +2 -4
  30. package/packages/core/src/editor/Modal.ts +1 -1
  31. package/packages/core/src/editor/PreviewModal.ts +3 -8
  32. package/packages/core/src/editor/RichTextToolbar.ts +26 -15
  33. package/packages/core/src/editor/RightPanel.ts +141 -125
  34. package/packages/core/src/editor/SourceView.ts +5 -3
  35. package/packages/core/src/editor/Topbar.ts +79 -34
  36. package/packages/core/src/editor/VariablePickerPanel.ts +3 -5
  37. package/packages/core/src/editor/imageAssets.ts +1 -1
  38. package/packages/core/src/editor/styles.css +258 -86
  39. package/packages/core/src/i18n/index.ts +139 -41
  40. package/packages/core/src/index.ts +11 -1
  41. package/packages/core/src/renderer/index.ts +3 -6
  42. package/packages/core/src/renderer/mjml.ts +4 -9
  43. package/packages/core/src/store/store.ts +2 -6
  44. package/packages/core/src/types.ts +3 -3
  45. package/packages/core/src/utils/accentColor.ts +3 -3
  46. package/packages/core/src/utils/docClipboard.ts +107 -2
  47. package/packages/core/src/utils/dynamicVariantHtml.ts +1 -1
  48. package/packages/core/src/utils/emailListStyles.ts +1 -1
  49. package/packages/core/src/utils/fontWeightSteps.ts +7 -6
  50. package/packages/core/src/utils/html.ts +3 -2
  51. package/packages/core/src/utils/inlineListEditing.ts +19 -35
  52. package/packages/core/src/utils/link.ts +3 -6
  53. package/packages/core/src/utils/lockedMjml.ts +1 -1
  54. package/packages/core/src/utils/modalSize.ts +4 -3
  55. package/packages/core/src/utils/richHtmlEmpty.ts +3 -1
  56. package/packages/core/src/variables/index.ts +4 -5
  57. package/packages/core/vite.config.ts +1 -1
  58. package/playground/vanilla/index.html +17 -0
  59. package/playground/vanilla/src/main.ts +152 -123
  60. package/playground/vanilla/vite.config.ts +5 -2
@@ -1,16 +1,16 @@
1
- import { EditorView, basicSetup } from 'codemirror';
2
1
  import { html as cmHtml } from '@codemirror/lang-html';
3
2
  import { EditorState } from '@codemirror/state';
3
+ import { EditorView, basicSetup } from 'codemirror';
4
+ import type { SimpleMailT } from '../i18n';
4
5
  import type { Registry } from '../registry/registry';
5
- import { htmlContainsMjmlTags } from '../utils/lockedMjml';
6
- import { invalidHrefMessage } from '../utils/link';
6
+ import { defaultSocialIconBackground } from '../socialDefaults';
7
7
  import {
8
+ type DocChangedDetail,
9
+ type Store,
8
10
  findBlockLocation,
9
11
  findSection,
10
12
  isMultiColumnLayout,
11
13
  setSectionLayout,
12
- type Store,
13
- type DocChangedDetail,
14
14
  } from '../store/store';
15
15
  import type {
16
16
  Block,
@@ -21,27 +21,36 @@ import type {
21
21
  SectionLayout,
22
22
  Variable,
23
23
  } from '../types';
24
- import { getSectionDynamicVariantKey } from '../utils/dynamicVariantSection';
25
- import { defaultSocialIconBackground } from '../socialDefaults';
26
- import { bindColorPickerInput } from './ColorPickerPopover';
27
24
  import { normalizeAccentHex } from '../utils/accentColor';
25
+ import {
26
+ metaWidthInputString,
27
+ parseMetaWidthFromUserInput,
28
+ parseSectionWidthFromUserInput,
29
+ sectionWidthInputString,
30
+ } from '../utils/contentWidth';
28
31
  import { clear, h } from '../utils/dom';
29
- import { metaWidthInputString, parseMetaWidthFromUserInput, parseSectionWidthFromUserInput, sectionWidthInputString } from '../utils/contentWidth';
32
+ import { getSectionDynamicVariantKey } from '../utils/dynamicVariantSection';
30
33
  import {
31
34
  LIST_INDENT_PRESETS_PX,
32
35
  normalizeGlobalListIndentStorage,
33
36
  resolveGlobalListIndentPx,
34
37
  } from '../utils/emailListStyles';
35
38
  import { FONT_WEIGHT_STEP_OPTIONS, normalizeFontWeightStep } from '../utils/fontWeightSteps';
36
- import { resolveAssetPermission, type ImageAssetsHandlers, type ImageFieldContext } from './imageAssets';
39
+ import { invalidHrefMessage } from '../utils/link';
40
+ import { htmlContainsMjmlTags } from '../utils/lockedMjml';
41
+ import { bindColorPickerInput } from './ColorPickerPopover';
37
42
  import { FocusBreadcrumb } from './FocusBreadcrumb';
38
43
  import { openImageGalleryModal } from './ImageGalleryModal';
39
44
  import {
45
+ type VariablePickerHandlers,
40
46
  buildVariablePickerBody,
41
47
  buildVariablePickerHead,
42
- type VariablePickerHandlers,
43
48
  } from './VariablePickerPanel';
44
- import type { SimpleMailT } from '../i18n';
49
+ import {
50
+ type ImageAssetsHandlers,
51
+ type ImageFieldContext,
52
+ resolveAssetPermission,
53
+ } from './imageAssets';
45
54
 
46
55
  /** 与社交组 block 中每行元素结构一致(core 不依赖 blocks) */
47
56
  type SocialLinkRow = {
@@ -66,7 +75,7 @@ function layoutWidthNumeric(raw: string, mode: 'px' | '%'): number {
66
75
  const t = (raw ?? '').trim();
67
76
  if (!t) return mode === '%' ? 100 : 280;
68
77
  const m = t.match(/^(\d+(\.\d+)?)/);
69
- const n = m ? parseFloat(m[1]) : mode === '%' ? 100 : 280;
78
+ const n = m ? Number.parseFloat(m[1]) : mode === '%' ? 100 : 280;
70
79
  if (mode === '%') return Math.min(100, Math.max(1, Math.round(n)));
71
80
  return Math.min(1200, Math.max(1, Math.round(n)));
72
81
  }
@@ -231,10 +240,7 @@ export class RightPanel {
231
240
  let selEnd = 0;
232
241
  if (ae && this.el.contains(ae)) {
233
242
  token = ae.getAttribute('data-sm-focus');
234
- if (
235
- token &&
236
- (ae instanceof HTMLInputElement || ae instanceof HTMLTextAreaElement)
237
- ) {
243
+ if (token && (ae instanceof HTMLInputElement || ae instanceof HTMLTextAreaElement)) {
238
244
  selStart = ae.selectionStart ?? 0;
239
245
  selEnd = ae.selectionEnd ?? 0;
240
246
  }
@@ -311,7 +317,7 @@ export class RightPanel {
311
317
  onclick: () => {
312
318
  this.opts.store.update((d) => {
313
319
  const l = findBlockLocation(d, loc.block.id);
314
- if (l) delete l.block.lockedMjml;
320
+ if (l) l.block.lockedMjml = undefined;
315
321
  });
316
322
  },
317
323
  },
@@ -357,23 +363,31 @@ export class RightPanel {
357
363
  if (!hideMeta) {
358
364
  rows.push(
359
365
  h('div', { class: 'sm-panel__title' }, [this.opts.t('rightPanel.doc.title')]),
360
- this._textField(this.opts.t('rightPanel.doc.subject'), doc.meta.subject, (v) =>
361
- this.opts.store.update((d) => {
362
- d.meta.subject = v;
363
- }),
366
+ this._textField(
367
+ this.opts.t('rightPanel.doc.subject'),
368
+ doc.meta.subject,
369
+ (v) =>
370
+ this.opts.store.update((d) => {
371
+ d.meta.subject = v;
372
+ }),
364
373
  '',
365
374
  'doc:meta.subject',
366
375
  ),
367
- this._textField(this.opts.t('rightPanel.doc.preheader'), doc.meta.preheader ?? '', (v) =>
368
- this.opts.store.update((d) => {
369
- d.meta.preheader = v;
370
- }),
376
+ this._textField(
377
+ this.opts.t('rightPanel.doc.preheader'),
378
+ doc.meta.preheader ?? '',
379
+ (v) =>
380
+ this.opts.store.update((d) => {
381
+ d.meta.preheader = v;
382
+ }),
371
383
  '',
372
384
  'doc:meta.preheader',
373
385
  ),
374
386
  );
375
387
  } else {
376
- rows.push(h('div', { class: 'sm-panel__title' }, [this.opts.t('rightPanel.doc.layoutTitle')]));
388
+ rows.push(
389
+ h('div', { class: 'sm-panel__title' }, [this.opts.t('rightPanel.doc.layoutTitle')]),
390
+ );
377
391
  }
378
392
  rows.push(
379
393
  this._textField(
@@ -509,7 +523,7 @@ export class RightPanel {
509
523
  this.opts.store.update((d) => {
510
524
  const s = findSection(d, section.id);
511
525
  if (s) s.attrs.backgroundColor = v || undefined;
512
- }),
526
+ }),
513
527
  `section:${section.id}:attrs.bg`,
514
528
  this.opts.t('rightPanel.section.transparentPlaceholder'),
515
529
  ),
@@ -520,7 +534,7 @@ export class RightPanel {
520
534
  this.opts.store.update((d) => {
521
535
  const s = findSection(d, section.id);
522
536
  if (s) s.attrs.width = parseSectionWidthFromUserInput(v);
523
- }),
537
+ }),
524
538
  `section:${section.id}:attrs.width`,
525
539
  this.opts.t('rightPanel.section.widthHelp'),
526
540
  ),
@@ -612,8 +626,7 @@ export class RightPanel {
612
626
  const def = this.opts.registry.get(block.type);
613
627
  const defaults = def?.defaultProps as Record<string, unknown> | undefined;
614
628
  const raw = (block.props as Record<string, unknown>)[field.key];
615
- const value: unknown =
616
- raw !== undefined && raw !== null ? raw : defaults?.[field.key];
629
+ const value: unknown = raw !== undefined && raw !== null ? raw : defaults?.[field.key];
617
630
  const onChange = (v: any) => this._updateBlock(blockId, field.key, v);
618
631
 
619
632
  switch (field.type) {
@@ -625,10 +638,7 @@ export class RightPanel {
625
638
  onChange,
626
639
  field.help,
627
640
  fp,
628
- (v) =>
629
- htmlContainsMjmlTags(v)
630
- ? this.opts.t('rightPanel.block.mjmlWarning')
631
- : null,
641
+ (v) => (htmlContainsMjmlTags(v) ? this.opts.t('rightPanel.block.mjmlWarning') : null),
632
642
  );
633
643
  }
634
644
  return this._textareaField(field.label, String(value ?? ''), onChange, field.help, fp);
@@ -644,8 +654,7 @@ export class RightPanel {
644
654
  );
645
655
  case 'color': {
646
656
  const rawColor = (block.props as Record<string, unknown>)[field.key];
647
- const stored =
648
- rawColor !== undefined && rawColor !== null ? String(rawColor).trim() : '';
657
+ const stored = rawColor !== undefined && rawColor !== null ? String(rawColor).trim() : '';
649
658
  return this._colorField(
650
659
  field.label,
651
660
  stored,
@@ -656,14 +665,9 @@ export class RightPanel {
656
665
  }
657
666
  case 'select': {
658
667
  const isFw = field.key === 'fontWeight' || field.key === 'labelFontWeight';
659
- const opts = field.options?.length
660
- ? field.options
661
- : isFw
662
- ? FONT_WEIGHT_STEP_OPTIONS
663
- : [];
668
+ const opts = field.options?.length ? field.options : isFw ? FONT_WEIGHT_STEP_OPTIONS : [];
664
669
  if (isFw && field.inheritGlobal) {
665
- const stored =
666
- raw !== undefined && raw !== null ? String(raw).trim() : '';
670
+ const stored = raw !== undefined && raw !== null ? String(raw).trim() : '';
667
671
  return this._fontWeightInheritField(
668
672
  field.label,
669
673
  stored,
@@ -707,8 +711,7 @@ export class RightPanel {
707
711
  );
708
712
  }
709
713
  if (field.key === 'fontSize' && field.inheritGlobal) {
710
- const rawFs =
711
- raw !== undefined && raw !== null ? String(raw).trim() : '';
714
+ const rawFs = raw !== undefined && raw !== null ? String(raw).trim() : '';
712
715
  return this._fontSizeSliderField(
713
716
  field.label,
714
717
  rawFs,
@@ -747,14 +750,20 @@ export class RightPanel {
747
750
  Number(dp?.paddingBottom ?? 0),
748
751
  Number(dp?.paddingLeft ?? 0),
749
752
  ];
750
- return this._spacingField(field.label, quad, (vals) => {
751
- this.opts.store.update((d) => {
752
- const loc = findBlockLocation(d, blockId);
753
- if (!loc) return;
754
- const pr = loc.block.props as Record<string, number>;
755
- [pr.paddingTop, pr.paddingRight, pr.paddingBottom, pr.paddingLeft] = vals;
756
- });
757
- }, `${fp}:pad`, resetQuad);
753
+ return this._spacingField(
754
+ field.label,
755
+ quad,
756
+ (vals) => {
757
+ this.opts.store.update((d) => {
758
+ const loc = findBlockLocation(d, blockId);
759
+ if (!loc) return;
760
+ const pr = loc.block.props as Record<string, number>;
761
+ [pr.paddingTop, pr.paddingRight, pr.paddingBottom, pr.paddingLeft] = vals;
762
+ });
763
+ },
764
+ `${fp}:pad`,
765
+ resetQuad,
766
+ );
758
767
  }
759
768
  case 'socialLinkList': {
760
769
  const rows = Array.isArray(value)
@@ -811,9 +820,7 @@ export class RightPanel {
811
820
  };
812
821
 
813
822
  const syncItem = (index: number, patch: Partial<SocialLinkRow>) => {
814
- patchElements((rows) =>
815
- rows.map((x, i) => (i === index ? { ...x, ...patch } : x)),
816
- );
823
+ patchElements((rows) => rows.map((x, i) => (i === index ? { ...x, ...patch } : x)));
817
824
  };
818
825
 
819
826
  const removeAt = (index: number) => {
@@ -827,8 +834,7 @@ export class RightPanel {
827
834
  const sel = h('select', {
828
835
  class: 'sm-select sm-social-link-list__network',
829
836
  ...(focusPrefix ? { 'data-sm-focus': `${focusPrefix}:row:${i}:net` } : {}),
830
- onchange: (e: Event) =>
831
- syncItem(i, { network: (e.target as HTMLSelectElement).value }),
837
+ onchange: (e: Event) => syncItem(i, { network: (e.target as HTMLSelectElement).value }),
832
838
  });
833
839
  for (const o of networkOptions) {
834
840
  const opt = document.createElement('option');
@@ -865,9 +871,9 @@ export class RightPanel {
865
871
  syncHrefWarn(rowData.href);
866
872
  const rm = h(
867
873
  'button',
868
- {
869
- class: 'sm-social-link-list__remove',
870
- type: 'button',
874
+ {
875
+ class: 'sm-social-link-list__remove',
876
+ type: 'button',
871
877
  title: this.opts.t('common.delete'),
872
878
  onclick: () => removeAt(i),
873
879
  },
@@ -879,8 +885,7 @@ export class RightPanel {
879
885
  const extras = h('div', { class: 'sm-social-link-list__row-extras' });
880
886
  const bgResolved = defaultSocialIconBackground(rowData.network);
881
887
  const bgStored = rowData.backgroundColor?.trim();
882
- const bgDisplay =
883
- bgStored && bgStored.startsWith('#') ? bgStored : bgResolved;
888
+ const bgDisplay = bgStored?.startsWith('#') ? bgStored : bgResolved;
884
889
  extras.append(
885
890
  h('input', {
886
891
  class: 'sm-input sm-social-link-list__label',
@@ -988,8 +993,7 @@ export class RightPanel {
988
993
  const assets = this.opts.imageAssets;
989
994
  const showUploadBtn = !!assets?.uploadImage && assets.showUpload !== false;
990
995
  const showGalleryBtn =
991
- assets?.showGallery === true &&
992
- (!!assets.imageGallery || !!assets.pickImageFromGallery);
996
+ assets?.showGallery === true && (!!assets.imageGallery || !!assets.pickImageFromGallery);
993
997
  const hasAssetCallbacks = !!(
994
998
  assets?.uploadImage ||
995
999
  assets?.pickImageFromGallery ||
@@ -1020,7 +1024,7 @@ export class RightPanel {
1020
1024
  urlInp.value = t;
1021
1025
  };
1022
1026
 
1023
- const runAsync = (p: Promise<string | null | void>, labelErr: string) => {
1027
+ const runAsync = (p: Promise<unknown>, labelErr: string) => {
1024
1028
  p.catch((e) => {
1025
1029
  console.error(`[simple-mail] ${labelErr}`, e);
1026
1030
  });
@@ -1060,9 +1064,7 @@ export class RightPanel {
1060
1064
  ? this.opts.t('rightPanel.image.uploadTitle')
1061
1065
  : uploadPerm.tip,
1062
1066
  disabled: !uploadPerm.allowed,
1063
- ...(uploadPerm.allowed
1064
- ? { onclick: () => fileInp.click() }
1065
- : {}),
1067
+ ...(uploadPerm.allowed ? { onclick: () => fileInp.click() } : {}),
1066
1068
  },
1067
1069
  [this.opts.t('rightPanel.image.upload')],
1068
1070
  ),
@@ -1155,16 +1157,12 @@ export class RightPanel {
1155
1157
  warnEl.style.display = '';
1156
1158
  }
1157
1159
  }
1158
- return h(
1159
- 'div',
1160
- { class: 'sm-field' },
1161
- [
1162
- h('label', { class: 'sm-field__label' }, [label]),
1163
- ta,
1164
- warnEl,
1165
- help ? h('div', { class: 'sm-field__help' }, [help]) : null,
1166
- ],
1167
- );
1160
+ return h('div', { class: 'sm-field' }, [
1161
+ h('label', { class: 'sm-field__label' }, [label]),
1162
+ ta,
1163
+ warnEl,
1164
+ help ? h('div', { class: 'sm-field__help' }, [help]) : null,
1165
+ ]);
1168
1166
  }
1169
1167
  private _numberField(
1170
1168
  label: string,
@@ -1178,10 +1176,11 @@ export class RightPanel {
1178
1176
  ): HTMLElement {
1179
1177
  const field = this._buildNumberField(label, value, min, max, onChange, step, focusToken);
1180
1178
  if (!help) return field;
1181
- return h('div', { class: 'sm-field', style: 'flex-direction:column;align-items:flex-start;gap:6px;' }, [
1182
- field,
1183
- h('div', { class: 'sm-field__help' }, [help]),
1184
- ]);
1179
+ return h(
1180
+ 'div',
1181
+ { class: 'sm-field', style: 'flex-direction:column;align-items:flex-start;gap:6px;' },
1182
+ [field, h('div', { class: 'sm-field__help' }, [help])],
1183
+ );
1185
1184
  }
1186
1185
 
1187
1186
  private _buildNumberField(
@@ -1464,7 +1463,7 @@ export class RightPanel {
1464
1463
  (v) =>
1465
1464
  this.opts.store.update((d) => {
1466
1465
  const stored = normalizeGlobalListIndentStorage(Number(v));
1467
- if (stored === undefined) delete d.styles.listIndentDefaultPx;
1466
+ if (stored === undefined) d.styles.listIndentDefaultPx = undefined;
1468
1467
  else d.styles.listIndentDefaultPx = stored;
1469
1468
  }),
1470
1469
  'doc:styles.listIndentDefaultPx',
@@ -1517,23 +1516,28 @@ export class RightPanel {
1517
1516
  help?: string,
1518
1517
  focusToken?: string,
1519
1518
  ): HTMLElement {
1520
- const row = h('label', {
1521
- class: 'sm-field',
1522
- style: 'flex-direction:row;align-items:center;gap:8px;',
1523
- }, [
1524
- h('input', {
1525
- type: 'checkbox',
1526
- ...(focusToken ? { 'data-sm-focus': focusToken } : {}),
1527
- onchange: (e: Event) => onChange((e.target as HTMLInputElement).checked),
1528
- ...(value ? { checked: true } : {}),
1529
- } as any),
1530
- h('span', { class: 'sm-field__label' }, [label]),
1531
- ]);
1519
+ const row = h(
1520
+ 'label',
1521
+ {
1522
+ class: 'sm-field',
1523
+ style: 'flex-direction:row;align-items:center;gap:8px;',
1524
+ },
1525
+ [
1526
+ h('input', {
1527
+ type: 'checkbox',
1528
+ ...(focusToken ? { 'data-sm-focus': focusToken } : {}),
1529
+ onchange: (e: Event) => onChange((e.target as HTMLInputElement).checked),
1530
+ ...(value ? { checked: true } : {}),
1531
+ } as any),
1532
+ h('span', { class: 'sm-field__label' }, [label]),
1533
+ ],
1534
+ );
1532
1535
  if (!help) return row;
1533
- return h('div', { class: 'sm-field', style: 'flex-direction:column;align-items:flex-start;gap:6px;' }, [
1534
- row,
1535
- h('div', { class: 'sm-field__help' }, [help]),
1536
- ]);
1536
+ return h(
1537
+ 'div',
1538
+ { class: 'sm-field', style: 'flex-direction:column;align-items:flex-start;gap:6px;' },
1539
+ [row, h('div', { class: 'sm-field__help' }, [help])],
1540
+ );
1537
1541
  }
1538
1542
  private _spacingField(
1539
1543
  label: string,
@@ -1662,7 +1666,9 @@ export class RightPanel {
1662
1666
 
1663
1667
  return h('div', { class: 'sm-field' }, [
1664
1668
  h('div', { class: 'sm-spacing-field__label-row' }, [
1665
- h('label', { class: 'sm-field__label' }, [`${label}${this.opts.t('rightPanel.spacing.suffix')}`]),
1669
+ h('label', { class: 'sm-field__label' }, [
1670
+ `${label}${this.opts.t('rightPanel.spacing.suffix')}`,
1671
+ ]),
1666
1672
  resetQuad
1667
1673
  ? h(
1668
1674
  'button',
@@ -1707,10 +1713,14 @@ export class RightPanel {
1707
1713
  ...(focusToken ? { 'data-sm-focus': focusToken } : {}),
1708
1714
  ...(inheritOn ? { checked: true } : {}),
1709
1715
  }) as HTMLInputElement;
1710
- const valueEl = h('span', {
1711
- class: 'sm-inherit-switch__value',
1712
- hidden: !inheritOn,
1713
- }, [globalDisplay]);
1716
+ const valueEl = h(
1717
+ 'span',
1718
+ {
1719
+ class: 'sm-inherit-switch__value',
1720
+ hidden: !inheritOn,
1721
+ },
1722
+ [globalDisplay],
1723
+ );
1714
1724
  const el = h('div', { class: 'sm-inherit-switch' }, [
1715
1725
  h('label', { class: 'sm-inherit-switch__label' }, [
1716
1726
  input,
@@ -1737,6 +1747,7 @@ export class RightPanel {
1737
1747
  help?: string,
1738
1748
  ): HTMLElement {
1739
1749
  let inheriting = !stored;
1750
+ let storedValue = stored;
1740
1751
  const group = h('div', {
1741
1752
  class: 'sm-segmented sm-segmented--fill',
1742
1753
  role: 'group',
@@ -1745,7 +1756,7 @@ export class RightPanel {
1745
1756
 
1746
1757
  const syncGroup = () => {
1747
1758
  group.classList.toggle('sm-segmented--disabled', inheriting);
1748
- const display = inheriting ? '' : normalizeFontWeightStep(stored || globalWeight);
1759
+ const display = inheriting ? '' : normalizeFontWeightStep(storedValue || globalWeight);
1749
1760
  clear(group);
1750
1761
  for (const o of options) {
1751
1762
  const active = !inheriting && o.value === display;
@@ -1760,7 +1771,7 @@ export class RightPanel {
1760
1771
  ...(focusToken ? { 'data-sm-focus': `${focusToken}:${o.value}` } : {}),
1761
1772
  onclick: () => {
1762
1773
  inheriting = false;
1763
- stored = o.value;
1774
+ storedValue = o.value;
1764
1775
  inheritSwitch.setInherit(false);
1765
1776
  syncGroup();
1766
1777
  onChange(o.value);
@@ -1778,14 +1789,14 @@ export class RightPanel {
1778
1789
  (on) => {
1779
1790
  if (on) {
1780
1791
  inheriting = true;
1781
- stored = '';
1792
+ storedValue = '';
1782
1793
  inheritSwitch.setInherit(true, globalWeight);
1783
1794
  syncGroup();
1784
1795
  onChange('');
1785
1796
  return;
1786
1797
  }
1787
1798
  inheriting = false;
1788
- stored = globalWeight;
1799
+ storedValue = globalWeight;
1789
1800
  inheritSwitch.setInherit(false);
1790
1801
  syncGroup();
1791
1802
  onChange(globalWeight);
@@ -1814,6 +1825,7 @@ export class RightPanel {
1814
1825
  ): HTMLElement {
1815
1826
  const globalPx = this._snapFontSizeToPx(globalFontSize);
1816
1827
  let inheriting = !stored.trim();
1828
+ let storedValue = stored;
1817
1829
  const input = h('input', {
1818
1830
  class: 'sm-input',
1819
1831
  type: 'text',
@@ -1822,7 +1834,7 @@ export class RightPanel {
1822
1834
 
1823
1835
  const syncInput = () => {
1824
1836
  input.disabled = inheriting;
1825
- input.value = inheriting ? '' : stored;
1837
+ input.value = inheriting ? '' : storedValue;
1826
1838
  input.placeholder = inheriting ? '' : this.opts.t('rightPanel.fontSize.placeholder');
1827
1839
  input.classList.toggle('sm-input--inherit-disabled', inheriting);
1828
1840
  };
@@ -1833,17 +1845,17 @@ export class RightPanel {
1833
1845
  (on) => {
1834
1846
  if (on) {
1835
1847
  inheriting = true;
1836
- stored = '';
1848
+ storedValue = '';
1837
1849
  inheritSwitch.setInherit(true, `${globalPx}px`);
1838
1850
  syncInput();
1839
1851
  onChange('');
1840
1852
  return;
1841
1853
  }
1842
1854
  inheriting = false;
1843
- stored = `${globalPx}px`;
1855
+ storedValue = `${globalPx}px`;
1844
1856
  inheritSwitch.setInherit(false);
1845
1857
  syncInput();
1846
- onChange(stored);
1858
+ onChange(storedValue);
1847
1859
  },
1848
1860
  `${focusToken}:inherit`,
1849
1861
  );
@@ -1852,17 +1864,17 @@ export class RightPanel {
1852
1864
 
1853
1865
  input.addEventListener('input', () => {
1854
1866
  if (inheriting) return;
1855
- stored = input.value;
1856
- onChange(stored);
1867
+ storedValue = input.value;
1868
+ onChange(storedValue);
1857
1869
  });
1858
1870
 
1859
1871
  input.addEventListener('blur', () => {
1860
1872
  if (inheriting) return;
1861
1873
  const t = input.value.trim();
1862
1874
  if (!t) {
1863
- stored = `${globalPx}px`;
1864
- input.value = stored;
1865
- onChange(stored);
1875
+ storedValue = `${globalPx}px`;
1876
+ input.value = storedValue;
1877
+ onChange(storedValue);
1866
1878
  }
1867
1879
  });
1868
1880
 
@@ -1878,7 +1890,7 @@ export class RightPanel {
1878
1890
  private _snapFontSizeToPx(value: string): number {
1879
1891
  const t = (value ?? '').trim();
1880
1892
  const m = t.match(/^(\d+(\.\d+)?)/);
1881
- if (m) return Math.min(48, Math.max(10, Math.round(parseFloat(m[1]))));
1893
+ if (m) return Math.min(48, Math.max(10, Math.round(Number.parseFloat(m[1]))));
1882
1894
  return 16;
1883
1895
  }
1884
1896
 
@@ -2043,9 +2055,7 @@ export class RightPanel {
2043
2055
  const wrap = h('div', { class: 'sm-field' });
2044
2056
  wrap.append(h('label', { class: 'sm-field__label' }, [label]));
2045
2057
  wrap.append(
2046
- h('div', { class: 'sm-field__help' }, [
2047
- this.opts.t('rightPanel.width.customHelp'),
2048
- ]),
2058
+ h('div', { class: 'sm-field__help' }, [this.opts.t('rightPanel.width.customHelp')]),
2049
2059
  );
2050
2060
  wrap.append(
2051
2061
  h('input', {
@@ -2165,7 +2175,13 @@ export class RightPanel {
2165
2175
 
2166
2176
  private _docFontWeightField(value: string, onChange: (v: string) => void, focusToken: string) {
2167
2177
  const cur = normalizeFontWeightStep(value);
2168
- return this._segmentedSelectField(this.opts.t('rightPanel.doc.fontWeight'), cur, FONT_WEIGHT_STEP_OPTIONS, onChange, focusToken);
2178
+ return this._segmentedSelectField(
2179
+ this.opts.t('rightPanel.doc.fontWeight'),
2180
+ cur,
2181
+ FONT_WEIGHT_STEP_OPTIONS,
2182
+ onChange,
2183
+ focusToken,
2184
+ );
2169
2185
  }
2170
2186
 
2171
2187
  private _multiColumnLayoutField(
@@ -2219,7 +2235,7 @@ export class RightPanel {
2219
2235
  onclick: () => {
2220
2236
  this.opts.store.update((d) => {
2221
2237
  const l = findBlockLocation(d, block.id);
2222
- if (l) delete l.block.lockedMjml;
2238
+ if (l) l.block.lockedMjml = undefined;
2223
2239
  });
2224
2240
  },
2225
2241
  },
@@ -1,12 +1,12 @@
1
+ import { html as cmHtml } from '@codemirror/lang-html';
1
2
  import { EditorState } from '@codemirror/state';
2
3
  import { EditorView, basicSetup } from 'codemirror';
3
- import { html as cmHtml } from '@codemirror/lang-html';
4
+ import type { SimpleMailT } from '../i18n';
4
5
  import type { Registry } from '../registry/registry';
5
6
  import { renderDoc } from '../renderer';
6
7
  import type { Store } from '../store/store';
7
8
  import { clear, h } from '../utils/dom';
8
9
  import { richTextExecCommand } from '../utils/richTextCommand';
9
- import type { SimpleMailT } from '../i18n';
10
10
 
11
11
  export interface SourceViewOptions {
12
12
  store: Store;
@@ -49,7 +49,9 @@ export class SourceView {
49
49
  }
50
50
 
51
51
  private _isVisible(): boolean {
52
- return this.el.isConnected && (this.el.offsetParent !== null || this.el.getClientRects().length > 0);
52
+ return (
53
+ this.el.isConnected && (this.el.offsetParent !== null || this.el.getClientRects().length > 0)
54
+ );
53
55
  }
54
56
 
55
57
  private _render() {