email-builder-utils 1.1.50 → 1.1.52

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.
@@ -55,15 +55,15 @@ function appendOutlookForButton(buttonData) {
55
55
  const tableMargin = containerAlign === 'center' ? 'margin:0 auto;'
56
56
  : containerAlign === 'right' ? 'margin-left:auto;margin-right:0;'
57
57
  : '';
58
- const nonMsoAnchor = `<!--[if !mso]><!-->
59
- <table border="0" cellpadding="0" cellspacing="0" role="presentation"${tableAlignAttr} style="border-collapse:separate;${tableMargin}">
60
- <tr>
61
- <td bgcolor="${bgColor}" align="center" valign="middle"${tdWidthAttr}${tdHeightAttr} style="background-color:${bgColor};border-radius:${br}px;${borderCss}${tdWidthCss}${tdHeightCss}box-sizing:border-box;mso-padding-alt:0;text-align:center;">
62
- <a href="${safeHref}" target="_blank" rel="noreferrer noopener"
63
- style="${anchorBoxStyles}color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};text-decoration:none;text-align:center;white-space:nowrap;-webkit-text-size-adjust:none;box-sizing:border-box;">${text}</a>
64
- </td>
65
- </tr>
66
- </table>
58
+ const nonMsoAnchor = `<!--[if !mso]><!-->
59
+ <table border="0" cellpadding="0" cellspacing="0" role="presentation"${tableAlignAttr} style="border-collapse:separate;${tableMargin}">
60
+ <tr>
61
+ <td bgcolor="${bgColor}" align="center" valign="middle"${tdWidthAttr}${tdHeightAttr} style="background-color:${bgColor};border-radius:${br}px;${borderCss}${tdWidthCss}${tdHeightCss}box-sizing:border-box;mso-padding-alt:0;text-align:center;">
62
+ <a href="${safeHref}" target="_blank" rel="noreferrer noopener"
63
+ style="${anchorBoxStyles}color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};text-decoration:none;text-align:center;white-space:nowrap;-webkit-text-size-adjust:none;box-sizing:border-box;">${text}</a>
64
+ </td>
65
+ </tr>
66
+ </table>
67
67
  <!--<![endif]-->`;
68
68
  // ── MSO: VML bulletproof button (classic Outlook / Word engine) ──
69
69
  // VML arcsize is a percentage of half the shorter side. Clamp to 50% (pill).
@@ -71,18 +71,18 @@ function appendOutlookForButton(buttonData) {
71
71
  const strokeAttrs = bw > 0
72
72
  ? `stroke="true" strokecolor="${bdColor}" strokeweight="${bw}px"`
73
73
  : `stroke="false"`;
74
- const msoButton = `<!--[if mso]>
75
- <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
76
- href="${safeHref}"
77
- style="height:${finalHeight}px;v-text-anchor:middle;width:${vmlWidth}px;"
78
- arcsize="${arcSizePct}%"
79
- ${strokeAttrs}
80
- fillcolor="${bgColor}">
81
- <w:anchorlock/>
82
- <v:textbox inset="0,0,0,0">
83
- <center style="color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};mso-line-height-rule:exactly;line-height:${finalHeight}px;text-decoration:none;">${text}</center>
84
- </v:textbox>
85
- </v:roundrect>
74
+ const msoButton = `<!--[if mso]>
75
+ <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
76
+ href="${safeHref}"
77
+ style="height:${finalHeight}px;v-text-anchor:middle;width:${vmlWidth}px;"
78
+ arcsize="${arcSizePct}%"
79
+ ${strokeAttrs}
80
+ fillcolor="${bgColor}">
81
+ <w:anchorlock/>
82
+ <v:textbox inset="0,0,0,0">
83
+ <center style="color:${safeColor};font-family:${safeFF};font-size:${fs}px;font-weight:${fontWeight};mso-line-height-rule:exactly;line-height:${finalHeight}px;text-decoration:none;">${text}</center>
84
+ </v:textbox>
85
+ </v:roundrect>
86
86
  <![endif]-->`;
87
87
  const innerContent = containerAlign === "center"
88
88
  ? `<center>${msoButton}${nonMsoAnchor}</center>`
@@ -167,14 +167,14 @@ function convertButtonBlock(blockData) {
167
167
  hideOnDesktop: Boolean(props.hideOnDesktop),
168
168
  hideOnMobile: Boolean(props.hideOnMobile),
169
169
  });
170
- return `
171
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" class="${visibilityClass}" data-block-type="button" data-block-props="${buttonBlockProps}" style="border-collapse:collapse;table-layout:fixed;">
172
- <tr>
173
- <td align="${computed.containerAlign}"${(0, common_1.buildOutlookBgAttr)(containerBg)}
174
- style="padding:${padding?.top || 0}px ${padding?.right || 0}px ${padding?.bottom || 0}px ${padding?.left || 0}px;background-color:${containerBg || 'transparent'};">
175
- ${innerContent}
176
- </td>
177
- </tr>
178
- </table>
170
+ return `
171
+ <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" class="${visibilityClass}" data-block-type="button" data-block-props="${buttonBlockProps}" style="border-collapse:collapse;table-layout:fixed;">
172
+ <tr>
173
+ <td align="${computed.containerAlign}"${(0, common_1.buildOutlookBgAttr)(containerBg)}
174
+ style="padding:${padding?.top || 0}px ${padding?.right || 0}px ${padding?.bottom || 0}px ${padding?.left || 0}px;background-color:${containerBg || 'transparent'};">
175
+ ${innerContent}
176
+ </td>
177
+ </tr>
178
+ </table>
179
179
  `;
180
180
  }
@@ -21,23 +21,23 @@ function convertDividerBlockToHtml(blockData) {
21
21
  hideOnMobile ? "hide-mobile" : "",
22
22
  hideOnDesktop ? "hide-desktop" : "",
23
23
  ].filter(Boolean).join(" ");
24
- const dividerContent = `
25
- <table
26
- align="${alignAttr}"
27
- width="${dividerWidth}%"
28
- cellpadding="0"
29
- cellspacing="0"
30
- style="margin:${alignMargin};"
31
- >
32
- <tr>
33
- <td
34
- height="${thickness}"
35
- style="font-size:1px; line-height:1px; background:${dividerColor}; width:100%;"
36
- >
37
- &nbsp;
38
- </td>
39
- </tr>
40
- </table>
24
+ const dividerContent = `
25
+ <table
26
+ align="${alignAttr}"
27
+ width="${dividerWidth}%"
28
+ cellpadding="0"
29
+ cellspacing="0"
30
+ style="margin:${alignMargin};"
31
+ >
32
+ <tr>
33
+ <td
34
+ height="${thickness}"
35
+ style="font-size:1px; line-height:1px; background:${dividerColor}; width:100%;"
36
+ >
37
+ &nbsp;
38
+ </td>
39
+ </tr>
40
+ </table>
41
41
  `;
42
42
  return (0, outlookSupport_1.appendOutlookSupport)(dividerContent, contentStyle, visibilityClass);
43
43
  }
@@ -55,18 +55,18 @@ function convertVerticalDividerBlockToHtml(blockData) {
55
55
  perChanges: [],
56
56
  pxChanges: buildStyles_1.allPxAttributes,
57
57
  });
58
- return `
59
- <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
60
- style="${buildStyles_1.tableCommonStyle}; max-width:600px;" class="${visibilityClass}" data-block-type="vdivider">
61
- <tr>
62
- <td style="${outerStyles}; text-align:center; vertical-align:middle;">
63
- <!--[if mso | IE]>
64
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
65
- <![endif]-->
66
- <!--[if !mso]><!-->
67
- <div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;">&nbsp;</div>
68
- <!--<![endif]-->
69
- </td>
70
- </tr>
58
+ return `
59
+ <table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation"
60
+ style="${buildStyles_1.tableCommonStyle}; max-width:600px;" class="${visibilityClass}" data-block-type="vdivider">
61
+ <tr>
62
+ <td style="${outerStyles}; text-align:center; vertical-align:middle;">
63
+ <!--[if mso | IE]>
64
+ <v:rect xmlns:v="urn:schemas-microsoft-com:vml" fillcolor="${dividerColor}" style="width:${width}px;height:${height}px;" stroke="f"></v:rect>
65
+ <![endif]-->
66
+ <!--[if !mso]><!-->
67
+ <div style="display:inline-block;width:${width}px;height:${height}px;background:${dividerColor};line-height:0;font-size:0;">&nbsp;</div>
68
+ <!--<![endif]-->
69
+ </td>
70
+ </tr>
71
71
  </table>`;
72
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/grid.ts"],"names":[],"mappings":"AAMA,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBAgTtB;AAED,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,EACzB,mBAAmB,UAAQ;;;GAwG5B"}
1
+ {"version":3,"file":"grid.d.ts","sourceRoot":"","sources":["../../../src/utils/blocks/grid.ts"],"names":[],"mappings":"AAMA,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,MAAM,mBA0TtB;AAED,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,GAAG,EACd,QAAQ,EAAE,GAAG,EACb,gBAAgB,EAAE,MAAM,EACxB,iBAAiB,EAAE,MAAM,EACzB,mBAAmB,UAAQ;;;GAwG5B"}
@@ -91,18 +91,18 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
91
91
  : '';
92
92
  const divWrapOpen = divBorderStyle ? `<div style="${divBorderStyle}${divWrapBg}">` : '';
93
93
  const divWrapClose = divBorderStyle ? `</div>` : '';
94
- let html = `
95
- <!--[if mso]>
96
- <table border="0" cellpadding="0" cellspacing="0" width="${msoTableWidth}"${msoBgAttr}
97
- style="border-collapse:collapse;width:${msoTableWidth}px;${msoBgStyle}${innerBgTransparent}"
98
- class="${gridVisibilityClass}">
99
- <![endif]-->
100
- <!--[if !mso]><!-->
101
- <table border="0" cellpadding="0" cellspacing="0" width="100%" align="center"
102
- role="presentation"${nonMsoBgAttr}
103
- style="border-collapse:collapse; table-layout:fixed; ${innerBgTransparent}${tableStyles}; max-width:600px;"
104
- class="${gridVisibilityClass}">
105
- <!--<![endif]-->
94
+ let html = `
95
+ <!--[if mso]>
96
+ <table border="0" cellpadding="0" cellspacing="0" width="${msoTableWidth}"${msoBgAttr}
97
+ style="border-collapse:collapse;width:${msoTableWidth}px;${msoBgStyle}${innerBgTransparent}"
98
+ class="${gridVisibilityClass}">
99
+ <![endif]-->
100
+ <!--[if !mso]><!-->
101
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" align="center"
102
+ role="presentation"${nonMsoBgAttr}
103
+ style="border-collapse:collapse; table-layout:fixed; ${innerBgTransparent}${tableStyles}; max-width:600px;"
104
+ class="${gridVisibilityClass}">
105
+ <!--<![endif]-->
106
106
  `;
107
107
  for (let r = 0; r < visualRows; r++) {
108
108
  html += "<tr>";
@@ -157,47 +157,57 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
157
157
  const visibilityClass = (0, common_1.getVisibilityClass)(childProps);
158
158
  if (childVisible) {
159
159
  const { html: childHtml, styles } = await convertGridCellBlock(child, rootData, widthPercent, adjustedTableWidth, Boolean(divBorderStyle));
160
- // bgcolor on the cell <td> ensures background-color survives Outlook
161
- // compose paste (Word/Web editors strip CSS but keep bgcolor attribute).
160
+ // bgcolor on the cell <td> for Old Outlook (Word engine).
161
+ // When the cell has no border-radius and the grid has no border, use the grid's
162
+ // backgroundColor as the fallback so the grid background shows through empty cells.
163
+ // Skipped for border-radius cells — bgcolor is rectangular and would bleed through
164
+ // the div's rounded-corner clip in CSS-capable clients (corner squares).
162
165
  const cellBgColor = cellStyle.backgroundColor || '';
163
- const cellBgAttr = (0, common_1.buildOutlookBgAttr)(cellBgColor);
164
- html += `
165
- <td
166
- width="${Math.max(1, Math.round(widthPercent))}%"${cellBgAttr}
167
- class="${[responsive ? "stack-column" : "", visibilityClass].filter(Boolean).join(" ")}"
168
- style="width:${widthPercent}%;vertical-align:${verticalAlign};word-break:break-word;${styles}"
169
- >
170
- ${childHtml}
166
+ const cellHasBorderRadius = Boolean(cellStyle.borderRadius);
167
+ const canApplyGridBgFallback = !cellHasBorderRadius && !divBorderStyle;
168
+ const effectiveCellBg = cellBgColor || (canApplyGridBgFallback ? msoBgColor : '');
169
+ const cellBgAttr = (0, common_1.buildOutlookBgAttr)(effectiveCellBg);
170
+ html += `
171
+ <td
172
+ width="${Math.max(1, Math.round(widthPercent))}%"${cellBgAttr}
173
+ class="${[responsive ? "stack-column" : "", visibilityClass].filter(Boolean).join(" ")}"
174
+ style="width:${widthPercent}%;vertical-align:${verticalAlign};word-break:break-word;${styles}"
175
+ >
176
+ ${childHtml}
171
177
  </td>`;
172
178
  // Spacer td between columns — uses width attribute only (no inline style width) so
173
179
  // Outlook mobile (which strips <style>) treats it proportionally, not as a fixed blocker.
174
180
  // col-gap-spacer class hides it when columns stack via CSS media query.
175
181
  if (columnGap > 0 && c !== lastVisibleCol) {
176
- html += `<td width="${columnGap}" class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
182
+ const gapBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
183
+ html += `<td width="${columnGap}"${gapBgAttr} class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
177
184
  }
178
185
  }
179
186
  }
180
187
  else {
181
- html += `
182
- <td width="${Math.max(1, Math.round(widthPercent))}%"
183
- ${responsive ? 'class="stack-column"' : ""}
184
- style="width:${widthPercent}%;vertical-align:top;">
188
+ const emptyBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
189
+ html += `
190
+ <td width="${Math.max(1, Math.round(widthPercent))}%"
191
+ ${emptyBgAttr}
192
+ ${responsive ? 'class="stack-column"' : ""}
193
+ style="width:${widthPercent}%;vertical-align:top;">
185
194
  </td>`;
186
195
  if (columnGap > 0 && c !== lastVisibleCol) {
187
- html += `<td width="${columnGap}" class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
196
+ const gapBgAttr = divBorderStyle ? '' : (0, common_1.buildOutlookBgAttr)(msoBgColor);
197
+ html += `<td width="${columnGap}"${gapBgAttr} class="col-gap-spacer" style="font-size:0;line-height:0;padding:0;" aria-hidden="true"></td>`;
188
198
  }
189
199
  }
190
200
  }
191
201
  html += "</tr>";
192
202
  }
193
203
  // Close both MSO and non-MSO tables
194
- html += `
195
- <!--[if mso]>
196
- </table>
197
- <![endif]-->
198
- <!--[if !mso]><!-->
199
- </table>
200
- <!--<![endif]-->
204
+ html += `
205
+ <!--[if mso]>
206
+ </table>
207
+ <![endif]-->
208
+ <!--[if !mso]><!-->
209
+ </table>
210
+ <!--<![endif]-->
201
211
  `;
202
212
  // ── Background image: canonical multi-client approach ────────────────────
203
213
  //
@@ -223,35 +233,35 @@ async function convertGridBlock(blockData, rootData, cellWidthInPx) {
223
233
  return `<v:fill type="gradient" color="${c1}" color2="${c2}" angle="${vmlAngle}" />`;
224
234
  })()
225
235
  : `<v:fill type="frame" src="${rawBgImageUrl}" color="${fallbackBgColor}" />`;
226
- html = `
227
- <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
228
- style="border-collapse:collapse;table-layout:fixed;width:100%;max-width:${msoTableWidth}px;">
229
- <tr>
230
- <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
231
- ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
232
- style="
233
- width:100%;max-width:${msoTableWidth}px;
234
- background-color:${fallbackBgColor};
235
- ${isGradient ? `background:${effectiveGradient};` : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`}
236
- ">
237
-
238
- <!--[if gte mso 9]>
239
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
240
- fill="true" stroke="false"
241
- style="width:${msoTableWidth}px;">
242
- ${vmlFill}
243
- <v:textbox inset="0,0,0,0">
244
- <![endif]-->
245
-
246
- ${html}
247
-
248
- <!--[if gte mso 9]>
249
- </v:textbox>
250
- </v:rect>
251
- <![endif]-->
252
-
253
- </td>
254
- </tr>
236
+ html = `
237
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
238
+ style="border-collapse:collapse;table-layout:fixed;width:100%;max-width:${msoTableWidth}px;">
239
+ <tr>
240
+ <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
241
+ ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
242
+ style="
243
+ width:100%;max-width:${msoTableWidth}px;
244
+ background-color:${fallbackBgColor};
245
+ ${isGradient ? `background:${effectiveGradient};` : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`}
246
+ ">
247
+
248
+ <!--[if gte mso 9]>
249
+ <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
250
+ fill="true" stroke="false"
251
+ style="width:${msoTableWidth}px;">
252
+ ${vmlFill}
253
+ <v:textbox inset="0,0,0,0">
254
+ <![endif]-->
255
+
256
+ ${html}
257
+
258
+ <!--[if gte mso 9]>
259
+ </v:textbox>
260
+ </v:rect>
261
+ <![endif]-->
262
+
263
+ </td>
264
+ </tr>
255
265
  </table>`;
256
266
  }
257
267
  // Wrap the entire grid (including any bg-image outer table) in a div when the block
@@ -33,23 +33,23 @@ function buildVMLShape({ shape, widthPx, heightPx, imageUrl, backgroundColor, bo
33
33
  const hAlign = hAlignMap[textAlign] || "center";
34
34
  const safeFontSize = Math.max(Math.round(textSize), 10);
35
35
  const textboxMarkup = text && !msoHasBakedText
36
- ? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
37
- <div style="display:table;width:100%;height:100%;">
38
- <div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
39
- <div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
40
- ${text}
41
- </div>
42
- </div>
43
- </div>
36
+ ? `<v:textbox inset="6pt,6pt,6pt,6pt" style="mso-fit-shape-to-text:false;">
37
+ <div style="display:table;width:100%;height:100%;">
38
+ <div style="display:table-cell;vertical-align:${vAlign};text-align:${hAlign};padding:0 6px;">
39
+ <div style="color:${textColor};font-family:Arial, sans-serif;font-size:${safeFontSize}px;line-height:1.3;word-wrap:break-word;">
40
+ ${text}
41
+ </div>
42
+ </div>
43
+ </div>
44
44
  </v:textbox>`
45
45
  : `<v:textbox inset="0,0,0,0"><div style="display:none;">.</div></v:textbox>`;
46
- return `
47
- <v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
48
- style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
49
- ${borderAttrs}
50
- fill="true" fillcolor="${fillColor}"${extraAttr}>
51
- ${fillMarkup}
52
- ${textboxMarkup}
46
+ return `
47
+ <v:${tag} xmlns:v="urn:schemas-microsoft-com:vml"
48
+ style="width:${widthPx}px;height:${heightPx}px;display:inline-block;"
49
+ ${borderAttrs}
50
+ fill="true" fillcolor="${fillColor}"${extraAttr}>
51
+ ${fillMarkup}
52
+ ${textboxMarkup}
53
53
  </v:${tag}>`;
54
54
  }
55
55
  function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth, opts, visibilityClass) {
@@ -70,28 +70,28 @@ function appendOutlookForShape(content, outerContainerWidth, innerContainerWidth
70
70
  const valign = opts.verticalAlign || "middle";
71
71
  const shouldHideInOutlook = visibilityClass.includes("hide-desktop");
72
72
  if (shouldHideInOutlook) {
73
- return `<!--[if !mso]><!-->
74
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
75
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
76
- <tr>
77
- <td valign="${valign}"
78
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
79
- ${vml}
80
- </td>
81
- </tr>
82
- </table>
73
+ return `<!--[if !mso]><!-->
74
+ <table align="${align}" border="0" cellpadding="0" cellspacing="0"
75
+ style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
76
+ <tr>
77
+ <td valign="${valign}"
78
+ style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
79
+ ${vml}
80
+ </td>
81
+ </tr>
82
+ </table>
83
83
  <!--<![endif]-->`;
84
84
  }
85
- return `<!--[if mso]>
86
- <table align="${align}" border="0" cellpadding="0" cellspacing="0"
87
- style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
88
- <tr>
89
- <td valign="${valign}"
90
- style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
91
- ${vml}
92
- </td>
93
- </tr>
94
- </table>
85
+ return `<!--[if mso]>
86
+ <table align="${align}" border="0" cellpadding="0" cellspacing="0"
87
+ style="width:${widthPx}px;height:${heightPx}px;border-collapse:collapse;" class="${visibilityClass}">
88
+ <tr>
89
+ <td valign="${valign}"
90
+ style="padding:${pad.top || 0}px ${pad.right || 0}px ${pad.bottom || 0}px ${pad.left || 0}px;">
91
+ ${vml}
92
+ </td>
93
+ </tr>
94
+ </table>
95
95
  <![endif]-->`;
96
96
  }
97
97
  async function convertShapeBlock(blockData) {
@@ -146,58 +146,58 @@ async function convertShapeBlock(blockData) {
146
146
  let nonMsoContent = "";
147
147
  // --- Case 1: Image + Text ---
148
148
  if (imageUrl && text) {
149
- nonMsoContent = `
150
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
151
- border:${borderWidth}px ${borderStyle} ${borderColor};
152
- border-radius:${resolvedBorderRadius};
153
- background-color:${finalBackgroundColor};
154
- background-image:url('${imageUrl}');
155
- background-position:center center;
156
- background-size:cover;
157
- background-repeat:no-repeat;
158
- overflow:hidden;${alignmentStyle}${customCss || ""}">
159
- <table border="0" cellpadding="0" cellspacing="0" width="100%"
160
- style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
161
- <tr>
162
- <td align="${textAlignStyle}" valign="${verticalAlign}"
163
- height="${resolvedHeightPx}"
164
- style="padding:6px;vertical-align:${verticalAlign};text-align:${textAlignStyle};overflow:hidden;box-sizing:border-box;">
165
- <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text}</div>
166
- </td>
167
- </tr>
168
- </table>
149
+ nonMsoContent = `
150
+ <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
151
+ border:${borderWidth}px ${borderStyle} ${borderColor};
152
+ border-radius:${resolvedBorderRadius};
153
+ background-color:${finalBackgroundColor};
154
+ background-image:url('${imageUrl}');
155
+ background-position:center center;
156
+ background-size:cover;
157
+ background-repeat:no-repeat;
158
+ overflow:hidden;${alignmentStyle}${customCss || ""}">
159
+ <table border="0" cellpadding="0" cellspacing="0" width="100%"
160
+ style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
161
+ <tr>
162
+ <td align="${textAlignStyle}" valign="${verticalAlign}"
163
+ height="${resolvedHeightPx}"
164
+ style="padding:6px;vertical-align:${verticalAlign};text-align:${textAlignStyle};overflow:hidden;box-sizing:border-box;">
165
+ <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text}</div>
166
+ </td>
167
+ </tr>
168
+ </table>
169
169
  </div>`;
170
170
  }
171
171
  // --- Case 2: Image only ---
172
172
  else if (imageUrl) {
173
- nonMsoContent = `
174
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
175
- border:${borderWidth}px ${borderStyle} ${borderColor};
176
- border-radius:${resolvedBorderRadius};
177
- overflow:hidden;${alignmentStyle}${customCss || ""}">
178
- <img src="${imageUrl}" alt="${text || "shape image"}"
179
- width="${resolvedWidthPx}" height="${resolvedHeightPx}"
180
- style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
173
+ nonMsoContent = `
174
+ <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
175
+ border:${borderWidth}px ${borderStyle} ${borderColor};
176
+ border-radius:${resolvedBorderRadius};
177
+ overflow:hidden;${alignmentStyle}${customCss || ""}">
178
+ <img src="${imageUrl}" alt="${text || "shape image"}"
179
+ width="${resolvedWidthPx}" height="${resolvedHeightPx}"
180
+ style="width:100%;height:100%;object-fit:cover;border-radius:${resolvedBorderRadius};display:block;" />
181
181
  </div>`;
182
182
  }
183
183
  // --- Case 3: Text only ---
184
184
  else {
185
- nonMsoContent = `
186
- <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
187
- background-color:${finalBackgroundColor};
188
- border:${borderWidth}px ${borderStyle} ${borderColor};
189
- border-radius:${resolvedBorderRadius};
190
- overflow:hidden;${alignmentStyle}${customCss || ""}">
191
- <table border="0" cellpadding="0" cellspacing="0" width="100%"
192
- style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
193
- <tr>
194
- <td align="${textAlignStyle}" valign="${verticalAlign}"
195
- height="${resolvedHeightPx}"
196
- style="padding:8px;vertical-align:${verticalAlign};text-align:${textAlignStyle};box-sizing:border-box;">
197
- <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text || ""}</div>
198
- </td>
199
- </tr>
200
- </table>
185
+ nonMsoContent = `
186
+ <div style="display:inline-block;width:${resolvedWidthPx}px;height:${resolvedHeightPx}px;max-width:100%;box-sizing:border-box;
187
+ background-color:${finalBackgroundColor};
188
+ border:${borderWidth}px ${borderStyle} ${borderColor};
189
+ border-radius:${resolvedBorderRadius};
190
+ overflow:hidden;${alignmentStyle}${customCss || ""}">
191
+ <table border="0" cellpadding="0" cellspacing="0" width="100%"
192
+ style="width:100%;height:${resolvedHeightPx}px;border-collapse:collapse;">
193
+ <tr>
194
+ <td align="${textAlignStyle}" valign="${verticalAlign}"
195
+ height="${resolvedHeightPx}"
196
+ style="padding:8px;vertical-align:${verticalAlign};text-align:${textAlignStyle};box-sizing:border-box;">
197
+ <div style="${textSizeStyle}text-align:${textAlignStyle};max-width:90%;overflow:hidden;">${text || ""}</div>
198
+ </td>
199
+ </tr>
200
+ </table>
201
201
  </div>`;
202
202
  }
203
203
  // Outlook (VML) fallback
@@ -242,15 +242,15 @@ async function convertShapeBlock(blockData) {
242
242
  hideOnMobile: Boolean(props.hideOnMobile),
243
243
  });
244
244
  // Combine into table wrapper
245
- return `
246
- <table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}" data-block-type="shape" data-block-props="${shapeBlockProps}">
247
- <tr>
248
- <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
249
- ${outlookContent}
250
- <!--[if !mso]><!-->
251
- ${nonMsoContent}
252
- <!--<![endif]-->
253
- </td>
254
- </tr>
245
+ return `
246
+ <table width="100%" style="border-collapse:collapse;table-layout:fixed;max-width:600px;" class="${visibilityClass}" data-block-type="shape" data-block-props="${shapeBlockProps}">
247
+ <tr>
248
+ <td style="padding:${padding.top || 0}px ${padding.right || 0}px ${padding.bottom || 0}px ${padding.left || 0}px;text-align:${alignment};">
249
+ ${outlookContent}
250
+ <!--[if !mso]><!-->
251
+ ${nonMsoContent}
252
+ <!--<![endif]-->
253
+ </td>
254
+ </tr>
255
255
  </table>`;
256
256
  }
@@ -104,31 +104,31 @@ function convertTextBlock(blockData, cellWidthInPx) {
104
104
  const bgCss = isGradient
105
105
  ? `background:${effectiveGradient};`
106
106
  : `background-image:url('${rawBgImageUrl}'); background-position:center center; background-size:cover; background-repeat:no-repeat;`;
107
- const wrappedContent = `
108
- <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
109
- style="border-collapse:collapse;width:100%;max-width:${msoWidth}px;" class="${visibilityClass}">
110
- <tr>
111
- <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
112
- ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
113
- style="width:100%;max-width:${msoWidth}px;background-color:${fallbackBgColor};${bgCss}">
114
-
115
- <!--[if gte mso 9]>
116
- <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
117
- fill="true" stroke="false"
118
- style="width:${msoWidth}px;">
119
- ${vmlFill}
120
- <v:textbox inset="0,0,0,0">
121
- <![endif]-->
122
-
123
- ${textContent}
124
-
125
- <!--[if gte mso 9]>
126
- </v:textbox>
127
- </v:rect>
128
- <![endif]-->
129
-
130
- </td>
131
- </tr>
107
+ const wrappedContent = `
108
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" role="presentation"
109
+ style="border-collapse:collapse;width:100%;max-width:${msoWidth}px;" class="${visibilityClass}">
110
+ <tr>
111
+ <td width="100%"${(0, common_1.buildOutlookBgAttr)(fallbackBgColor)} valign="top"
112
+ ${!isGradient && rawBgImageUrl ? `background="${rawBgImageUrl}"` : ""}
113
+ style="width:100%;max-width:${msoWidth}px;background-color:${fallbackBgColor};${bgCss}">
114
+
115
+ <!--[if gte mso 9]>
116
+ <v:rect xmlns:v="urn:schemas-microsoft-com:vml"
117
+ fill="true" stroke="false"
118
+ style="width:${msoWidth}px;">
119
+ ${vmlFill}
120
+ <v:textbox inset="0,0,0,0">
121
+ <![endif]-->
122
+
123
+ ${textContent}
124
+
125
+ <!--[if gte mso 9]>
126
+ </v:textbox>
127
+ </v:rect>
128
+ <![endif]-->
129
+
130
+ </td>
131
+ </tr>
132
132
  </table>`;
133
133
  return navigateToUrl
134
134
  ? `<a href="${navigateToUrl}" rel="noreferrer noopener" style="${linkColorStyle}text-decoration:none;cursor:pointer;">${wrappedContent}</a>`