fable-editor 1.3.2 → 1.4.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.
@@ -2725,6 +2725,17 @@ class FableEditor {
2725
2725
  const cur = (el.getAttribute('style') || '').trim();
2726
2726
  el.setAttribute('style', cur ? cur.replace(/;?$/, '; ') + decl : decl);
2727
2727
  };
2728
+ /* Replaces the `width:` an element already carries instead of appending a
2729
+ second one and leaving it to the mail client which of the two wins.
2730
+ `max-width` / `min-width` are left alone. */
2731
+ const setWidthPx = (el, w) => {
2732
+ const kept = (el.getAttribute('style') || '')
2733
+ .split(';')
2734
+ .map((d) => d.trim())
2735
+ .filter((d) => d && !/^width\s*:/i.test(d));
2736
+ kept.push(`width:${w}px`);
2737
+ el.setAttribute('style', kept.join('; '));
2738
+ };
2728
2739
  const box = document.createElement('div');
2729
2740
  box.innerHTML = this.ed.innerHTML;
2730
2741
  let found = null;
@@ -2757,18 +2768,26 @@ class FableEditor {
2757
2768
  built from the same HTML) and make it explicit. The width attribute is what
2758
2769
  Outlook desktop follows; max-width keeps it inside narrow mobile clients.
2759
2770
  Documents saved before this existed are fixed here too, without rewriting
2760
- anything the host has stored. */
2771
+ anything the host has stored.
2772
+ The measured width overrides what the markup carries rather than only
2773
+ filling a gap: an image that `max-width:100%` is clamping, and one whose
2774
+ stored size predates a resize, are on screen at the measured width and have
2775
+ to leave at it too — that is what makes the mail match the preview.
2776
+ offsetWidth is the laid-out width, so a rotate/flip transform on the image
2777
+ cannot distort it, and a stale height attribute goes out with the old width:
2778
+ the aspect ratio then comes from the picture itself, exactly as the editor's
2779
+ `height:auto` gives it. */
2761
2780
  const liveImgs = this.ed.querySelectorAll('img');
2762
2781
  box.querySelectorAll('img').forEach((img, i) => {
2763
2782
  if (img.closest('.tpl-media'))
2764
2783
  return;
2765
2784
  const live = liveImgs[i];
2766
- const w = Math.round(live?.getBoundingClientRect().width || 0);
2767
- if (w > 0 && !img.getAttribute('width'))
2785
+ const w = Math.round(live?.offsetWidth || 0);
2786
+ if (w > 0) {
2768
2787
  img.setAttribute('width', String(w));
2769
- const style = img.getAttribute('style') || '';
2770
- if (w > 0 && !/(^|;)\s*width\s*:/i.test(style))
2771
- setStyle(img, `width:${w}px`);
2788
+ img.removeAttribute('height');
2789
+ setWidthPx(img, w);
2790
+ }
2772
2791
  if (!/max-width\s*:/i.test(img.getAttribute('style') || ''))
2773
2792
  setStyle(img, 'max-width:100%');
2774
2793
  if (!/(^|;)\s*height\s*:/i.test(img.getAttribute('style') || ''))
@@ -4607,14 +4626,18 @@ class FableEditor {
4607
4626
  image is exactly the size the user left it at — a narrower preview pane
4608
4627
  would silently shrink it and misreport what will be sent. The dialog gets
4609
4628
  a wider cap to make that width reachable; on a viewport too small for it
4610
- the image's max-width:100% scales things down rather than clipping. */
4629
+ the image's max-width:100% scales things down rather than clipping.
4630
+ box-sizing is pinned because a host page with the usual global
4631
+ border-box rule would otherwise take the 14px padding out of that width
4632
+ and preview every full-width image 28px narrower than it will be sent. */
4611
4633
  body.closest('.dlg')?.classList.add('dlg-preview');
4612
4634
  const box = document.createElement('div');
4613
4635
  box.className = 'pv-box';
4614
4636
  const w = this.contentWidth();
4615
4637
  box.style.cssText =
4616
4638
  `width:${w > 0 ? Math.round(w) : 640}px;max-width:100%;max-height:52vh;overflow:auto;` +
4617
- 'border:1px solid #e3e3e3;border-radius:6px;padding:14px;font-family:Helvetica,Arial,sans-serif;font-size:14px';
4639
+ 'box-sizing:content-box;border:1px solid #e3e3e3;border-radius:6px;padding:14px;' +
4640
+ 'font-family:Helvetica,Arial,sans-serif;font-size:14px';
4618
4641
  box.innerHTML = this.ed.innerHTML;
4619
4642
  body.appendChild(box);
4620
4643
  });
@@ -6242,11 +6265,19 @@ class FableEditor {
6242
6265
  return;
6243
6266
  this.cancelImgHide();
6244
6267
  const startX = e.clientX;
6245
- const startW = img.getBoundingClientRect().width;
6268
+ const startW = img.offsetWidth || img.getBoundingClientRect().width;
6246
6269
  const sign = corner === 'nw' || corner === 'sw' ? -1 : 1;
6270
+ /* `.earea img { max-width:100% }` clamps an image to the text column, so a drag
6271
+ past that edge would store a width the editor never renders — and a mail, which
6272
+ carries no such rule, would then go out at that unseen width. Cap the drag at
6273
+ the column so the stored size is always the size on screen. */
6274
+ const max = Math.round(this.contentWidth());
6247
6275
  this.withNoTextSelect((restore) => {
6248
6276
  const mv = (ev) => {
6249
- img.style.width = Math.max(24, Math.round(startW + sign * (ev.clientX - startX))) + 'px';
6277
+ let w = Math.max(24, Math.round(startW + sign * (ev.clientX - startX)));
6278
+ if (max > 0)
6279
+ w = Math.min(w, max);
6280
+ img.style.width = w + 'px';
6250
6281
  img.style.height = 'auto';
6251
6282
  this.positionImageHandles();
6252
6283
  };
@@ -6256,9 +6287,12 @@ class FableEditor {
6256
6287
  restore();
6257
6288
  /* mirror the final size onto the width attribute: Outlook desktop
6258
6289
  follows the attribute rather than the CSS, so without this a resized
6259
- image still goes out at its original size */
6260
- const w = parseInt(img.style.width, 10);
6290
+ image still goes out at its original size. Read it back from the laid
6291
+ out box offsetWidth ignores any rotate/flip transform — so the
6292
+ attribute, the inline width and what the user sees cannot disagree. */
6293
+ const w = Math.round(img.offsetWidth) || parseInt(img.style.width, 10);
6261
6294
  if (w > 0 && !img.closest('.tpl-media')) {
6295
+ img.style.width = w + 'px';
6262
6296
  img.setAttribute('width', String(w));
6263
6297
  img.removeAttribute('height');
6264
6298
  }