@formfeed/engine 0.3.10 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/index.cjs +2471 -354
  2. package/index.js +2471 -354
  3. package/package.json +1 -1
  4. package/types/frame/fit.d.cts +13 -0
  5. package/types/frame/fit.d.ts +13 -0
  6. package/types/frame/fit.entry.d.cts +1 -0
  7. package/types/frame/fit.entry.d.ts +1 -0
  8. package/types/frame/stage.d.cts +22 -0
  9. package/types/frame/stage.d.ts +22 -0
  10. package/types/frame/stage.entry.d.cts +1 -0
  11. package/types/frame/stage.entry.d.ts +1 -0
  12. package/types/frame/text-edit.d.cts +75 -0
  13. package/types/frame/text-edit.d.ts +75 -0
  14. package/types/frame/text-edit.entry.d.cts +1 -0
  15. package/types/frame/text-edit.entry.d.ts +1 -0
  16. package/types/index.d.cts +21 -14
  17. package/types/index.d.ts +21 -14
  18. package/types/lib/assemble.d.cts +5 -0
  19. package/types/lib/assemble.d.ts +5 -0
  20. package/types/lib/design/emit.d.cts +20 -0
  21. package/types/lib/design/emit.d.ts +20 -0
  22. package/types/lib/design/fonts.d.cts +27 -0
  23. package/types/lib/design/fonts.d.ts +27 -0
  24. package/types/lib/design/hash.d.cts +6 -0
  25. package/types/lib/design/hash.d.ts +6 -0
  26. package/types/lib/design/index.d.cts +12 -0
  27. package/types/lib/design/index.d.ts +12 -0
  28. package/types/lib/design/layers.d.cts +12 -0
  29. package/types/lib/design/layers.d.ts +12 -0
  30. package/types/lib/design/parse.d.cts +25 -0
  31. package/types/lib/design/parse.d.ts +25 -0
  32. package/types/lib/design/starters.d.cts +14 -0
  33. package/types/lib/design/starters.d.ts +14 -0
  34. package/types/lib/design/types.d.cts +171 -0
  35. package/types/lib/design/types.d.ts +171 -0
  36. package/types/lib/dialect.d.cts +32 -0
  37. package/types/lib/dialect.d.ts +32 -0
  38. package/types/lib/generated/frames.d.cts +6 -0
  39. package/types/lib/generated/frames.d.ts +6 -0
  40. package/types/lib/preview.d.cts +16 -2
  41. package/types/lib/preview.d.ts +16 -2
  42. package/types/lib/source-edit.d.cts +112 -0
  43. package/types/lib/source-edit.d.ts +112 -0
  44. package/types/lib/source-positions.d.cts +12 -3
  45. package/types/lib/source-positions.d.ts +12 -3
  46. package/types/lib/source-scan.d.cts +61 -0
  47. package/types/lib/source-scan.d.ts +61 -0
  48. package/types/lib/translation.d.cts +17 -0
  49. package/types/lib/translation.d.ts +17 -0
package/index.js CHANGED
@@ -182,11 +182,11 @@ function validateEpc(input) {
182
182
  const purpose = String(input.purpose ?? "");
183
183
  if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
184
184
  const reference = String(input.reference ?? "").trim();
185
- const text = String(input.text ?? "").trim();
186
- if (reference && text) problems.push({ field: "reference", code: "reference_or_text" });
185
+ const text2 = String(input.text ?? "").trim();
186
+ if (reference && text2) problems.push({ field: "reference", code: "reference_or_text" });
187
187
  if (reference.length > 35)
188
188
  problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
189
- if (text.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text.length });
189
+ if (text2.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text2.length });
190
190
  const bytes = byteLength(epcPayload(input));
191
191
  if (bytes > EPC_MAX_BYTES)
192
192
  problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
@@ -222,6 +222,12 @@ var barcodeTypes = {
222
222
  datamatrix: "datamatrix",
223
223
  pdf417: "pdf417"
224
224
  };
225
+ var ownTextLayout = /* @__PURE__ */ new Set(["ean13", "ean8", "upca"]);
226
+ function codeText(helper, value) {
227
+ const text2 = value === void 0 || value === null ? "" : String(value);
228
+ if (text2 === "") throw new Error(`${helper}: nothing to encode, the value is empty or missing from the data`);
229
+ return text2;
230
+ }
225
231
  var codeHelpers = [
226
232
  {
227
233
  name: "qrcode",
@@ -238,7 +244,7 @@ var codeHelpers = [
238
244
  },
239
245
  fn: (ctx, value, options) => {
240
246
  const o = options ?? {};
241
- const svg = qrSvg(String(value ?? ""), o);
247
+ const svg = qrSvg(codeText("qrcode", value), o);
242
248
  if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "QR code" });
243
249
  return svgDataUri(svg);
244
250
  }
@@ -258,16 +264,17 @@ var codeHelpers = [
258
264
  fn: (ctx, value, options) => {
259
265
  const o = options ?? {};
260
266
  const bcid = barcodeTypes[String(o.type ?? "code128").toLowerCase()] ?? "code128";
267
+ const text2 = codeText("barcode", value);
261
268
  const svg = toSVG({
262
269
  bcid,
263
- text: String(value ?? ""),
270
+ text: text2,
264
271
  height: o.height ?? 12,
265
272
  ...o.width === void 0 ? {} : { width: o.width },
266
273
  scale: o.scale ?? 2,
267
274
  includetext: o.text !== false,
268
- textxalign: "center"
275
+ ...ownTextLayout.has(bcid) ? {} : { textxalign: "center" }
269
276
  });
270
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${String(value ?? "")}` });
277
+ if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text2}` });
271
278
  return svgDataUri(svg);
272
279
  }
273
280
  },
@@ -318,6 +325,22 @@ import { toCardinal as frWords } from "n2words/fr";
318
325
  import { toCardinal as itWords } from "n2words/it";
319
326
  import { toCardinal as nlWords } from "n2words/nl";
320
327
 
328
+ // packages/engine/src/lib/translation.ts
329
+ function resolveTranslation(dictionaries, locale, key) {
330
+ const lang = locale.split("-")[0] ?? locale;
331
+ for (const candidate of [locale, lang, "en"]) {
332
+ const text2 = dictionaries?.[candidate]?.[key];
333
+ if (typeof text2 === "string") return { text: text2, from: candidate };
334
+ }
335
+ return { text: key, from: null };
336
+ }
337
+ function translationTarget(dictionaries, locale, key) {
338
+ const lang = locale.split("-")[0] ?? locale;
339
+ const { from } = resolveTranslation(dictionaries, locale, key);
340
+ if (from === locale || from === lang) return from;
341
+ return dictionaries?.[locale] ? locale : lang;
342
+ }
343
+
321
344
  // packages/engine/src/lib/helpers/office.ts
322
345
  var officeUnsupportedHelpers = /* @__PURE__ */ new Set(["chart", "markdown"]);
323
346
  function officeUnsupported(name) {
@@ -362,7 +385,9 @@ var str = (v) => v === void 0 || v === null ? "" : String(v);
362
385
  var opt = (v, fallback) => v === void 0 || v === null || v === "" ? fallback : v;
363
386
  function requireFinite(helper, value) {
364
387
  if (typeof value === "number" && !Number.isFinite(value))
365
- throw new Error(`${helper}: got ${value}, the result of a calculation with a value that is missing or not a number`);
388
+ throw new Error(
389
+ `${helper}: got ${value}, the result of a calculation with a value that is missing or not a number`
390
+ );
366
391
  }
367
392
  var wordsByLang = {
368
393
  de: deWords,
@@ -540,13 +565,20 @@ var formatHelpers = [
540
565
  if (Number.isNaN(n)) return "";
541
566
  const named = args.find(isNamedArgs) ?? {};
542
567
  const [decimals, method] = args.filter((a) => !isNamedArgs(a));
543
- const places = Number(opt(decimals, opt(named["precision"], opt(named["decimals"], 0))));
568
+ const places = Number(
569
+ opt(decimals, opt(named["precision"], opt(named["decimals"], 0)))
570
+ );
544
571
  const how = String(opt(method, opt(named["method"], "common")));
545
572
  const f = 10 ** places;
546
573
  const scaled = Number((Math.abs(n) * f).toPrecision(15));
547
- if (how === "floor") return (n < 0 ? -Math.ceil(scaled) : Math.floor(scaled)) / f;
548
- if (how === "ceil") return (n < 0 ? -Math.floor(scaled) : Math.ceil(scaled)) / f;
549
- if (how !== "common") throw new Error(`round: method must be 'common', 'floor' or 'ceil', not '${how}'`);
574
+ if (how === "floor")
575
+ return (n < 0 ? -Math.ceil(scaled) : Math.floor(scaled)) / f;
576
+ if (how === "ceil")
577
+ return (n < 0 ? -Math.floor(scaled) : Math.ceil(scaled)) / f;
578
+ if (how !== "common")
579
+ throw new Error(
580
+ `round: method must be 'common', 'floor' or 'ceil', not '${how}'`
581
+ );
550
582
  return Math.round(scaled + Number.EPSILON) / f * Math.sign(n);
551
583
  }
552
584
  },
@@ -778,11 +810,9 @@ var formatHelpers = [
778
810
  category: "text"
779
811
  },
780
812
  fn: (ctx, key, params) => {
781
- const dictionaries = ctx.i18n ?? {};
782
- const lang = ctx.locale.split("-")[0] ?? ctx.locale;
783
- const text = dictionaries[ctx.locale]?.[str(key)] ?? dictionaries[lang]?.[str(key)] ?? dictionaries["en"]?.[str(key)] ?? str(key);
813
+ const { text: text2 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
784
814
  const p = params ?? {};
785
- return text.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
815
+ return text2.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
786
816
  }
787
817
  },
788
818
  {
@@ -1107,8 +1137,8 @@ function imageMarkup(url, options = {}) {
1107
1137
  options.fit ? `object-fit:${options.fit}` : ""
1108
1138
  ].filter(Boolean).join(";");
1109
1139
  const cls = options.class ? ` class="${escapeHtml(options.class)}"` : "";
1110
- const style = styles ? ` style="${styles}"` : "";
1111
- return `<img src="${escapeHtml(url)}" alt="${escapeHtml(options.alt ?? "")}"${cls}${style}>`;
1140
+ const style2 = styles ? ` style="${styles}"` : "";
1141
+ return `<img src="${escapeHtml(url)}" alt="${escapeHtml(options.alt ?? "")}"${cls}${style2}>`;
1112
1142
  }
1113
1143
  function resolveAsset(base, url) {
1114
1144
  if (!base || /^[a-z][a-z0-9+.-]*:/i.test(url)) return url;
@@ -1396,11 +1426,11 @@ function scanBlocks(source, spec) {
1396
1426
  while (m = pattern.exec(source)) {
1397
1427
  const keyword = m[1] ?? "";
1398
1428
  const rest = m[2] ?? "";
1399
- const range = rangeOf(source, m.index, m.index + m[0].length);
1429
+ const range2 = rangeOf(source, m.index, m.index + m[0].length);
1400
1430
  if (inRaw) {
1401
1431
  if (spec.closers[keyword] === inRaw) {
1402
- const open = stack.pop();
1403
- if (open) open.block.close = range;
1432
+ const open2 = stack.pop();
1433
+ if (open2) open2.block.close = range2;
1404
1434
  inRaw = null;
1405
1435
  }
1406
1436
  continue;
@@ -1408,7 +1438,7 @@ function scanBlocks(source, spec) {
1408
1438
  if (isBlockOpener(keyword, rest)) {
1409
1439
  const block = {
1410
1440
  type: spec.openers[keyword] ?? "other",
1411
- open: range
1441
+ open: range2
1412
1442
  };
1413
1443
  blocks.push(block);
1414
1444
  stack.push({ keyword, block });
@@ -1422,7 +1452,7 @@ function scanBlocks(source, spec) {
1422
1452
  code: "unexpected-end-tag",
1423
1453
  message: `Unexpected {% ${keyword} %} without an open block`,
1424
1454
  args: { keyword },
1425
- range
1455
+ range: range2
1426
1456
  });
1427
1457
  } else if (top.keyword !== expected) {
1428
1458
  diagnostics.push({
@@ -1430,16 +1460,16 @@ function scanBlocks(source, spec) {
1430
1460
  code: "mismatched-end-tag",
1431
1461
  message: `Expected {% end${top.keyword} %} to close the {% ${top.keyword} %} at line ${top.block.open.start.line}, found {% ${keyword} %}`,
1432
1462
  args: { open: top.keyword, line: top.block.open.start.line, found: keyword },
1433
- range
1463
+ range: range2
1434
1464
  });
1435
1465
  const idx = stack.map((s) => s.keyword).lastIndexOf(expected);
1436
1466
  if (idx >= 0) {
1437
1467
  const closed = stack.splice(idx, 1)[0];
1438
- if (closed) closed.block.close = range;
1468
+ if (closed) closed.block.close = range2;
1439
1469
  }
1440
1470
  } else {
1441
1471
  stack.pop();
1442
- top.block.close = range;
1472
+ top.block.close = range2;
1443
1473
  }
1444
1474
  } else if (spec.continuations.includes(keyword) && !stack.length) {
1445
1475
  diagnostics.push({
@@ -1447,17 +1477,17 @@ function scanBlocks(source, spec) {
1447
1477
  code: "unexpected-tag",
1448
1478
  message: `{% ${keyword} %} outside of a block`,
1449
1479
  args: { keyword },
1450
- range
1480
+ range: range2
1451
1481
  });
1452
1482
  }
1453
1483
  }
1454
- for (const open of stack) {
1484
+ for (const open2 of stack) {
1455
1485
  diagnostics.push({
1456
1486
  severity: "error",
1457
1487
  code: "unclosed-block",
1458
- message: `{% ${open.keyword} %} is never closed; add {% end${open.keyword} %}`,
1459
- args: { keyword: open.keyword },
1460
- range: open.block.open
1488
+ message: `{% ${open2.keyword} %} is never closed; add {% end${open2.keyword} %}`,
1489
+ args: { keyword: open2.keyword },
1490
+ range: open2.block.open
1461
1491
  });
1462
1492
  }
1463
1493
  return { blocks, diagnostics };
@@ -1889,8 +1919,8 @@ var SPEC = /^(?:([\s\S])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?([,_])?(?:\.(\d+))?([b
1889
1919
  function group(digits, separator) {
1890
1920
  return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
1891
1921
  }
1892
- function pythonExponent(text) {
1893
- return text.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
1922
+ function pythonExponent(text2) {
1923
+ return text2.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
1894
1924
  }
1895
1925
  function formatNumber(value, type, precision, alternate) {
1896
1926
  if (!Number.isFinite(value)) return Number.isNaN(value) ? "nan" : "inf";
@@ -1903,20 +1933,20 @@ function formatNumber(value, type, precision, alternate) {
1903
1933
  return `${(abs * 100).toFixed(precision ?? 6)}%`;
1904
1934
  case "e":
1905
1935
  case "E": {
1906
- const text = pythonExponent(abs.toExponential(precision ?? 6));
1907
- return type === "E" ? text.toUpperCase() : text;
1936
+ const text2 = pythonExponent(abs.toExponential(precision ?? 6));
1937
+ return type === "E" ? text2.toUpperCase() : text2;
1908
1938
  }
1909
1939
  case "g":
1910
1940
  case "G": {
1911
1941
  const p = precision === 0 ? 1 : precision ?? 6;
1912
1942
  const exp = abs === 0 ? 0 : Math.floor(Math.log10(abs));
1913
- let text = exp < -4 || exp >= p ? pythonExponent(abs.toExponential(p - 1)) : abs.toFixed(Math.max(0, p - 1 - exp));
1943
+ let text2 = exp < -4 || exp >= p ? pythonExponent(abs.toExponential(p - 1)) : abs.toFixed(Math.max(0, p - 1 - exp));
1914
1944
  if (!alternate) {
1915
- const [mantissa = "", exponent] = text.split("e");
1945
+ const [mantissa = "", exponent] = text2.split("e");
1916
1946
  const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
1917
- text = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
1947
+ text2 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
1918
1948
  }
1919
- return type === "G" ? text.toUpperCase() : text;
1949
+ return type === "G" ? text2.toUpperCase() : text2;
1920
1950
  }
1921
1951
  case "d":
1922
1952
  case "n":
@@ -1985,35 +2015,35 @@ function lookupField(name, positional, named, next) {
1985
2015
  function pythonFormat(template, positional, named = {}) {
1986
2016
  let auto = 0;
1987
2017
  const next = () => auto++;
1988
- const render = (text) => {
2018
+ const render = (text2) => {
1989
2019
  let out = "";
1990
- for (let i = 0; i < text.length; i++) {
1991
- const ch = text[i];
2020
+ for (let i = 0; i < text2.length; i++) {
2021
+ const ch = text2[i];
1992
2022
  if (ch === "}") {
1993
2023
  out += "}";
1994
- if (text[i + 1] === "}") i++;
2024
+ if (text2[i + 1] === "}") i++;
1995
2025
  continue;
1996
2026
  }
1997
2027
  if (ch !== "{") {
1998
2028
  out += ch;
1999
2029
  continue;
2000
2030
  }
2001
- if (text[i + 1] === "{") {
2031
+ if (text2[i + 1] === "{") {
2002
2032
  out += "{";
2003
2033
  i++;
2004
2034
  continue;
2005
2035
  }
2006
2036
  let depth = 1;
2007
2037
  let j = i + 1;
2008
- for (; j < text.length && depth > 0; j++) {
2009
- if (text[j] === "{") depth++;
2010
- else if (text[j] === "}") depth--;
2038
+ for (; j < text2.length && depth > 0; j++) {
2039
+ if (text2[j] === "{") depth++;
2040
+ else if (text2[j] === "}") depth--;
2011
2041
  }
2012
2042
  if (depth > 0) {
2013
- out += text.slice(i);
2043
+ out += text2.slice(i);
2014
2044
  break;
2015
2045
  }
2016
- const field = text.slice(i + 1, j - 1);
2046
+ const field = text2.slice(i + 1, j - 1);
2017
2047
  const colon = field.indexOf(":");
2018
2048
  const head = colon < 0 ? field : field.slice(0, colon);
2019
2049
  const name = head.replace(/![rsa]$/, "");
@@ -3535,7 +3565,7 @@ function layeredPartials(own, shared2, onShared) {
3535
3565
  };
3536
3566
  }
3537
3567
 
3538
- // packages/engine/src/lib/snippets.ts
3568
+ // packages/engine/src/lib/dialect.ts
3539
3569
  var loop = (engine, item, list2, body) => engine === "handlebars" ? `{{#each ${list2}}}
3540
3570
  ${body.replace(new RegExp(`(?<![\\w.])${item}\\.`, "g"), "")}
3541
3571
  {{/each}}` : `{% for ${item} in ${list2} %}
@@ -3556,6 +3586,32 @@ var tr = (engine, key, params = {}) => {
3556
3586
  return `{{ '${key}' | t${entries.length ? ": " + entries.map(([k, v]) => `${k}: ${v}`).join(", ") : ""} }}`;
3557
3587
  return `{{ t('${key}'${entries.length ? `, { ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")} }` : ""}) }}`;
3558
3588
  };
3589
+ var ifBlock = (engine, cond, body) => engine === "handlebars" ? `{{#if ${cond}}}${body}{{/if}}` : `{% if ${cond} %}${body}{% endif %}`;
3590
+ function stringLiteral(engine, value) {
3591
+ if (engine === "liquid") {
3592
+ if (!value.includes("'")) return `'${value}'`;
3593
+ if (!value.includes('"')) return `"${value}"`;
3594
+ return `'${value.replace(/'/g, "\u2019")}'`;
3595
+ }
3596
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
3597
+ }
3598
+ function filterCall(engine, input, name, args = []) {
3599
+ if (engine === "handlebars")
3600
+ return `{{${name} ${input}${args.map((a) => ` ${a}`).join("")}}}`;
3601
+ if (engine === "liquid")
3602
+ return `{{ ${input} | ${name}${args.length ? `: ${args.join(", ")}` : ""} }}`;
3603
+ return `{{ ${input} | ${name}${args.length ? `(${args.join(", ")})` : ""} }}`;
3604
+ }
3605
+ function helperCall(engine, name, input, options = {}) {
3606
+ const entries = Object.entries(options);
3607
+ if (engine === "handlebars")
3608
+ return `{{${name} ${input}${entries.map(([k, v]) => ` ${k}=${v}`).join("")}}}`;
3609
+ if (engine === "liquid")
3610
+ return `{{ ${input} | ${name}${entries.length ? `: ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")}` : ""} }}`;
3611
+ return `{{ ${name}(${input}${entries.length ? `, { ${entries.map(([k, v]) => `${k}: ${v}`).join(", ")} }` : ""}) }}`;
3612
+ }
3613
+
3614
+ // packages/engine/src/lib/snippets.ts
3559
3615
  function forAll(build) {
3560
3616
  return {
3561
3617
  jinja2: build("jinja2"),
@@ -3596,8 +3652,18 @@ var snippets = [
3596
3652
  </div>$0`
3597
3653
  ),
3598
3654
  sampleData: {
3599
- company: { name: "Fennlor Studio GmbH", street: "Musterstra\xDFe 1", zip: "12345", city: "Musterstadt" },
3600
- customer: { name: "Olvarest GmbH", street: "Beispielweg 2", zip: "54321", city: "Beispielstadt" }
3655
+ company: {
3656
+ name: "Fennlor Studio GmbH",
3657
+ street: "Musterstra\xDFe 1",
3658
+ zip: "12345",
3659
+ city: "Musterstadt"
3660
+ },
3661
+ customer: {
3662
+ name: "Olvarest GmbH",
3663
+ street: "Beispielweg 2",
3664
+ zip: "54321",
3665
+ city: "Beispielstadt"
3666
+ }
3601
3667
  },
3602
3668
  // Form B puts the field 45 mm below the top edge and 20 mm from the left; the page margins decide where it lands
3603
3669
  css: `.ff-window { width: 85mm; height: 45mm; box-sizing: border-box; padding: 0 5mm; overflow: hidden; }
@@ -3624,8 +3690,16 @@ var snippets = [
3624
3690
  .ff-reference th { text-align: left; font-weight: normal; color: #555; padding: 1px 12px 1px 0; }
3625
3691
  .ff-reference td { text-align: right; padding: 1px 0; }`,
3626
3692
  i18n: {
3627
- en: { "reference.customer": "Customer no.", "reference.invoice": "Invoice no.", "reference.date": "Date" },
3628
- de: { "reference.customer": "Kundennummer", "reference.invoice": "Rechnungsnummer", "reference.date": "Datum" }
3693
+ en: {
3694
+ "reference.customer": "Customer no.",
3695
+ "reference.invoice": "Invoice no.",
3696
+ "reference.date": "Date"
3697
+ },
3698
+ de: {
3699
+ "reference.customer": "Kundennummer",
3700
+ "reference.invoice": "Rechnungsnummer",
3701
+ "reference.date": "Datum"
3702
+ }
3629
3703
  }
3630
3704
  },
3631
3705
  {
@@ -3640,14 +3714,24 @@ var snippets = [
3640
3714
  </div>$0`
3641
3715
  ),
3642
3716
  sampleData: {
3643
- invoice: { number: "2026-0042", period_start: "2026-09-01", period_end: "2026-09-30" }
3717
+ invoice: {
3718
+ number: "2026-0042",
3719
+ period_start: "2026-09-01",
3720
+ period_end: "2026-09-30"
3721
+ }
3644
3722
  },
3645
3723
  css: `.ff-heading { margin: 24px 0 16px; }
3646
3724
  .ff-heading h1 { font-family: var(--brand-font-heading, inherit); font-size: 18pt; color: var(--brand-color-primary, inherit); margin: 0 0 4px; }
3647
3725
  .ff-heading p { margin: 0; color: #555; }`,
3648
3726
  i18n: {
3649
- en: { "heading.title": "Invoice {number}", "heading.period": "Service period:" },
3650
- de: { "heading.title": "Rechnung {number}", "heading.period": "Leistungszeitraum:" }
3727
+ en: {
3728
+ "heading.title": "Invoice {number}",
3729
+ "heading.period": "Service period:"
3730
+ },
3731
+ de: {
3732
+ "heading.title": "Rechnung {number}",
3733
+ "heading.period": "Leistungszeitraum:"
3734
+ }
3651
3735
  }
3652
3736
  },
3653
3737
  {
@@ -3774,11 +3858,17 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
3774
3858
  group: "invoice",
3775
3859
  title: "Small business VAT note",
3776
3860
  description: "States that no VAT is charged under \xA7 19 UStG (Kleinunternehmerregelung).",
3777
- source: forAll((e) => `<p class="ff-note">${tr(e, "vat.small_business")}</p>$0`),
3861
+ source: forAll(
3862
+ (e) => `<p class="ff-note">${tr(e, "vat.small_business")}</p>$0`
3863
+ ),
3778
3864
  css: `.ff-note { margin-top: 12px; font-size: 9pt; color: #444; }`,
3779
3865
  i18n: {
3780
- en: { "vat.small_business": "No VAT is charged under the small business scheme of \xA7 19 UStG." },
3781
- de: { "vat.small_business": "Gem\xE4\xDF \xA7 19 UStG wird keine Umsatzsteuer berechnet." }
3866
+ en: {
3867
+ "vat.small_business": "No VAT is charged under the small business scheme of \xA7 19 UStG."
3868
+ },
3869
+ de: {
3870
+ "vat.small_business": "Gem\xE4\xDF \xA7 19 UStG wird keine Umsatzsteuer berechnet."
3871
+ }
3782
3872
  }
3783
3873
  },
3784
3874
  {
@@ -3839,7 +3929,9 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
3839
3929
  <span>${tr(e, "footer.page")} <span class="pageNumber"></span> ${tr(e, "footer.of")} <span class="totalPages"></span></span>
3840
3930
  </div>$0`
3841
3931
  ),
3842
- sampleData: { company: { name: "Fennlor Studio GmbH", email: "hello@fennlor.example" } },
3932
+ sampleData: {
3933
+ company: { name: "Fennlor Studio GmbH", email: "hello@fennlor.example" }
3934
+ },
3843
3935
  i18n: {
3844
3936
  en: { "footer.page": "Page", "footer.of": "of" },
3845
3937
  de: { "footer.page": "Seite", "footer.of": "von" }
@@ -3924,7 +4016,10 @@ ${loop(e, "line", "invoice.lines", ` <tr><td>${val(e, "line.description")}</t
3924
4016
  },
3925
4017
  i18n: {
3926
4018
  en: { "qr.alt": "Payment QR code", "qr.caption": "Scan to pay" },
3927
- de: { "qr.alt": "QR-Code zur Zahlung", "qr.caption": "Zum Bezahlen scannen" }
4019
+ de: {
4020
+ "qr.alt": "QR-Code zur Zahlung",
4021
+ "qr.caption": "Zum Bezahlen scannen"
4022
+ }
3928
4023
  },
3929
4024
  sampleData: {
3930
4025
  payment: {
@@ -4043,7 +4138,9 @@ var selectorKey = (prelude) => (prelude.split(";").pop() ?? "").trim().replace(/
4043
4138
  function mergeSnippetCss(css2, snippetCss) {
4044
4139
  if (!snippetCss?.trim()) return css2;
4045
4140
  const defined = new Set(
4046
- [...stripComments(css2).matchAll(/([^{}]+)\{/g)].map((m) => selectorKey(m[1] ?? ""))
4141
+ [...stripComments(css2).matchAll(/([^{}]+)\{/g)].map(
4142
+ (m) => selectorKey(m[1] ?? "")
4143
+ )
4047
4144
  );
4048
4145
  const missing = [...stripComments(snippetCss).matchAll(/([^{}]+)\{[^{}]*\}/g)].filter((m) => !defined.has(selectorKey(m[1] ?? ""))).map((m) => m[0].trim());
4049
4146
  if (!missing.length) return css2;
@@ -4091,9 +4188,9 @@ function pick(input, ...keys) {
4091
4188
  function toLength(value) {
4092
4189
  if (value === void 0 || value === null || value === "") return void 0;
4093
4190
  if (typeof value === "number") return `${value}mm`;
4094
- const text = String(value).trim();
4095
- if (/^-?\d+(\.\d+)?$/.test(text)) return `${text}mm`;
4096
- if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text)) return text.toLowerCase();
4191
+ const text2 = String(value).trim();
4192
+ if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
4193
+ if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
4097
4194
  return void 0;
4098
4195
  }
4099
4196
  function toMarginLength(value) {
@@ -4113,7 +4210,7 @@ function convertHeaderFooter(html) {
4113
4210
  if (typeof html !== "string" || !html.trim()) return void 0;
4114
4211
  return chromePlaceholders.reduce((out, [pattern, span]) => out.replace(pattern, span), html);
4115
4212
  }
4116
- var escapeText = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4213
+ var escapeText = (text2) => text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4117
4214
  var visibleMarkup = (html) => html.replace(/<style[\s\S]*?<\/style>/gi, "").trim();
4118
4215
  function apitemplateChrome(take, kind) {
4119
4216
  const custom = take(`custom_${kind}`);
@@ -4127,7 +4224,7 @@ function apitemplateChrome(take, kind) {
4127
4224
  if (!slots.some(Boolean)) return { fromSlots: false };
4128
4225
  const sizeText = size === void 0 ? "" : String(size).trim();
4129
4226
  const fontSize2 = /^\d+(\.\d+)?$/.test(sizeText) ? `${sizeText}px` : /^\d+(\.\d+)?(px|pt|mm)$/.test(sizeText) ? sizeText : "9px";
4130
- const cells = slots.map((text, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text)}</span>`).join("");
4227
+ const cells = slots.map((text2, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text2)}</span>`).join("");
4131
4228
  const styles = customHtml.match(/<style[\s\S]*?<\/style>/gi)?.join("") ?? "";
4132
4229
  return {
4133
4230
  html: convertHeaderFooter(`${styles}<div style="display:flex;gap:4mm;font-size:${fontSize2}">${cells}</div>`),
@@ -4136,8 +4233,8 @@ function apitemplateChrome(take, kind) {
4136
4233
  }
4137
4234
  function clearsChromePadding(html, kind) {
4138
4235
  if (!html) return false;
4139
- for (const style of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi))
4140
- for (const rule2 of (style[1] ?? "").matchAll(/([^{}]+)\{([^}]*)\}/g))
4236
+ for (const style2 of html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/gi))
4237
+ for (const rule2 of (style2[1] ?? "").matchAll(/([^{}]+)\{([^}]*)\}/g))
4141
4238
  if (new RegExp(`#${kind}\\b`).test(rule2[1] ?? "") && /(^|;)\s*padding\s*:\s*0(px|mm)?\s*(!important\s*)?(;|$)/i.test((rule2[2] ?? "").trim()))
4142
4239
  return true;
4143
4240
  return false;
@@ -4288,9 +4385,9 @@ function checkTemplate(engineId, html, sampleData, name, docs) {
4288
4385
  function lengthMm(value) {
4289
4386
  if (value === void 0 || value === null || value === "") return void 0;
4290
4387
  if (typeof value === "number" && Number.isFinite(value)) return `${value}mm`;
4291
- const text = String(value).trim();
4292
- if (/^-?\d+(\.\d+)?$/.test(text)) return `${text}mm`;
4293
- if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text)) return text.toLowerCase();
4388
+ const text2 = String(value).trim();
4389
+ if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
4390
+ if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
4294
4391
  return void 0;
4295
4392
  }
4296
4393
  function toBase64(value) {
@@ -4693,7 +4790,7 @@ var unsupportedFilters = {
4693
4790
  in_time_zone: 'pass the zone to `date` as its second argument: `date: "dd.MM.yyyy", "Europe/Berlin"`'
4694
4791
  };
4695
4792
  function mapLiquid(source, fn) {
4696
- return source.replace(/(\{\{-?|\{%-?)([\s\S]*?)(-?\}\}|-?%\})/g, (_m, open, inner, close) => `${open}${fn(inner)}${close}`);
4793
+ return source.replace(/(\{\{-?|\{%-?)([\s\S]*?)(-?\}\}|-?%\})/g, (_m, open2, inner, close) => `${open2}${fn(inner)}${close}`);
4697
4794
  }
4698
4795
  function convertDialect(source, changes) {
4699
4796
  const errors = [];
@@ -4807,11 +4904,11 @@ function inlineIncludes(source, blocks, errors, changes) {
4807
4904
  }
4808
4905
  var cssPaperFormats = { a3: "A3", a4: "A4", a5: "A5", letter: "Letter" };
4809
4906
  var isoSizes = { a0: [841, 1189], a1: [594, 841], a2: [420, 594], a6: [105, 148] };
4810
- var escapeHtml2 = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4907
+ var escapeHtml2 = (text2) => text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4811
4908
  function simpleHeaderFooter(part, margin) {
4812
4909
  const cells = ["left", "center", "right"].map((side) => part[side] ?? "");
4813
4910
  if (!cells.some((c) => c.trim())) return void 0;
4814
- const cell3 = (text, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
4911
+ const cell3 = (text2, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text2).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
4815
4912
  return `<div style="display: flex; width: 100%; font-size: 10px; padding: 0 ${margin.right} 0 ${margin.left}">${cell3(cells[0] ?? "", "left")}${cell3(cells[1] ?? "", "center")}${cell3(cells[2] ?? "", "right")}</div>`;
4816
4913
  }
4817
4914
  function mapSettings(input, kind, warnings, changes) {
@@ -4980,11 +5077,11 @@ var DOCS3 = "/migrate/jsreport";
4980
5077
  var COLLECTIONS = ["templates", "data", "assets", "components", "scripts", "folders"];
4981
5078
  function readJsreportExport(files) {
4982
5079
  const bundle = { templates: [], data: [], assets: [], components: [], scripts: [], folders: [] };
4983
- for (const [path, text] of Object.entries(files)) {
5080
+ for (const [path, text2] of Object.entries(files)) {
4984
5081
  const normalized = path.replace(/\\/g, "/").replace(/^\.?\//, "");
4985
5082
  if (normalized === "metadata.json") {
4986
5083
  try {
4987
- bundle.version = String(JSON.parse(text).reporterVersion ?? "");
5084
+ bundle.version = String(JSON.parse(text2).reporterVersion ?? "");
4988
5085
  } catch {
4989
5086
  }
4990
5087
  continue;
@@ -4992,7 +5089,7 @@ function readJsreportExport(files) {
4992
5089
  const collection = normalized.split("/")[0];
4993
5090
  if (!COLLECTIONS.includes(collection) || !normalized.endsWith(".json")) continue;
4994
5091
  try {
4995
- bundle[collection].push(JSON.parse(text));
5092
+ bundle[collection].push(JSON.parse(text2));
4996
5093
  } catch {
4997
5094
  }
4998
5095
  }
@@ -5034,7 +5131,7 @@ function assetBase64(content) {
5034
5131
  }
5035
5132
  return toBase64(content);
5036
5133
  }
5037
- var escapeMustaches = (text) => text.replace(/\{\{/g, "\\{{");
5134
+ var escapeMustaches = (text2) => text2.replace(/\{\{/g, "\\{{");
5038
5135
  function resolveAssets(source, bundle, options, warnings, changes) {
5039
5136
  const v3 = /\{\{\{?\s*asset\s+(["'])([^"']+)\1(?:\s+(["'])([^"']+)\3)?\s*\}?\}\}/g;
5040
5137
  const v2 = /\{#asset\s+([^\s}]+)(?:\s+@encoding=(\w+))?\s*\}/g;
@@ -5271,6 +5368,11 @@ function importJsreport(bundle, ref, options = {}) {
5271
5368
  };
5272
5369
  }
5273
5370
 
5371
+ // packages/engine/src/lib/generated/frames.ts
5372
+ var textEditRuntime = '"use strict";(()=>{var H=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function R(s,l={}){let g=l.clicks!==!1,p=l.live===!0,o=s.document,i=null,a=null,c=e=>{try{s.parent.postMessage(e,"*")}catch{}},b=o.createElement("style");b.setAttribute("data-formfeed","edit"),b.setAttribute("data-pagedjs-ignore",""),b.textContent=H,(o.head??o.documentElement).appendChild(b),g&&o.documentElement.classList.add("ff-edit-mode");let D=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!g)return;if(i&&!i.done&&i.element.contains(e.target)){e.stopImmediatePropagation();return}let t=D(e.target);if(!t)return;e.preventDefault(),e.stopImmediatePropagation(),i&&!i.done&&y();let n=t.getAttribute("data-ff-src")??"",r=t.getBoundingClientRect();a={src:n,element:t,x:e.clientX,y:e.clientY},c({type:"formfeed:edit-request",src:n,edit:t.getAttribute("data-ff-edit"),text:t.textContent??"",count:o.querySelectorAll(`[data-ff-src="${n.replace(/"/g,"")}"]`).length,split:t.hasAttribute("data-split-from")||t.hasAttribute("data-split-to"),rect:{left:r.left,top:r.top,width:r.width,height:r.height}})},!0),o.addEventListener("keydown",e=>{if(!g||i&&!i.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),c({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),s.addEventListener("message",e=>{if(e.source!==s.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?F(t):(t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end")&&(a=null,i&&!i.done&&h(!1)))});function F(e){i&&!i.done&&h(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):a&&a.src===e.src?a.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),n=e.at,r=e.layer?n&&Number.isFinite(n.x)&&Number.isFinite(n.y)?{x:Number(n.x),y:Number(n.y)}:null:a&&a.src===e.src?a:null;if(a=null,!t||!Array.isArray(e.items)){c({type:"formfeed:edit-cancel",token:String(e.token)});return}let m=Array.from(t.childNodes),f=o.createDocumentFragment();for(let u of e.items)if(u.kind==="text")f.appendChild(o.createTextNode(String(u.text)));else if(u.kind==="break")f.appendChild(o.createElement("br"));else if(u.kind==="chip"){let d=o.createElement("span");d.className="ff-chip",d.setAttribute("data-ff-chip",String(u.index)),d.setAttribute("contenteditable","false"),u.title&&(d.title=String(u.title)),d.textContent=String(u.text),f.appendChild(d)}for(let u of m)t.removeChild(u);t.appendChild(f),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,i={token:String(e.token),element:t,saved:m,allowBreaks:e.allowBreaks===!0,badge:e.note?B(t,String(e.note)):null,done:!1},t.addEventListener("keydown",E),t.addEventListener("beforeinput",v),t.addEventListener("paste",C),t.addEventListener("drop",L),t.addEventListener("focusout",A),p&&t.addEventListener("input",x),t.focus(),e.select==="all"?w(t):P(t,r),c({type:"formfeed:edit-started",token:i.token}),typeof e.insert=="string"&&e.insert&&k(e.insert.replace(/\\r\\n?|\\n/g," "))}function k(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=s.getSelection(),n=t&&t.rangeCount?t.getRangeAt(0):null;n&&(n.deleteContents(),n.insertNode(o.createTextNode(e)),n.collapse(!1),p&&x())}function x(){i&&!i.done&&c({type:"formfeed:edit-input",token:i.token,items:S(i.element)})}function B(e,t){let n=o.createElement("div");n.className="ff-edit-badge",n.textContent=t;let r=e.getBoundingClientRect();return n.style.left=`${r.left+s.scrollX}px`,n.style.top=`${Math.max(0,r.top+s.scrollY-22)}px`,o.body.appendChild(n),n}function w(e){let t=s.getSelection();if(!t)return;let n=o.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}function P(e,t){let n=s.getSelection();if(!n)return;let r=null,m=o;if(t){if(m.caretRangeFromPoint)r=m.caretRangeFromPoint(t.x,t.y);else if(m.caretPositionFromPoint){let f=m.caretPositionFromPoint(t.x,t.y);f&&(r=o.createRange(),r.setStart(f.offsetNode,f.offset))}}if(!r||!e.contains(r.startContainer))r=o.createRange(),r.selectNodeContents(e),r.collapse(!1);else if(t){let f=r.startContainer,d=(f.nodeType===1?f:f.parentElement)?.closest("[data-ff-chip]")??null;if(d&&d!==e&&e.contains(d)){let N=d.getBoundingClientRect();r=o.createRange(),t.x<N.left+N.width/2?r.setStartBefore(d):r.setStartAfter(d),r.collapse(!0)}}n.removeAllRanges(),n.addRange(r)}function E(e){let t=e.key;t==="Escape"?(e.preventDefault(),h(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),y()):t==="Enter"?(e.preventDefault(),i?.allowBreaks&&M()):t==="Tab"?(e.preventDefault(),y()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),p&&i&&!e.altKey&&c({type:"formfeed:edit-style",token:i.token,key:t.toLowerCase()}))}function v(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function C(e){e.preventDefault(),k((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function L(e){e.preventDefault()}function A(){s.setTimeout(()=>{i&&!i.done&&(!o.hasFocus()||!i.element.contains(o.activeElement))&&y()},0)}function M(){let e=s.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let n=o.createElement("br");t.insertNode(n),n.nextSibling||n.parentNode?.appendChild(o.createElement("br"));let r=o.createRange();r.setStartAfter(n),r.collapse(!0),e.removeAllRanges(),e.addRange(r)}function T(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",E),t.removeEventListener("beforeinput",v),t.removeEventListener("paste",C),t.removeEventListener("drop",L),t.removeEventListener("focusout",A),t.removeEventListener("input",x),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),i===e&&(i=null)}function y(){let e=i;if(!e||e.done)return;let t=S(e.element);T(e),c({type:"formfeed:edit-commit",token:e.token,items:t})}function h(e){let t=i;if(!t||t.done)return;let n=t.element;for(;n.firstChild;)n.removeChild(n.firstChild);for(let r of t.saved)n.appendChild(r);T(t),e&&c({type:"formfeed:edit-cancel",token:t.token})}}function S(s){let l=[],g=o=>{let i=l[l.length-1];i?.kind==="text"?i.text+=o:o&&l.push({kind:"text",text:o})},p=o=>{for(let i of Array.from(o.childNodes))if(i.nodeType===3)g(i.data);else if(i.nodeType===1){let a=i,c=a.getAttribute("data-ff-chip");c!==null?l.push({kind:"chip",index:Number(c)}):a.tagName==="BR"?l.push({kind:"break"}):((a.tagName==="DIV"||a.tagName==="P")&&l.length&&l.push({kind:"break"}),p(a))}};for(p(s);l[l.length-1]?.kind==="break";)l.pop();return l}R(window);})();';
5373
+ var fitRuntime = '"use strict";(()=>{function a(t,e,n){if(e===t)return t.scrollHeight<=t.clientHeight+.5&&t.scrollWidth<=t.clientWidth+.5;let o=n.getComputedStyle(t),i=t.clientHeight-(parseFloat(o.paddingTop)||0)-(parseFloat(o.paddingBottom)||0);return e.scrollHeight<=i+.5&&e.scrollWidth<=e.clientWidth+.5}function m(t,e){let n=t.firstElementChild??t;n.style.fontSize="";let o=parseFloat(e.getComputedStyle(n).fontSize)||16,i=Math.min(o,parseFloat(t.getAttribute("data-ff-fit-min")??"")||o/2);if(a(t,n,e))return o;let l=i,s=o;for(let d=0;d<14;d++){let r=(l+s)/2;n.style.fontSize=`${r}px`,a(t,n,e)?l=r:s=r}let f=Math.floor(l*100)/100;return n.style.fontSize=`${f}px`,f}function c(t){let e=()=>{for(let o of Array.from(t.document.querySelectorAll(\'[data-ff-fit="shrink"]\')))m(o,t);t.document.documentElement.setAttribute("data-ff-fit","done")};t.formfeedFit=e;let n=t.document.fonts;n?n.ready.then(e):t.addEventListener("load",e)}c(window);})();';
5374
+ var stageRuntime = '"use strict";(()=>{var I=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function D(a,l={}){let f=l.clicks!==!1,c=l.live===!0,o=a.document,n=null,s=null,u=e=>{try{a.parent.postMessage(e,"*")}catch{}},p=o.createElement("style");p.setAttribute("data-formfeed","edit"),p.setAttribute("data-pagedjs-ignore",""),p.textContent=I,(o.head??o.documentElement).appendChild(p),f&&o.documentElement.classList.add("ff-edit-mode");let d=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!f)return;if(n&&!n.done&&n.element.contains(e.target)){e.stopImmediatePropagation();return}let t=d(e.target);if(!t)return;e.preventDefault(),e.stopImmediatePropagation(),n&&!n.done&&v();let r=t.getAttribute("data-ff-src")??"",i=t.getBoundingClientRect();s={src:r,element:t,x:e.clientX,y:e.clientY},u({type:"formfeed:edit-request",src:r,edit:t.getAttribute("data-ff-edit"),text:t.textContent??"",count:o.querySelectorAll(`[data-ff-src="${r.replace(/"/g,"")}"]`).length,split:t.hasAttribute("data-split-from")||t.hasAttribute("data-split-to"),rect:{left:i.left,top:i.top,width:i.width,height:i.height}})},!0),o.addEventListener("keydown",e=>{if(!f||n&&!n.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),u({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),a.addEventListener("message",e=>{if(e.source!==a.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?b(t):(t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end")&&(s=null,n&&!n.done&&L(!1)))});function b(e){n&&!n.done&&L(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):s&&s.src===e.src?s.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),r=e.at,i=e.layer?r&&Number.isFinite(r.x)&&Number.isFinite(r.y)?{x:Number(r.x),y:Number(r.y)}:null:s&&s.src===e.src?s:null;if(s=null,!t||!Array.isArray(e.items)){u({type:"formfeed:edit-cancel",token:String(e.token)});return}let x=Array.from(t.childNodes),g=o.createDocumentFragment();for(let y of e.items)if(y.kind==="text")g.appendChild(o.createTextNode(String(y.text)));else if(y.kind==="break")g.appendChild(o.createElement("br"));else if(y.kind==="chip"){let m=o.createElement("span");m.className="ff-chip",m.setAttribute("data-ff-chip",String(y.index)),m.setAttribute("contenteditable","false"),y.title&&(m.title=String(y.title)),m.textContent=String(y.text),g.appendChild(m)}for(let y of x)t.removeChild(y);t.appendChild(g),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,n={token:String(e.token),element:t,saved:x,allowBreaks:e.allowBreaks===!0,badge:e.note?E(t,String(e.note)):null,done:!1},t.addEventListener("keydown",C),t.addEventListener("beforeinput",S),t.addEventListener("paste",A),t.addEventListener("drop",T),t.addEventListener("focusout",N),c&&t.addEventListener("input",k),t.focus(),e.select==="all"?H(t):M(t,i),u({type:"formfeed:edit-started",token:n.token}),typeof e.insert=="string"&&e.insert&&h(e.insert.replace(/\\r\\n?|\\n/g," "))}function h(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=a.getSelection(),r=t&&t.rangeCount?t.getRangeAt(0):null;r&&(r.deleteContents(),r.insertNode(o.createTextNode(e)),r.collapse(!1),c&&k())}function k(){n&&!n.done&&u({type:"formfeed:edit-input",token:n.token,items:w(n.element)})}function E(e,t){let r=o.createElement("div");r.className="ff-edit-badge",r.textContent=t;let i=e.getBoundingClientRect();return r.style.left=`${i.left+a.scrollX}px`,r.style.top=`${Math.max(0,i.top+a.scrollY-22)}px`,o.body.appendChild(r),r}function H(e){let t=a.getSelection();if(!t)return;let r=o.createRange();r.selectNodeContents(e),t.removeAllRanges(),t.addRange(r)}function M(e,t){let r=a.getSelection();if(!r)return;let i=null,x=o;if(t){if(x.caretRangeFromPoint)i=x.caretRangeFromPoint(t.x,t.y);else if(x.caretPositionFromPoint){let g=x.caretPositionFromPoint(t.x,t.y);g&&(i=o.createRange(),i.setStart(g.offsetNode,g.offset))}}if(!i||!e.contains(i.startContainer))i=o.createRange(),i.selectNodeContents(e),i.collapse(!1);else if(t){let g=i.startContainer,m=(g.nodeType===1?g:g.parentElement)?.closest("[data-ff-chip]")??null;if(m&&m!==e&&e.contains(m)){let F=m.getBoundingClientRect();i=o.createRange(),t.x<F.left+F.width/2?i.setStartBefore(m):i.setStartAfter(m),i.collapse(!0)}}r.removeAllRanges(),r.addRange(i)}function C(e){let t=e.key;t==="Escape"?(e.preventDefault(),L(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),v()):t==="Enter"?(e.preventDefault(),n?.allowBreaks&&P()):t==="Tab"?(e.preventDefault(),v()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),c&&n&&!e.altKey&&u({type:"formfeed:edit-style",token:n.token,key:t.toLowerCase()}))}function S(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function A(e){e.preventDefault(),h((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function T(e){e.preventDefault()}function N(){a.setTimeout(()=>{n&&!n.done&&(!o.hasFocus()||!n.element.contains(o.activeElement))&&v()},0)}function P(){let e=a.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let r=o.createElement("br");t.insertNode(r),r.nextSibling||r.parentNode?.appendChild(o.createElement("br"));let i=o.createRange();i.setStartAfter(r),i.collapse(!0),e.removeAllRanges(),e.addRange(i)}function R(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",C),t.removeEventListener("beforeinput",S),t.removeEventListener("paste",A),t.removeEventListener("drop",T),t.removeEventListener("focusout",N),t.removeEventListener("input",k),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),n===e&&(n=null)}function v(){let e=n;if(!e||e.done)return;let t=w(e.element);R(e),u({type:"formfeed:edit-commit",token:e.token,items:t})}function L(e){let t=n;if(!t||t.done)return;let r=t.element;for(;r.firstChild;)r.removeChild(r.firstChild);for(let i of t.saved)r.appendChild(i);R(t),e&&u({type:"formfeed:edit-cancel",token:t.token})}}function w(a){let l=[],f=o=>{let n=l[l.length-1];n?.kind==="text"?n.text+=o:o&&l.push({kind:"text",text:o})},c=o=>{for(let n of Array.from(o.childNodes))if(n.nodeType===3)f(n.data);else if(n.nodeType===1){let s=n,u=s.getAttribute("data-ff-chip");u!==null?l.push({kind:"chip",index:Number(u)}):s.tagName==="BR"?l.push({kind:"break"}):((s.tagName==="DIV"||s.tagName==="P")&&l.length&&l.push({kind:"break"}),c(s))}};for(c(a);l[l.length-1]?.kind==="break";)l.pop();return l}function K(a,l){return Array.from(a.querySelectorAll("[data-ff-layer]")).map(f=>{let c=f.querySelector(":scope > .ff-text"),o=!1;if(c&&f.style.height!=="auto"){let n=l.getComputedStyle(f),s=f.clientHeight-(parseFloat(n.paddingTop)||0)-(parseFloat(n.paddingBottom)||0);o=n.overflow==="hidden"&&(c.scrollHeight>s+1||c.scrollWidth>c.clientWidth+1)}return{id:f.getAttribute("data-ff-layer")??"",width:f.offsetWidth,height:f.offsetHeight,overflow:o}})}function B(a){let l=a.document,f=p=>{try{a.parent.postMessage(p,"*")}catch{}},c=p=>typeof p=="string"?l.querySelector(`[data-ff-layer="${p.replace(/"/g,"")}"]`):null,o=()=>{for(let p of Array.from(l.querySelectorAll("[data-ff-layer]")))for(let d of["left","top","width","height","transform"])p.style.removeProperty(d)},n=()=>{a.formfeedFit?.(),f({type:"formfeed:ready",layers:K(l,a)})};a.addEventListener("message",p=>{if(p.source!==a.parent)return;let d=p.data;if(!(!d||typeof d.type!="string")){if(d.type==="formfeed:layer-style"){let b=c(d.id);if(!b)return;let h=(k,E)=>{typeof E=="number"&&Number.isFinite(E)&&b.style.setProperty(k,`${E}px`)};h("left",d.left),h("top",d.top),h("width",d.width),h("height",d.height),typeof d.rotation=="number"&&Number.isFinite(d.rotation)&&b.style.setProperty("transform",d.rotation?`rotate(${d.rotation}deg)`:"none")}else if(d.type==="formfeed:layer-style-clear")o();else if(d.type==="formfeed:css"&&typeof d.css=="string"){let b=l.querySelector(\'style[data-formfeed="template"]\');b&&(b.textContent=d.css),o(),n()}}}),D(a,{clicks:!1,live:!0});let s=l.fonts,u=()=>s?void s.ready.then(n):n();l.readyState==="complete"?u():a.addEventListener("load",u)}B(window);})();';
5375
+
5274
5376
  // packages/engine/src/lib/assemble.ts
5275
5377
  var DEFAULT_CHROME_PADDING = "0 10mm";
5276
5378
  function defaultSettings() {
@@ -5370,9 +5472,12 @@ function chartScripts(html, vendor) {
5370
5472
  if (!vendor?.chartJs || !html.includes("data-ff-chart"))
5371
5473
  return { head: "", body: "" };
5372
5474
  const lib = "inline" in vendor.chartJs ? `<script>${vendor.chartJs.inline}</script>` : `<script src="${escapeHtml(vendor.chartJs.src)}"></script>`;
5373
- return { head: `${lib}
5374
- `, body: `${chartInitScript()}
5375
- ` };
5475
+ return {
5476
+ head: `${lib}
5477
+ `,
5478
+ body: `${chartInitScript()}
5479
+ `
5480
+ };
5376
5481
  }
5377
5482
  function assembleDocument(input) {
5378
5483
  const settings = mergeSettings(input.settings);
@@ -5399,6 +5504,8 @@ ${brandFontFamilies(input.brand)}`
5399
5504
  ` : "" : input.extraCss ? `<style data-formfeed="extra">${input.extraCss}</style>
5400
5505
  ` : "";
5401
5506
  const charts = chartScripts(input.html, input.vendor);
5507
+ const fit = input.html.includes("data-ff-fit") ? `<script>${fitRuntime.replace(/<\/script/gi, "<\\/script")}</script>
5508
+ ` : "";
5402
5509
  return `<!doctype html>
5403
5510
  <html lang="${escapeHtml(locale.split("_")[0] ?? locale)}">
5404
5511
  <head>
@@ -5410,7 +5517,7 @@ ${tailwind}<style data-formfeed="template">${input.css ?? ""}</style>
5410
5517
  ${charts.head}</head>
5411
5518
  <body class="formfeed-body formfeed-${kind}${input.mode === "preview" ? " formfeed-preview" : ""}">
5412
5519
  ${input.html}
5413
- ${charts.body}</body>
5520
+ ${charts.body}${fit}</body>
5414
5521
  </html>
5415
5522
  `;
5416
5523
  }
@@ -5485,7 +5592,10 @@ function paperOf(settings) {
5485
5592
  }
5486
5593
  var pageNumberSpans = (html, page, total) => {
5487
5594
  const fill = (source, name, value) => source.replace(
5488
- new RegExp(`<([a-z][a-z0-9]*)(\\s[^>]*?\\bclass=(["'])(?:[^"']*\\s)?${name}(?:\\s[^"']*)?\\3[^>]*)>[^<]*</\\1>`, "gi"),
5595
+ new RegExp(
5596
+ `<([a-z][a-z0-9]*)(\\s[^>]*?\\bclass=(["'])(?:[^"']*\\s)?${name}(?:\\s[^"']*)?\\3[^>]*)>[^<]*</\\1>`,
5597
+ "gi"
5598
+ ),
5489
5599
  (_match, tag, attrs) => `<${tag}${attrs}>${value}</${tag}>`
5490
5600
  );
5491
5601
  return fill(fill(html, "pageNumber", page), "totalPages", total);
@@ -5508,6 +5618,7 @@ document.addEventListener('click', function (event) {
5508
5618
  } catch (e) {}
5509
5619
  }, true);
5510
5620
  </script>`;
5621
+ var textEditScript = `<script>${textEditRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
5511
5622
  var pageNavScript = `<script>
5512
5623
  (function () {
5513
5624
  var pages = function () { return document.querySelectorAll('.pagedjs_page'); };
@@ -5609,8 +5720,11 @@ var zoomGestureScript = `<script>
5609
5720
  })();
5610
5721
  </script>`;
5611
5722
  function intoHead(document, markup) {
5612
- return document.replace(/<head[^>]*>(?:\s*<meta\b[^>]*>)*/i, (head) => `${head}
5613
- ${markup}`);
5723
+ return document.replace(
5724
+ /<head[^>]*>(?:\s*<meta\b[^>]*>)*/i,
5725
+ (head) => `${head}
5726
+ ${markup}`
5727
+ );
5614
5728
  }
5615
5729
  function chromeBoxStyle(settings, which) {
5616
5730
  const margin = settings.margin ?? {};
@@ -5625,7 +5739,7 @@ function flowChromeStyle(settings, which) {
5625
5739
  const height = settings[which]?.height;
5626
5740
  return `position:absolute;${which === "header" ? "top" : "bottom"}:0;left:0;width:100%;box-sizing:border-box;padding:${padding};${height ? `height:${height};` : ""}`;
5627
5741
  }
5628
- function flowDocument(draft) {
5742
+ function flowDocument(draft, options = {}) {
5629
5743
  const { settings, kind, headerHtml, footerHtml } = draft;
5630
5744
  const margin = settings.margin ?? {};
5631
5745
  const paper = paperOf(settings);
@@ -5637,7 +5751,15 @@ body.formfeed-preview { position: relative; box-sizing: border-box; width: ${pap
5637
5751
  </style>` : `<style data-formfeed="preview">html { background: #e5e7eb; } body.formfeed-preview { margin: 24px auto; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); }</style>`;
5638
5752
  const header = headerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
5639
5753
  const footer = footerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
5640
- return intoHead(draft.document, `${chrome}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}`).replace(/(<body[^>]*>)/, `$1${header}`).replace("</body>", `${footer}</body>`);
5754
+ return intoHead(
5755
+ draft.document,
5756
+ `${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}`
5757
+ ).replace(/(<body[^>]*>)/, `$1${header}`).replace("</body>", `${footer}</body>`);
5758
+ }
5759
+ function canvasDocument(draft) {
5760
+ const chrome = `<style data-formfeed="stage">html, body { background: transparent !important; margin: 0 !important; }</style>`;
5761
+ const runtime = `<script>${stageRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
5762
+ return intoHead(draft.document, `${chrome}${runtime}${zoomGestureScript}`);
5641
5763
  }
5642
5764
  function pagedDocument(draft, options) {
5643
5765
  const { headerHtml, footerHtml, settings } = draft;
@@ -5659,82 +5781,17 @@ html { background: #e5e7eb; }
5659
5781
  `;
5660
5782
  const selectorGuard = `<script>(function(){var q=DocumentFragment.prototype.querySelectorAll;DocumentFragment.prototype.querySelectorAll=function(s){try{return q.call(this,s);}catch(e){console.warn('formfeed paged preview: skipped a selector Paged.js could not use:',s);return q.call(this,':not(*)');}};})();</script>`;
5661
5783
  const boxSizingGuard = `<script>(function(){var rule='.pagedjs_pagebox *';var o=new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(n.nodeName==='STYLE'&&n.textContent&&n.textContent.indexOf(rule)>=0){n.textContent=n.textContent.split(rule).join('.pagedjs_pagebox [class*="pagedjs_"]');o.disconnect();}});});});o.observe(document.documentElement,{childList:true,subtree:true});})();</script>`;
5662
- const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); } };</script>`;
5784
+ const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { if (window.formfeedFit) window.formfeedFit(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); } };</script>`;
5663
5785
  const script = `<script src="${options.pagedScriptUrl}"></script>`;
5664
5786
  const header = headerHtml ? `<div class="ff-running-header">${pageNumberSpans(headerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
5665
5787
  const footer = footerHtml ? `<div class="ff-running-footer">${pageNumberSpans(footerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
5666
5788
  return intoHead(
5667
5789
  draft.document,
5668
- `<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${inspectScript}${pageNavScript}${zoomGestureScript}`
5790
+ `<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
5669
5791
  ).replace(/(<body[^>]*>)/, `$1${header}${footer}`);
5670
5792
  }
5671
5793
 
5672
- // packages/engine/src/lib/source-positions.ts
5673
- var sourceAttribute = "data-ff-src";
5674
- function parseSourceAttribute(value) {
5675
- const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
5676
- if (!match) return null;
5677
- const line = Number(match[2]);
5678
- const column = Number(match[3]);
5679
- if (line < 1 || column < 1) return null;
5680
- return { file: match[1], line, column };
5681
- }
5682
- function annotateSourcePositions(source, engine, file) {
5683
- const scanner = new Scanner(source, engine);
5684
- const lines = lineStarts(source);
5685
- const out = [];
5686
- let copied = 0;
5687
- const n = source.length;
5688
- let i = 0;
5689
- while (i < n) {
5690
- const ch = source[i];
5691
- if (ch === "{") {
5692
- i = scanner.skipTemplate(i) ?? i + 1;
5693
- continue;
5694
- }
5695
- if (ch !== "<") {
5696
- i++;
5697
- continue;
5698
- }
5699
- if (source.startsWith("<!--", i)) {
5700
- i = scanner.skipTo(i + 4, "-->");
5701
- continue;
5702
- }
5703
- if (source.startsWith("<![CDATA[", i)) {
5704
- i = scanner.skipTo(i + 9, "]]>");
5705
- continue;
5706
- }
5707
- const next = source[i + 1];
5708
- if (next === "!" || next === "?") {
5709
- i = scanner.tagEnd(i + 2);
5710
- continue;
5711
- }
5712
- if (next === "/") {
5713
- i += 2;
5714
- continue;
5715
- }
5716
- tagName.lastIndex = i + 1;
5717
- const name = tagName.exec(source)?.[0];
5718
- if (!name) {
5719
- i++;
5720
- continue;
5721
- }
5722
- const nameEnd = i + 1 + name.length;
5723
- const end = scanner.tagEnd(nameEnd);
5724
- const after = source[nameEnd];
5725
- const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
5726
- if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
5727
- const { line, column } = locate(lines, i);
5728
- out.push(source.slice(copied, nameEnd), ` ${sourceAttribute}="${file}:${line}:${column}"`);
5729
- copied = nameEnd;
5730
- }
5731
- i = end;
5732
- if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
5733
- i = scanner.skipToClosingTag(end, name);
5734
- }
5735
- out.push(source.slice(copied));
5736
- return out.join("");
5737
- }
5794
+ // packages/engine/src/lib/source-scan.ts
5738
5795
  var tagName = /[A-Za-z][^\s/>{}<"'=]*/y;
5739
5796
  var rawTextElements = /* @__PURE__ */ new Set([
5740
5797
  "script",
@@ -5775,6 +5832,38 @@ var Scanner = class {
5775
5832
  return this.blockEnd(tag.end, tag.keyword);
5776
5833
  return tag.end;
5777
5834
  }
5835
+ /** The construct starting at `at` and what kind it is, or null when `{` starts none. */
5836
+ classify(at) {
5837
+ const s = this.source;
5838
+ const tag = this.templateTag(at);
5839
+ if (!tag) return null;
5840
+ if (this.engine === "handlebars") {
5841
+ if (s.startsWith("{{{{", at)) return { type: "statement", end: tag.end };
5842
+ if (s.startsWith("{{!", at)) return { type: "comment", end: tag.end };
5843
+ if (s.startsWith("{{{", at))
5844
+ return {
5845
+ type: "output",
5846
+ end: tag.end,
5847
+ inner: s.slice(at + 3, tag.end - 3)
5848
+ };
5849
+ const inner = s.slice(at + 2, tag.end - 2);
5850
+ const head = inner.replace(/^~?\s*/, "");
5851
+ if (/^[#/^>*]/.test(head) || /^else(?:\s|~?$)/.test(head))
5852
+ return { type: "statement", end: tag.end };
5853
+ return { type: "output", end: tag.end, inner };
5854
+ }
5855
+ if (s.startsWith("{#", at)) return { type: "comment", end: tag.end };
5856
+ if (s.startsWith("{{", at))
5857
+ return {
5858
+ type: "output",
5859
+ end: tag.end,
5860
+ inner: s.slice(at + 2, tag.end - 2)
5861
+ };
5862
+ if (/^\{%[-+~]?\s*#/.test(s.slice(at, at + 64)))
5863
+ return { type: "comment", end: tag.end };
5864
+ const end = tag.keyword && this.captures(tag.keyword, tag.rest ?? "") ? this.blockEnd(tag.end, tag.keyword) : tag.end;
5865
+ return { type: "statement", end, keyword: tag.keyword };
5866
+ }
5778
5867
  /** Index after `close`, stepping over template constructs; the end of the source when missing. */
5779
5868
  skipTo(from, close) {
5780
5869
  let j = from;
@@ -5839,7 +5928,8 @@ var Scanner = class {
5839
5928
  j++;
5840
5929
  continue;
5841
5930
  }
5842
- if (tag.keyword === keyword && this.captures(keyword, tag.rest ?? "")) depth++;
5931
+ if (tag.keyword === keyword && this.captures(keyword, tag.rest ?? ""))
5932
+ depth++;
5843
5933
  else if (tag.keyword === `end${keyword}` && --depth === 0) return tag.end;
5844
5934
  j = tag.end;
5845
5935
  }
@@ -5850,22 +5940,32 @@ var Scanner = class {
5850
5940
  if (s[at] !== "{") return null;
5851
5941
  if (this.engine === "handlebars") {
5852
5942
  if (s.startsWith("{{{{", at)) return { end: this.handlebarsRawBlock(at) };
5853
- if (s.startsWith("{{!--", at)) return { end: this.indexAfter(at + 5, "--}}") };
5854
- if (s.startsWith("{{!", at)) return { end: this.indexAfter(at + 3, "}}") };
5855
- if (s.startsWith("{{{", at)) return { end: this.expressionEnd(at + 3, "}}}") };
5856
- if (s.startsWith("{{", at)) return { end: this.expressionEnd(at + 2, "}}") };
5943
+ if (s.startsWith("{{!--", at))
5944
+ return { end: this.indexAfter(at + 5, "--}}") };
5945
+ if (s.startsWith("{{!", at))
5946
+ return { end: this.indexAfter(at + 3, "}}") };
5947
+ if (s.startsWith("{{{", at))
5948
+ return { end: this.expressionEnd(at + 3, "}}}") };
5949
+ if (s.startsWith("{{", at))
5950
+ return { end: this.expressionEnd(at + 2, "}}") };
5857
5951
  return null;
5858
5952
  }
5859
5953
  if (this.engine === "jinja2" && s.startsWith("{#", at))
5860
5954
  return { end: this.indexAfter(at + 2, "#}") };
5861
- if (s.startsWith("{{", at)) return { end: this.expressionEnd(at + 2, "}}") };
5955
+ if (s.startsWith("{{", at))
5956
+ return { end: this.expressionEnd(at + 2, "}}") };
5862
5957
  if (!s.startsWith("{%", at)) return null;
5863
5958
  const end = /^[-+~]?\s*#/.test(s.slice(at + 2, at + 64)) ? this.indexAfter(at + 2, "%}") : this.expressionEnd(at + 2, "%}");
5864
- const inner = /^[-+~]?\s*(\w+)([\s\S]*)$/.exec(s.slice(at + 2, Math.max(at + 2, end - 2)));
5959
+ const inner = /^[-+~]?\s*(\w+)([\s\S]*)$/.exec(
5960
+ s.slice(at + 2, Math.max(at + 2, end - 2))
5961
+ );
5865
5962
  const keyword = inner?.[1];
5866
5963
  if (!keyword) return { end };
5867
5964
  if (literalBlocks[this.engine].has(keyword)) {
5868
- const close = new RegExp(`\\{%[-+~]?\\s*end${keyword}\\s*[-+~]?%\\}`, "g");
5965
+ const close = new RegExp(
5966
+ `\\{%[-+~]?\\s*end${keyword}\\s*[-+~]?%\\}`,
5967
+ "g"
5968
+ );
5869
5969
  close.lastIndex = end;
5870
5970
  const match = close.exec(s);
5871
5971
  return { end: match ? match.index + match[0].length : this.n, keyword };
@@ -5920,125 +6020,2110 @@ function locate(starts, offset) {
5920
6020
  }
5921
6021
  return { line: low + 1, column: offset - (starts[low] ?? 0) + 1 };
5922
6022
  }
6023
+ function offsetOf(starts, sourceLength, line, column) {
6024
+ const start = starts[line - 1];
6025
+ if (start === void 0 || column < 1) return null;
6026
+ const offset = start + column - 1;
6027
+ const next = starts[line] ?? sourceLength + 1;
6028
+ return offset < next ? offset : null;
6029
+ }
5923
6030
 
5924
- // packages/engine/src/lib/office/errors.ts
5925
- var OfficeError = class extends Error {
5926
- constructor(code, message, details = {}) {
5927
- super(message);
5928
- this.code = code;
5929
- this.details = details;
5930
- this.name = "OfficeError";
5931
- }
5932
- };
5933
- var officeLimits = {
5934
- /** Entries in one container. */
5935
- maxEntries: 2e3,
5936
- /** Sum of the uncompressed sizes the central directory declares. */
5937
- maxDeclaredBytes: 200 * 1024 * 1024,
5938
- /** One part we inflate; enforced while inflating, because the declared size can lie. */
5939
- maxPartBytes: 20 * 1024 * 1024,
5940
- /** A conversion upload or a template file. */
5941
- maxFileBytes: 20 * 1024 * 1024
5942
- };
5943
-
5944
- // node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
5945
- var u8 = Uint8Array;
5946
- var u16 = Uint16Array;
5947
- var i32 = Int32Array;
5948
- var fleb = new u8([
5949
- 0,
5950
- 0,
5951
- 0,
5952
- 0,
5953
- 0,
5954
- 0,
5955
- 0,
5956
- 0,
5957
- 1,
5958
- 1,
5959
- 1,
5960
- 1,
5961
- 2,
5962
- 2,
5963
- 2,
5964
- 2,
5965
- 3,
5966
- 3,
5967
- 3,
5968
- 3,
5969
- 4,
5970
- 4,
5971
- 4,
5972
- 4,
5973
- 5,
5974
- 5,
5975
- 5,
5976
- 5,
5977
- 0,
5978
- /* unused */
5979
- 0,
5980
- 0,
5981
- /* impossible */
5982
- 0
6031
+ // packages/engine/src/lib/source-edit.ts
6032
+ var MAX_REGION = 2e4;
6033
+ var voidElements = /* @__PURE__ */ new Set([
6034
+ "area",
6035
+ "base",
6036
+ "br",
6037
+ "col",
6038
+ "embed",
6039
+ "hr",
6040
+ "img",
6041
+ "input",
6042
+ "link",
6043
+ "meta",
6044
+ "param",
6045
+ "source",
6046
+ "track",
6047
+ "wbr"
5983
6048
  ]);
5984
- var fdeb = new u8([
5985
- 0,
5986
- 0,
5987
- 0,
5988
- 0,
5989
- 1,
5990
- 1,
5991
- 2,
5992
- 2,
5993
- 3,
5994
- 3,
5995
- 4,
5996
- 4,
5997
- 5,
5998
- 5,
5999
- 6,
6000
- 6,
6001
- 7,
6002
- 7,
6003
- 8,
6004
- 8,
6005
- 9,
6006
- 9,
6007
- 10,
6008
- 10,
6009
- 11,
6010
- 11,
6011
- 12,
6012
- 12,
6013
- 13,
6014
- 13,
6015
- /* unused */
6016
- 0,
6017
- 0
6049
+ var phrasingElements = /* @__PURE__ */ new Set([
6050
+ "a",
6051
+ "abbr",
6052
+ "b",
6053
+ "bdi",
6054
+ "bdo",
6055
+ "cite",
6056
+ "code",
6057
+ "data",
6058
+ "del",
6059
+ "dfn",
6060
+ "em",
6061
+ "font",
6062
+ "i",
6063
+ "img",
6064
+ "ins",
6065
+ "kbd",
6066
+ "label",
6067
+ "mark",
6068
+ "q",
6069
+ "s",
6070
+ "samp",
6071
+ "small",
6072
+ "span",
6073
+ "strong",
6074
+ "sub",
6075
+ "sup",
6076
+ "svg",
6077
+ "time",
6078
+ "u",
6079
+ "var",
6080
+ "wbr"
6018
6081
  ]);
6019
- var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
6020
- var freb = function(eb, start) {
6021
- var b = new u16(31);
6022
- for (var i = 0; i < 31; ++i) {
6023
- b[i] = start += 1 << eb[i - 1];
6082
+ var namedEntities = {
6083
+ amp: 38,
6084
+ lt: 60,
6085
+ gt: 62,
6086
+ quot: 34,
6087
+ apos: 39,
6088
+ nbsp: 160,
6089
+ shy: 173,
6090
+ ensp: 8194,
6091
+ emsp: 8195,
6092
+ thinsp: 8201,
6093
+ zwnj: 8204,
6094
+ zwj: 8205,
6095
+ ndash: 8211,
6096
+ mdash: 8212,
6097
+ lsquo: 8216,
6098
+ rsquo: 8217,
6099
+ sbquo: 8218,
6100
+ ldquo: 8220,
6101
+ rdquo: 8221,
6102
+ bdquo: 8222,
6103
+ laquo: 171,
6104
+ raquo: 187,
6105
+ lsaquo: 8249,
6106
+ rsaquo: 8250,
6107
+ hellip: 8230,
6108
+ bull: 8226,
6109
+ middot: 183,
6110
+ euro: 8364,
6111
+ pound: 163,
6112
+ yen: 165,
6113
+ cent: 162,
6114
+ copy: 169,
6115
+ reg: 174,
6116
+ trade: 8482,
6117
+ deg: 176,
6118
+ sect: 167,
6119
+ para: 182,
6120
+ times: 215,
6121
+ divide: 247,
6122
+ plusmn: 177,
6123
+ micro: 181,
6124
+ frac12: 189,
6125
+ frac14: 188,
6126
+ frac34: 190,
6127
+ sup2: 178,
6128
+ sup3: 179,
6129
+ dagger: 8224,
6130
+ Dagger: 8225,
6131
+ larr: 8592,
6132
+ rarr: 8594,
6133
+ uarr: 8593,
6134
+ darr: 8595,
6135
+ auml: 228,
6136
+ ouml: 246,
6137
+ uuml: 252,
6138
+ Auml: 196,
6139
+ Ouml: 214,
6140
+ Uuml: 220,
6141
+ szlig: 223,
6142
+ eacute: 233,
6143
+ egrave: 232,
6144
+ agrave: 224,
6145
+ aacute: 225,
6146
+ ccedil: 231,
6147
+ oacute: 243,
6148
+ iacute: 237,
6149
+ uacute: 250,
6150
+ ntilde: 241
6151
+ };
6152
+ var entityPattern = /&(#[0-9]{1,7}|#[xX][0-9a-fA-F]{1,6}|[A-Za-z][A-Za-z0-9]{0,31});/y;
6153
+ var NBSP = String.fromCharCode(160);
6154
+ var BREAK = String.fromCharCode(57344);
6155
+ var REMOVED = String.fromCharCode(57345);
6156
+ var CHIP_OPEN = String.fromCharCode(57346);
6157
+ var CHIP_CLOSE = String.fromCharCode(57347);
6158
+ function editableRegion(source, engine, location) {
6159
+ const offset = offsetOf(
6160
+ lineStarts(source),
6161
+ source.length,
6162
+ location.line,
6163
+ location.column
6164
+ );
6165
+ return offset === null ? { reason: "not-an-element" } : editableRegionAt(source, engine, offset);
6166
+ }
6167
+ function editableRegionAt(source, engine, at) {
6168
+ const scanned = scanRegion(source, engine, at);
6169
+ if ("reason" in scanned) return scanned;
6170
+ const region = { kind: "text", ...scanned };
6171
+ const visible = region.parts.filter(
6172
+ (p) => !(p.kind === "text" && !source.slice(p.start, p.end).trim())
6173
+ );
6174
+ const only = visible.length === 1 ? visible[0] : void 0;
6175
+ if (only?.kind === "chip" && only.chip === "expression") {
6176
+ const call = translationCall(engine, only.inner ?? "");
6177
+ if (call)
6178
+ return { ...region, kind: "t", key: call.key, params: call.params };
6179
+ }
6180
+ const hasText = region.parts.some(
6181
+ (p) => p.kind === "text" && decodeText2(source.slice(p.start, p.end)).text.trim() !== ""
6182
+ );
6183
+ return hasText ? region : { reason: "no-text" };
6184
+ }
6185
+ function findRegionAgain(source, engine, outer) {
6186
+ const first = source.indexOf(outer);
6187
+ if (first < 0 || source.indexOf(outer, first + 1) >= 0) return null;
6188
+ const found = editableRegionAt(source, engine, first);
6189
+ return "reason" in found ? null : found;
6190
+ }
6191
+ function regionChips(source, region) {
6192
+ const chips = [];
6193
+ for (const part of region.parts)
6194
+ if (part.kind === "chip")
6195
+ chips.push({
6196
+ index: chips.length,
6197
+ kind: part.chip,
6198
+ label: part.label,
6199
+ source: source.slice(part.start, part.end)
6200
+ });
6201
+ return chips;
6202
+ }
6203
+ function regionItems(source, region) {
6204
+ const items = [];
6205
+ let chip = 0;
6206
+ for (const part of region.parts) {
6207
+ if (part.kind === "text")
6208
+ items.push({
6209
+ kind: "text",
6210
+ text: decodeText2(source.slice(part.start, part.end)).text
6211
+ });
6212
+ else if (part.kind === "break") items.push({ kind: "break" });
6213
+ else items.push({ kind: "chip", index: chip++ });
6024
6214
  }
6025
- var r = new i32(b[30]);
6026
- for (var i = 1; i < 30; ++i) {
6027
- for (var j = b[i]; j < b[i + 1]; ++j) {
6028
- r[j] = j - b[i] << 5 | i;
6215
+ return items;
6216
+ }
6217
+ function applyRegionEdit(source, engine, region, items) {
6218
+ const chipParts = region.parts.filter(
6219
+ (p) => p.kind === "chip"
6220
+ );
6221
+ const kept = [];
6222
+ for (const item of items)
6223
+ if (item.kind === "chip") {
6224
+ if (!Number.isInteger(item.index) || item.index < 0 || item.index >= chipParts.length)
6225
+ return { error: "chips" };
6226
+ if (kept.length && item.index <= (kept[kept.length - 1] ?? -1))
6227
+ return { error: "chips" };
6228
+ kept.push(item.index);
6229
+ }
6230
+ const breakSpelling = source.slice(...spanOf(region.parts.find((p) => p.kind === "break"))) || "<br>";
6231
+ const newSegments = [""];
6232
+ for (const item of items) {
6233
+ if (item.kind === "chip") newSegments.push("");
6234
+ else
6235
+ newSegments[newSegments.length - 1] += item.kind === "break" ? BREAK : item.text.replace(/[\uE000-\uE003]/g, "");
6236
+ }
6237
+ const pieces = [];
6238
+ let segStart = region.start;
6239
+ let partIndex = 0;
6240
+ for (let k = 0; k <= kept.length; k++) {
6241
+ const boundary = k < kept.length ? chipParts[kept[k] ?? 0] : void 0;
6242
+ const segEnd = boundary ? boundary.start : region.end;
6243
+ const segParts = [];
6244
+ while (partIndex < region.parts.length && (region.parts[partIndex]?.start ?? Infinity) < segEnd)
6245
+ segParts.push(region.parts[partIndex++]);
6246
+ if (boundary) partIndex++;
6247
+ pieces.push(
6248
+ rewriteSegment(
6249
+ source,
6250
+ segStart,
6251
+ segEnd,
6252
+ segParts,
6253
+ newSegments[k] ?? "",
6254
+ breakSpelling
6255
+ )
6256
+ );
6257
+ if (boundary) {
6258
+ pieces.push(source.slice(boundary.start, boundary.end));
6259
+ segStart = boundary.end;
6029
6260
  }
6030
6261
  }
6031
- return { b, r };
6032
- };
6033
- var _a = freb(fleb, 2);
6034
- var fl = _a.b;
6035
- var revfl = _a.r;
6036
- fl[28] = 258, revfl[258] = 28;
6037
- var _b = freb(fdeb, 0);
6038
- var fd = _b.b;
6039
- var revfd = _b.r;
6040
- var rev = new u16(32768);
6041
- for (i = 0; i < 32768; ++i) {
6262
+ const expected = flatExpected(
6263
+ items,
6264
+ chipParts.map((c) => source.slice(c.start, c.end))
6265
+ );
6266
+ const attempt = source.slice(0, region.start) + pieces.join("") + source.slice(region.end);
6267
+ if (readsBack(attempt, engine, region.tagStart, expected))
6268
+ return { source: attempt };
6269
+ const whole = items.map(
6270
+ (item) => item.kind === "chip" ? source.slice(chipParts[item.index]?.start, chipParts[item.index]?.end) : item.kind === "break" ? breakSpelling : encodeText(item.text, true, true)
6271
+ ).join("");
6272
+ const rewritten = source.slice(0, region.start) + whole + source.slice(region.end);
6273
+ return readsBack(rewritten, engine, region.tagStart, expected) ? { source: rewritten } : { error: "invalid" };
6274
+ }
6275
+ function translationItems(text2) {
6276
+ const items = [];
6277
+ const names = [];
6278
+ let last = 0;
6279
+ for (const match of text2.matchAll(/\{(\w+)\}/g)) {
6280
+ if (match.index > last)
6281
+ items.push({ kind: "text", text: text2.slice(last, match.index) });
6282
+ items.push({ kind: "chip", index: names.length });
6283
+ names.push(match[1] ?? "");
6284
+ last = match.index + match[0].length;
6285
+ }
6286
+ if (last < text2.length) items.push({ kind: "text", text: text2.slice(last) });
6287
+ return { items, names };
6288
+ }
6289
+ function translationText(items, names) {
6290
+ return items.map(
6291
+ (item) => item.kind === "chip" ? `{${names[item.index] ?? ""}}` : item.kind === "break" ? " " : item.text.replace(new RegExp(NBSP, "g"), " ")
6292
+ ).join("");
6293
+ }
6294
+ function chipValues(rendered, items) {
6295
+ const slots = [""];
6296
+ for (const item of items) {
6297
+ if (item.kind === "chip") slots.push("");
6298
+ else if (item.kind === "text") slots[slots.length - 1] += item.text;
6299
+ }
6300
+ const chips = slots.length - 1;
6301
+ if (!rendered.startsWith(slots[0] ?? "")) return null;
6302
+ let found = null;
6303
+ let solutions = 0;
6304
+ let steps = 0;
6305
+ const values = [];
6306
+ const place = (k, pos) => {
6307
+ if (solutions > 1 || ++steps > 1e4) return;
6308
+ if (k === chips) {
6309
+ if (pos === rendered.length) {
6310
+ solutions++;
6311
+ found = [...values];
6312
+ }
6313
+ return;
6314
+ }
6315
+ const next = slots[k + 1] ?? "";
6316
+ const last = k + 1 === chips;
6317
+ for (let q = pos; q <= rendered.length; q++) {
6318
+ q = rendered.indexOf(next, q);
6319
+ if (q < 0) return;
6320
+ if (!last || q + next.length === rendered.length) {
6321
+ values[k] = rendered.slice(pos, q);
6322
+ place(k + 1, q + next.length);
6323
+ if (solutions > 1) return;
6324
+ }
6325
+ }
6326
+ };
6327
+ place(0, (slots[0] ?? "").length);
6328
+ return solutions === 1 && steps <= 1e4 ? found : null;
6329
+ }
6330
+ function scanRegion(source, engine, at) {
6331
+ if (source[at] !== "<") return { reason: "not-an-element" };
6332
+ tagName.lastIndex = at + 1;
6333
+ const name = tagName.exec(source)?.[0];
6334
+ if (!name) return { reason: "not-an-element" };
6335
+ const nameEnd = at + 1 + name.length;
6336
+ if (!plainNameEnd(source[nameEnd])) return { reason: "dynamic-tag" };
6337
+ const tag = name.toLowerCase();
6338
+ const scanner = new Scanner(source, engine);
6339
+ const start = scanner.tagEnd(nameEnd);
6340
+ if (voidElements.has(tag) || source[start - 2] === "/")
6341
+ return { reason: "void" };
6342
+ if (rawTextElements.has(tag)) return { reason: "raw-text" };
6343
+ const parts = [];
6344
+ let textStart = start;
6345
+ const flush = (to) => {
6346
+ if (to > textStart) parts.push({ kind: "text", start: textStart, end: to });
6347
+ };
6348
+ let i = start;
6349
+ while (i < source.length) {
6350
+ if (i - start > MAX_REGION) return { reason: "too-large" };
6351
+ const ch = source[i];
6352
+ if (ch === "{") {
6353
+ const construct = scanner.classify(i);
6354
+ if (!construct) {
6355
+ i++;
6356
+ continue;
6357
+ }
6358
+ if (construct.type === "statement") return { reason: "control-flow" };
6359
+ flush(i);
6360
+ parts.push(
6361
+ construct.type === "output" ? {
6362
+ kind: "chip",
6363
+ chip: "expression",
6364
+ label: expressionLabel(construct.inner),
6365
+ inner: construct.inner,
6366
+ start: i,
6367
+ end: construct.end
6368
+ } : {
6369
+ kind: "chip",
6370
+ chip: "comment",
6371
+ label: "comment",
6372
+ start: i,
6373
+ end: construct.end
6374
+ }
6375
+ );
6376
+ i = textStart = construct.end;
6377
+ continue;
6378
+ }
6379
+ if (ch === "&") {
6380
+ entityPattern.lastIndex = i;
6381
+ const match = entityPattern.exec(source);
6382
+ if (match && entityChar(match[1] ?? "") === null && !(match[1] ?? "").startsWith("#")) {
6383
+ flush(i);
6384
+ parts.push({
6385
+ kind: "chip",
6386
+ chip: "entity",
6387
+ label: match[0],
6388
+ start: i,
6389
+ end: i + match[0].length
6390
+ });
6391
+ i = textStart = i + match[0].length;
6392
+ continue;
6393
+ }
6394
+ i++;
6395
+ continue;
6396
+ }
6397
+ if (ch !== "<") {
6398
+ i++;
6399
+ continue;
6400
+ }
6401
+ if (source.startsWith("<!--", i)) {
6402
+ const end = scanner.skipTo(i + 4, "-->");
6403
+ flush(i);
6404
+ parts.push({
6405
+ kind: "chip",
6406
+ chip: "comment",
6407
+ label: "comment",
6408
+ start: i,
6409
+ end
6410
+ });
6411
+ i = textStart = end;
6412
+ continue;
6413
+ }
6414
+ const next = source[i + 1];
6415
+ if (next === "/") {
6416
+ tagName.lastIndex = i + 2;
6417
+ const closing = tagName.exec(source)?.[0];
6418
+ if (closing?.toLowerCase() !== tag) return { reason: "unclosed" };
6419
+ flush(i);
6420
+ return {
6421
+ tag,
6422
+ tagStart: at,
6423
+ start,
6424
+ end: i,
6425
+ outerEnd: scanner.tagEnd(i + 2 + closing.length),
6426
+ parts
6427
+ };
6428
+ }
6429
+ if (next === "!" || next === "?") return { reason: "block-child" };
6430
+ tagName.lastIndex = i + 1;
6431
+ const child2 = tagName.exec(source)?.[0];
6432
+ if (!child2) {
6433
+ i++;
6434
+ continue;
6435
+ }
6436
+ const childNameEnd = i + 1 + child2.length;
6437
+ if (!plainNameEnd(source[childNameEnd])) return { reason: "dynamic-tag" };
6438
+ const childTag = child2.toLowerCase();
6439
+ const childStartEnd = scanner.tagEnd(childNameEnd);
6440
+ if (childTag === "br") {
6441
+ flush(i);
6442
+ parts.push({ kind: "break", start: i, end: childStartEnd });
6443
+ i = textStart = childStartEnd;
6444
+ continue;
6445
+ }
6446
+ if (rawTextElements.has(childTag)) return { reason: "raw-text" };
6447
+ if (!phrasingElements.has(childTag)) return { reason: "block-child" };
6448
+ let childEnd = childStartEnd;
6449
+ if (!voidElements.has(childTag) && source[childStartEnd - 2] !== "/") {
6450
+ childEnd = matchingEnd(source, scanner, childStartEnd, childTag);
6451
+ if (childEnd === null) return { reason: "unclosed" };
6452
+ }
6453
+ flush(i);
6454
+ parts.push({
6455
+ kind: "chip",
6456
+ chip: "element",
6457
+ label: `<${childTag}>`,
6458
+ start: i,
6459
+ end: childEnd
6460
+ });
6461
+ i = textStart = childEnd;
6462
+ }
6463
+ return { reason: "unclosed" };
6464
+ }
6465
+ function plainNameEnd(after) {
6466
+ return after === void 0 || after === ">" || after === "/" || /\s/.test(after);
6467
+ }
6468
+ function matchingEnd(source, scanner, from, name) {
6469
+ let depth = 1;
6470
+ let j = from;
6471
+ while (j < source.length) {
6472
+ const ch = source[j];
6473
+ if (ch === "{") {
6474
+ j = scanner.skipTemplate(j) ?? j + 1;
6475
+ continue;
6476
+ }
6477
+ if (ch !== "<") {
6478
+ j++;
6479
+ continue;
6480
+ }
6481
+ if (source.startsWith("<!--", j)) {
6482
+ j = scanner.skipTo(j + 4, "-->");
6483
+ continue;
6484
+ }
6485
+ if (source[j + 1] === "/") {
6486
+ tagName.lastIndex = j + 2;
6487
+ const closing = tagName.exec(source)?.[0];
6488
+ const end2 = scanner.tagEnd(j + 2);
6489
+ if (closing?.toLowerCase() === name && --depth === 0) return end2;
6490
+ j = end2;
6491
+ continue;
6492
+ }
6493
+ tagName.lastIndex = j + 1;
6494
+ const opening = tagName.exec(source)?.[0];
6495
+ if (!opening) {
6496
+ j++;
6497
+ continue;
6498
+ }
6499
+ const end = scanner.tagEnd(j + 1 + opening.length);
6500
+ const lower = opening.toLowerCase();
6501
+ if (lower === name && source[end - 2] !== "/") depth++;
6502
+ j = rawTextElements.has(lower) ? scanner.skipToClosingTag(end, lower) : end;
6503
+ }
6504
+ return null;
6505
+ }
6506
+ function expressionLabel(inner) {
6507
+ const label2 = inner.replace(/^[-~+&]?\s*/, "").replace(/\s*[-~+]?$/, "").replace(/\s+/g, " ");
6508
+ return label2.length > 60 ? `${label2.slice(0, 59)}\u2026` : label2;
6509
+ }
6510
+ function translationCall(engine, inner) {
6511
+ const expression = inner.replace(/^[-~+]?\s*/, "").replace(/\s*[-~+]?$/, "");
6512
+ let match;
6513
+ if (engine === "jinja2") {
6514
+ match = /^(?:t|translate)\(\s*(['"])((?:(?!\1).)*)\1\s*(?:,\s*\{([\s\S]*)\}\s*)?\)$/.exec(
6515
+ expression
6516
+ );
6517
+ return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", ":", ",") } : null;
6518
+ }
6519
+ if (engine === "liquid") {
6520
+ match = /^(['"])((?:(?!\1).)*)\1\s*\|\s*(?:t|translate)\b\s*(?::\s*([\s\S]*))?$/.exec(
6521
+ expression
6522
+ );
6523
+ return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", ":", ",") } : null;
6524
+ }
6525
+ match = /^(?:t|translate)\s+(['"])((?:(?!\1).)*)\1([\s\S]*)$/.exec(
6526
+ expression
6527
+ );
6528
+ return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", "=", " ") } : null;
6529
+ }
6530
+ function parseParams(text2, assign, separator) {
6531
+ const params = {};
6532
+ for (const piece of splitTopLevel(text2, separator)) {
6533
+ const at = piece.indexOf(assign);
6534
+ if (at <= 0) continue;
6535
+ const name = piece.slice(0, at).trim().replace(/^['"]|['"]$/g, "");
6536
+ if (/^[\w-]+$/.test(name)) params[name] = piece.slice(at + 1).trim();
6537
+ }
6538
+ return params;
6539
+ }
6540
+ function splitTopLevel(text2, separator) {
6541
+ const pieces = [];
6542
+ let depth = 0;
6543
+ let quote2 = null;
6544
+ let current = "";
6545
+ for (const ch of text2) {
6546
+ if (quote2) {
6547
+ if (ch === quote2) quote2 = null;
6548
+ } else if (ch === '"' || ch === "'") quote2 = ch;
6549
+ else if ("([{".includes(ch)) depth++;
6550
+ else if (")]}".includes(ch)) depth--;
6551
+ else if (depth === 0 && (separator === " " ? /\s/.test(ch) : ch === separator)) {
6552
+ if (current.trim()) pieces.push(current);
6553
+ current = "";
6554
+ continue;
6555
+ }
6556
+ current += ch;
6557
+ }
6558
+ if (current.trim()) pieces.push(current);
6559
+ return pieces;
6560
+ }
6561
+ function entityChar(reference) {
6562
+ if (reference.startsWith("#")) {
6563
+ const code2 = reference[1] === "x" || reference[1] === "X" ? parseInt(reference.slice(2), 16) : parseInt(reference.slice(1), 10);
6564
+ return Number.isFinite(code2) && code2 > 0 && code2 <= 1114111 && !(code2 >= 55296 && code2 <= 57343) ? String.fromCodePoint(code2) : null;
6565
+ }
6566
+ const code = namedEntities[reference];
6567
+ return code === void 0 ? null : String.fromCharCode(code);
6568
+ }
6569
+ function decodeText2(raw) {
6570
+ let text2 = "";
6571
+ const map = [];
6572
+ let i = 0;
6573
+ while (i < raw.length) {
6574
+ if (raw[i] === "&") {
6575
+ entityPattern.lastIndex = i;
6576
+ const match = entityPattern.exec(raw);
6577
+ const decoded = match ? entityChar(match[1] ?? "") : null;
6578
+ if (match && decoded !== null) {
6579
+ for (let u = 0; u < decoded.length; u++) map.push(i);
6580
+ text2 += decoded;
6581
+ i += match[0].length;
6582
+ continue;
6583
+ }
6584
+ }
6585
+ text2 += raw[i];
6586
+ map.push(i);
6587
+ i++;
6588
+ }
6589
+ map.push(raw.length);
6590
+ return { text: text2, map };
6591
+ }
6592
+ function encodeText(text2, keepNbsp, allBraces = false) {
6593
+ let out = "";
6594
+ for (let k = 0; k < text2.length; k++) {
6595
+ const ch = text2[k] ?? "";
6596
+ const next = text2[k + 1];
6597
+ if (ch === "&") out += "&amp;";
6598
+ else if (ch === "<") out += "&lt;";
6599
+ else if (ch === ">") out += "&gt;";
6600
+ else if (ch === NBSP) out += keepNbsp ? "&nbsp;" : " ";
6601
+ else if (ch === "{" && (allBraces || next === void 0 || next === "{" || next === "%" || next === "#"))
6602
+ out += "&#123;";
6603
+ else if (ch === "#" && next === "}") out += "&#35;";
6604
+ else out += ch;
6605
+ }
6606
+ return out;
6607
+ }
6608
+ function encodeTemplateText(text2) {
6609
+ return encodeText(text2, true);
6610
+ }
6611
+ function spanOf(part) {
6612
+ return part ? [part.start, part.end] : [0, 0];
6613
+ }
6614
+ function rewriteSegment(source, segStart, segEnd, parts, next, breakSpelling) {
6615
+ let old = "";
6616
+ const map = [];
6617
+ for (const part of parts) {
6618
+ if (part.kind === "text") {
6619
+ const decoded = decodeText2(source.slice(part.start, part.end));
6620
+ old += decoded.text;
6621
+ for (let k = 0; k < decoded.text.length; k++)
6622
+ map.push(part.start + (decoded.map[k] ?? 0));
6623
+ } else {
6624
+ old += part.kind === "break" ? BREAK : REMOVED;
6625
+ map.push(part.start);
6626
+ }
6627
+ }
6628
+ map.push(segEnd);
6629
+ if (old === next) return source.slice(segStart, segEnd);
6630
+ let prefix = 0;
6631
+ const limit = Math.min(old.length, next.length);
6632
+ while (prefix < limit && old[prefix] === next[prefix]) prefix++;
6633
+ let suffix = 0;
6634
+ while (suffix < limit - prefix && old[old.length - 1 - suffix] === next[next.length - 1 - suffix])
6635
+ suffix++;
6636
+ if (prefix > 0 && isHighSurrogate(old.charCodeAt(prefix - 1))) prefix--;
6637
+ if (suffix > 0 && isLowSurrogate(old.charCodeAt(old.length - suffix)))
6638
+ suffix--;
6639
+ const from = map[prefix] ?? segEnd;
6640
+ const to = map[old.length - suffix] ?? segEnd;
6641
+ const middle = next.slice(prefix, next.length - suffix);
6642
+ const keepNbsp = old.slice(prefix, old.length - suffix).includes(NBSP);
6643
+ const written = middle.split(BREAK).map((text2) => encodeText(text2, keepNbsp)).join(breakSpelling);
6644
+ return source.slice(segStart, from) + written + source.slice(to, segEnd);
6645
+ }
6646
+ var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
6647
+ var isLowSurrogate = (code) => code >= 56320 && code <= 57343;
6648
+ function flatExpected(items, chipSources) {
6649
+ return items.map(
6650
+ (item) => item.kind === "chip" ? CHIP_OPEN + (chipSources[item.index] ?? "") + CHIP_CLOSE : item.kind === "break" ? BREAK : item.text.replace(/[\uE000-\uE003]/g, "")
6651
+ ).join("").replace(new RegExp(NBSP, "g"), " ");
6652
+ }
6653
+ function readsBack(source, engine, tagStart, expected) {
6654
+ const scanned = scanRegion(source, engine, tagStart);
6655
+ if ("reason" in scanned) return false;
6656
+ if (engine === "jinja2" && scanned.parts.some(
6657
+ (p) => p.kind === "text" && source.slice(p.start, p.end).includes("#}")
6658
+ ))
6659
+ return false;
6660
+ const flat = scanned.parts.map(
6661
+ (part) => part.kind === "text" ? decodeText2(source.slice(part.start, part.end)).text : part.kind === "break" ? BREAK : CHIP_OPEN + source.slice(part.start, part.end) + CHIP_CLOSE
6662
+ ).join("").replace(new RegExp(NBSP, "g"), " ");
6663
+ return flat === expected;
6664
+ }
6665
+
6666
+ // packages/engine/src/lib/source-positions.ts
6667
+ var sourceAttribute = "data-ff-src";
6668
+ var editAttribute = "data-ff-edit";
6669
+ function parseSourceAttribute(value) {
6670
+ const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
6671
+ if (!match) return null;
6672
+ const line = Number(match[2]);
6673
+ const column = Number(match[3]);
6674
+ if (line < 1 || column < 1) return null;
6675
+ return { file: match[1], line, column };
6676
+ }
6677
+ function annotateSourcePositions(source, engine, file, options = {}) {
6678
+ const scanner = new Scanner(source, engine);
6679
+ const lines = lineStarts(source);
6680
+ const out = [];
6681
+ let copied = 0;
6682
+ const n = source.length;
6683
+ let i = 0;
6684
+ while (i < n) {
6685
+ const ch = source[i];
6686
+ if (ch === "{") {
6687
+ i = scanner.skipTemplate(i) ?? i + 1;
6688
+ continue;
6689
+ }
6690
+ if (ch !== "<") {
6691
+ i++;
6692
+ continue;
6693
+ }
6694
+ if (source.startsWith("<!--", i)) {
6695
+ i = scanner.skipTo(i + 4, "-->");
6696
+ continue;
6697
+ }
6698
+ if (source.startsWith("<![CDATA[", i)) {
6699
+ i = scanner.skipTo(i + 9, "]]>");
6700
+ continue;
6701
+ }
6702
+ const next = source[i + 1];
6703
+ if (next === "!" || next === "?") {
6704
+ i = scanner.tagEnd(i + 2);
6705
+ continue;
6706
+ }
6707
+ if (next === "/") {
6708
+ i += 2;
6709
+ continue;
6710
+ }
6711
+ tagName.lastIndex = i + 1;
6712
+ const name = tagName.exec(source)?.[0];
6713
+ if (!name) {
6714
+ i++;
6715
+ continue;
6716
+ }
6717
+ const nameEnd = i + 1 + name.length;
6718
+ const end = scanner.tagEnd(nameEnd);
6719
+ const after = source[nameEnd];
6720
+ const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
6721
+ if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
6722
+ const { line, column } = locate(lines, i);
6723
+ const region = options.editable ? editableRegionAt(source, engine, i) : null;
6724
+ const edit = region && !("reason" in region) ? ` ${editAttribute}="${region.kind}"` : "";
6725
+ out.push(
6726
+ source.slice(copied, nameEnd),
6727
+ ` ${sourceAttribute}="${file}:${line}:${column}"${edit}`
6728
+ );
6729
+ copied = nameEnd;
6730
+ }
6731
+ i = end;
6732
+ if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
6733
+ i = scanner.skipToClosingTag(end, name);
6734
+ }
6735
+ out.push(source.slice(copied));
6736
+ return out.join("");
6737
+ }
6738
+
6739
+ // packages/engine/src/lib/design/types.ts
6740
+ var DESIGN_VERSION = 1;
6741
+
6742
+ // packages/engine/src/lib/design/parse.ts
6743
+ var DESIGN_LIMITS = {
6744
+ layers: 200,
6745
+ textPerLayer: 5e3,
6746
+ bytes: 256 * 1024
6747
+ };
6748
+ var layerIdPattern = /^[a-z][a-z0-9-]{0,31}$/;
6749
+ var dataPathPattern = /^[A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*$/;
6750
+ var colourPattern = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
6751
+ var tokenPattern = /^[a-z][a-z0-9-]{0,31}$/;
6752
+ var symbologies = [
6753
+ "code128",
6754
+ "ean13",
6755
+ "ean8",
6756
+ "upca",
6757
+ "itf14",
6758
+ "code39"
6759
+ ];
6760
+ var Reader = class {
6761
+ constructor(at) {
6762
+ this.at = at;
6763
+ }
6764
+ problems = [];
6765
+ problem(message) {
6766
+ this.problems.push(`${this.at}: ${message}`);
6767
+ }
6768
+ number(value, name, min, max2, fallback) {
6769
+ if (typeof value !== "number" || !Number.isFinite(value)) {
6770
+ if (fallback !== void 0 && value === void 0) return fallback;
6771
+ this.problem(`${name} must be a number`);
6772
+ return fallback ?? min;
6773
+ }
6774
+ return Math.min(max2, Math.max(min, value));
6775
+ }
6776
+ string(value, name, max2, pattern) {
6777
+ if (typeof value !== "string" || value.length > max2 || pattern && !pattern.test(value)) {
6778
+ this.problem(`${name} is not valid`);
6779
+ return "";
6780
+ }
6781
+ return value;
6782
+ }
6783
+ oneOf(value, name, options, fallback) {
6784
+ if (value === void 0 && fallback !== void 0) return fallback;
6785
+ if (typeof value === "string" && options.includes(value))
6786
+ return value;
6787
+ this.problem(`${name} must be one of ${options.join(", ")}`);
6788
+ return options[0];
6789
+ }
6790
+ bool(value, fallback) {
6791
+ return typeof value === "boolean" ? value : fallback;
6792
+ }
6793
+ paint(value, name) {
6794
+ const v = value;
6795
+ if (v && typeof v["color"] === "string" && colourPattern.test(v["color"]))
6796
+ return { color: v["color"] };
6797
+ if (v && typeof v["brand"] === "string" && tokenPattern.test(v["brand"]) && typeof v["fallback"] === "string" && colourPattern.test(v["fallback"]))
6798
+ return { brand: v["brand"], fallback: v["fallback"] };
6799
+ this.problem(`${name} is not a colour`);
6800
+ return { color: "#000000" };
6801
+ }
6802
+ paintOrNull(value, name) {
6803
+ return value === null || value === void 0 ? null : this.paint(value, name);
6804
+ }
6805
+ path(value, name) {
6806
+ return this.string(value, name, 200, dataPathPattern);
6807
+ }
6808
+ };
6809
+ function designBytes(value) {
6810
+ return new TextEncoder().encode(JSON.stringify(value) ?? "").length;
6811
+ }
6812
+ function parseDesign(value) {
6813
+ const root = new Reader("design");
6814
+ const v = value;
6815
+ if (!v || typeof v !== "object" || Array.isArray(v))
6816
+ return { design: null, problems: ["design: not an object"] };
6817
+ if (designBytes(v) > DESIGN_LIMITS.bytes)
6818
+ return {
6819
+ design: null,
6820
+ problems: [`design: larger than ${DESIGN_LIMITS.bytes / 1024} kB`]
6821
+ };
6822
+ if (v["version"] !== DESIGN_VERSION)
6823
+ root.problem(`version must be ${DESIGN_VERSION}`);
6824
+ const output = typeof v["output"] === "string" ? v["output"].slice(0, 64) : "";
6825
+ const background = root.paintOrNull(v["background"], "background");
6826
+ const rawLayers = Array.isArray(v["layers"]) ? v["layers"] : null;
6827
+ if (!rawLayers) root.problem("layers must be a list");
6828
+ if (rawLayers && rawLayers.length > DESIGN_LIMITS.layers)
6829
+ root.problem(`at most ${DESIGN_LIMITS.layers} layers`);
6830
+ const problems = [...root.problems];
6831
+ const layers = [];
6832
+ const ids = /* @__PURE__ */ new Set();
6833
+ for (const [index, raw] of (rawLayers ?? []).slice(0, DESIGN_LIMITS.layers).entries()) {
6834
+ const id = raw?.["id"];
6835
+ const reader = new Reader(
6836
+ `layer ${typeof id === "string" ? id : index + 1}`
6837
+ );
6838
+ const layer = readLayer(reader, raw);
6839
+ if (layer && ids.has(layer.id)) reader.problem("id is used twice");
6840
+ if (layer) ids.add(layer.id);
6841
+ problems.push(...reader.problems);
6842
+ if (layer) layers.push(layer);
6843
+ }
6844
+ let guides;
6845
+ const g = v["guides"];
6846
+ if (g && typeof g === "object") {
6847
+ const list2 = (x) => (Array.isArray(x) ? x : []).filter((n) => typeof n === "number" && Number.isFinite(n)).slice(0, 100);
6848
+ guides = { x: list2(g["x"]), y: list2(g["y"]) };
6849
+ }
6850
+ if (problems.length) return { design: null, problems };
6851
+ return {
6852
+ design: {
6853
+ version: DESIGN_VERSION,
6854
+ output,
6855
+ background,
6856
+ layers,
6857
+ ...guides ? { guides } : {}
6858
+ },
6859
+ problems: []
6860
+ };
6861
+ }
6862
+ function readLayer(r, raw) {
6863
+ const v = raw;
6864
+ if (!v || typeof v !== "object") {
6865
+ r.problem("not an object");
6866
+ return null;
6867
+ }
6868
+ const base = {
6869
+ id: r.string(v["id"], "id", 32, layerIdPattern),
6870
+ name: typeof v["name"] === "string" && v["name"].trim() ? v["name"].slice(0, 100) : String(v["id"] ?? ""),
6871
+ x: r.number(v["x"], "x", -1e4, 1e4),
6872
+ y: r.number(v["y"], "y", -1e4, 1e4),
6873
+ width: r.number(v["width"], "width", 1, 1e4),
6874
+ height: r.number(v["height"], "height", 1, 1e4),
6875
+ rotation: r.number(v["rotation"], "rotation", -360, 360, 0),
6876
+ opacity: r.number(v["opacity"], "opacity", 0, 1, 1),
6877
+ visible: r.bool(v["visible"], true),
6878
+ ...v["locked"] === true ? { locked: true } : {}
6879
+ };
6880
+ const showWhen = v["showWhen"];
6881
+ if (showWhen) base.showWhen = { path: r.path(showWhen["path"], "showWhen") };
6882
+ if (v["radius"] !== void 0)
6883
+ base.radius = r.number(v["radius"], "radius", 0, 5e3);
6884
+ if (v["shadow"]) base.shadow = readShadow(r, v["shadow"]);
6885
+ if (v["stroke"]) base.stroke = readStroke(r, v["stroke"]);
6886
+ switch (v["type"]) {
6887
+ case "text": {
6888
+ const style2 = readStyle(r, v["style"]);
6889
+ const layer = {
6890
+ ...base,
6891
+ type: "text",
6892
+ content: readContent(r, v["content"]),
6893
+ style: style2,
6894
+ sizing: r.oneOf(
6895
+ v["sizing"],
6896
+ "sizing",
6897
+ ["fixed", "shrink", "grow"],
6898
+ "fixed"
6899
+ ),
6900
+ valign: r.oneOf(
6901
+ v["valign"],
6902
+ "valign",
6903
+ ["top", "middle", "bottom"],
6904
+ "top"
6905
+ )
6906
+ };
6907
+ if (v["minFontSize"] !== void 0)
6908
+ layer.minFontSize = r.number(
6909
+ v["minFontSize"],
6910
+ "minFontSize",
6911
+ 1,
6912
+ style2.size
6913
+ );
6914
+ if (v["maxLines"] !== void 0)
6915
+ layer.maxLines = Math.round(
6916
+ r.number(v["maxLines"], "maxLines", 1, 100)
6917
+ );
6918
+ if (v["fill"] !== void 0)
6919
+ layer.fill = r.paintOrNull(v["fill"], "fill");
6920
+ if (v["padding"] !== void 0)
6921
+ layer.padding = r.number(v["padding"], "padding", 0, 1e3);
6922
+ return layer;
6923
+ }
6924
+ case "image": {
6925
+ const layer = {
6926
+ ...base,
6927
+ type: "image",
6928
+ source: readImageSource(r, v["source"]),
6929
+ fit: r.oneOf(
6930
+ v["fit"],
6931
+ "fit",
6932
+ ["cover", "contain", "fill"],
6933
+ "cover"
6934
+ )
6935
+ };
6936
+ const focus = v["focus"];
6937
+ if (focus)
6938
+ layer.focus = {
6939
+ x: r.number(focus["x"], "focus", 0, 100),
6940
+ y: r.number(focus["y"], "focus", 0, 100)
6941
+ };
6942
+ return layer;
6943
+ }
6944
+ case "shape":
6945
+ return {
6946
+ ...base,
6947
+ type: "shape",
6948
+ shape: r.oneOf(v["shape"], "shape", [
6949
+ "rectangle",
6950
+ "ellipse",
6951
+ "line"
6952
+ ]),
6953
+ fill: r.paintOrNull(v["fill"], "fill")
6954
+ };
6955
+ case "qr":
6956
+ return {
6957
+ ...base,
6958
+ type: "qr",
6959
+ value: readBinding(r, v["value"]),
6960
+ ecc: r.oneOf(v["ecc"], "ecc", ["L", "M", "Q", "H"], "M"),
6961
+ color: r.paint(v["color"] ?? { color: "#000000" }, "color"),
6962
+ background: r.paintOrNull(v["background"], "background")
6963
+ };
6964
+ case "barcode":
6965
+ return {
6966
+ ...base,
6967
+ type: "barcode",
6968
+ value: readBinding(r, v["value"]),
6969
+ symbology: r.oneOf(v["symbology"], "symbology", symbologies, "code128"),
6970
+ showText: r.bool(v["showText"], true)
6971
+ };
6972
+ default:
6973
+ r.problem("unknown layer type");
6974
+ return null;
6975
+ }
6976
+ }
6977
+ function readShadow(r, raw) {
6978
+ const v = raw ?? {};
6979
+ const color = r.string(v["color"], "shadow colour", 9, colourPattern) || "#00000040";
6980
+ return {
6981
+ x: r.number(v["x"], "shadow x", -500, 500, 0),
6982
+ y: r.number(v["y"], "shadow y", -500, 500, 0),
6983
+ blur: r.number(v["blur"], "shadow blur", 0, 500, 0),
6984
+ color
6985
+ };
6986
+ }
6987
+ function readStroke(r, raw) {
6988
+ const v = raw ?? {};
6989
+ return {
6990
+ width: r.number(v["width"], "stroke width", 0, 200),
6991
+ paint: r.paint(v["paint"], "stroke colour")
6992
+ };
6993
+ }
6994
+ function readStyle(r, raw) {
6995
+ const v = raw ?? {};
6996
+ const style2 = {
6997
+ font: readFont(r, v["font"]),
6998
+ weight: Math.round(r.number(v["weight"], "weight", 100, 900, 400) / 100) * 100,
6999
+ size: r.number(v["size"], "size", 1, 2e3),
7000
+ lineHeight: r.number(v["lineHeight"], "lineHeight", 0.5, 5, 1.2),
7001
+ letterSpacing: r.number(v["letterSpacing"], "letterSpacing", -50, 200, 0),
7002
+ color: r.paint(v["color"], "colour"),
7003
+ align: r.oneOf(
7004
+ v["align"],
7005
+ "align",
7006
+ ["left", "center", "right", "justify"],
7007
+ "left"
7008
+ )
7009
+ };
7010
+ if (v["italic"] === true) style2.italic = true;
7011
+ if (v["uppercase"] === true) style2.uppercase = true;
7012
+ if (v["underline"] === true) style2.underline = true;
7013
+ if (v["strike"] === true) style2.strike = true;
7014
+ return style2;
7015
+ }
7016
+ function readFont(r, raw) {
7017
+ const v = raw ?? {};
7018
+ if (v["brand"] === "heading" || v["brand"] === "body")
7019
+ return { brand: v["brand"] };
7020
+ const family = r.string(v["family"], "font", 100, /^[^"'\\<>\n\r;{}]+$/);
7021
+ return {
7022
+ family,
7023
+ source: r.oneOf(
7024
+ v["source"],
7025
+ "font source",
7026
+ ["google", "organisation", "system"],
7027
+ "system"
7028
+ )
7029
+ };
7030
+ }
7031
+ function readContent(r, raw) {
7032
+ if (!Array.isArray(raw)) {
7033
+ r.problem("content must be a list");
7034
+ return [];
7035
+ }
7036
+ let length = 0;
7037
+ const items = [];
7038
+ for (const item of raw) {
7039
+ if (item && typeof item["text"] === "string") {
7040
+ length += item["text"].length;
7041
+ items.push({ text: item["text"] });
7042
+ } else if (item && item["break"] === true) items.push({ break: true });
7043
+ else if (item && typeof item["path"] === "string") {
7044
+ const path = r.path(item["path"], "content path");
7045
+ const format = item["format"] === void 0 ? void 0 : readFormat(r, item["format"]);
7046
+ items.push(format ? { path, format } : { path });
7047
+ } else
7048
+ r.problem("content holds something that is not text, a path or a break");
7049
+ }
7050
+ if (length > 5e3) r.problem("at most 5000 characters of text");
7051
+ return items;
7052
+ }
7053
+ function readFormat(r, raw) {
7054
+ const v = raw ?? {};
7055
+ switch (v["helper"]) {
7056
+ case "upper":
7057
+ case "lower":
7058
+ case "title":
7059
+ case "money":
7060
+ return { helper: v["helper"] };
7061
+ case "number":
7062
+ return {
7063
+ helper: "number",
7064
+ decimals: Math.round(r.number(v["decimals"], "decimals", 0, 10, 2))
7065
+ };
7066
+ case "date":
7067
+ return {
7068
+ helper: "date",
7069
+ pattern: r.string(v["pattern"], "date pattern", 64, /^[^{}\n\r]+$/)
7070
+ };
7071
+ case "truncate":
7072
+ return {
7073
+ helper: "truncate",
7074
+ length: Math.round(r.number(v["length"], "length", 1, 5e3))
7075
+ };
7076
+ case "default":
7077
+ return {
7078
+ helper: "default",
7079
+ value: r.string(v["value"], "default value", 500, /^[^{}\n\r]*$/)
7080
+ };
7081
+ default:
7082
+ r.problem("unknown format");
7083
+ return void 0;
7084
+ }
7085
+ }
7086
+ function readImageSource(r, raw) {
7087
+ const v = raw ?? {};
7088
+ if (v["url"] === "") return { url: "" };
7089
+ if (v["asset"] === "") return { asset: "" };
7090
+ if (v["path"] === "") return { path: "" };
7091
+ if (typeof v["url"] === "string")
7092
+ return {
7093
+ url: r.string(v["url"], "image url", 2e3, /^https?:\/\/[^\s"'<>]+$/)
7094
+ };
7095
+ if (typeof v["asset"] === "string")
7096
+ return {
7097
+ asset: r.string(v["asset"], "file name", 200, /^[^"\\<>\n\r{}]+$/)
7098
+ };
7099
+ if (typeof v["path"] === "string")
7100
+ return { path: r.path(v["path"], "image path") };
7101
+ if (v["brandLogo"] !== void 0)
7102
+ return {
7103
+ brandLogo: r.oneOf(v["brandLogo"], "brand logo", [
7104
+ "primary",
7105
+ "inverse",
7106
+ "mark"
7107
+ ])
7108
+ };
7109
+ r.problem("the image has no source");
7110
+ return { url: "" };
7111
+ }
7112
+ function readBinding(r, raw) {
7113
+ const v = raw ?? {};
7114
+ if (typeof v["path"] === "string")
7115
+ return { path: r.path(v["path"], "value path") };
7116
+ if (typeof v["text"] === "string")
7117
+ return { text: r.string(v["text"], "value", 2e3, /^[^\n\r{}]*$/) };
7118
+ r.problem("the code has no value");
7119
+ return { text: "" };
7120
+ }
7121
+
7122
+ // packages/engine/src/lib/design/fonts.ts
7123
+ var range = (from, to) => Array.from({ length: (to - from) / 100 + 1 }, (_, i) => from + i * 100);
7124
+ var googleFonts = [
7125
+ {
7126
+ family: "Inter",
7127
+ category: "sans-serif",
7128
+ weights: range(100, 900),
7129
+ italic: true
7130
+ },
7131
+ {
7132
+ family: "Roboto",
7133
+ category: "sans-serif",
7134
+ weights: range(100, 900),
7135
+ italic: true
7136
+ },
7137
+ {
7138
+ family: "Open Sans",
7139
+ category: "sans-serif",
7140
+ weights: range(300, 800),
7141
+ italic: true
7142
+ },
7143
+ {
7144
+ family: "Lato",
7145
+ category: "sans-serif",
7146
+ weights: [100, 300, 400, 700, 900],
7147
+ italic: true
7148
+ },
7149
+ {
7150
+ family: "Montserrat",
7151
+ category: "sans-serif",
7152
+ weights: range(100, 900),
7153
+ italic: true
7154
+ },
7155
+ {
7156
+ family: "Poppins",
7157
+ category: "sans-serif",
7158
+ weights: range(100, 900),
7159
+ italic: true
7160
+ },
7161
+ {
7162
+ family: "Source Sans 3",
7163
+ category: "sans-serif",
7164
+ weights: range(200, 900),
7165
+ italic: true
7166
+ },
7167
+ {
7168
+ family: "Nunito",
7169
+ category: "sans-serif",
7170
+ weights: range(200, 900),
7171
+ italic: true
7172
+ },
7173
+ {
7174
+ family: "Raleway",
7175
+ category: "sans-serif",
7176
+ weights: range(100, 900),
7177
+ italic: true
7178
+ },
7179
+ {
7180
+ family: "Work Sans",
7181
+ category: "sans-serif",
7182
+ weights: range(100, 900),
7183
+ italic: true
7184
+ },
7185
+ {
7186
+ family: "DM Sans",
7187
+ category: "sans-serif",
7188
+ weights: range(100, 900),
7189
+ italic: true
7190
+ },
7191
+ {
7192
+ family: "Manrope",
7193
+ category: "sans-serif",
7194
+ weights: range(200, 800),
7195
+ italic: false
7196
+ },
7197
+ {
7198
+ family: "Rubik",
7199
+ category: "sans-serif",
7200
+ weights: range(300, 900),
7201
+ italic: true
7202
+ },
7203
+ {
7204
+ family: "Noto Sans",
7205
+ category: "sans-serif",
7206
+ weights: range(100, 900),
7207
+ italic: true
7208
+ },
7209
+ {
7210
+ family: "IBM Plex Sans",
7211
+ category: "sans-serif",
7212
+ weights: range(100, 700),
7213
+ italic: true
7214
+ },
7215
+ {
7216
+ family: "Space Grotesk",
7217
+ category: "sans-serif",
7218
+ weights: range(300, 700),
7219
+ italic: false
7220
+ },
7221
+ {
7222
+ family: "Outfit",
7223
+ category: "sans-serif",
7224
+ weights: range(100, 900),
7225
+ italic: false
7226
+ },
7227
+ {
7228
+ family: "Plus Jakarta Sans",
7229
+ category: "sans-serif",
7230
+ weights: range(200, 800),
7231
+ italic: true
7232
+ },
7233
+ {
7234
+ family: "Figtree",
7235
+ category: "sans-serif",
7236
+ weights: range(300, 900),
7237
+ italic: true
7238
+ },
7239
+ {
7240
+ family: "Karla",
7241
+ category: "sans-serif",
7242
+ weights: range(200, 800),
7243
+ italic: true
7244
+ },
7245
+ {
7246
+ family: "Barlow",
7247
+ category: "sans-serif",
7248
+ weights: range(100, 900),
7249
+ italic: true
7250
+ },
7251
+ {
7252
+ family: "Archivo",
7253
+ category: "sans-serif",
7254
+ weights: range(100, 900),
7255
+ italic: true
7256
+ },
7257
+ {
7258
+ family: "Oswald",
7259
+ category: "display",
7260
+ weights: range(200, 700),
7261
+ italic: false
7262
+ },
7263
+ { family: "Bebas Neue", category: "display", weights: [400], italic: false },
7264
+ { family: "Anton", category: "display", weights: [400], italic: false },
7265
+ {
7266
+ family: "Playfair Display",
7267
+ category: "serif",
7268
+ weights: range(400, 900),
7269
+ italic: true
7270
+ },
7271
+ {
7272
+ family: "Merriweather",
7273
+ category: "serif",
7274
+ weights: range(300, 900),
7275
+ italic: true
7276
+ },
7277
+ { family: "Lora", category: "serif", weights: range(400, 700), italic: true },
7278
+ {
7279
+ family: "Libre Baskerville",
7280
+ category: "serif",
7281
+ weights: [400, 700],
7282
+ italic: true
7283
+ },
7284
+ {
7285
+ family: "Source Serif 4",
7286
+ category: "serif",
7287
+ weights: range(200, 900),
7288
+ italic: true
7289
+ },
7290
+ {
7291
+ family: "EB Garamond",
7292
+ category: "serif",
7293
+ weights: range(400, 800),
7294
+ italic: true
7295
+ },
7296
+ {
7297
+ family: "Cormorant Garamond",
7298
+ category: "serif",
7299
+ weights: range(300, 700),
7300
+ italic: true
7301
+ },
7302
+ {
7303
+ family: "DM Serif Display",
7304
+ category: "serif",
7305
+ weights: [400],
7306
+ italic: true
7307
+ },
7308
+ {
7309
+ family: "Fraunces",
7310
+ category: "serif",
7311
+ weights: range(100, 900),
7312
+ italic: true
7313
+ },
7314
+ {
7315
+ family: "Roboto Slab",
7316
+ category: "serif",
7317
+ weights: range(100, 900),
7318
+ italic: false
7319
+ },
7320
+ {
7321
+ family: "Zilla Slab",
7322
+ category: "serif",
7323
+ weights: [300, 400, 500, 600, 700],
7324
+ italic: true
7325
+ },
7326
+ {
7327
+ family: "JetBrains Mono",
7328
+ category: "monospace",
7329
+ weights: range(100, 800),
7330
+ italic: true
7331
+ },
7332
+ {
7333
+ family: "IBM Plex Mono",
7334
+ category: "monospace",
7335
+ weights: range(100, 700),
7336
+ italic: true
7337
+ },
7338
+ {
7339
+ family: "Space Mono",
7340
+ category: "monospace",
7341
+ weights: [400, 700],
7342
+ italic: true
7343
+ },
7344
+ {
7345
+ family: "Caveat",
7346
+ category: "handwriting",
7347
+ weights: range(400, 700),
7348
+ italic: false
7349
+ },
7350
+ {
7351
+ family: "Pacifico",
7352
+ category: "handwriting",
7353
+ weights: [400],
7354
+ italic: false
7355
+ },
7356
+ {
7357
+ family: "Dancing Script",
7358
+ category: "handwriting",
7359
+ weights: range(400, 700),
7360
+ italic: false
7361
+ }
7362
+ ];
7363
+ var byFamily = new Map(googleFonts.map((font) => [font.family, font]));
7364
+ function googleFont(family) {
7365
+ return byFamily.get(family);
7366
+ }
7367
+ function genericFamily(font) {
7368
+ if (!font) return "sans-serif";
7369
+ return font.category === "serif" ? "serif" : font.category === "monospace" ? "monospace" : font.category === "handwriting" ? "cursive" : "sans-serif";
7370
+ }
7371
+ function nearestWeight(font, weight) {
7372
+ return font.weights.reduce(
7373
+ (best, w) => Math.abs(w - weight) < Math.abs(best - weight) || Math.abs(w - weight) === Math.abs(best - weight) && w > best ? w : best,
7374
+ font.weights[0] ?? 400
7375
+ );
7376
+ }
7377
+ function googleFontsUrl(uses) {
7378
+ const wanted = /* @__PURE__ */ new Map();
7379
+ for (const use of uses) {
7380
+ const font = byFamily.get(use.family);
7381
+ if (!font) continue;
7382
+ const italic = use.italic && font.italic ? 1 : 0;
7383
+ const set = wanted.get(font.family) ?? /* @__PURE__ */ new Set();
7384
+ set.add(`${italic},${nearestWeight(font, use.weight)}`);
7385
+ wanted.set(font.family, set);
7386
+ }
7387
+ if (!wanted.size) return null;
7388
+ const families = [...wanted.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([family, set]) => {
7389
+ const tuples = [...set].map((t) => t.split(",").map(Number)).sort((a, b) => a[0] - b[0] || a[1] - b[1]);
7390
+ const name = family.replace(/ /g, "+");
7391
+ return tuples.some(([italic]) => italic) ? `family=${name}:ital,wght@${tuples.map(([i, w]) => `${i},${w}`).join(";")}` : `family=${name}:wght@${tuples.map(([, w]) => w).join(";")}`;
7392
+ });
7393
+ return `https://fonts.googleapis.com/css2?${families.join("&")}&display=block`;
7394
+ }
7395
+
7396
+ // packages/engine/src/lib/design/hash.ts
7397
+ function designOutputHash(html, css2) {
7398
+ const text2 = `${html}\0${css2}`;
7399
+ let a = 2166136261;
7400
+ let b = 16777619 ^ 2654435769;
7401
+ for (let i = 0; i < text2.length; i++) {
7402
+ const code = text2.charCodeAt(i);
7403
+ if (code === 13) continue;
7404
+ a = Math.imul(a ^ code, 16777619) >>> 0;
7405
+ b = Math.imul(b ^ code, 16777619) >>> 0;
7406
+ b = b + (a >>> 13) >>> 0;
7407
+ }
7408
+ return a.toString(16).padStart(8, "0") + b.toString(16).padStart(8, "0");
7409
+ }
7410
+
7411
+ // packages/engine/src/lib/design/layers.ts
7412
+ function imageIsEmpty(source) {
7413
+ if ("url" in source) return source.url === "";
7414
+ if ("asset" in source) return source.asset === "";
7415
+ if ("path" in source) return source.path === "";
7416
+ return false;
7417
+ }
7418
+ function emptyDesign() {
7419
+ return {
7420
+ version: DESIGN_VERSION,
7421
+ output: "",
7422
+ background: { color: "#ffffff" },
7423
+ layers: []
7424
+ };
7425
+ }
7426
+ function nextLayerId(design2, type) {
7427
+ const taken = new Set(design2.layers.map((layer) => layer.id));
7428
+ for (let n = 1; ; n++) {
7429
+ const id = `${type}-${n}`;
7430
+ if (!taken.has(id)) return id;
7431
+ }
7432
+ }
7433
+ var baseText = {
7434
+ type: "text",
7435
+ rotation: 0,
7436
+ opacity: 1,
7437
+ visible: true,
7438
+ content: [{ text: "Text" }],
7439
+ style: {
7440
+ font: { family: "Inter", source: "google" },
7441
+ // regular: the editor's Bold is a toggle, and a new text that was already bold confused it
7442
+ weight: 400,
7443
+ size: 48,
7444
+ lineHeight: 1.2,
7445
+ letterSpacing: 0,
7446
+ color: { color: "#111827" },
7447
+ align: "left"
7448
+ },
7449
+ sizing: "fixed",
7450
+ valign: "top"
7451
+ };
7452
+ function defaultLayer(type, id, name, canvas) {
7453
+ const centred = (width, height) => ({
7454
+ x: Math.round((canvas.width - width) / 2),
7455
+ y: Math.round((canvas.height - height) / 2),
7456
+ width,
7457
+ height
7458
+ });
7459
+ const common = { id, name, rotation: 0, opacity: 1, visible: true };
7460
+ switch (type) {
7461
+ case "text":
7462
+ return {
7463
+ ...baseText,
7464
+ ...common,
7465
+ ...centred(Math.min(canvas.width - 80, 600), 80)
7466
+ };
7467
+ case "image":
7468
+ return {
7469
+ ...common,
7470
+ type: "image",
7471
+ ...centred(Math.min(canvas.width, 400), Math.min(canvas.height, 300)),
7472
+ // not chosen yet (`imageIsEmpty`), and the file library first: that is where an upload goes
7473
+ source: { asset: "" },
7474
+ fit: "cover"
7475
+ };
7476
+ case "shape":
7477
+ return {
7478
+ ...common,
7479
+ type: "shape",
7480
+ ...centred(240, 160),
7481
+ shape: "rectangle",
7482
+ fill: { color: "#e5e7eb" }
7483
+ };
7484
+ case "qr":
7485
+ return {
7486
+ ...common,
7487
+ type: "qr",
7488
+ ...centred(240, 240),
7489
+ value: { text: "https://example.test" },
7490
+ ecc: "M",
7491
+ color: { color: "#000000" },
7492
+ background: { color: "#ffffff" }
7493
+ };
7494
+ case "barcode":
7495
+ return {
7496
+ ...common,
7497
+ type: "barcode",
7498
+ ...centred(400, 140),
7499
+ value: { text: "4006381333931" },
7500
+ symbology: "ean13",
7501
+ showText: true
7502
+ };
7503
+ }
7504
+ }
7505
+
7506
+ // packages/engine/src/lib/design/emit.ts
7507
+ var colourPattern2 = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
7508
+ var tokenPattern2 = /^[a-z][a-z0-9-]{0,31}$/;
7509
+ function emitDesign(design2, engine, size) {
7510
+ const lines = ['<div class="ff-design">'];
7511
+ const map = {};
7512
+ const rules = [];
7513
+ for (const layer of design2.layers) {
7514
+ if (!layer.visible) continue;
7515
+ const markup = layerHtml(layer, engine);
7516
+ lines.push(
7517
+ ` ${layer.showWhen?.path ? ifBlock(engine, layer.showWhen.path, markup) : markup}`
7518
+ );
7519
+ map[layer.id] = { start: lines.length, end: lines.length };
7520
+ rules.push(...layerCss(layer));
7521
+ }
7522
+ lines.push("</div>");
7523
+ const html = `${lines.join("\n")}
7524
+ `;
7525
+ const fonts = googleFontsUrl(
7526
+ design2.layers.filter(
7527
+ (l) => l.visible && l.type === "text" && "family" in l.style.font && l.style.font.source === "google"
7528
+ ).map((l) => ({
7529
+ family: l.style.font.family,
7530
+ weight: l.style.weight,
7531
+ italic: l.style.italic === true
7532
+ }))
7533
+ );
7534
+ const head = [
7535
+ ...fonts ? [`@import url("${fonts}");`] : [],
7536
+ `.ff-design { ${decl({
7537
+ position: "relative",
7538
+ width: px(size.width),
7539
+ height: px(size.height),
7540
+ overflow: "hidden",
7541
+ background: design2.background ? paint(design2.background) : void 0
7542
+ })} }`,
7543
+ ".ff-layer { position: absolute; box-sizing: border-box; margin: 0; }"
7544
+ ];
7545
+ const css2 = `${[...head, ...rules].join("\n")}
7546
+ `;
7547
+ return { html, css: css2, map, output: designOutputHash(html, css2) };
7548
+ }
7549
+ function open(layer, tag, extra = "") {
7550
+ return `<${tag} class="ff-layer l-${layer.id}" data-ff-layer="${layer.id}"${extra}`;
7551
+ }
7552
+ function layerHtml(layer, engine) {
7553
+ switch (layer.type) {
7554
+ case "text": {
7555
+ const fit = layer.sizing === "shrink" ? ` data-ff-fit="shrink" data-ff-fit-min="${num(layer.minFontSize ?? Math.max(8, Math.round(layer.style.size / 3)))}"` : "";
7556
+ return `${open(layer, "div", fit)}><p class="ff-text">${textHtml(layer.content, engine)}</p></div>`;
7557
+ }
7558
+ case "image":
7559
+ if (imageIsEmpty(layer.source)) return `${open(layer, "div")}></div>`;
7560
+ return `${open(layer, "img", ` src="${imageSource(layer.source, engine)}" alt=""`)}>`;
7561
+ case "shape":
7562
+ return `${open(layer, "div")}></div>`;
7563
+ case "qr":
7564
+ return `${open(
7565
+ layer,
7566
+ "img",
7567
+ ` src="${helperCall(engine, "qrcode", binding(layer.value, engine), {
7568
+ ecc: stringLiteral(engine, layer.ecc),
7569
+ margin: "0",
7570
+ color: stringLiteral(engine, literalColour(layer.color)),
7571
+ background: stringLiteral(
7572
+ engine,
7573
+ layer.background ? literalColour(layer.background) : "#ffffff00"
7574
+ )
7575
+ })}" alt=""`
7576
+ )}>`;
7577
+ case "barcode":
7578
+ return `${open(
7579
+ layer,
7580
+ "img",
7581
+ ` src="${helperCall(engine, "barcode", binding(layer.value, engine), {
7582
+ type: stringLiteral(engine, layer.symbology),
7583
+ text: layer.showText ? "true" : "false"
7584
+ })}" alt=""`
7585
+ )}>`;
7586
+ }
7587
+ }
7588
+ function textHtml(items, engine) {
7589
+ return items.map((item) => {
7590
+ if ("break" in item) return "<br>";
7591
+ if ("text" in item)
7592
+ return encodeTemplateText(item.text.replace(/[\r\n]+/g, " "));
7593
+ return valueHtml(item.path, item.format, engine);
7594
+ }).join("");
7595
+ }
7596
+ function valueHtml(path, format, engine) {
7597
+ if (!format) return `{{ ${path} }}`;
7598
+ switch (format.helper) {
7599
+ case "upper":
7600
+ case "lower":
7601
+ case "title":
7602
+ case "money":
7603
+ return filterCall(engine, path, format.helper);
7604
+ case "number":
7605
+ return filterCall(engine, path, "number", [String(format.decimals)]);
7606
+ case "date":
7607
+ return filterCall(engine, path, "date", [
7608
+ stringLiteral(engine, format.pattern)
7609
+ ]);
7610
+ case "truncate":
7611
+ return filterCall(engine, path, "truncate", [String(format.length)]);
7612
+ case "default":
7613
+ return filterCall(engine, path, "default", [
7614
+ stringLiteral(engine, format.value)
7615
+ ]);
7616
+ }
7617
+ }
7618
+ function binding(value, engine) {
7619
+ return "path" in value ? value.path : stringLiteral(engine, value.text);
7620
+ }
7621
+ function imageSource(source, engine) {
7622
+ if ("url" in source) return escapeAttribute(source.url);
7623
+ if ("asset" in source)
7624
+ return helperCall(engine, "asset", stringLiteral(engine, source.asset));
7625
+ if ("path" in source) return `{{ ${source.path} }}`;
7626
+ return `{{ brand.logo.${source.brandLogo} }}`;
7627
+ }
7628
+ function layerCss(layer) {
7629
+ const text2 = layer.type === "text" ? layer : null;
7630
+ const box = {
7631
+ left: px(layer.x),
7632
+ top: px(layer.y),
7633
+ width: px(layer.width),
7634
+ height: text2?.sizing === "grow" ? void 0 : px(layer.height),
7635
+ opacity: layer.opacity < 1 ? num(layer.opacity) : void 0,
7636
+ transform: layer.rotation ? `rotate(${num(layer.rotation)}deg)` : void 0,
7637
+ "border-radius": layer.type === "shape" && layer.shape === "ellipse" ? "50%" : layer.radius ? px(layer.radius) : void 0,
7638
+ border: layer.stroke && layer.stroke.width > 0 ? `${px(layer.stroke.width)} solid ${paint(layer.stroke.paint)}` : void 0,
7639
+ "box-shadow": layer.shadow && !text2 ? shadow(layer.shadow) : void 0
7640
+ };
7641
+ if (text2) {
7642
+ Object.assign(box, {
7643
+ display: "flex",
7644
+ "flex-direction": "column",
7645
+ "justify-content": text2.valign === "middle" ? "center" : text2.valign === "bottom" ? "flex-end" : "flex-start",
7646
+ overflow: text2.sizing === "grow" ? void 0 : "hidden",
7647
+ padding: text2.padding ? px(text2.padding) : void 0,
7648
+ background: text2.fill ? paint(text2.fill) : void 0
7649
+ });
7650
+ const s = text2.style;
7651
+ const clamp = text2.maxLines ? {
7652
+ display: "-webkit-box",
7653
+ "-webkit-box-orient": "vertical",
7654
+ "-webkit-line-clamp": String(text2.maxLines),
7655
+ overflow: "hidden"
7656
+ } : {};
7657
+ const inner = decl({
7658
+ margin: "0",
7659
+ width: "100%",
7660
+ "font-family": fontFamily(s.font),
7661
+ "font-weight": String(servedWeight(s.font, s.weight)),
7662
+ "font-size": px(s.size),
7663
+ "line-height": num(s.lineHeight),
7664
+ "letter-spacing": s.letterSpacing ? px(s.letterSpacing) : void 0,
7665
+ color: paint(s.color),
7666
+ "text-align": s.align,
7667
+ "font-style": s.italic ? "italic" : void 0,
7668
+ "text-transform": s.uppercase ? "uppercase" : void 0,
7669
+ "text-decoration": [s.underline && "underline", s.strike && "line-through"].filter(Boolean).join(" ") || void 0,
7670
+ "text-shadow": text2.shadow ? shadow(text2.shadow) : void 0,
7671
+ "overflow-wrap": "break-word",
7672
+ ...clamp
7673
+ });
7674
+ return [
7675
+ `.l-${layer.id} { ${decl(box)} }`,
7676
+ `.l-${layer.id} .ff-text { ${inner} }`
7677
+ ];
7678
+ }
7679
+ if (layer.type === "image")
7680
+ Object.assign(box, {
7681
+ display: "block",
7682
+ "object-fit": layer.fit,
7683
+ "object-position": layer.focus ? `${num(layer.focus.x)}% ${num(layer.focus.y)}%` : void 0
7684
+ });
7685
+ if (layer.type === "qr" || layer.type === "barcode")
7686
+ Object.assign(box, { display: "block", "object-fit": "contain" });
7687
+ if (layer.type === "shape")
7688
+ box["background"] = layer.fill ? paint(layer.fill) : void 0;
7689
+ return [`.l-${layer.id} { ${decl(box)} }`];
7690
+ }
7691
+ function decl(properties) {
7692
+ return Object.entries(properties).filter(([, value]) => value !== void 0).map(([name, value]) => `${name}: ${value};`).join(" ");
7693
+ }
7694
+ function num(n) {
7695
+ return String(Math.round(n * 100) / 100);
7696
+ }
7697
+ function px(n) {
7698
+ const value = num(n);
7699
+ return value === "0" ? "0" : `${value}px`;
7700
+ }
7701
+ function safeColour(value) {
7702
+ return colourPattern2.test(value) ? value : "#000000";
7703
+ }
7704
+ function paint(p) {
7705
+ if ("brand" in p)
7706
+ return tokenPattern2.test(p.brand) ? `var(--brand-color-${p.brand}, ${safeColour(p.fallback)})` : safeColour(p.fallback);
7707
+ return safeColour(p.color);
7708
+ }
7709
+ function literalColour(p) {
7710
+ return safeColour("brand" in p ? p.fallback : p.color);
7711
+ }
7712
+ function shadow(s) {
7713
+ return `${px(s.x)} ${px(s.y)} ${px(s.blur)} ${safeColour(s.color)}`;
7714
+ }
7715
+ function servedWeight(font, weight) {
7716
+ const google = "family" in font && font.source === "google" ? googleFont(font.family) : void 0;
7717
+ return google ? nearestWeight(google, weight) : weight;
7718
+ }
7719
+ function fontFamily(font) {
7720
+ if ("brand" in font) return `var(--brand-font-${font.brand}, sans-serif)`;
7721
+ const family = font.family.replace(/["'\\<>\n\r;{}]/g, "");
7722
+ const generic = font.source === "google" ? genericFamily(googleFont(family)) : "sans-serif";
7723
+ return `"${family}", ${generic}`;
7724
+ }
7725
+ function escapeAttribute(value) {
7726
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\{/g, "%7B").replace(/\}/g, "%7D");
7727
+ }
7728
+
7729
+ // packages/engine/src/lib/design/starters.ts
7730
+ var style = (overrides) => ({
7731
+ font: { family: "Inter", source: "google" },
7732
+ weight: 600,
7733
+ size: 40,
7734
+ lineHeight: 1.2,
7735
+ letterSpacing: 0,
7736
+ color: { color: "#111827" },
7737
+ align: "left",
7738
+ ...overrides
7739
+ });
7740
+ var text = (layer) => ({
7741
+ type: "text",
7742
+ rotation: 0,
7743
+ opacity: 1,
7744
+ visible: true,
7745
+ sizing: "fixed",
7746
+ valign: "top",
7747
+ ...layer
7748
+ });
7749
+ var design = (background, layers) => ({
7750
+ version: DESIGN_VERSION,
7751
+ output: "",
7752
+ background,
7753
+ layers
7754
+ });
7755
+ var designStarters = [
7756
+ {
7757
+ id: "blank",
7758
+ title: "Blank",
7759
+ size: { width: 1200, height: 630 },
7760
+ design: design({ color: "#ffffff" }, []),
7761
+ sampleData: {}
7762
+ },
7763
+ {
7764
+ id: "article",
7765
+ title: "Article card",
7766
+ size: { width: 1200, height: 630 },
7767
+ design: design({ brand: "primary", fallback: "#0f172a" }, [
7768
+ text({
7769
+ id: "site",
7770
+ name: "Site",
7771
+ x: 80,
7772
+ y: 64,
7773
+ width: 1040,
7774
+ height: 40,
7775
+ content: [{ path: "site.name" }],
7776
+ style: style({
7777
+ size: 26,
7778
+ weight: 600,
7779
+ letterSpacing: 2,
7780
+ uppercase: true,
7781
+ color: { color: "#cbd5e1" }
7782
+ })
7783
+ }),
7784
+ text({
7785
+ id: "title",
7786
+ name: "Title",
7787
+ x: 80,
7788
+ y: 130,
7789
+ width: 1040,
7790
+ height: 320,
7791
+ content: [{ path: "post.title" }],
7792
+ sizing: "shrink",
7793
+ minFontSize: 40,
7794
+ valign: "middle",
7795
+ style: style({
7796
+ size: 76,
7797
+ weight: 800,
7798
+ lineHeight: 1.1,
7799
+ color: { color: "#ffffff" }
7800
+ })
7801
+ }),
7802
+ {
7803
+ id: "rule",
7804
+ name: "Rule",
7805
+ type: "shape",
7806
+ shape: "rectangle",
7807
+ x: 80,
7808
+ y: 478,
7809
+ width: 120,
7810
+ height: 6,
7811
+ rotation: 0,
7812
+ opacity: 1,
7813
+ visible: true,
7814
+ fill: { brand: "accent", fallback: "#38bdf8" }
7815
+ },
7816
+ text({
7817
+ id: "byline",
7818
+ name: "Byline",
7819
+ x: 80,
7820
+ y: 508,
7821
+ width: 1040,
7822
+ height: 50,
7823
+ content: [{ text: "By " }, { path: "post.author" }],
7824
+ showWhen: { path: "post.author" },
7825
+ style: style({ size: 30, weight: 500, color: { color: "#cbd5e1" } })
7826
+ })
7827
+ ]),
7828
+ sampleData: {
7829
+ site: { name: "Fennlor Studio" },
7830
+ post: {
7831
+ title: "How we cut our invoice run from an hour to four minutes",
7832
+ author: "Max Mustermann"
7833
+ }
7834
+ }
7835
+ },
7836
+ {
7837
+ id: "quote",
7838
+ title: "Quote card",
7839
+ size: { width: 1080, height: 1080 },
7840
+ design: design({ color: "#f5efe6" }, [
7841
+ text({
7842
+ id: "mark",
7843
+ name: "Quotation mark",
7844
+ x: 96,
7845
+ y: 60,
7846
+ width: 240,
7847
+ height: 220,
7848
+ content: [{ text: "\u201C" }],
7849
+ style: style({
7850
+ font: { family: "Playfair Display", source: "google" },
7851
+ size: 220,
7852
+ weight: 700,
7853
+ lineHeight: 1,
7854
+ color: { color: "#d97706" }
7855
+ })
7856
+ }),
7857
+ text({
7858
+ id: "quote",
7859
+ name: "Quote",
7860
+ x: 96,
7861
+ y: 260,
7862
+ width: 888,
7863
+ height: 520,
7864
+ content: [{ path: "review.quote" }],
7865
+ sizing: "shrink",
7866
+ minFontSize: 32,
7867
+ valign: "middle",
7868
+ style: style({
7869
+ font: { family: "Playfair Display", source: "google" },
7870
+ size: 60,
7871
+ weight: 600,
7872
+ lineHeight: 1.22,
7873
+ color: { color: "#1c1917" }
7874
+ })
7875
+ }),
7876
+ text({
7877
+ id: "author",
7878
+ name: "Author",
7879
+ x: 96,
7880
+ y: 836,
7881
+ width: 888,
7882
+ height: 50,
7883
+ content: [{ path: "review.author" }],
7884
+ style: style({ size: 36, weight: 600, color: { color: "#1c1917" } })
7885
+ }),
7886
+ text({
7887
+ id: "role",
7888
+ name: "Role",
7889
+ x: 96,
7890
+ y: 896,
7891
+ width: 888,
7892
+ height: 44,
7893
+ content: [{ path: "review.role" }],
7894
+ showWhen: { path: "review.role" },
7895
+ style: style({ size: 28, weight: 400, color: { color: "#78716c" } })
7896
+ })
7897
+ ]),
7898
+ sampleData: {
7899
+ review: {
7900
+ quote: "Delivery took two days, and the one shade that arrived dented was replaced before we had finished the call.",
7901
+ author: "Erika Mustermann",
7902
+ role: "Office manager, Olvarest GmbH"
7903
+ }
7904
+ }
7905
+ },
7906
+ {
7907
+ id: "event",
7908
+ title: "Event story",
7909
+ size: { width: 1080, height: 1920 },
7910
+ design: design({ color: "#111827" }, [
7911
+ text({
7912
+ id: "label",
7913
+ name: "Label",
7914
+ x: 96,
7915
+ y: 200,
7916
+ width: 888,
7917
+ height: 50,
7918
+ content: [{ text: "Event" }],
7919
+ style: style({
7920
+ size: 36,
7921
+ weight: 700,
7922
+ letterSpacing: 6,
7923
+ uppercase: true,
7924
+ color: { color: "#fbbf24" }
7925
+ })
7926
+ }),
7927
+ text({
7928
+ id: "title",
7929
+ name: "Title",
7930
+ x: 96,
7931
+ y: 290,
7932
+ width: 888,
7933
+ height: 620,
7934
+ content: [{ path: "event.title" }],
7935
+ sizing: "shrink",
7936
+ minFontSize: 56,
7937
+ valign: "bottom",
7938
+ style: style({
7939
+ size: 112,
7940
+ weight: 800,
7941
+ lineHeight: 1.05,
7942
+ color: { color: "#ffffff" }
7943
+ })
7944
+ }),
7945
+ text({
7946
+ id: "date",
7947
+ name: "Date",
7948
+ x: 96,
7949
+ y: 980,
7950
+ width: 888,
7951
+ height: 70,
7952
+ content: [
7953
+ {
7954
+ path: "event.date",
7955
+ format: { helper: "date", pattern: "d MMMM yyyy" }
7956
+ }
7957
+ ],
7958
+ style: style({ size: 52, weight: 600, color: { color: "#e5e7eb" } })
7959
+ }),
7960
+ text({
7961
+ id: "place",
7962
+ name: "Place",
7963
+ x: 96,
7964
+ y: 1060,
7965
+ width: 888,
7966
+ height: 60,
7967
+ content: [{ path: "event.place" }],
7968
+ style: style({ size: 38, weight: 400, color: { color: "#9ca3af" } })
7969
+ }),
7970
+ {
7971
+ id: "qr",
7972
+ name: "QR code",
7973
+ type: "qr",
7974
+ x: 96,
7975
+ y: 1440,
7976
+ width: 300,
7977
+ height: 300,
7978
+ rotation: 0,
7979
+ opacity: 1,
7980
+ visible: true,
7981
+ radius: 16,
7982
+ value: { path: "event.url" },
7983
+ ecc: "M",
7984
+ color: { color: "#111827" },
7985
+ background: { color: "#ffffff" }
7986
+ },
7987
+ text({
7988
+ id: "scan",
7989
+ name: "Scan note",
7990
+ x: 440,
7991
+ y: 1560,
7992
+ width: 544,
7993
+ height: 60,
7994
+ content: [{ text: "Scan for your ticket" }],
7995
+ style: style({ size: 40, weight: 600, color: { color: "#ffffff" } })
7996
+ })
7997
+ ]),
7998
+ sampleData: {
7999
+ event: {
8000
+ title: "Formfeed meetup: documents as code",
8001
+ date: "2026-11-12",
8002
+ place: "Musterstra\xDFe 1 \xB7 12345 Musterstadt",
8003
+ url: "https://fennlor.test/events/meetup"
8004
+ }
8005
+ }
8006
+ }
8007
+ ];
8008
+
8009
+ // packages/engine/src/lib/office/errors.ts
8010
+ var OfficeError = class extends Error {
8011
+ constructor(code, message, details = {}) {
8012
+ super(message);
8013
+ this.code = code;
8014
+ this.details = details;
8015
+ this.name = "OfficeError";
8016
+ }
8017
+ };
8018
+ var officeLimits = {
8019
+ /** Entries in one container. */
8020
+ maxEntries: 2e3,
8021
+ /** Sum of the uncompressed sizes the central directory declares. */
8022
+ maxDeclaredBytes: 200 * 1024 * 1024,
8023
+ /** One part we inflate; enforced while inflating, because the declared size can lie. */
8024
+ maxPartBytes: 20 * 1024 * 1024,
8025
+ /** A conversion upload or a template file. */
8026
+ maxFileBytes: 20 * 1024 * 1024
8027
+ };
8028
+
8029
+ // node_modules/.pnpm/fflate@0.8.3/node_modules/fflate/esm/browser.js
8030
+ var u8 = Uint8Array;
8031
+ var u16 = Uint16Array;
8032
+ var i32 = Int32Array;
8033
+ var fleb = new u8([
8034
+ 0,
8035
+ 0,
8036
+ 0,
8037
+ 0,
8038
+ 0,
8039
+ 0,
8040
+ 0,
8041
+ 0,
8042
+ 1,
8043
+ 1,
8044
+ 1,
8045
+ 1,
8046
+ 2,
8047
+ 2,
8048
+ 2,
8049
+ 2,
8050
+ 3,
8051
+ 3,
8052
+ 3,
8053
+ 3,
8054
+ 4,
8055
+ 4,
8056
+ 4,
8057
+ 4,
8058
+ 5,
8059
+ 5,
8060
+ 5,
8061
+ 5,
8062
+ 0,
8063
+ /* unused */
8064
+ 0,
8065
+ 0,
8066
+ /* impossible */
8067
+ 0
8068
+ ]);
8069
+ var fdeb = new u8([
8070
+ 0,
8071
+ 0,
8072
+ 0,
8073
+ 0,
8074
+ 1,
8075
+ 1,
8076
+ 2,
8077
+ 2,
8078
+ 3,
8079
+ 3,
8080
+ 4,
8081
+ 4,
8082
+ 5,
8083
+ 5,
8084
+ 6,
8085
+ 6,
8086
+ 7,
8087
+ 7,
8088
+ 8,
8089
+ 8,
8090
+ 9,
8091
+ 9,
8092
+ 10,
8093
+ 10,
8094
+ 11,
8095
+ 11,
8096
+ 12,
8097
+ 12,
8098
+ 13,
8099
+ 13,
8100
+ /* unused */
8101
+ 0,
8102
+ 0
8103
+ ]);
8104
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
8105
+ var freb = function(eb, start) {
8106
+ var b = new u16(31);
8107
+ for (var i = 0; i < 31; ++i) {
8108
+ b[i] = start += 1 << eb[i - 1];
8109
+ }
8110
+ var r = new i32(b[30]);
8111
+ for (var i = 1; i < 30; ++i) {
8112
+ for (var j = b[i]; j < b[i + 1]; ++j) {
8113
+ r[j] = j - b[i] << 5 | i;
8114
+ }
8115
+ }
8116
+ return { b, r };
8117
+ };
8118
+ var _a = freb(fleb, 2);
8119
+ var fl = _a.b;
8120
+ var revfl = _a.r;
8121
+ fl[28] = 258, revfl[258] = 28;
8122
+ var _b = freb(fdeb, 0);
8123
+ var fd = _b.b;
8124
+ var revfd = _b.r;
8125
+ var rev = new u16(32768);
8126
+ for (i = 0; i < 32768; ++i) {
6042
8127
  x = (i & 43690) >> 1 | (i & 21845) << 1;
6043
8128
  x = (x & 52428) >> 2 | (x & 13107) << 2;
6044
8129
  x = (x & 61680) >> 4 | (x & 3855) << 4;
@@ -7223,8 +9308,8 @@ function assertWellFormed(tokens, part) {
7223
9308
  break;
7224
9309
  }
7225
9310
  case "close": {
7226
- const open = stack.pop();
7227
- if (open !== token2.name) throw invalid2(`</${token2.name}> closes <${open ?? "nothing"}>`, part);
9311
+ const open2 = stack.pop();
9312
+ if (open2 !== token2.name) throw invalid2(`</${token2.name}> closes <${open2 ?? "nothing"}>`, part);
7228
9313
  break;
7229
9314
  }
7230
9315
  case "text":
@@ -7240,12 +9325,12 @@ function assertWellFormed(tokens, part) {
7240
9325
  }
7241
9326
  var FORBIDDEN = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
7242
9327
  var ENTITY = /&(?:#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/y;
7243
- function checkText(text, part) {
7244
- if (FORBIDDEN.test(text)) throw invalid2("a character XML does not allow", part);
7245
- if (text.includes("<")) throw invalid2("unescaped < in text", part);
7246
- for (let i = text.indexOf("&"); i !== -1; i = text.indexOf("&", i + 1)) {
9328
+ function checkText(text2, part) {
9329
+ if (FORBIDDEN.test(text2)) throw invalid2("a character XML does not allow", part);
9330
+ if (text2.includes("<")) throw invalid2("unescaped < in text", part);
9331
+ for (let i = text2.indexOf("&"); i !== -1; i = text2.indexOf("&", i + 1)) {
7247
9332
  ENTITY.lastIndex = i;
7248
- if (!ENTITY.test(text)) throw invalid2(`invalid entity reference near "${text.slice(i, i + 12)}"`, part);
9333
+ if (!ENTITY.test(text2)) throw invalid2(`invalid entity reference near "${text2.slice(i, i + 12)}"`, part);
7249
9334
  }
7250
9335
  }
7251
9336
  function checkAttributes(raw, part) {
@@ -7260,7 +9345,7 @@ function checkAttributes(raw, part) {
7260
9345
  });
7261
9346
  if (rest.trim() !== "") throw invalid2(`malformed attributes in ${raw.slice(0, 40)}`, part);
7262
9347
  }
7263
- function decodeText2(raw) {
9348
+ function decodeText3(raw) {
7264
9349
  return raw.replace(/&(#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/g, (whole, name) => {
7265
9350
  switch (name) {
7266
9351
  case "amp":
@@ -7280,13 +9365,13 @@ function decodeText2(raw) {
7280
9365
  }
7281
9366
  });
7282
9367
  }
7283
- function escapeText2(text) {
7284
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9368
+ function escapeText2(text2) {
9369
+ return text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7285
9370
  }
7286
9371
  var FORBIDDEN_GLOBAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
7287
- function stripForbidden(text) {
9372
+ function stripForbidden(text2) {
7288
9373
  let removed = 0;
7289
- const out = text.replace(FORBIDDEN_GLOBAL, () => {
9374
+ const out = text2.replace(FORBIDDEN_GLOBAL, () => {
7290
9375
  removed++;
7291
9376
  return "";
7292
9377
  });
@@ -7537,13 +9622,13 @@ function placeDrawings(xml2, pattern, markupFor) {
7537
9622
  pattern.lastIndex = 0;
7538
9623
  if (pattern.test(t.raw)) {
7539
9624
  const segments = t.raw.split(pattern);
7540
- let text = segments[0];
9625
+ let text2 = segments[0];
7541
9626
  for (let s = 1; s < segments.length; s += 2) {
7542
9627
  const index = Number(segments[s]);
7543
- text += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
9628
+ text2 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
7544
9629
  }
7545
9630
  out[textOpen] = withPreserve(out[textOpen]);
7546
- out.push(text);
9631
+ out.push(text2);
7547
9632
  continue;
7548
9633
  }
7549
9634
  }
@@ -7566,13 +9651,13 @@ function withRelationships(rels2, relationships) {
7566
9651
  return rels2.slice(0, end) + added + rels2.slice(end);
7567
9652
  }
7568
9653
  function relationshipIds(rels2) {
7569
- return new Set([...(rels2 ?? "").matchAll(/\sId="([^"]+)"/g)].map((m) => decodeText2(m[1])));
9654
+ return new Set([...(rels2 ?? "").matchAll(/\sId="([^"]+)"/g)].map((m) => decodeText3(m[1])));
7570
9655
  }
7571
9656
  function withImageTypes(contentTypes, extensions) {
7572
9657
  let out = contentTypes;
7573
9658
  for (const ext of extensions) {
7574
9659
  if (new RegExp(`<Default\\b[^>]*Extension="${ext}"`, "i").test(out)) continue;
7575
- out = out.replace(/<Types\b[^>]*>/, (open) => `${open}<Default Extension="${ext}" ContentType="image/${ext}"/>`);
9660
+ out = out.replace(/<Types\b[^>]*>/, (open2) => `${open2}<Default Extension="${ext}" ContentType="image/${ext}"/>`);
7576
9661
  }
7577
9662
  return out;
7578
9663
  }
@@ -7594,7 +9679,7 @@ var THEME_REFERENCES = /^\+(mj|mn)-/;
7594
9679
  function documentFonts(archive) {
7595
9680
  const fonts = /* @__PURE__ */ new Map();
7596
9681
  const add2 = (name, embedded = false) => {
7597
- const clean = decodeText2(name).trim();
9682
+ const clean = decodeText3(name).trim();
7598
9683
  if (!clean || THEME_REFERENCES.test(clean)) return;
7599
9684
  const key = clean.toLowerCase();
7600
9685
  const known = fonts.get(key);
@@ -7775,7 +9860,7 @@ function toTemplateSource(xml2, flavour, nonce, part) {
7775
9860
  for (const token2 of tokens) {
7776
9861
  if (token2.kind === "text" && inText) {
7777
9862
  flush();
7778
- source += decodeText2(token2.raw);
9863
+ source += decodeText3(token2.raw);
7779
9864
  continue;
7780
9865
  }
7781
9866
  pending.raw += token2.raw;
@@ -7798,11 +9883,11 @@ function fromTemplateOutput(output, template, part) {
7798
9883
  let last = 0;
7799
9884
  let inText = false;
7800
9885
  const emit = (segment) => {
7801
- const { text, removed } = stripForbidden(segment);
9886
+ const { text: text2, removed } = stripForbidden(segment);
7802
9887
  removedCharacters += removed;
7803
- if (!inText && text.trim() !== "")
7804
- throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text.trim().slice(0, 40)}"`);
7805
- xml2 += escapeText2(text);
9888
+ if (!inText && text2.trim() !== "")
9889
+ throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text2.trim().slice(0, 40)}"`);
9890
+ xml2 += escapeText2(text2);
7806
9891
  };
7807
9892
  for (const m of output.matchAll(placeholder)) {
7808
9893
  emit(output.slice(last, m.index));
@@ -7844,15 +9929,15 @@ function layoutText(xml2, flavour, part) {
7844
9929
  out.push(token2.raw);
7845
9930
  continue;
7846
9931
  }
7847
- const text = token2.raw.replace(/\r\n?/g, "\n");
9932
+ const text2 = token2.raw.replace(/\r\n?/g, "\n");
7848
9933
  if (flavour === "wordprocessing") {
7849
- const pieces = text.split(/(\n|\t)/);
7850
- if (pieces.length > 1 || /^\s|\s$/.test(text)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
9934
+ const pieces = text2.split(/(\n|\t)/);
9935
+ if (pieces.length > 1 || /^\s|\s$/.test(text2)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
7851
9936
  out.push(
7852
9937
  pieces.map((p) => p === "\n" ? '</w:t><w:br/><w:t xml:space="preserve">' : p === " " ? '</w:t><w:tab/><w:t xml:space="preserve">' : p).join("")
7853
9938
  );
7854
9939
  } else {
7855
- out.push(text.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
9940
+ out.push(text2.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
7856
9941
  }
7857
9942
  }
7858
9943
  return out.join("");
@@ -7885,17 +9970,17 @@ var AUTOCORRECTED = [
7885
9970
  [/\u200B|\u200C|\u200D|\uFEFF/g, ""]
7886
9971
  ];
7887
9972
  function normaliseTags(template, part) {
7888
- const { text, origin, placeholders } = stripPlaceholders(template);
9973
+ const { text: text2, origin, placeholders } = stripPlaceholders(template);
7889
9974
  const sourceRange = (from, to) => [
7890
9975
  origin[from] ?? template.source.length,
7891
9976
  (origin[to - 1] ?? template.source.length - 1) + 1
7892
9977
  ];
7893
9978
  const diagnostics = [];
7894
9979
  const edits = [];
7895
- for (const tag of findTags(text)) {
9980
+ for (const tag of findTags(text2)) {
7896
9981
  const inside = placeholders.filter((p) => p.at > tag.start && p.at < tag.end);
7897
9982
  const paragraph2 = paragraphOf(template, placeholders, tag.start);
7898
- const raw = text.slice(tag.start, tag.end);
9983
+ const raw = text2.slice(tag.start, tag.end);
7899
9984
  const unsafe = inside.filter((p) => {
7900
9985
  const piece = template.markup[p.index];
7901
9986
  return !piece || piece.paragraphBoundary || piece.containerBoundary;
@@ -7949,8 +10034,8 @@ function normaliseTags(template, part) {
7949
10034
  return { template: { ...template, source: applyEdits(template.source, edits) }, diagnostics };
7950
10035
  }
7951
10036
  var ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
7952
- function decodeEntities(text) {
7953
- return text.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
10037
+ function decodeEntities(text2) {
10038
+ return text2.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
7954
10039
  if (name[0] !== "#") return ENTITIES[name.toLowerCase()] ?? whole;
7955
10040
  const code = name[1] === "x" || name[1] === "X" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);
7956
10041
  return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
@@ -7958,28 +10043,28 @@ function decodeEntities(text) {
7958
10043
  }
7959
10044
  function stripPlaceholders(template) {
7960
10045
  const pattern = placeholderPattern(template.nonce);
7961
- let text = "";
10046
+ let text2 = "";
7962
10047
  const origin = [];
7963
10048
  const placeholders = [];
7964
10049
  let last = 0;
7965
10050
  for (const m of template.source.matchAll(pattern)) {
7966
10051
  for (let i = last; i < (m.index ?? 0); i++) {
7967
- text += template.source[i];
10052
+ text2 += template.source[i];
7968
10053
  origin.push(i);
7969
10054
  }
7970
- placeholders.push({ index: Number(m[1]), at: text.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
10055
+ placeholders.push({ index: Number(m[1]), at: text2.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
7971
10056
  last = (m.index ?? 0) + m[0].length;
7972
10057
  }
7973
10058
  for (let i = last; i < template.source.length; i++) {
7974
- text += template.source[i];
10059
+ text2 += template.source[i];
7975
10060
  origin.push(i);
7976
10061
  }
7977
- return { text, origin, placeholders };
10062
+ return { text: text2, origin, placeholders };
7978
10063
  }
7979
10064
  function listTags(template, part) {
7980
- const { text, placeholders } = stripPlaceholders(template);
7981
- return findTags(text).map((tag) => {
7982
- const raw = text.slice(tag.start, tag.end);
10065
+ const { text: text2, placeholders } = stripPlaceholders(template);
10066
+ return findTags(text2).map((tag) => {
10067
+ const raw = text2.slice(tag.start, tag.end);
7983
10068
  const kind = raw.startsWith("{#") ? "comment" : raw.startsWith("{%") || /^\{\{~?\s*[#/^]/.test(raw) || /^\{\{~?\s*else\b/.test(raw) ? "block" : "output";
7984
10069
  return { part, paragraph: paragraphOf(template, placeholders, tag.start), text: raw.replace(/\s+/g, " ").trim(), kind };
7985
10070
  });
@@ -8000,15 +10085,15 @@ function undoAutocorrect(tag) {
8000
10085
  for (const [pattern, replacement] of AUTOCORRECTED) out = out.replace(pattern, replacement);
8001
10086
  return out;
8002
10087
  }
8003
- function findTags(text) {
10088
+ function findTags(text2) {
8004
10089
  const tags = [];
8005
- for (let i = 0; i < text.length - 1; i++) {
8006
- const opener = OPENERS.find((o) => text.startsWith(o, i));
10090
+ for (let i = 0; i < text2.length - 1; i++) {
10091
+ const opener = OPENERS.find((o) => text2.startsWith(o, i));
8007
10092
  if (!opener) continue;
8008
10093
  const closer = CLOSERS[opener];
8009
- const end = text.indexOf(closer, i + opener.length);
10094
+ const end = text2.indexOf(closer, i + opener.length);
8010
10095
  if (end === -1) continue;
8011
- const stop = end + closer.length + (opener === "{{" && text[end + closer.length] === "}" ? 1 : 0);
10096
+ const stop = end + closer.length + (opener === "{{" && text2[end + closer.length] === "}" ? 1 : 0);
8012
10097
  tags.push({ start: i, end: stop });
8013
10098
  i = stop - 1;
8014
10099
  }
@@ -8038,8 +10123,8 @@ var CONTENT = /* @__PURE__ */ new Set(["w:drawing", "w:pict", "w:object", "w:fld
8038
10123
  var JINJA_OPEN = /^(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
8039
10124
  var JINJA_CLOSE = /^end(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
8040
10125
  var JINJA_MIDDLE = /^(else|elif|elsif|when)\b/;
8041
- function blockRole(text) {
8042
- const t = text.trim();
10126
+ function blockRole(text2) {
10127
+ const t = text2.trim();
8043
10128
  let m = /^\{%-?\s*([\s\S]*?)\s*-?%\}$/.exec(t);
8044
10129
  if (m && !t.slice(2, -2).includes("%}")) {
8045
10130
  const body = m[1] ?? "";
@@ -8071,14 +10156,14 @@ function dropTaggedFallbacks(xml2, part) {
8071
10156
  if (t.kind === "open" && t.name === "mc:Fallback" && !t.selfClosing) {
8072
10157
  let depth = 1;
8073
10158
  let j = i + 1;
8074
- let text = "";
10159
+ let text2 = "";
8075
10160
  for (; j < tokens.length && depth > 0; j++) {
8076
10161
  const u = tokens[j];
8077
10162
  if (u.kind === "open" && u.name === "mc:Fallback" && !u.selfClosing) depth++;
8078
10163
  else if (u.kind === "close" && u.name === "mc:Fallback") depth--;
8079
- else if (u.kind === "text") text += decodeText2(u.raw);
10164
+ else if (u.kind === "text") text2 += decodeText3(u.raw);
8080
10165
  }
8081
- if (/\{\{|\{%|\{#/.test(text)) {
10166
+ if (/\{\{|\{%|\{#/.test(text2)) {
8082
10167
  i = j - 1;
8083
10168
  continue;
8084
10169
  }
@@ -8119,25 +10204,25 @@ function applyStructure(xml2, part) {
8119
10204
  if (s.role === "open")
8120
10205
  stack.push({ keyword: s.keyword, parent, paragraph: s.info.number, tag: s.tag, after: unitOf(span).end });
8121
10206
  else if (s.role === "close" || s.role === "middle") {
8122
- const open = s.role === "close" ? stack.pop() : stack.at(-1);
8123
- if (open && s.role === "close" && LOOPS.has(open.keyword)) {
8124
- const body = tokens.slice(open.after, unitOf(span).start);
10207
+ const open2 = s.role === "close" ? stack.pop() : stack.at(-1);
10208
+ if (open2 && s.role === "close" && LOOPS.has(open2.keyword)) {
10209
+ const body = tokens.slice(open2.after, unitOf(span).start);
8125
10210
  const reference = body.find((t) => t.kind === "open" && REFERENCES.has(t.name));
8126
10211
  if (reference && reference.kind === "open")
8127
10212
  diagnostics.push({
8128
10213
  severity: "error",
8129
10214
  code: "office-reference-in-loop",
8130
- message: `The loop from paragraph ${open.paragraph} repeats a ${reference.name.includes("comment") ? "comment" : "footnote or endnote"}, which cannot be copied. Move it out of the loop.`,
10215
+ message: `The loop from paragraph ${open2.paragraph} repeats a ${reference.name.includes("comment") ? "comment" : "footnote or endnote"}, which cannot be copied. Move it out of the loop.`,
8131
10216
  part,
8132
- paragraph: open.paragraph,
8133
- text: short2(open.tag)
10217
+ paragraph: open2.paragraph,
10218
+ text: short2(open2.tag)
8134
10219
  });
8135
10220
  }
8136
- if (open && open.parent !== parent)
10221
+ if (open2 && open2.parent !== parent)
8137
10222
  diagnostics.push({
8138
10223
  severity: "error",
8139
10224
  code: "office-block-unbalanced",
8140
- message: `${short2(open.tag)} (paragraph ${open.paragraph}) and ${short2(s.tag)} are in different places: one in a table cell or text box, the other outside it. Put both in the same place.`,
10225
+ message: `${short2(open2.tag)} (paragraph ${open2.paragraph}) and ${short2(s.tag)} are in different places: one in a table cell or text box, the other outside it. Put both in the same place.`,
8141
10226
  part,
8142
10227
  paragraph: s.info.number,
8143
10228
  text: short2(s.tag)
@@ -8151,9 +10236,9 @@ function applyStructure(xml2, part) {
8151
10236
  for (const [span, s] of structural) {
8152
10237
  if (rowOf.has(span)) continue;
8153
10238
  const keep = s.info.sectionProperties;
8154
- let text = element(s.tag);
8155
- if (keep) text = s.role === "open" ? `<w:p>${keep}</w:p>${text}` : `${text}<w:p>${keep}</w:p>`;
8156
- replacements.push({ start: span.start, end: span.end, text });
10239
+ let text2 = element(s.tag);
10240
+ if (keep) text2 = s.role === "open" ? `<w:p>${keep}</w:p>${text2}` : `${text2}<w:p>${keep}</w:p>`;
10241
+ replacements.push({ start: span.start, end: span.end, text: text2 });
8157
10242
  const holder = span.parent && BODIES.has(span.parent.name) ? span.parent : null;
8158
10243
  if (holder && !keep) cellsEmptied.set(holder, (cellsEmptied.get(holder) ?? 0) + 1);
8159
10244
  }
@@ -8207,7 +10292,7 @@ function collectParagraphs(tokens, root) {
8207
10292
  const out = [];
8208
10293
  const walk3 = (span) => {
8209
10294
  if (PARAGRAPH.has(span.name)) {
8210
- let text = "";
10295
+ let text2 = "";
8211
10296
  let hasContent = false;
8212
10297
  let sectionProperties = null;
8213
10298
  let inText = false;
@@ -8218,10 +10303,10 @@ function collectParagraphs(tokens, root) {
8218
10303
  else if (CONTENT.has(t.name)) hasContent = true;
8219
10304
  else if (t.name === "w:sectPr") sectionProperties = "";
8220
10305
  } else if (t.kind === "close" && TEXT.has(t.name)) inText = false;
8221
- else if (t.kind === "text" && inText) text += decodeText2(t.raw);
10306
+ else if (t.kind === "text" && inText) text2 += decodeText3(t.raw);
8222
10307
  }
8223
10308
  if (sectionProperties !== null) sectionProperties = propertiesOf(tokens, span);
8224
- out.push({ span, number: out.length + 1, text, hasContent, sectionProperties });
10309
+ out.push({ span, number: out.length + 1, text: text2, hasContent, sectionProperties });
8225
10310
  }
8226
10311
  for (const child2 of span.children) walk3(child2);
8227
10312
  };
@@ -8329,7 +10414,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
8329
10414
  for (const container of containers) {
8330
10415
  const indexes = [];
8331
10416
  const edits = [];
8332
- let text = "";
10417
+ let text2 = "";
8333
10418
  let inText = false;
8334
10419
  for (let i = container.start + 1; i < container.end; i++) {
8335
10420
  const t = tokens[i];
@@ -8338,7 +10423,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
8338
10423
  else if (t.kind === "text" && inText) {
8339
10424
  pattern.lastIndex = 0;
8340
10425
  if (!pattern.test(t.raw)) {
8341
- text += decodeText2(t.raw);
10426
+ text2 += decodeText3(t.raw);
8342
10427
  continue;
8343
10428
  }
8344
10429
  const segments = t.raw.split(pattern);
@@ -8347,7 +10432,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
8347
10432
  if (s % 2 === 1) indexes.push(Number(segments[s]));
8348
10433
  else kept += segments[s];
8349
10434
  }
8350
- text += decodeText2(kept);
10435
+ text2 += decodeText3(kept);
8351
10436
  edits.push({ at: i, raw: kept });
8352
10437
  }
8353
10438
  }
@@ -8366,7 +10451,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
8366
10451
  }
8367
10452
  const autoFit = tokens.slice(container.start, container.end).some((t) => t.kind === "open" && t.name === "a:spAutoFit");
8368
10453
  const placed = layoutPictures(pictures, box, autoFit).map((b, i) => pictureMarkup(pictures[i], b)).join("");
8369
- const onlyPictures = container.name === "p:sp" && text.trim() === "";
10454
+ const onlyPictures = container.name === "p:sp" && text2.trim() === "";
8370
10455
  if (onlyPictures) replacements.push({ start: container.start, end: container.end, text: placed });
8371
10456
  else {
8372
10457
  for (const edit of edits) replacements.push({ start: edit.at, end: edit.at, text: edit.raw });
@@ -8639,8 +10724,8 @@ function starterTags(engine) {
8639
10724
  codeFit: engine === "liquid" ? "{{ offer.url | qrcode }}" : "{{ qrcode(offer.url) }}"
8640
10725
  };
8641
10726
  }
8642
- var paragraph = (text, style, bold = false) => `<w:p>${style ? `<w:pPr><w:pStyle w:val="${style}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText2(text)}</w:t></w:r></w:p>`;
8643
- var cell = (text, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text, void 0, bold)}</w:tc>`;
10727
+ var paragraph = (text2, style2, bold = false) => `<w:p>${style2 ? `<w:pPr><w:pStyle w:val="${style2}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText2(text2)}</w:t></w:r></w:p>`;
10728
+ var cell = (text2, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text2, void 0, bold)}</w:tc>`;
8644
10729
  var row = (cells) => `<w:tr>${cells}</w:tr>`;
8645
10730
  function starterDocument(engine) {
8646
10731
  const t = starterTags(engine);
@@ -8683,7 +10768,7 @@ function starterDocument(engine) {
8683
10768
  ["word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="${W}"><w:body>${body}</w:body></w:document>`]
8684
10769
  ];
8685
10770
  return {
8686
- bytes: writeZip(files.map(([name, text]) => ({ name, data: encoder2.encode(text) }))),
10771
+ bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
8687
10772
  sampleData: starterSampleData()
8688
10773
  };
8689
10774
  }
@@ -8711,15 +10796,15 @@ var CM = 36e4;
8711
10796
  var FONT = "Carlito";
8712
10797
  var ACCENT = "0F766E";
8713
10798
  var emptyTree = '<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/>';
8714
- var run = (text, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText2(text)}</a:t></a:r>`;
8715
- var para = (text, size, options = {}) => `<a:p>${run(text, size, options)}</a:p>`;
10799
+ var run = (text2, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText2(text2)}</a:t></a:r>`;
10800
+ var para = (text2, size, options = {}) => `<a:p>${run(text2, size, options)}</a:p>`;
8716
10801
  function textBox(id, name, x, y, cx, cy, paragraphs) {
8717
10802
  return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="${name}"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:noAutofit/></a:bodyPr><a:lstStyle/>${paragraphs}</p:txBody></p:sp>`;
8718
10803
  }
8719
- function cell2(text, header) {
10804
+ function cell2(text2, header) {
8720
10805
  const fill = header ? `<a:solidFill><a:srgbClr val="${ACCENT}"/></a:solidFill>` : "<a:noFill/>";
8721
10806
  const line = `<a:lnB w="6350"><a:solidFill><a:srgbClr val="BFBFBF"/></a:solidFill></a:lnB>`;
8722
- return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line}${fill}</a:tcPr></a:tc>`;
10807
+ return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text2, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line}${fill}</a:tcPr></a:tc>`;
8723
10808
  }
8724
10809
  var tableRow = (a, b, header = false) => `<a:tr h="${Math.round(0.9 * CM)}">${cell2(a, header)}${cell2(b, header)}</a:tr>`;
8725
10810
  function table2(id, x, y, rows) {
@@ -8809,12 +10894,14 @@ function starterPresentation(engine) {
8809
10894
  ];
8810
10895
  const encoder2 = new TextEncoder();
8811
10896
  return {
8812
- bytes: writeZip(files.map(([name, text]) => ({ name, data: encoder2.encode(text) }))),
10897
+ bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
8813
10898
  sampleData: starterSampleData()
8814
10899
  };
8815
10900
  }
8816
10901
  export {
8817
10902
  DEFAULT_CHROME_PADDING,
10903
+ DESIGN_LIMITS,
10904
+ DESIGN_VERSION,
8818
10905
  DefaultHelperRegistry,
8819
10906
  EPC_MAX_BYTES,
8820
10907
  EngineSyntaxError,
@@ -8828,32 +10915,52 @@ export {
8828
10915
  analyzeOffice,
8829
10916
  annotateSourcePositions,
8830
10917
  apitemplateRegions,
10918
+ applyRegionEdit,
8831
10919
  assembleDocument,
8832
10920
  brandCss,
8833
10921
  builtinHelpers,
10922
+ canvasDocument,
8834
10923
  chartInitScript,
8835
10924
  chartMarkup,
8836
10925
  chartPayload,
10926
+ chipValues,
8837
10927
  convertScss,
8838
10928
  createHelperRegistry,
10929
+ dataPathPattern,
8839
10930
  defaultHelpers,
10931
+ defaultLayer,
8840
10932
  defaultLimits,
8841
10933
  defaultOutput,
8842
10934
  defaultSettings,
8843
10935
  depthGuard,
10936
+ designBytes,
10937
+ designOutputHash,
10938
+ designStarters,
8844
10939
  diffSchemas,
10940
+ editAttribute,
10941
+ editableRegion,
10942
+ editableRegionAt,
10943
+ emitDesign,
8845
10944
  emptyBrand,
10945
+ emptyDesign,
10946
+ encodeTemplateText,
8846
10947
  enforceLimits,
8847
10948
  engineIds,
8848
10949
  engines,
8849
10950
  epcPayload,
8850
10951
  escapeHtml,
10952
+ findRegionAgain,
8851
10953
  flowDocument,
8852
10954
  fontFaceCss,
10955
+ genericFamily,
8853
10956
  getEngine,
10957
+ googleFont,
10958
+ googleFonts,
10959
+ googleFontsUrl,
8854
10960
  handlebarsEngine,
8855
10961
  helperDocs,
8856
10962
  ibanChecksum,
10963
+ imageIsEmpty,
8857
10964
  imageMarkup,
8858
10965
  imageOutput,
8859
10966
  importApitemplate,
@@ -8870,6 +10977,7 @@ export {
8870
10977
  jinja2Engine,
8871
10978
  jinjaTags,
8872
10979
  jsreportTemplates,
10980
+ layerIdPattern,
8873
10981
  layeredPartials,
8874
10982
  liquidBuiltinFilters,
8875
10983
  liquidEngine,
@@ -8878,6 +10986,8 @@ export {
8878
10986
  mergeSettings,
8879
10987
  mergeSnippetCss,
8880
10988
  mergeSnippetI18n,
10989
+ nearestWeight,
10990
+ nextLayerId,
8881
10991
  normaliseIban,
8882
10992
  normalisePartialName,
8883
10993
  nunjucksBuiltinFilters,
@@ -8890,14 +11000,18 @@ export {
8890
11000
  pageSize,
8891
11001
  pagedDocument,
8892
11002
  paperOf,
11003
+ parseDesign,
8893
11004
  parseSourceAttribute,
8894
11005
  partialInclude,
8895
11006
  printReset,
8896
11007
  qrSvg,
8897
11008
  readJsreportExport,
11009
+ regionChips,
11010
+ regionItems,
8898
11011
  renderOffice,
8899
11012
  renderVersion,
8900
11013
  resolvePath,
11014
+ resolveTranslation,
8901
11015
  scanBlocks,
8902
11016
  schemaPaths,
8903
11017
  slugFromName,
@@ -8912,6 +11026,9 @@ export {
8912
11026
  svgDataUri,
8913
11027
  toDate,
8914
11028
  toNumber,
11029
+ translationItems,
11030
+ translationTarget,
11031
+ translationText,
8915
11032
  validateEpc,
8916
11033
  zoomGestureScript
8917
11034
  };