@templatical/import-beefree 0.9.1 → 0.10.1
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.ts +124 -123
- package/dist/index.js +580 -577
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -1,633 +1,636 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// src/block-mapper.ts
|
|
8
|
-
import {
|
|
9
|
-
createTitleBlock,
|
|
10
|
-
createParagraphBlock,
|
|
11
|
-
createImageBlock,
|
|
12
|
-
createButtonBlock,
|
|
13
|
-
createDividerBlock,
|
|
14
|
-
createSpacerBlock,
|
|
15
|
-
createHtmlBlock,
|
|
16
|
-
createSocialIconsBlock,
|
|
17
|
-
createMenuBlock,
|
|
18
|
-
createTableBlock,
|
|
19
|
-
createVideoBlock,
|
|
20
|
-
generateId
|
|
21
|
-
} from "@templatical/types";
|
|
22
|
-
|
|
23
|
-
// src/style-parser.ts
|
|
1
|
+
import { createButtonBlock, createDefaultTemplateContent, createDividerBlock, createHtmlBlock, createImageBlock, createMenuBlock, createParagraphBlock, createSectionBlock, createSocialIconsBlock, createSpacerBlock, createTableBlock, createTitleBlock, createVideoBlock, generateId } from "@templatical/types";
|
|
2
|
+
//#region src/style-parser.ts
|
|
3
|
+
/**
|
|
4
|
+
* Parses CSS-like style values from BeeFree descriptors.
|
|
5
|
+
*/
|
|
24
6
|
function parsePxValue(value) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
7
|
+
if (!value) return 0;
|
|
8
|
+
const match = value.match(/^(-?\d+(?:\.\d+)?)\s*px/);
|
|
9
|
+
return match ? Math.round(parseFloat(match[1])) : 0;
|
|
28
10
|
}
|
|
29
11
|
function parseColor(value) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
12
|
+
if (!value || value === "transparent") return "";
|
|
13
|
+
const trimmed = value.trim();
|
|
14
|
+
if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) return trimmed.toLowerCase();
|
|
15
|
+
if (/^#[0-9a-fA-F]{3}$/.test(trimmed)) {
|
|
16
|
+
const r = trimmed[1];
|
|
17
|
+
const g = trimmed[2];
|
|
18
|
+
const b = trimmed[3];
|
|
19
|
+
return `#${r}${r}${g}${g}${b}${b}`.toLowerCase();
|
|
20
|
+
}
|
|
21
|
+
return trimmed;
|
|
40
22
|
}
|
|
41
23
|
function parseBorderTop(value) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
if (!value) return {
|
|
25
|
+
width: 0,
|
|
26
|
+
style: "solid",
|
|
27
|
+
color: "#000000"
|
|
28
|
+
};
|
|
29
|
+
const parts = value.trim().split(/\s+/);
|
|
30
|
+
return {
|
|
31
|
+
width: parsePxValue(parts[0]),
|
|
32
|
+
style: parts[1] || "solid",
|
|
33
|
+
color: parseColor(parts[2]) || "#000000"
|
|
34
|
+
};
|
|
49
35
|
}
|
|
50
36
|
function extractPadding(style) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
37
|
+
if (!style) return {
|
|
38
|
+
top: 0,
|
|
39
|
+
right: 0,
|
|
40
|
+
bottom: 0,
|
|
41
|
+
left: 0
|
|
42
|
+
};
|
|
43
|
+
if (style.padding) return parseShorthandPadding(style.padding);
|
|
44
|
+
return {
|
|
45
|
+
top: parsePxValue(style["padding-top"]),
|
|
46
|
+
right: parsePxValue(style["padding-right"]),
|
|
47
|
+
bottom: parsePxValue(style["padding-bottom"]),
|
|
48
|
+
left: parsePxValue(style["padding-left"])
|
|
49
|
+
};
|
|
61
50
|
}
|
|
62
51
|
function parseShorthandPadding(value) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
right: values[1],
|
|
91
|
-
bottom: values[2],
|
|
92
|
-
left: values[3]
|
|
93
|
-
};
|
|
94
|
-
}
|
|
52
|
+
const values = value.trim().split(/\s+/).map((p) => parsePxValue(p));
|
|
53
|
+
switch (values.length) {
|
|
54
|
+
case 1: return {
|
|
55
|
+
top: values[0],
|
|
56
|
+
right: values[0],
|
|
57
|
+
bottom: values[0],
|
|
58
|
+
left: values[0]
|
|
59
|
+
};
|
|
60
|
+
case 2: return {
|
|
61
|
+
top: values[0],
|
|
62
|
+
right: values[1],
|
|
63
|
+
bottom: values[0],
|
|
64
|
+
left: values[1]
|
|
65
|
+
};
|
|
66
|
+
case 3: return {
|
|
67
|
+
top: values[0],
|
|
68
|
+
right: values[1],
|
|
69
|
+
bottom: values[2],
|
|
70
|
+
left: values[1]
|
|
71
|
+
};
|
|
72
|
+
default: return {
|
|
73
|
+
top: values[0],
|
|
74
|
+
right: values[1],
|
|
75
|
+
bottom: values[2],
|
|
76
|
+
left: values[3]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
95
79
|
}
|
|
96
80
|
function parseWidthPercent(value) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
81
|
+
if (!value) return 100;
|
|
82
|
+
const match = value.match(/^(\d+(?:\.\d+)?)\s*%/);
|
|
83
|
+
if (match) return Math.round(parseFloat(match[1]));
|
|
84
|
+
return 100;
|
|
101
85
|
}
|
|
102
86
|
function parseFontFamily(value) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
87
|
+
if (!value) return "";
|
|
88
|
+
return value.split(",")[0].trim().replace(/['"]/g, "");
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/block-mapper.ts
|
|
92
|
+
/**
|
|
93
|
+
* Maps BeeFree module type strings to short keys.
|
|
94
|
+
*/
|
|
95
|
+
const MODULE_TYPE_MAP = {
|
|
96
|
+
"mailup-bee-newsletter-modules-text": "paragraph",
|
|
97
|
+
"mailup-bee-newsletter-modules-paragraph": "paragraph",
|
|
98
|
+
"mailup-bee-newsletter-modules-heading": "title",
|
|
99
|
+
"mailup-bee-newsletter-modules-list": "list",
|
|
100
|
+
"mailup-bee-newsletter-modules-image": "image",
|
|
101
|
+
"mailup-bee-newsletter-modules-button": "button",
|
|
102
|
+
"mailup-bee-newsletter-modules-divider": "divider",
|
|
103
|
+
"mailup-bee-newsletter-modules-spacer": "spacer",
|
|
104
|
+
"mailup-bee-newsletter-modules-html": "html",
|
|
105
|
+
"mailup-bee-newsletter-modules-social": "social",
|
|
106
|
+
"mailup-bee-newsletter-modules-video": "video",
|
|
107
|
+
"mailup-bee-newsletter-modules-menu": "menu",
|
|
108
|
+
"mailup-bee-newsletter-modules-table": "table"
|
|
122
109
|
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
const SOCIAL_PLATFORM_MAP = {
|
|
111
|
+
facebook: "facebook",
|
|
112
|
+
twitter: "twitter",
|
|
113
|
+
x: "twitter",
|
|
114
|
+
instagram: "instagram",
|
|
115
|
+
linkedin: "linkedin",
|
|
116
|
+
youtube: "youtube",
|
|
117
|
+
tiktok: "tiktok",
|
|
118
|
+
pinterest: "pinterest",
|
|
119
|
+
email: "email",
|
|
120
|
+
whatsapp: "whatsapp",
|
|
121
|
+
telegram: "telegram",
|
|
122
|
+
discord: "discord",
|
|
123
|
+
snapchat: "snapchat",
|
|
124
|
+
reddit: "reddit",
|
|
125
|
+
github: "github",
|
|
126
|
+
dribbble: "dribbble",
|
|
127
|
+
behance: "behance"
|
|
141
128
|
};
|
|
142
129
|
function toAlign(value, fallback = "left") {
|
|
143
|
-
|
|
144
|
-
|
|
130
|
+
if (value === "left" || value === "center" || value === "right") return value;
|
|
131
|
+
return fallback;
|
|
145
132
|
}
|
|
146
133
|
function toLineStyle(value, fallback = "solid") {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
134
|
+
if (value === "solid" || value === "dashed" || value === "dotted") return value;
|
|
135
|
+
return fallback;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Strip every `<…>` from a button label and reject any stray `<` or `>`
|
|
139
|
+
* left behind (e.g. a truncated `<script`). The original
|
|
140
|
+
* `value.replace(/<[^>]*>/g, "")` was both polynomial-ReDoS over
|
|
141
|
+
* `<<<<…` inputs and an incomplete sanitizer — an unterminated `<script`
|
|
142
|
+
* would survive the strip. Downstream HTML-escapes the label at render
|
|
143
|
+
* time, but stripping here keeps the imported JSON clean.
|
|
144
|
+
*/
|
|
151
145
|
function stripTagsPlain(text) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
return out;
|
|
171
|
-
}
|
|
172
|
-
function defaultMargin() {
|
|
173
|
-
return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
146
|
+
let out = "";
|
|
147
|
+
let i = 0;
|
|
148
|
+
while (i < text.length) {
|
|
149
|
+
if (text[i] === "<") {
|
|
150
|
+
const close = text.indexOf(">", i + 1);
|
|
151
|
+
if (close === -1) break;
|
|
152
|
+
i = close + 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (text[i] === ">") {
|
|
156
|
+
i++;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
out += text[i];
|
|
160
|
+
i++;
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
174
163
|
}
|
|
175
164
|
function makeStyles(descriptor) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
165
|
+
const padding = extractPadding(descriptor.style);
|
|
166
|
+
const bg = parseColor(descriptor.style?.["background-color"]);
|
|
167
|
+
return {
|
|
168
|
+
padding,
|
|
169
|
+
...bg ? { backgroundColor: bg } : {}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Apply BeeFree text styles as TipTap-compatible inline markup.
|
|
174
|
+
* - text-align → added to each <p> tag's style attribute
|
|
175
|
+
* - color, font-size, font-weight, font-family → wrapped in <span style="..."> inside each <p>
|
|
176
|
+
*/
|
|
184
177
|
function inlineStylesToHtml(html, style) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
178
|
+
const spanParts = [];
|
|
179
|
+
const fontSize = parsePxValue(style["font-size"]);
|
|
180
|
+
if (fontSize && fontSize !== 16) spanParts.push(`font-size: ${fontSize}px`);
|
|
181
|
+
const color = parseColor(style.color);
|
|
182
|
+
if (color && color !== "#1a1a1a") spanParts.push(`color: ${color}`);
|
|
183
|
+
const fontWeight = style["font-weight"];
|
|
184
|
+
if (fontWeight && fontWeight !== "normal" && fontWeight !== "400") spanParts.push(`font-weight: ${fontWeight}`);
|
|
185
|
+
const fontFamily = parseFontFamily(style["font-family"]);
|
|
186
|
+
if (fontFamily) spanParts.push(`font-family: ${fontFamily}`);
|
|
187
|
+
const textAlign = style["text-align"];
|
|
188
|
+
const pStyle = textAlign && textAlign !== "left" ? `text-align: ${textAlign}` : "";
|
|
189
|
+
if (!pStyle && spanParts.length === 0) return html;
|
|
190
|
+
const spanStyle = spanParts.join("; ");
|
|
191
|
+
let result = html;
|
|
192
|
+
if (pStyle) result = result.replace(/<p style="([^"]*)">/g, `<p style="$1; ${pStyle}">`).replaceAll("<p>", `<p style="${pStyle}">`);
|
|
193
|
+
if (spanStyle) result = wrapParagraphInner(result, spanStyle);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Wrap the inner content of every `<p …>…</p>` with `<span style="…">…</span>`.
|
|
198
|
+
*
|
|
199
|
+
* Hand-rolled linear scanner instead of `/<p([^>]*)>([\s\S]*?)<\/p>/g` because
|
|
200
|
+
* the regex is polynomial-ReDoS: the engine retries `[\s\S]*?<\/p>` at every
|
|
201
|
+
* `<p` start, so inputs like `<p>a<p>a<p>a…` (no `</p>` ever) cost O(n²).
|
|
202
|
+
* The scanner advances `i` monotonically via `indexOf`, keeping the work
|
|
203
|
+
* strictly O(n).
|
|
204
|
+
*/
|
|
208
205
|
function wrapParagraphInner(html, spanStyle) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
206
|
+
let out = "";
|
|
207
|
+
let i = 0;
|
|
208
|
+
while (i < html.length) {
|
|
209
|
+
const open = html.indexOf("<p", i);
|
|
210
|
+
if (open === -1) {
|
|
211
|
+
out += html.substring(i);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
const afterTagName = html[open + 2];
|
|
215
|
+
if (afterTagName !== ">" && afterTagName !== " " && afterTagName !== " " && afterTagName !== "\n" && afterTagName !== "\r" && afterTagName !== "/") {
|
|
216
|
+
out += html.substring(i, open + 2);
|
|
217
|
+
i = open + 2;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const openEnd = html.indexOf(">", open + 2);
|
|
221
|
+
if (openEnd === -1) {
|
|
222
|
+
out += html.substring(i);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
const closeStart = html.indexOf("</p>", openEnd + 1);
|
|
226
|
+
if (closeStart === -1) {
|
|
227
|
+
out += html.substring(i);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
const inner = html.substring(openEnd + 1, closeStart);
|
|
231
|
+
out += html.substring(i, openEnd + 1);
|
|
232
|
+
out += `<span style="${spanStyle}">${inner}</span></p>`;
|
|
233
|
+
i = closeStart + 4;
|
|
234
|
+
}
|
|
235
|
+
return out;
|
|
239
236
|
}
|
|
240
237
|
function convertText(descriptor) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
styles: makeStyles(descriptor)
|
|
247
|
-
});
|
|
238
|
+
const textContent = descriptor.text ?? descriptor.paragraph ?? descriptor.list;
|
|
239
|
+
return createParagraphBlock({
|
|
240
|
+
content: inlineStylesToHtml(textContent?.html ?? "", textContent?.style ?? {}),
|
|
241
|
+
styles: makeStyles(descriptor)
|
|
242
|
+
});
|
|
248
243
|
}
|
|
249
244
|
function parseHeadingLevel(tag) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
245
|
+
const match = tag.match(/^h(\d)$/i);
|
|
246
|
+
if (match) {
|
|
247
|
+
const num = Number(match[1]);
|
|
248
|
+
if (num >= 1 && num <= 4) return num;
|
|
249
|
+
}
|
|
250
|
+
return 2;
|
|
256
251
|
}
|
|
257
252
|
function convertHeading(descriptor) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
253
|
+
const heading = descriptor.heading;
|
|
254
|
+
if (!heading) return convertText(descriptor);
|
|
255
|
+
const style = heading.style ?? {};
|
|
256
|
+
const tag = heading.title ?? "h2";
|
|
257
|
+
const text = heading.text ?? "";
|
|
258
|
+
const content = text.startsWith("<") ? text.replace(/^<h\d[^>]*>|<\/h\d>$/gi, "") : text;
|
|
259
|
+
return createTitleBlock({
|
|
260
|
+
content: content ? `<p>${content}</p>` : "<p></p>",
|
|
261
|
+
level: parseHeadingLevel(tag),
|
|
262
|
+
color: parseColor(style.color) || "#1a1a1a",
|
|
263
|
+
textAlign: toAlign(style["text-align"]),
|
|
264
|
+
fontFamily: parseFontFamily(style["font-family"]) || void 0,
|
|
265
|
+
styles: makeStyles(descriptor)
|
|
266
|
+
});
|
|
272
267
|
}
|
|
273
268
|
function convertImage(descriptor) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
styles: makeStyles(descriptor)
|
|
285
|
-
});
|
|
269
|
+
const image = descriptor.image;
|
|
270
|
+
if (!image) return createImageBlock({ styles: makeStyles(descriptor) });
|
|
271
|
+
return createImageBlock({
|
|
272
|
+
src: image.src || "",
|
|
273
|
+
alt: image.alt || "",
|
|
274
|
+
width: parsePxValue(image.width) || 600,
|
|
275
|
+
align: toAlign(image.style?.["text-align"], "center"),
|
|
276
|
+
linkUrl: image.href || void 0,
|
|
277
|
+
styles: makeStyles(descriptor)
|
|
278
|
+
});
|
|
286
279
|
}
|
|
287
280
|
function convertButton(descriptor) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
},
|
|
308
|
-
styles: makeStyles(descriptor)
|
|
309
|
-
});
|
|
281
|
+
const button = descriptor.button;
|
|
282
|
+
if (!button) return createButtonBlock({ styles: makeStyles(descriptor) });
|
|
283
|
+
const style = button.style ?? {};
|
|
284
|
+
return createButtonBlock({
|
|
285
|
+
text: button.label ? stripTagsPlain(button.label) : "Button",
|
|
286
|
+
url: button.href || "#",
|
|
287
|
+
backgroundColor: parseColor(style["background-color"]) || "#4f46e5",
|
|
288
|
+
textColor: parseColor(style.color) || "#ffffff",
|
|
289
|
+
borderRadius: parsePxValue(style["border-radius"]),
|
|
290
|
+
fontSize: parsePxValue(style["font-size"]) || 16,
|
|
291
|
+
fontFamily: parseFontFamily(style["font-family"]) || void 0,
|
|
292
|
+
buttonPadding: {
|
|
293
|
+
top: parsePxValue(style["padding-top"]) || 12,
|
|
294
|
+
right: parsePxValue(style["padding-right"]) || 24,
|
|
295
|
+
bottom: parsePxValue(style["padding-bottom"]) || 12,
|
|
296
|
+
left: parsePxValue(style["padding-left"]) || 24
|
|
297
|
+
},
|
|
298
|
+
styles: makeStyles(descriptor)
|
|
299
|
+
});
|
|
310
300
|
}
|
|
311
301
|
function convertDivider(descriptor) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
});
|
|
302
|
+
const style = descriptor.divider?.style ?? {};
|
|
303
|
+
const border = parseBorderTop(style["border-top"]);
|
|
304
|
+
return createDividerBlock({
|
|
305
|
+
lineStyle: toLineStyle(border.style),
|
|
306
|
+
color: border.color,
|
|
307
|
+
thickness: border.width || 1,
|
|
308
|
+
width: parseWidthPercent(style.width),
|
|
309
|
+
styles: makeStyles(descriptor)
|
|
310
|
+
});
|
|
322
311
|
}
|
|
323
312
|
function convertSpacer(descriptor) {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
});
|
|
313
|
+
const spacer = descriptor.spacer;
|
|
314
|
+
return createSpacerBlock({
|
|
315
|
+
height: parsePxValue(spacer?.style?.height) || 24,
|
|
316
|
+
styles: makeStyles(descriptor)
|
|
317
|
+
});
|
|
330
318
|
}
|
|
331
319
|
function convertHtml(descriptor) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
});
|
|
320
|
+
return createHtmlBlock({
|
|
321
|
+
content: descriptor.html?.html ?? "",
|
|
322
|
+
styles: makeStyles(descriptor)
|
|
323
|
+
});
|
|
337
324
|
}
|
|
338
325
|
function convertSocial(descriptor, warnings) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
return createSocialIconsBlock({
|
|
360
|
-
icons,
|
|
361
|
-
styles: makeStyles(descriptor)
|
|
362
|
-
});
|
|
326
|
+
const iconsList = descriptor.iconsList;
|
|
327
|
+
if (!iconsList?.icons) return createSocialIconsBlock({ styles: makeStyles(descriptor) });
|
|
328
|
+
const icons = [];
|
|
329
|
+
for (const beeIcon of iconsList.icons) {
|
|
330
|
+
const id = (beeIcon.id ?? beeIcon.name ?? "").toLowerCase();
|
|
331
|
+
const platform = SOCIAL_PLATFORM_MAP[id];
|
|
332
|
+
if (!platform) {
|
|
333
|
+
warnings.push(`Unrecognized social icon "${beeIcon.name || id}" was skipped.`);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
icons.push({
|
|
337
|
+
id: generateId(),
|
|
338
|
+
platform,
|
|
339
|
+
url: beeIcon.image?.href || "#"
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return createSocialIconsBlock({
|
|
343
|
+
icons,
|
|
344
|
+
styles: makeStyles(descriptor)
|
|
345
|
+
});
|
|
363
346
|
}
|
|
364
347
|
function convertVideo(descriptor) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
styles: makeStyles(descriptor)
|
|
376
|
-
});
|
|
348
|
+
const video = descriptor.video;
|
|
349
|
+
if (!video) return createVideoBlock({ styles: makeStyles(descriptor) });
|
|
350
|
+
return createVideoBlock({
|
|
351
|
+
url: video.src || "",
|
|
352
|
+
thumbnailUrl: video.thumbnail || "",
|
|
353
|
+
alt: video.alt || "",
|
|
354
|
+
width: parsePxValue(video.style?.width) || 600,
|
|
355
|
+
align: toAlign(video.style?.["text-align"], "center"),
|
|
356
|
+
styles: makeStyles(descriptor)
|
|
357
|
+
});
|
|
377
358
|
}
|
|
378
359
|
function convertMenu(descriptor) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
textAlign: toAlign(style["text-align"], "center"),
|
|
400
|
-
styles: makeStyles(descriptor)
|
|
401
|
-
});
|
|
360
|
+
const menu = descriptor.menu;
|
|
361
|
+
if (!menu) return createMenuBlock({ styles: makeStyles(descriptor) });
|
|
362
|
+
const style = menu.style ?? {};
|
|
363
|
+
return createMenuBlock({
|
|
364
|
+
items: (menu.items ?? []).map((item) => ({
|
|
365
|
+
id: generateId(),
|
|
366
|
+
text: item.text || "",
|
|
367
|
+
url: item.link || item.href || "#",
|
|
368
|
+
openInNewTab: item.target === "_blank",
|
|
369
|
+
bold: false,
|
|
370
|
+
underline: false
|
|
371
|
+
})),
|
|
372
|
+
separator: menu.separator || "|",
|
|
373
|
+
separatorColor: parseColor(menu.separatorColor) || "#999999",
|
|
374
|
+
fontSize: parsePxValue(style["font-size"]) || 14,
|
|
375
|
+
color: parseColor(style.color) || "#1a1a1a",
|
|
376
|
+
fontFamily: parseFontFamily(style["font-family"]) || void 0,
|
|
377
|
+
textAlign: toAlign(style["text-align"], "center"),
|
|
378
|
+
styles: makeStyles(descriptor)
|
|
379
|
+
});
|
|
402
380
|
}
|
|
403
381
|
function convertTable(descriptor) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
fontSize: parsePxValue(style["font-size"]) || 14,
|
|
426
|
-
color: parseColor(style.color) || "#1a1a1a",
|
|
427
|
-
textAlign: toAlign(style["text-align"]),
|
|
428
|
-
styles: makeStyles(descriptor)
|
|
429
|
-
});
|
|
382
|
+
const table = descriptor.table;
|
|
383
|
+
if (!table) return createTableBlock({ styles: makeStyles(descriptor) });
|
|
384
|
+
const style = table.style ?? {};
|
|
385
|
+
return createTableBlock({
|
|
386
|
+
rows: (table.rows ?? []).map((row) => ({
|
|
387
|
+
id: generateId(),
|
|
388
|
+
cells: (row.cells ?? []).map((cell) => ({
|
|
389
|
+
id: generateId(),
|
|
390
|
+
content: cell.content ?? cell.html ?? ""
|
|
391
|
+
}))
|
|
392
|
+
})),
|
|
393
|
+
hasHeaderRow: table.hasHeaderRow ?? false,
|
|
394
|
+
headerBackgroundColor: parseColor(table.headerBackgroundColor) || void 0,
|
|
395
|
+
borderColor: parseColor(style["border-color"]) || "#dddddd",
|
|
396
|
+
borderWidth: parsePxValue(style["border-width"]) || 1,
|
|
397
|
+
cellPadding: typeof table.cellPadding === "number" ? table.cellPadding : parsePxValue(table.cellPadding) || 8,
|
|
398
|
+
fontSize: parsePxValue(style["font-size"]) || 14,
|
|
399
|
+
color: parseColor(style.color) || "#1a1a1a",
|
|
400
|
+
textAlign: toAlign(style["text-align"]),
|
|
401
|
+
styles: makeStyles(descriptor)
|
|
402
|
+
});
|
|
430
403
|
}
|
|
431
404
|
function convertHtmlFallback(module) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
405
|
+
const descriptor = module.descriptor;
|
|
406
|
+
let html = "";
|
|
407
|
+
if (descriptor.text?.html) html = descriptor.text.html;
|
|
408
|
+
else if (descriptor.html?.html) html = descriptor.html.html;
|
|
409
|
+
else if (descriptor.heading?.text) html = descriptor.heading.text;
|
|
410
|
+
else html = `<!-- Unsupported BeeFree module: ${module.type} -->`;
|
|
411
|
+
return createHtmlBlock({
|
|
412
|
+
content: html,
|
|
413
|
+
styles: makeStyles(descriptor)
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Converts a single BeeFree module to a Templatical block.
|
|
418
|
+
* Returns the block and a report entry.
|
|
419
|
+
*/
|
|
443
420
|
function convertModule(module, warnings) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
421
|
+
const mappedType = MODULE_TYPE_MAP[module.type];
|
|
422
|
+
const descriptor = module.descriptor;
|
|
423
|
+
if (!mappedType) return {
|
|
424
|
+
block: convertHtmlFallback(module),
|
|
425
|
+
entry: {
|
|
426
|
+
beeFreeModuleType: module.type,
|
|
427
|
+
templaticalBlockType: "html",
|
|
428
|
+
status: "html-fallback",
|
|
429
|
+
note: `Unknown module type "${module.type}" converted to HTML block.`
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
let block;
|
|
433
|
+
let isApproximation = false;
|
|
434
|
+
switch (mappedType) {
|
|
435
|
+
case "paragraph":
|
|
436
|
+
case "list":
|
|
437
|
+
block = convertText(descriptor);
|
|
438
|
+
break;
|
|
439
|
+
case "title":
|
|
440
|
+
block = convertHeading(descriptor);
|
|
441
|
+
break;
|
|
442
|
+
case "image":
|
|
443
|
+
block = convertImage(descriptor);
|
|
444
|
+
break;
|
|
445
|
+
case "button":
|
|
446
|
+
block = convertButton(descriptor);
|
|
447
|
+
break;
|
|
448
|
+
case "divider":
|
|
449
|
+
block = convertDivider(descriptor);
|
|
450
|
+
break;
|
|
451
|
+
case "spacer":
|
|
452
|
+
block = convertSpacer(descriptor);
|
|
453
|
+
break;
|
|
454
|
+
case "html":
|
|
455
|
+
block = convertHtml(descriptor);
|
|
456
|
+
break;
|
|
457
|
+
case "social":
|
|
458
|
+
block = convertSocial(descriptor, warnings);
|
|
459
|
+
break;
|
|
460
|
+
case "video":
|
|
461
|
+
block = convertVideo(descriptor);
|
|
462
|
+
break;
|
|
463
|
+
case "menu":
|
|
464
|
+
block = convertMenu(descriptor);
|
|
465
|
+
isApproximation = true;
|
|
466
|
+
break;
|
|
467
|
+
case "table":
|
|
468
|
+
block = convertTable(descriptor);
|
|
469
|
+
break;
|
|
470
|
+
default:
|
|
471
|
+
block = convertHtmlFallback(module);
|
|
472
|
+
return {
|
|
473
|
+
block,
|
|
474
|
+
entry: {
|
|
475
|
+
beeFreeModuleType: module.type,
|
|
476
|
+
templaticalBlockType: "html",
|
|
477
|
+
status: "html-fallback"
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
block,
|
|
483
|
+
entry: {
|
|
484
|
+
beeFreeModuleType: module.type,
|
|
485
|
+
templaticalBlockType: block.type,
|
|
486
|
+
status: isApproximation ? "approximated" : "converted"
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region src/converter.ts
|
|
492
|
+
/**
|
|
493
|
+
* Determines the Templatical ColumnLayout from BeeFree column grid values.
|
|
494
|
+
*/
|
|
517
495
|
function resolveColumnLayout(columns, warnings) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
}
|
|
496
|
+
if (columns.length === 1) return null;
|
|
497
|
+
if (columns.length === 3) return "3";
|
|
498
|
+
if (columns.length === 2) {
|
|
499
|
+
const left = columns[0]["grid-columns"] ?? 6;
|
|
500
|
+
const ratio = left / (left + (columns[1]["grid-columns"] ?? 6));
|
|
501
|
+
if (ratio > .58) return "2-1";
|
|
502
|
+
if (ratio < .42) return "1-2";
|
|
503
|
+
return "2";
|
|
504
|
+
}
|
|
505
|
+
if (columns.length >= 4) {
|
|
506
|
+
warnings.push(`Row with ${columns.length} columns was flattened to a single column. BeeFree supports arbitrary columns, but Templatical supports up to 3.`);
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
return "2";
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Converts all modules in a column to Templatical blocks.
|
|
513
|
+
*/
|
|
537
514
|
function convertColumnModules(column, entries, warnings) {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
}
|
|
515
|
+
const blocks = [];
|
|
516
|
+
for (const module of column.modules) {
|
|
517
|
+
const { block, entry } = convertModule(module, warnings);
|
|
518
|
+
blocks.push(block);
|
|
519
|
+
entries.push(entry);
|
|
520
|
+
}
|
|
521
|
+
return blocks;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Processes a single BeeFree row into one or more Templatical blocks.
|
|
525
|
+
*/
|
|
546
526
|
function processRow(row, entries, warnings) {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
527
|
+
const columns = row.columns;
|
|
528
|
+
if (!columns || columns.length === 0) return [];
|
|
529
|
+
if (row.locked) warnings.push("A locked row was imported. Lock metadata was dropped; content is preserved.");
|
|
530
|
+
if (row.synced) warnings.push("A synced row was imported. Sync metadata was dropped; content is preserved.");
|
|
531
|
+
const layout = resolveColumnLayout(columns, warnings);
|
|
532
|
+
const rowBg = parseColor(row.content?.style?.["background-color"]);
|
|
533
|
+
if (!layout) {
|
|
534
|
+
const blocks = [];
|
|
535
|
+
for (const column of columns) blocks.push(...convertColumnModules(column, entries, warnings));
|
|
536
|
+
if (rowBg) return [createSectionBlock({
|
|
537
|
+
columns: "1",
|
|
538
|
+
children: [blocks],
|
|
539
|
+
styles: {
|
|
540
|
+
padding: {
|
|
541
|
+
top: 0,
|
|
542
|
+
right: 0,
|
|
543
|
+
bottom: 0,
|
|
544
|
+
left: 0
|
|
545
|
+
},
|
|
546
|
+
backgroundColor: rowBg
|
|
547
|
+
}
|
|
548
|
+
})];
|
|
549
|
+
return blocks;
|
|
550
|
+
}
|
|
551
|
+
return [createSectionBlock({
|
|
552
|
+
columns: layout,
|
|
553
|
+
children: columns.map((col) => convertColumnModules(col, entries, warnings)),
|
|
554
|
+
styles: {
|
|
555
|
+
padding: {
|
|
556
|
+
top: 0,
|
|
557
|
+
right: 0,
|
|
558
|
+
bottom: 0,
|
|
559
|
+
left: 0
|
|
560
|
+
},
|
|
561
|
+
...rowBg ? { backgroundColor: rowBg } : {}
|
|
562
|
+
}
|
|
563
|
+
})];
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Extracts template-level settings from the BeeFree body.
|
|
567
|
+
*/
|
|
582
568
|
function extractSettings(template) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
569
|
+
const body = template.page.body;
|
|
570
|
+
const contentStyle = body?.content?.style ?? {};
|
|
571
|
+
const containerStyle = body?.container?.style ?? {};
|
|
572
|
+
const width = parsePxValue(contentStyle["width"] ?? contentStyle.width);
|
|
573
|
+
const bgColor = parseColor(contentStyle["background-color"]) || parseColor(containerStyle["background-color"]) || "#ffffff";
|
|
574
|
+
const fontFamily = parseFontFamily(contentStyle["font-family"]) || "Arial";
|
|
575
|
+
return {
|
|
576
|
+
width: width || 600,
|
|
577
|
+
backgroundColor: bgColor,
|
|
578
|
+
fontFamily,
|
|
579
|
+
locale: "en"
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Converts a BeeFree template JSON to Templatical TemplateContent.
|
|
584
|
+
*
|
|
585
|
+
* @param template - The parsed BeeFree JSON object
|
|
586
|
+
* @returns An ImportResult with the converted content and a detailed report
|
|
587
|
+
*
|
|
588
|
+
* @example
|
|
589
|
+
* ```ts
|
|
590
|
+
* import { convertBeeFreeTemplate } from '@templatical/import-beefree';
|
|
591
|
+
*
|
|
592
|
+
* const beeFreeJson = JSON.parse(fileContent);
|
|
593
|
+
* const { content, report } = convertBeeFreeTemplate(beeFreeJson);
|
|
594
|
+
*
|
|
595
|
+
* // Use the content with the editor
|
|
596
|
+
* const editor = init({ container: '#editor', content });
|
|
597
|
+
*
|
|
598
|
+
* // Check the report for any issues
|
|
599
|
+
* console.log(report.summary);
|
|
600
|
+
* console.log(report.warnings);
|
|
601
|
+
* ```
|
|
602
|
+
*/
|
|
596
603
|
function convertBeeFreeTemplate(template) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
export {
|
|
631
|
-
convertBeeFreeTemplate
|
|
632
|
-
};
|
|
604
|
+
if (!template?.page?.rows) throw new Error("Invalid BeeFree template: missing page.rows. Ensure you are passing a valid BeeFree JSON export.");
|
|
605
|
+
const entries = [];
|
|
606
|
+
const warnings = [];
|
|
607
|
+
const blocks = [];
|
|
608
|
+
const webFonts = template.page.body?.webFonts;
|
|
609
|
+
if (webFonts && webFonts.length > 1) warnings.push(`Template uses ${webFonts.length} web fonts. Only the primary font is preserved; additional fonts may need to be configured via the fonts option.`);
|
|
610
|
+
for (const row of template.page.rows) {
|
|
611
|
+
if (row.empty) continue;
|
|
612
|
+
blocks.push(...processRow(row, entries, warnings));
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
content: {
|
|
616
|
+
...createDefaultTemplateContent(),
|
|
617
|
+
blocks,
|
|
618
|
+
settings: extractSettings(template)
|
|
619
|
+
},
|
|
620
|
+
report: {
|
|
621
|
+
entries,
|
|
622
|
+
warnings,
|
|
623
|
+
summary: {
|
|
624
|
+
total: entries.length,
|
|
625
|
+
converted: entries.filter((e) => e.status === "converted").length,
|
|
626
|
+
approximated: entries.filter((e) => e.status === "approximated").length,
|
|
627
|
+
htmlFallback: entries.filter((e) => e.status === "html-fallback").length,
|
|
628
|
+
skipped: entries.filter((e) => e.status === "skipped").length
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
//#endregion
|
|
634
|
+
export { convertBeeFreeTemplate };
|
|
635
|
+
|
|
633
636
|
//# sourceMappingURL=index.js.map
|