@templatical/import-beefree 0.10.0 → 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 -572
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -1,628 +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;
|
|
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;
|
|
171
163
|
}
|
|
172
164
|
function makeStyles(descriptor) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
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
|
+
*/
|
|
180
177
|
function inlineStylesToHtml(html, style) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
*/
|
|
204
205
|
function wrapParagraphInner(html, spanStyle) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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;
|
|
235
236
|
}
|
|
236
237
|
function convertText(descriptor) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
styles: makeStyles(descriptor)
|
|
243
|
-
});
|
|
238
|
+
const textContent = descriptor.text ?? descriptor.paragraph ?? descriptor.list;
|
|
239
|
+
return createParagraphBlock({
|
|
240
|
+
content: inlineStylesToHtml(textContent?.html ?? "", textContent?.style ?? {}),
|
|
241
|
+
styles: makeStyles(descriptor)
|
|
242
|
+
});
|
|
244
243
|
}
|
|
245
244
|
function parseHeadingLevel(tag) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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;
|
|
252
251
|
}
|
|
253
252
|
function convertHeading(descriptor) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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
|
+
});
|
|
268
267
|
}
|
|
269
268
|
function convertImage(descriptor) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
styles: makeStyles(descriptor)
|
|
281
|
-
});
|
|
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
|
+
});
|
|
282
279
|
}
|
|
283
280
|
function convertButton(descriptor) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
},
|
|
304
|
-
styles: makeStyles(descriptor)
|
|
305
|
-
});
|
|
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
|
+
});
|
|
306
300
|
}
|
|
307
301
|
function convertDivider(descriptor) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
});
|
|
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
|
+
});
|
|
318
311
|
}
|
|
319
312
|
function convertSpacer(descriptor) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
});
|
|
313
|
+
const spacer = descriptor.spacer;
|
|
314
|
+
return createSpacerBlock({
|
|
315
|
+
height: parsePxValue(spacer?.style?.height) || 24,
|
|
316
|
+
styles: makeStyles(descriptor)
|
|
317
|
+
});
|
|
326
318
|
}
|
|
327
319
|
function convertHtml(descriptor) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
});
|
|
320
|
+
return createHtmlBlock({
|
|
321
|
+
content: descriptor.html?.html ?? "",
|
|
322
|
+
styles: makeStyles(descriptor)
|
|
323
|
+
});
|
|
333
324
|
}
|
|
334
325
|
function convertSocial(descriptor, warnings) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return createSocialIconsBlock({
|
|
356
|
-
icons,
|
|
357
|
-
styles: makeStyles(descriptor)
|
|
358
|
-
});
|
|
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
|
+
});
|
|
359
346
|
}
|
|
360
347
|
function convertVideo(descriptor) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
styles: makeStyles(descriptor)
|
|
372
|
-
});
|
|
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
|
+
});
|
|
373
358
|
}
|
|
374
359
|
function convertMenu(descriptor) {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
textAlign: toAlign(style["text-align"], "center"),
|
|
396
|
-
styles: makeStyles(descriptor)
|
|
397
|
-
});
|
|
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
|
+
});
|
|
398
380
|
}
|
|
399
381
|
function convertTable(descriptor) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
fontSize: parsePxValue(style["font-size"]) || 14,
|
|
422
|
-
color: parseColor(style.color) || "#1a1a1a",
|
|
423
|
-
textAlign: toAlign(style["text-align"]),
|
|
424
|
-
styles: makeStyles(descriptor)
|
|
425
|
-
});
|
|
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
|
+
});
|
|
426
403
|
}
|
|
427
404
|
function convertHtmlFallback(module) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
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
|
+
*/
|
|
439
420
|
function convertModule(module, warnings) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
-
|
|
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
|
+
*/
|
|
513
495
|
function resolveColumnLayout(columns, warnings) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
}
|
|
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
|
+
*/
|
|
533
514
|
function convertColumnModules(column, entries, warnings) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
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
|
+
*/
|
|
542
526
|
function processRow(row, entries, warnings) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
-
}
|
|
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
|
+
*/
|
|
577
568
|
function extractSettings(template) {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
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
|
+
*/
|
|
591
603
|
function convertBeeFreeTemplate(template) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
export {
|
|
626
|
-
convertBeeFreeTemplate
|
|
627
|
-
};
|
|
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
|
+
|
|
628
636
|
//# sourceMappingURL=index.js.map
|