@bloklabs/cli 1.16.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- //#region src/components/shared/color-presets.ts
1
+ // src/shared/color-presets.ts
2
2
  var e = [
3
3
  {
4
4
  name: "gray",
@@ -92,8 +92,7 @@ var e = [
92
92
  bg: "#4e1a18"
93
93
  }
94
94
  ];
95
- //#endregion
96
- //#region src/components/utils/default-page-colors.ts
95
+ // src/shared/default-page-colors.ts
97
96
  function n(e) {
98
97
  let t = e.replace(/\s/g, "").toLowerCase();
99
98
  return t === "rgb(255,255,255)" || t === "#ffffff" || t === "#fff" || t === "white";
@@ -184,8 +183,7 @@ function l(e) {
184
183
  let t = o(e);
185
184
  return t !== null && t.r <= c && t.g <= c && t.b <= c;
186
185
  }
187
- //#endregion
188
- //#region src/components/utils/color-mapping.ts
186
+ // src/shared/color-mapping.ts
189
187
  function u(e, t, n) {
190
188
  let r = t / 100, i = n / 100;
191
189
  if (r === 0) {
@@ -233,12 +231,12 @@ function d(e) {
233
231
  ];
234
232
  let a = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*[\d.]+\s*)?\)$/i.exec(e);
235
233
  if (a) return [
236
- Number(a[1]),
237
- Number(a[2]),
238
- Number(a[3])
234
+ Math.min(255, Number(a[1])),
235
+ Math.min(255, Number(a[2])),
236
+ Math.min(255, Number(a[3]))
239
237
  ];
240
238
  let o = /^hsla?\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*(?:,\s*[\d.]+\s*)?\)$/i.exec(e);
241
- return o ? u(Number(o[1]), Number(o[2]), Number(o[3])) : null;
239
+ return o ? u(Number(o[1]), Math.min(100, Number(o[2])), Math.min(100, Number(o[3]))) : null;
242
240
  }
243
241
  var f = .1;
244
242
  function p(e) {
@@ -283,26 +281,27 @@ function h(t, r) {
283
281
  distance: Infinity
284
282
  }).color;
285
283
  }
286
- function g(r, a) {
287
- if (a === "bg" && (n(r) || i(r))) return null;
288
- let o = d(r);
289
- if (o === null) return null;
290
- let s = p(o);
284
+ function g(e, t) {
285
+ return t === "bg" && (n(e) || i(e)) ? null : ee(e, t);
286
+ }
287
+ function ee(n, r) {
288
+ let i = d(n);
289
+ if (i === null) return null;
290
+ let a = p(i);
291
291
  return [...e, ...t].reduce((e, t) => {
292
- let n = d(t[a]);
292
+ let n = d(t[r]);
293
293
  if (n === null) return e;
294
- let r = m(s, p(n));
295
- return e === null || r < e.distance ? {
294
+ let i = m(a, p(n));
295
+ return e === null || i < e.distance ? {
296
296
  name: t.name,
297
- distance: r
297
+ distance: i
298
298
  } : e;
299
299
  }, null)?.name ?? null;
300
300
  }
301
- //#endregion
302
- //#region src/shared/page-reference.ts
303
- var _ = "data-blok-page-id", ee = "Page", te = (e) => {
301
+ // src/shared/page-reference.ts
302
+ var _ = "data-blok-page-id", te = "Page", ne = (e) => {
304
303
  let t = e.getAttribute(_);
305
- return t !== null && t !== "" ? (e.textContent = ee, { [_]: !0 }) : {
304
+ return t !== null && t !== "" ? (e.textContent = te, { [_]: !0 }) : {
306
305
  href: !0,
307
306
  target: !0,
308
307
  rel: !0
@@ -324,7 +323,7 @@ var _ = "data-blok-page-id", ee = "Page", te = (e) => {
324
323
  "SMALL",
325
324
  "SPAN",
326
325
  "FONT"
327
- ]), ne = new Set([...v].filter((e) => e !== "A")), re = /* @__PURE__ */ new Set([
326
+ ]), re = new Set([...v].filter((e) => e !== "A")), ie = /* @__PURE__ */ new Set([
328
327
  "IMG",
329
328
  "IFRAME",
330
329
  "VIDEO",
@@ -334,12 +333,12 @@ var _ = "data-blok-page-id", ee = "Page", te = (e) => {
334
333
  "CANVAS",
335
334
  "OBJECT",
336
335
  "EMBED"
337
- ]), ie = /* @__PURE__ */ new Set([
336
+ ]), ae = /* @__PURE__ */ new Set([
338
337
  "U",
339
338
  "S",
340
339
  "DEL",
341
340
  "INS"
342
- ]), ae = /* @__PURE__ */ new Set([
341
+ ]), oe = /* @__PURE__ */ new Set([
343
342
  "background-color",
344
343
  "background",
345
344
  "text-decoration",
@@ -348,7 +347,7 @@ var _ = "data-blok-page-id", ee = "Page", te = (e) => {
348
347
  "border-bottom",
349
348
  "box-shadow",
350
349
  "outline"
351
- ]), oe = /* @__PURE__ */ new Set([
350
+ ]), se = /* @__PURE__ */ new Set([
352
351
  "",
353
352
  "none",
354
353
  "transparent",
@@ -360,16 +359,16 @@ var _ = "data-blok-page-id", ee = "Page", te = (e) => {
360
359
  }, b = (e) => e.attributes.some((t) => {
361
360
  let n = t.name.toLowerCase();
362
361
  return n === "id" || e.tagName === "A" && n === "data-blok-page-id";
363
- }), se = (e) => ie.has(e.tagName) ? !0 : e.styleDeclarations.some((e) => ae.has(e.property.toLowerCase()) && !oe.has(e.value.trim().toLowerCase())), ce = (e) => {
364
- if (!ne.has(e.tagName)) return !1;
362
+ }), ce = (e) => ae.has(e.tagName) ? !0 : e.styleDeclarations.some((e) => oe.has(e.property.toLowerCase()) && !se.has(e.value.trim().toLowerCase())), le = (e) => {
363
+ if (!re.has(e.tagName)) return !1;
365
364
  let t = e.text();
366
- return t.trim().length > 0 || e.hasVoidContentDescendant() ? !1 : t.length === 0 || !se(e);
367
- }, le = (e, t) => !v.has(e.tagName) || b(e) || b(t) ? !1 : y(e) === y(t), ue = (e, t) => {
365
+ return t.trim().length > 0 || e.hasVoidContentDescendant() ? !1 : t.length === 0 || !ce(e);
366
+ }, ue = (e, t) => !v.has(e.tagName) || b(e) || b(t) ? !1 : y(e) === y(t), de = (e, t) => {
368
367
  if (!v.has(e.tagName) || b(e)) return !1;
369
368
  let n = y(e);
370
369
  return t.some((e) => y(e) === n);
371
370
  }, x = { current: void 0 };
372
- function de(e) {
371
+ function S(e) {
373
372
  x.current ??= document.implementation.createHTMLDocument("");
374
373
  let t = x.current.createElement("div");
375
374
  return t.innerHTML = e, t;
@@ -377,9 +376,8 @@ function de(e) {
377
376
  function fe(e) {
378
377
  return new DOMParser().parseFromString(e, "text/html");
379
378
  }
380
- //#endregion
381
- //#region src/components/utils/inline-normalization.ts
382
- var S = (e) => {
379
+ // src/components/utils/inline-normalization.ts
380
+ var C = (e) => {
383
381
  let t = e.style, n = Array.from({ length: t.length }, (e, n) => {
384
382
  let r = t.item(n);
385
383
  return {
@@ -395,11 +393,11 @@ var S = (e) => {
395
393
  })),
396
394
  styleDeclarations: n,
397
395
  text: () => e.textContent ?? "",
398
- hasVoidContentDescendant: () => Array.from(e.querySelectorAll("*")).some((e) => re.has(e.tagName.toUpperCase()))
396
+ hasVoidContentDescendant: () => Array.from(e.querySelectorAll("*")).some((e) => ie.has(e.tagName.toUpperCase()))
399
397
  };
400
- }, C = (e, t) => {
398
+ }, w = (e, t) => {
401
399
  let n = e.parentElement;
402
- return n === null || n === t ? [] : [S(n), ...C(n, t)];
400
+ return n === null || n === t ? [] : [C(n), ...w(n, t)];
403
401
  }, pe = (e) => {
404
402
  let t = e.parentNode;
405
403
  if (t !== null) {
@@ -409,34 +407,32 @@ var S = (e) => {
409
407
  }, me = (e) => {
410
408
  let t = (t) => e.contains(t), n = (e) => t(e) && v.has(e.tagName.toUpperCase()), r = Array.from(e.querySelectorAll("*")).filter((t) => {
411
409
  if (!n(t)) return !1;
412
- let r = S(t);
413
- return ce(r) || ue(r, C(t, e));
410
+ let r = C(t);
411
+ return le(r) || de(r, w(t, e));
414
412
  });
415
413
  r.forEach(pe);
416
414
  let i = (e, t) => {
417
415
  let n = e.nextSibling;
418
- if (n === null || n.nodeType !== Node.ELEMENT_NODE || !le(t, S(n))) return !1;
416
+ if (n === null || n.nodeType !== Node.ELEMENT_NODE || !ue(t, C(n))) return !1;
419
417
  let r = n;
420
418
  for (; r.firstChild !== null;) e.appendChild(r.firstChild);
421
419
  return r.remove(), i(e, t), !0;
422
- }, a = Array.from(e.querySelectorAll("*")).filter(n).map((e) => t(e) && i(e, S(e))).some(Boolean);
420
+ }, a = Array.from(e.querySelectorAll("*")).filter(n).map((e) => t(e) && i(e, C(e))).some(Boolean);
423
421
  return r.length > 0 || a;
424
- }, w = (e) => {
422
+ }, T = (e) => {
425
423
  let t = (n) => n === 0 || !me(e) ? !1 : (t(n - 1), !0), n = t(10);
426
424
  return n && e.normalize?.(), n;
427
425
  }, he = "data-blok-columns-candidate", ge = /(<br\s*\/?>)/i, _e = /^<br\s*\/?>$/i;
428
- function T(e) {
426
+ function E(e) {
429
427
  let t = e.split(ge), n = t.reduce((e, t, n) => _e.test(t) || t.trim() === "" ? e : n, -1);
430
428
  return t.slice(0, n + 1).join("").trimEnd();
431
429
  }
432
- //#endregion
433
- //#region src/components/utils/spacer-paragraph.ts
434
- function E(e, t = {}) {
430
+ // src/components/utils/spacer-paragraph.ts
431
+ function D(e, t = {}) {
435
432
  let { lineBreaksAreContent: n = !0 } = t, r = Array.from(e.children);
436
433
  return !(n ? r.length > 0 : r.some((e) => e.tagName !== "BR")) && (e.textContent ?? "").replace(/\s/g, "") === "";
437
434
  }
438
- //#endregion
439
- //#region src/tools/list/paste-handler.ts
435
+ // src/tools/list/paste-handler.ts
440
436
  var ve = /* @__PURE__ */ new Set(/* @__PURE__ */ "decimal.decimal-leading-zero.lower-roman.upper-roman.lower-greek.lower-latin.upper-latin.lower-alpha.upper-alpha.arabic-indic.armenian.bengali.cambodian.cjk-decimal.devanagari.georgian.gujarati.gurmukhi.hebrew.kannada.khmer.lao.malayalam.mongolian.myanmar.oriya.persian.telugu.thai.tibetan".split(".")), ye = /* @__PURE__ */ new Set([
441
437
  "disc",
442
438
  "circle",
@@ -452,15 +448,15 @@ var ve = /* @__PURE__ */ new Set(/* @__PURE__ */ "decimal.decimal-leading-zero.l
452
448
  if (!r) return null;
453
449
  let i = r[1].trim().toLowerCase();
454
450
  return ve.has(i) ? "ordered" : ye.has(i) ? "unordered" : null;
455
- }, D = (e) => {
451
+ }, O = (e) => {
456
452
  if (e instanceof HTMLInputElement && e.type === "checkbox") return e;
457
453
  let t = e.querySelector("input[type=\"checkbox\"]");
458
454
  return t instanceof HTMLInputElement ? t : null;
459
455
  }, xe = (e, t) => {
460
- let n = e.parentElement, r = D(e) !== null;
456
+ let n = e.parentElement, r = O(e) !== null;
461
457
  return n?.tagName === "OL" ? "ordered" : n?.tagName === "UL" ? r ? "checklist" : "unordered" : be(e) || (r ? "checklist" : t);
462
458
  }, Se = (e) => {
463
- let t = D(e);
459
+ let t = O(e);
464
460
  if (t === null) return {
465
461
  text: e.innerHTML || e.textContent || "",
466
462
  checked: !1
@@ -473,11 +469,11 @@ var ve = /* @__PURE__ */ new Set(/* @__PURE__ */ "decimal.decimal-leading-zero.l
473
469
  }, Ce = (e) => {
474
470
  let t = e.getAttribute("aria-level");
475
471
  if (t) return Math.max(0, parseInt(t, 10) - 1);
476
- let n = O(e) - 1;
472
+ let n = k(e) - 1;
477
473
  return n > 0 ? n : 0;
478
- }, O = (e) => {
474
+ }, k = (e) => {
479
475
  let t = e.parentElement;
480
- return t === null ? 0 : +(t.tagName === "UL" || t.tagName === "OL") + O(t);
476
+ return t === null ? 0 : +(t.tagName === "UL" || t.tagName === "OL") + k(t);
481
477
  }, we = /<br\s*\/?>/i, Te = (e) => e.split(we).map((e) => e.trim()).filter(Boolean), Ee = (e) => e instanceof HTMLElement && (e.tagName === "UL" || e.tagName === "OL"), De = (e) => Array.from(e.querySelectorAll("li")).map((t) => {
482
478
  let n = xe(t, e.tagName === "OL" ? "ordered" : "unordered"), r = Ce(t), i = t.cloneNode(!0);
483
479
  i.querySelectorAll("ul, ol").forEach((e) => e.remove());
@@ -485,16 +481,16 @@ var ve = /* @__PURE__ */ new Set(/* @__PURE__ */ "decimal.decimal-leading-zero.l
485
481
  return {
486
482
  tool: "list",
487
483
  data: {
488
- text: T(a).trim(),
484
+ text: E(a).trim(),
489
485
  style: n,
490
486
  checked: o,
491
487
  depth: r
492
488
  }
493
489
  };
494
- }), k = (e) => Array.from(e.querySelectorAll("tr")).filter((t) => {
490
+ }), A = (e) => Array.from(e.querySelectorAll("tr")).filter((t) => {
495
491
  let n = t.closest("table")?.parentElement?.closest("table");
496
492
  return n == null || !e.contains(n);
497
- }), A = (e) => Array.from(e.children).filter((e) => e.tagName === "TD" || e.tagName === "TH"), Oe = (e) => {
493
+ }), j = (e) => Array.from(e.children).filter((e) => e.tagName === "TD" || e.tagName === "TH"), Oe = (e) => {
498
494
  let t = e.cloneNode(!0);
499
495
  return t.querySelectorAll("br").forEach((e) => e.replaceWith("\n")), t.textContent ?? "";
500
496
  }, ke = [
@@ -510,7 +506,7 @@ var ve = /* @__PURE__ */ new Set(/* @__PURE__ */ "decimal.decimal-leading-zero.l
510
506
  let e = t.ownerDocument;
511
507
  t.replaceWith(e.createElement("br"), ...Array.from(t.childNodes), e.createElement("br"));
512
508
  }
513
- }, je = (e) => e instanceof HTMLElement && (e.tagName === "PRE" || e.tagName === "BLOK-CELL-CODE"), j = (e) => {
509
+ }, je = (e) => e instanceof HTMLElement && (e.tagName === "PRE" || e.tagName === "BLOK-CELL-CODE"), M = (e) => {
514
510
  let t = [], n = [], r = () => {
515
511
  Te(n.join("")).forEach((e) => t.push({
516
512
  tool: "paragraph",
@@ -520,28 +516,27 @@ var ve = /* @__PURE__ */ new Set(/* @__PURE__ */ "decimal.decimal-leading-zero.l
520
516
  for (let i of e) Ee(i) ? (r(), t.push(...De(i))) : i instanceof HTMLElement && (i.tagName === "P" || i.tagName === "DIV") ? (r(), n.push(i.innerHTML), r()) : je(i) ? (r(), t.push({
521
517
  tool: "code",
522
518
  data: { code: Oe(i) }
523
- })) : i instanceof HTMLElement && i.tagName === "TABLE" ? (r(), k(i).flatMap(A).forEach((e) => t.push(...j(Array.from(e.childNodes))))) : n.push(i instanceof HTMLElement ? i.outerHTML : i.textContent ?? "");
519
+ })) : i instanceof HTMLElement && i.tagName === "TABLE" ? (r(), A(i).flatMap(j).forEach((e) => t.push(...M(Array.from(e.childNodes))))) : n.push(i instanceof HTMLElement ? i.outerHTML : i.textContent ?? "");
524
520
  return r(), t;
525
521
  }, Me = (e) => {
526
- let t = de(e), n = j(Array.from(t.childNodes));
522
+ let t = S(e), n = M(Array.from(t.childNodes));
527
523
  return n.length > 0 ? n : [{
528
524
  tool: "paragraph",
529
525
  data: { text: "" }
530
526
  }];
531
- }, Ne = /* @__PURE__ */ new Set(/* @__PURE__ */ "aliceblue.antiquewhite.aqua.aquamarine.azure.beige.bisque.black.blanchedalmond.blue.blueviolet.brown.burlywood.cadetblue.chartreuse.chocolate.coral.cornflowerblue.cornsilk.crimson.cyan.darkblue.darkcyan.darkgoldenrod.darkgray.darkgreen.darkgrey.darkkhaki.darkmagenta.darkolivegreen.darkorange.darkorchid.darkred.darksalmon.darkseagreen.darkslateblue.darkslategray.darkslategrey.darkturquoise.darkviolet.deeppink.deepskyblue.dimgray.dimgrey.dodgerblue.firebrick.floralwhite.forestgreen.fuchsia.gainsboro.ghostwhite.gold.goldenrod.gray.green.greenyellow.grey.honeydew.hotpink.indianred.indigo.ivory.khaki.lavender.lavenderblush.lawngreen.lemonchiffon.lightblue.lightcoral.lightcyan.lightgoldenrodyellow.lightgray.lightgreen.lightgrey.lightpink.lightsalmon.lightseagreen.lightskyblue.lightslategray.lightslategrey.lightsteelblue.lightyellow.lime.limegreen.linen.magenta.maroon.mediumaquamarine.mediumblue.mediumorchid.mediumpurple.mediumseagreen.mediumslateblue.mediumspringgreen.mediumturquoise.mediumvioletred.midnightblue.mintcream.mistyrose.moccasin.navajowhite.navy.oldlace.olive.olivedrab.orange.orangered.orchid.palegoldenrod.palegreen.paleturquoise.palevioletred.papayawhip.peachpuff.peru.pink.plum.powderblue.purple.rebeccapurple.red.rosybrown.royalblue.saddlebrown.salmon.sandybrown.seagreen.seashell.sienna.silver.skyblue.slateblue.slategray.slategrey.snow.springgreen.steelblue.tan.teal.thistle.tomato.turquoise.violet.wheat.white.whitesmoke.yellow.yellowgreen".split(".")), Pe = /* @__PURE__ */ new Set(["transparent", "currentcolor"]), Fe = 256, M = "[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)", N = `${M}%?`, P = `${M}%`, F = `${M}(?:deg|grad|rad|turn)?`, I = " *, *", L = " +", R = `(?: */ *${N})?`, Ie = RegExp(`^rgba?\\( *${N}${I}${N}${I}${N}(?:${I}${N})? *\\)$`, "i"), Le = RegExp(`^rgba?\\( *${N}${L}${N}${L}${N}${R} *\\)$`, "i"), Re = RegExp(`^hsla?\\( *${F}${I}${P}${I}${P}(?:${I}${N})? *\\)$`, "i"), ze = RegExp(`^hsla?\\( *${F}${L}${N}${L}${N}${R} *\\)$`, "i"), Be = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i, Ve = /^var\(--[a-zA-Z0-9_-]+\)$/, z = (e) => {
527
+ }, Ne = /* @__PURE__ */ new Set(/* @__PURE__ */ "aliceblue.antiquewhite.aqua.aquamarine.azure.beige.bisque.black.blanchedalmond.blue.blueviolet.brown.burlywood.cadetblue.chartreuse.chocolate.coral.cornflowerblue.cornsilk.crimson.cyan.darkblue.darkcyan.darkgoldenrod.darkgray.darkgreen.darkgrey.darkkhaki.darkmagenta.darkolivegreen.darkorange.darkorchid.darkred.darksalmon.darkseagreen.darkslateblue.darkslategray.darkslategrey.darkturquoise.darkviolet.deeppink.deepskyblue.dimgray.dimgrey.dodgerblue.firebrick.floralwhite.forestgreen.fuchsia.gainsboro.ghostwhite.gold.goldenrod.gray.green.greenyellow.grey.honeydew.hotpink.indianred.indigo.ivory.khaki.lavender.lavenderblush.lawngreen.lemonchiffon.lightblue.lightcoral.lightcyan.lightgoldenrodyellow.lightgray.lightgreen.lightgrey.lightpink.lightsalmon.lightseagreen.lightskyblue.lightslategray.lightslategrey.lightsteelblue.lightyellow.lime.limegreen.linen.magenta.maroon.mediumaquamarine.mediumblue.mediumorchid.mediumpurple.mediumseagreen.mediumslateblue.mediumspringgreen.mediumturquoise.mediumvioletred.midnightblue.mintcream.mistyrose.moccasin.navajowhite.navy.oldlace.olive.olivedrab.orange.orangered.orchid.palegoldenrod.palegreen.paleturquoise.palevioletred.papayawhip.peachpuff.peru.pink.plum.powderblue.purple.rebeccapurple.red.rosybrown.royalblue.saddlebrown.salmon.sandybrown.seagreen.seashell.sienna.silver.skyblue.slateblue.slategray.slategrey.snow.springgreen.steelblue.tan.teal.thistle.tomato.turquoise.violet.wheat.white.whitesmoke.yellow.yellowgreen".split(".")), Pe = /* @__PURE__ */ new Set(["transparent", "currentcolor"]), Fe = 256, N = "[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)", P = `${N}%?`, F = `${N}%`, I = `${N}(?:deg|grad|rad|turn)?`, L = " *, *", R = " +", z = `(?: */ *${P})?`, Ie = RegExp(`^rgba?\\( *${P}${L}${P}${L}${P}(?:${L}${P})? *\\)$`, "i"), Le = RegExp(`^rgba?\\( *${P}${R}${P}${R}${P}${z} *\\)$`, "i"), Re = RegExp(`^hsla?\\( *${I}${L}${F}${L}${F}(?:${L}${P})? *\\)$`, "i"), ze = RegExp(`^hsla?\\( *${I}${R}${P}${R}${P}${z} *\\)$`, "i"), Be = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i, Ve = /^var\(--[a-zA-Z0-9_-]+\)$/, B = (e) => {
532
528
  if (typeof e != "string" || e.length === 0 || e.length > Fe) return !1;
533
529
  let t = e.toLowerCase();
534
530
  return Ne.has(t) || Pe.has(t) || Be.test(e) || Ve.test(e) || Ie.test(e) || Le.test(e) || Re.test(e) || ze.test(e);
535
531
  };
536
- //#endregion
537
- //#region src/components/modules/paste/google-docs-preprocessor.ts
532
+ // src/components/modules/paste/google-docs-preprocessor.ts
538
533
  function He(e, { keepTables: t = !1 } = {}) {
539
- let n = de(e), r = qe(n);
540
- return nt(n, r), r && !t && it(n), ct(n), r && !t && at(n), r && Ke(n), w(n), n.innerHTML;
534
+ let n = S(e), r = qe(n);
535
+ return nt(n, r), r && !t && it(n), ct(n), r && !t && at(n), r && Ke(n), T(n), n.innerHTML;
541
536
  }
542
- function B(e, t) {
537
+ function V(e, t) {
543
538
  let n = e.parentElement;
544
- return n === null ? null : n === t ? e : B(n, t);
539
+ return n === null ? null : n === t ? e : V(n, t);
545
540
  }
546
541
  function Ue(e, t, n) {
547
542
  let r = e.cloneNode(!1), i = [];
@@ -571,15 +566,15 @@ function Ge(e, t) {
571
566
  };
572
567
  return n(e, null, null);
573
568
  }
574
- function V(e, t) {
569
+ function H(e, t) {
575
570
  let n = e.parentElement;
576
- return n === null || n === t ? !1 : n.tagName === "TABLE" || V(n, t);
571
+ return n === null || n === t ? !1 : n.tagName === "TABLE" || H(n, t);
577
572
  }
578
573
  function Ke(e) {
579
574
  let t = Array.from(e.querySelectorAll("img"));
580
575
  for (let n of t) {
581
- if (V(n, e)) continue;
582
- let t = B(n, e);
576
+ if (H(n, e)) continue;
577
+ let t = V(n, e);
583
578
  if (!t) continue;
584
579
  let { before: r, after: i } = Ge(n, t), a = document.createDocumentFragment();
585
580
  r && a.appendChild(r), a.appendChild(n), i && a.appendChild(i), t.replaceWith(a);
@@ -595,7 +590,7 @@ function qe(e) {
595
590
  function Je(e, t, n) {
596
591
  return e ? `background-color: ${n}` : t ? "background-color: transparent" : "";
597
592
  }
598
- function H(e) {
593
+ function U(e) {
599
594
  if (e.closest("a") !== null) return !0;
600
595
  let t = e.querySelector("a");
601
596
  return t !== null && t.textContent?.trim() === e.textContent?.trim();
@@ -648,9 +643,9 @@ var Qe = /* @__PURE__ */ new Set([
648
643
  "H5",
649
644
  "H6"
650
645
  ]);
651
- function U(e) {
646
+ function W(e) {
652
647
  let t = e.parentElement;
653
- return t === null ? !1 : Qe.has(t.tagName) ? !0 : U(t);
648
+ return t === null ? !1 : Qe.has(t.tagName) ? !0 : W(t);
654
649
  }
655
650
  var $e = {
656
651
  super: "sup",
@@ -663,10 +658,10 @@ function et(e) {
663
658
  let n = document.implementation.createHTMLDocument("").createElement("div");
664
659
  n.style.cssText = e;
665
660
  let r = n.style.backgroundColor;
666
- return z(r) && r.toLowerCase() !== "currentcolor" ? r : void 0;
661
+ return B(r) && r.toLowerCase() !== "currentcolor" ? r : void 0;
667
662
  }
668
663
  function tt(e, t) {
669
- let n = e.getAttribute("style") ?? "", r = /font-weight\s*:\s*(700|bold)/i.test(n) && !U(e), i = /font-style\s*:\s*italic/i.test(n), a = /(?<![a-z-])color\s*:\s*([^;]+)/i.exec(n)?.[1]?.trim(), o = et(n), c = !(a !== void 0 && H(e)) && (t ? a !== void 0 && !l(a) : a !== void 0 && !l(a) && !Xe(a)), u = o !== void 0 && !s(o), d = t ? /text-decoration(?:-line)?\s*:\s*([^;]+)/i.exec(n)?.[1] ?? "" : "", f = t ? /vertical-align\s*:\s*([^;]+)/i.exec(n)?.[1]?.trim() ?? "" : "", p = /underline/i.test(d) && !H(e), m = /line-through/i.test(d), h = $e[f.toLowerCase()];
664
+ let n = e.getAttribute("style") ?? "", r = /font-weight\s*:\s*(700|bold)/i.test(n) && !W(e), i = /font-style\s*:\s*italic/i.test(n), a = /(?<![a-z-])color\s*:\s*([^;]+)/i.exec(n)?.[1]?.trim(), o = et(n), c = !(a !== void 0 && U(e)) && (t ? a !== void 0 && !l(a) : a !== void 0 && !l(a) && !Xe(a)), u = o !== void 0 && !s(o), d = t ? /text-decoration(?:-line)?\s*:\s*([^;]+)/i.exec(n)?.[1] ?? "" : "", f = t ? /vertical-align\s*:\s*([^;]+)/i.exec(n)?.[1]?.trim() ?? "" : "", p = /underline/i.test(d) && !U(e), m = /line-through/i.test(d), h = $e[f.toLowerCase()];
670
665
  if (!r && !i && !c && !u && !p && !m && h === void 0) return null;
671
666
  let g = Ze(e.innerHTML, c, u, a, o);
672
667
  return [
@@ -693,14 +688,14 @@ function rt(e) {
693
688
  function it(e) {
694
689
  if (e.querySelector("google-sheets-html-origin") === null) for (let t of Array.from(e.querySelectorAll("table"))) {
695
690
  if (t.parentElement?.closest("table") !== null) continue;
696
- let e = k(t).map(A);
691
+ let e = A(t).map(j);
697
692
  e.length === 0 || e.some((e) => e.length !== 1) || (e.length === 1 || t.querySelector("img") !== null) && t.replaceWith(...e.flatMap(([e]) => Array.from(e.childNodes)));
698
693
  }
699
694
  }
700
695
  function at(e) {
701
696
  if (e.querySelector("google-sheets-html-origin") === null) for (let t of Array.from(e.querySelectorAll("table"))) {
702
697
  if (t.parentElement?.closest("table") !== null) continue;
703
- let e = k(t).map(A);
698
+ let e = A(t).map(j);
704
699
  if (e.length === 0) continue;
705
700
  let n = e[0].length;
706
701
  if (!e.every((e) => e.length === n)) continue;
@@ -709,35 +704,34 @@ function at(e) {
709
704
  }
710
705
  }
711
706
  function ot(e) {
712
- if (E(e)) {
707
+ if (D(e)) {
713
708
  e.remove();
714
709
  return;
715
710
  }
716
711
  let t = e.ownerDocument.createRange().createContextualFragment(e.innerHTML + "<br>");
717
712
  e.replaceWith(t);
718
713
  }
719
- var W = /(?<![a-z-])text-align\s*:\s*([^;]+)/i;
714
+ var G = /(?<![a-z-])text-align\s*:\s*([^;]+)/i;
720
715
  function st(e) {
721
- let t = W.exec(e.getAttribute("style") ?? "")?.[1], n = e.getAttribute("align")?.trim();
716
+ let t = G.exec(e.getAttribute("style") ?? "")?.[1], n = e.getAttribute("align")?.trim();
722
717
  return (t ?? (n !== void 0 && /^[a-z]+$/i.test(n) ? n : void 0))?.trim().toLowerCase();
723
718
  }
724
- function G(e) {
719
+ function K(e) {
725
720
  let t = e.getAttribute("style") ?? "";
726
- if (W.test(t)) return;
727
- let n = Array.from(e.querySelectorAll("p")).filter((t) => t.closest("td, th") === e && !E(t)), r = new Set(n.map(st)), [i] = r;
721
+ if (G.test(t)) return;
722
+ let n = Array.from(e.querySelectorAll("p")).filter((t) => t.closest("td, th") === e && !D(t)), r = new Set(n.map(st)), [i] = r;
728
723
  r.size === 1 && i !== void 0 && e.setAttribute("style", t === "" ? `text-align:${i}` : `${t};text-align:${i}`);
729
724
  }
730
725
  function ct(e) {
731
726
  let t = Array.from(e.querySelectorAll("td, th"));
732
- t.forEach(G);
727
+ t.forEach(K);
733
728
  for (let e of t) {
734
729
  let t = e.querySelectorAll("p");
735
- t.length !== 0 && (Array.from(t).forEach(ot), e.innerHTML = T(e.innerHTML));
730
+ t.length !== 0 && (Array.from(t).forEach(ot), e.innerHTML = E(e.innerHTML));
736
731
  }
737
732
  }
738
- //#endregion
739
- //#region src/tools/table/table-operations.ts
740
- var lt = 1e3, ut = 65534, K = (e, t, n) => {
733
+ // src/tools/table/table-operations.ts
734
+ var lt = 1e3, ut = 65534, q = (e, t, n) => {
741
735
  let r = Number.parseInt(e.getAttribute(t) ?? "", 10);
742
736
  return !Number.isFinite(r) || r < 1 ? 1 : Math.min(r, n);
743
737
  }, dt = (e, t) => {
@@ -749,8 +743,8 @@ var lt = 1e3, ut = 65534, K = (e, t, n) => {
749
743
  });
750
744
  };
751
745
  n.forEach((e, s) => {
752
- A(e).reduce((e, c) => {
753
- let l = a(r[s], e), u = K(c, "colspan", lt), d = Math.min(K(c, "rowspan", ut), n.length - s);
746
+ j(e).reduce((e, c) => {
747
+ let l = a(r[s], e), u = q(c, "colspan", lt), d = Math.min(q(c, "rowspan", ut), n.length - s);
754
748
  return r[s][l] = t.cell(c, {
755
749
  colspan: u,
756
750
  rowspan: d
@@ -759,7 +753,7 @@ var lt = 1e3, ut = 65534, K = (e, t, n) => {
759
753
  });
760
754
  let s = i.hasMerges ? r : r.filter((e) => e.length > 0), c = s.reduce((e, t) => Math.max(e, t.length), 0);
761
755
  return s.map((e) => Array.from({ length: c }, (n, r) => e[r] ?? t.filler()));
762
- }, q = "data-latex", ft = /* @__PURE__ */ new Set(["color", "background-color"]), pt = {
756
+ }, J = "data-latex", ft = /* @__PURE__ */ new Set(["color", "background-color"]), pt = {
763
757
  br: !0,
764
758
  strong: {},
765
759
  b: {},
@@ -768,18 +762,20 @@ var lt = 1e3, ut = 65534, K = (e, t, n) => {
768
762
  u: {},
769
763
  s: {},
770
764
  del: {},
771
- a: te,
765
+ a: ne,
772
766
  code: {},
773
767
  sup: {},
774
768
  sub: {},
775
769
  mark: (e) => {
776
- let t = e.style, n = Array.from({ length: t.length }, (e, n) => t.item(n));
770
+ let t = e.style;
771
+ if (t === void 0) return {};
772
+ let n = Array.from({ length: t.length }, (e, n) => t.item(n));
777
773
  for (let e of n) ft.has(e) || t.removeProperty(e);
778
774
  return t.length > 0 ? { style: !0 } : {};
779
775
  },
780
776
  span: (e) => {
781
- let t = e.getAttribute(q);
782
- return t !== null && (e.textContent !== t && (e.textContent = t), { [q]: !0 });
777
+ let t = e.getAttribute(J);
778
+ return t !== null && (e.textContent !== t && (e.textContent = t), { [J]: !0 });
783
779
  }
784
780
  }, mt = [
785
781
  "left",
@@ -809,9 +805,9 @@ function yt(e) {
809
805
  let n = document.implementation.createHTMLDocument("").createElement("div");
810
806
  return n.style.cssText = e, n.style.backgroundColor || void 0;
811
807
  }
812
- function J(e) {
808
+ function Y(e) {
813
809
  let t = (/(?<![a-z-])direction\s*:\s*(rtl|ltr)\b/i.exec(e.getAttribute("style") ?? "")?.[1] ?? e.getAttribute("dir"))?.trim().toLowerCase();
814
- return t === "rtl" || t === "ltr" ? t : e.parentElement === null ? "ltr" : J(e.parentElement);
810
+ return t === "rtl" || t === "ltr" ? t : e.parentElement === null ? "ltr" : Y(e.parentElement);
815
811
  }
816
812
  function bt(e, t = "ltr") {
817
813
  let n = {}, r = yt(e);
@@ -823,20 +819,18 @@ function bt(e, t = "ltr") {
823
819
  }
824
820
  function xt(e, t = "ltr") {
825
821
  let n = e.getAttribute("style") ?? "", r = [n], i = e.getAttribute("align")?.trim(), a = e.getAttribute("valign")?.trim(), o = e.getAttribute("bgcolor")?.trim();
826
- return i !== void 0 && /^[a-z]+$/i.test(i) && !/(?<![a-z-])text-align\s*:/i.test(n) && r.push(`text-align:${i}`), a !== void 0 && /^[a-z]+$/i.test(a) && !/vertical-align\s*:/i.test(n) && r.push(`vertical-align:${a}`), z(o) && !/(?<![a-z-])background(?:-color)?\s*:/i.test(n) && r.push(`background-color:${o}`), bt(r.join(";"), t);
822
+ return i !== void 0 && /^[a-z]+$/i.test(i) && !/(?<![a-z-])text-align\s*:/i.test(n) && r.push(`text-align:${i}`), a !== void 0 && /^[a-z]+$/i.test(a) && !/vertical-align\s*:/i.test(n) && r.push(`vertical-align:${a}`), B(o) && !/(?<![a-z-])background(?:-color)?\s*:/i.test(n) && r.push(`background-color:${o}`), bt(r.join(";"), t);
827
823
  }
828
- //#endregion
829
- //#region src/components/modules/paste/table-direction-preprocessor.ts
824
+ // src/components/modules/paste/table-direction-preprocessor.ts
830
825
  function St(e) {
831
826
  if (!/<table[\s>]/i.test(e)) return e;
832
827
  let t = fe(e), n = Array.from(t.querySelectorAll("table")).filter((e) => {
833
828
  let t = e.getAttribute("dir")?.trim().toLowerCase() === "rtl" ? "rtl" : "ltr";
834
- return J(e) !== t;
829
+ return Y(e) !== t;
835
830
  });
836
- return n.length === 0 ? e : (n.forEach((e) => e.setAttribute("dir", J(e))), t.documentElement.outerHTML);
831
+ return n.length === 0 ? e : (n.forEach((e) => e.setAttribute("dir", Y(e))), t.documentElement.outerHTML);
837
832
  }
838
- //#endregion
839
- //#region src/preprocess/legacy-cms-html.ts
833
+ // src/preprocess/legacy-cms-html.ts
840
834
  function Ct(e) {
841
835
  wt(e), Dt(e), At(e), Mt(e), Nt(e), Ft(e);
842
836
  }
@@ -845,7 +839,7 @@ function wt(e) {
845
839
  for (let n of Array.from(e.querySelectorAll("div[style]"))) {
846
840
  if (n.closest("table")) continue;
847
841
  let e = kt(n);
848
- if (!e || X(e)) continue;
842
+ if (!e || Z(e)) continue;
849
843
  let r = t.createElement("aside");
850
844
  r.style.backgroundColor = e, r.append(...Array.from(n.childNodes)), Tt(r), Et(r), n.replaceWith(r);
851
845
  }
@@ -863,12 +857,12 @@ function Et(e) {
863
857
  e?.tagName === "BR" && e.remove();
864
858
  }
865
859
  }
866
- var Y = 250;
860
+ var X = 250;
867
861
  function Dt(e) {
868
862
  let t = e.querySelectorAll("[style*=\"background-color\"]");
869
- for (let e of Array.from(t)) X(e.style.backgroundColor) && (e.style.removeProperty("background-color"), e.getAttribute("style")?.trim() === "" && e.removeAttribute("style"), Ot(e) && e.replaceWith(...Array.from(e.childNodes)));
863
+ for (let e of Array.from(t)) Z(e.style.backgroundColor) && (e.style.removeProperty("background-color"), e.getAttribute("style")?.trim() === "" && e.removeAttribute("style"), Ot(e) && e.replaceWith(...Array.from(e.childNodes)));
870
864
  }
871
- function X(e) {
865
+ function Z(e) {
872
866
  if (!e) return !1;
873
867
  let t = e.replace(/\s/g, "").toLowerCase();
874
868
  if (t === "transparent") return !0;
@@ -876,7 +870,7 @@ function X(e) {
876
870
  if (!n) return !1;
877
871
  if ((n[4] === void 0 ? 1 : parseFloat(n[4])) === 0) return !0;
878
872
  let r = parseInt(n[1], 10), i = parseInt(n[2], 10), a = parseInt(n[3], 10);
879
- return r >= Y && i >= Y && a >= Y;
873
+ return r >= X && i >= X && a >= X;
880
874
  }
881
875
  function Ot(e) {
882
876
  return e.attributes.length > 0 ? !1 : (e.textContent ?? "").replace(/[\s\u00A0]/g, "").length === 0;
@@ -908,7 +902,7 @@ function jt(e) {
908
902
  n.append(...Array.from(e.childNodes)), n.append(t.createElement("br")), e.replaceWith(n);
909
903
  }
910
904
  function Mt(e) {
911
- for (let t of Array.from(e.querySelectorAll("p"))) t.closest("td") || t.closest("th") || E(t, { lineBreaksAreContent: !1 }) && t.remove();
905
+ for (let t of Array.from(e.querySelectorAll("p"))) t.closest("td") || t.closest("th") || D(t, { lineBreaksAreContent: !1 }) && t.remove();
912
906
  }
913
907
  function Nt(e) {
914
908
  let t = e.ownerDocument;
@@ -958,8 +952,7 @@ function zt(e) {
958
952
  }
959
953
  return null;
960
954
  }
961
- //#endregion
962
- //#region src/cli/commands/convert-html/sanitizer.ts
955
+ // src/cli/commands/convert-html/sanitizer.ts
963
956
  var Bt = {
964
957
  B: !0,
965
958
  STRONG: !0,
@@ -1023,22 +1016,17 @@ function Ht(e) {
1023
1016
  return e.remove(), t.filter((e) => e.nodeType === e.ELEMENT_NODE);
1024
1017
  }
1025
1018
  function Ut(e, t) {
1026
- for (let n of Array.from(e.attributes)) (t === !0 || !t.has(n.name)) && e.removeAttribute(n.name);
1019
+ for (let n of Array.from(e.attributes)) t !== !0 && t.has(n.name) || e.removeAttribute(n.name);
1027
1020
  }
1028
1021
  function Wt(e) {
1029
1022
  let t = Array.from(e.childNodes);
1030
1023
  for (let e of t) {
1031
1024
  if (e.nodeType !== e.ELEMENT_NODE) continue;
1032
1025
  let n = e, r = Bt[n.tagName];
1033
- if (r === void 0) {
1034
- t.push(...Ht(n));
1035
- continue;
1036
- }
1037
- Ut(n, r), Wt(n);
1026
+ r === void 0 ? t.push(...Ht(n)) : (Ut(n, r), Wt(n));
1038
1027
  }
1039
1028
  }
1040
- //#endregion
1041
- //#region src/cli/commands/convert-html/id-generator.ts
1029
+ // src/cli/commands/convert-html/id-generator.ts
1042
1030
  function Gt() {
1043
1031
  let e = /* @__PURE__ */ new Map();
1044
1032
  return (t) => {
@@ -1046,8 +1034,7 @@ function Gt() {
1046
1034
  return e.set(t, n), `${t}-${n}`;
1047
1035
  };
1048
1036
  }
1049
- //#endregion
1050
- //#region src/components/modules/normalizeInlineImages.ts
1037
+ // src/components/modules/normalizeInlineImages.ts
1051
1038
  var Kt = (e) => {
1052
1039
  let t = document.createElement("template");
1053
1040
  return t.content.append(e), {
@@ -1085,15 +1072,15 @@ var Kt = (e) => {
1085
1072
  18,
1086
1073
  24
1087
1074
  ].map((t) => Jt[e >>> t & 63])).join("");
1088
- }, Z = (e, t, n, r = 0) => {
1075
+ }, Q = (e, t, n, r = 0) => {
1089
1076
  let i = Yt(`${e}:${t}:${r}`);
1090
- return n.has(i) ? Z(e, t, n, r + 1) : (n.add(i), i);
1077
+ return n.has(i) ? Q(e, t, n, r + 1) : (n.add(i), i);
1091
1078
  }, Xt = (e, t, n, r, i) => {
1092
1079
  let a = [], o = r.find((e) => e.kind === "text" && !e.empty);
1093
1080
  for (let [s, c] of r.entries()) {
1094
1081
  if (c.kind === "image") {
1095
1082
  a.push({
1096
- id: Z(t, s, i),
1083
+ id: Q(t, s, i),
1097
1084
  tool: "image",
1098
1085
  data: c.alt !== null && c.alt !== "" ? {
1099
1086
  url: c.url,
@@ -1117,7 +1104,7 @@ var Kt = (e) => {
1117
1104
  }
1118
1105
  let r = {
1119
1106
  ...e,
1120
- id: Z(t, s, i),
1107
+ id: Q(t, s, i),
1121
1108
  data: {
1122
1109
  ...e.data,
1123
1110
  text: c.html
@@ -1181,16 +1168,11 @@ var Kt = (e) => {
1181
1168
  continue;
1182
1169
  }
1183
1170
  let r = e === void 0 ? void 0 : n.get(e);
1184
- if (r !== void 0) {
1185
- a.push(...r.blocks);
1186
- continue;
1187
- }
1188
- a.push(t);
1171
+ r === void 0 ? a.push(t) : a.push(...r.blocks);
1189
1172
  }
1190
1173
  return a;
1191
1174
  };
1192
- //#endregion
1193
- //#region src/cli/commands/convert-html/block-builder.ts
1175
+ // src/cli/commands/convert-html/block-builder.ts
1194
1176
  function Qt(e) {
1195
1177
  let t = Gt(), n = [];
1196
1178
  for (let r of Array.from(e.childNodes)) en(r, n, t);
@@ -1232,15 +1214,12 @@ function en(e, t, n) {
1232
1214
  }
1233
1215
  if (e.nodeType !== Node.ELEMENT_NODE) return;
1234
1216
  let r = e, i = r.tagName;
1235
- if (i === "P") {
1236
- t.push({
1237
- id: n("paragraph"),
1238
- type: "paragraph",
1239
- data: { text: r.innerHTML }
1240
- });
1241
- return;
1242
- }
1243
- if (/^H[1-6]$/.test(i)) {
1217
+ if (i === "P") t.push({
1218
+ id: n("paragraph"),
1219
+ type: "paragraph",
1220
+ data: { text: r.innerHTML }
1221
+ });
1222
+ else if (/^H[1-6]$/.test(i)) {
1244
1223
  let e = Number(i[1]);
1245
1224
  t.push({
1246
1225
  id: n("header"),
@@ -1250,40 +1229,29 @@ function en(e, t, n) {
1250
1229
  level: e
1251
1230
  }
1252
1231
  });
1253
- return;
1254
- }
1255
- if (i === "BLOCKQUOTE") {
1256
- t.push({
1257
- id: n("quote"),
1258
- type: "quote",
1259
- data: {
1260
- text: r.innerHTML,
1261
- size: "default"
1262
- }
1263
- });
1264
- return;
1265
- }
1266
- if (i === "PRE") {
1267
- t.push({
1268
- id: n("code"),
1269
- type: "code",
1270
- data: {
1271
- code: r.textContent ?? "",
1272
- language: "plain-text"
1273
- }
1274
- });
1275
- return;
1276
- }
1277
- if (i === "HR") {
1278
- t.push({
1279
- id: n("divider"),
1280
- type: "divider",
1281
- data: {}
1282
- });
1283
- return;
1284
- }
1285
- if (i === "IMG") {
1286
- let e = r.getAttribute("src") ?? "", i = r.getAttribute("alt") ?? "", a = on(r, "width");
1232
+ } else if (i === "BLOCKQUOTE") t.push({
1233
+ id: n("quote"),
1234
+ type: "quote",
1235
+ data: {
1236
+ text: r.innerHTML,
1237
+ size: "default"
1238
+ }
1239
+ });
1240
+ else if (i === "PRE") t.push({
1241
+ id: n("code"),
1242
+ type: "code",
1243
+ data: {
1244
+ code: r.textContent ?? "",
1245
+ language: "plain-text"
1246
+ }
1247
+ });
1248
+ else if (i === "HR") t.push({
1249
+ id: n("divider"),
1250
+ type: "divider",
1251
+ data: {}
1252
+ });
1253
+ else if (i === "IMG") {
1254
+ let e = r.getAttribute("src") ?? "", i = r.getAttribute("alt") ?? "", a = sn(r, "width");
1287
1255
  t.push({
1288
1256
  id: n("image"),
1289
1257
  type: "image",
@@ -1295,36 +1263,20 @@ function en(e, t, n) {
1295
1263
  key: null,
1296
1264
  width: a
1297
1265
  });
1298
- return;
1299
- }
1300
- if (i === "DETAILS") {
1266
+ } else if (i === "DETAILS") {
1301
1267
  let e = r.querySelector("summary"), i = e ? e.innerHTML : r.innerHTML;
1302
1268
  t.push({
1303
1269
  id: n("toggle"),
1304
1270
  type: "toggle",
1305
1271
  data: { text: i }
1306
1272
  });
1307
- return;
1308
- }
1309
- if (i === "UL" || i === "OL") {
1310
- Q(r, i === "OL" ? "ordered" : "unordered", 0, t, n);
1311
- return;
1312
- }
1313
- if (i === "TABLE") {
1314
- tn(r, t, n);
1315
- return;
1316
- }
1317
- if (i === "ASIDE") {
1318
- nn(r, t, n);
1319
- return;
1320
- }
1321
- t.push({
1273
+ } else i === "UL" || i === "OL" ? tn(r, i === "OL" ? "ordered" : "unordered", 0, t, n) : i === "TABLE" ? nn(r, t, n) : i === "ASIDE" ? rn(r, t, n) : t.push({
1322
1274
  id: n("paragraph"),
1323
1275
  type: "paragraph",
1324
1276
  data: { text: r.innerHTML }
1325
1277
  });
1326
1278
  }
1327
- function Q(e, t, n, r, i) {
1279
+ function tn(e, t, n, r, i) {
1328
1280
  let a = e.getAttribute("start"), o = a ? Number(a) : null, s = Array.from(e.children).filter((e) => e.tagName === "LI");
1329
1281
  for (let [e, a] of s.entries()) {
1330
1282
  let s = a.cloneNode(!0), c = [];
@@ -1341,17 +1293,17 @@ function Q(e, t, n, r, i) {
1341
1293
  start: e === 0 && o !== null ? o : null
1342
1294
  }
1343
1295
  });
1344
- for (let e of c) Q(e, e.tagName === "OL" ? "ordered" : "unordered", n + 1, r, i);
1296
+ for (let e of c) tn(e, e.tagName === "OL" ? "ordered" : "unordered", n + 1, r, i);
1345
1297
  }
1346
1298
  }
1347
- function tn(e, t, n) {
1299
+ function nn(e, t, n) {
1348
1300
  let r = Array.from(e.children).find((e) => e.tagName === "CAPTION")?.innerHTML.trim() ?? "";
1349
1301
  r && t.push({
1350
1302
  id: n("paragraph"),
1351
1303
  type: "paragraph",
1352
1304
  data: { text: r }
1353
1305
  }), Ae(e);
1354
- let i = n("table"), a = k(e), o = dt(a, {
1306
+ let i = n("table"), a = A(e), o = dt(a, {
1355
1307
  cell: (e, t) => ({
1356
1308
  kind: "cell",
1357
1309
  el: e,
@@ -1362,14 +1314,14 @@ function tn(e, t, n) {
1362
1314
  origin: e
1363
1315
  }),
1364
1316
  filler: () => ({ kind: "filler" })
1365
- }), s = J(e), c = o.map((e) => e.map((e) => e.kind === "covered" ? {
1317
+ }), s = Y(e), c = o.map((e) => e.map((e) => e.kind === "covered" ? {
1366
1318
  blocks: [],
1367
1319
  mergedInto: e.origin
1368
1320
  } : e.kind === "filler" ? {
1369
1321
  blocks: [],
1370
1322
  color: null,
1371
1323
  textColor: null
1372
- } : rn(e, s, i, t, n))), l = (e) => e?.tagName === "TH", u = a[0] ? A(a[0]) : [], d = a.some((e) => e.parentElement?.tagName === "THEAD") || u.length > 0 && u.every(l), f = a.length >= 2 && a.every((e) => l(A(e)[0])), p = (o[0] ?? []).map((e) => {
1324
+ } : an(e, s, i, t, n))), l = (e) => e?.tagName === "TH", u = a[0] ? j(a[0]) : [], d = a.some((e) => e.parentElement?.tagName === "THEAD") || u.length > 0 && u.every(l), f = a.length >= 2 && a.every((e) => l(j(e)[0])), p = (o[0] ?? []).map((e) => {
1373
1325
  if (e.kind !== "cell" || e.colspan > 1) return null;
1374
1326
  let t = parseInt($(e.el, "width") ?? "", 10);
1375
1327
  return isNaN(t) ? null : t;
@@ -1385,10 +1337,10 @@ function tn(e, t, n) {
1385
1337
  }, h = t.findIndex((e) => e.parent === i);
1386
1338
  h >= 0 ? t.splice(h, 0, m) : t.push(m);
1387
1339
  }
1388
- function nn(e, t, r) {
1340
+ function rn(e, t, r) {
1389
1341
  let a = r("callout"), o = $(e, "background-color"), s = o && !n(o) && !i(o) ? g(o, "bg") : null, c = [];
1390
1342
  for (let n of Array.from(e.childNodes)) {
1391
- let e = an(n, a, t, r);
1343
+ let e = on(n, a, t, r);
1392
1344
  e && c.push(e);
1393
1345
  }
1394
1346
  let l = t.findIndex((e) => e.parent === a), u = {
@@ -1402,7 +1354,7 @@ function nn(e, t, r) {
1402
1354
  };
1403
1355
  l >= 0 ? t.splice(l, 0, u) : t.push(u);
1404
1356
  }
1405
- function rn(e, t, n, r, i) {
1357
+ function an(e, t, n, r, i) {
1406
1358
  let a = e.el, { color: o, textColor: s, placement: c } = xt(a, t), l = a.innerHTML.trim();
1407
1359
  return {
1408
1360
  blocks: l ? Me(l).map((e) => {
@@ -1421,7 +1373,7 @@ function rn(e, t, n, r, i) {
1421
1373
  ...e.rowspan > 1 ? { rowspan: e.rowspan } : {}
1422
1374
  };
1423
1375
  }
1424
- function an(e, t, n, r) {
1376
+ function on(e, t, n, r) {
1425
1377
  if (e.nodeType === Node.ELEMENT_NODE) {
1426
1378
  let i = e, a = r("paragraph");
1427
1379
  return n.push({
@@ -1444,7 +1396,7 @@ function an(e, t, n, r) {
1444
1396
  }
1445
1397
  return null;
1446
1398
  }
1447
- function on(e, t) {
1399
+ function sn(e, t) {
1448
1400
  let n = $(e, t);
1449
1401
  if (!n) return null;
1450
1402
  let r = parseInt(n, 10);
@@ -1456,5 +1408,4 @@ function $(e, t) {
1456
1408
  let r = RegExp(`(?<![\\-a-z])${t}\\s*:\\s*([^;]+)`).exec(n);
1457
1409
  return r ? r[1].trim() : null;
1458
1410
  }
1459
- //#endregion
1460
- export { G as a, St as i, Vt as n, He as o, Ct as r, w as s, Qt as t };
1411
+ export { K as a, St as i, Vt as n, He as o, Ct as r, T as s, Qt as t };
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as e from "node:fs";
2
- //#region src/cli/commands/migrationContent.ts
2
+ // src/cli/commands/migrationContent.ts
3
3
  var t = "# Migrating from EditorJS to Blok\n\nThis guide covers the breaking changes when migrating from EditorJS to Blok.\n\n## Table of Contents\n\n- [Supported Editor.js Versions](#supported-editorjs-versions)\n- [Core Changes](#core-changes)\n- [Data Attributes](#data-attributes)\n- [CSS Classes](#css-classes)\n- [Bundled Tools](#bundled-tools)\n - [Tool Configuration](#tool-configuration)\n - [Lifecycle Hooks](#lifecycle-hooks)\n - [Delimiter → Divider](#delimiter--divider)\n- [Migrating Custom Tools](#migrating-custom-tools)\n - [Custom Block Tools (port unchanged)](#custom-block-tools-port-unchanged)\n - [Custom Inline Tools (breaking rewrite)](#custom-inline-tools-breaking-rewrite)\n- [Saved Content / Data Migration](#saved-content--data-migration)\n - [Auto-Migrated on Load](#auto-migrated-on-load)\n - [Dropped Fields](#dropped-fields)\n- [Rich Text Is Saved as Segments](#rich-text-is-saved-as-segments)\n - [The Shape](#the-shape)\n - [What Returns Segments](#what-returns-segments)\n - [What Stays HTML](#what-stays-html)\n - [Input Takes Both Shapes](#input-takes-both-shapes)\n - [Custom Block Tools](#custom-block-tools)\n - [Types](#types)\n - [Controlled Components](#controlled-components)\n - [Collaboration](#collaboration)\n - [Converting Stored Documents](#converting-stored-documents)\n- [Configuration Defaults](#configuration-defaults)\n- [New API Methods](#new-api-methods)\n- [DOM Selectors](#dom-selectors)\n- [E2E Test Selectors](#e2e-test-selectors)\n\n---\n\n## Supported Editor.js Versions\n\nBlok targets the **Editor.js 2.x** line. The [codemod](./codemod) is built against the package versions a typical Editor.js 2.x project ships — `@editorjs/editorjs@^2.28.x`, `@editorjs/header@^2.8.x`, `@editorjs/paragraph@^2.11.x`, `@editorjs/list@^1.9.x` — and the runtime auto-migration reads the saved-data shapes those versions produce. Older 2.x data formats are still recognized; the matrix below describes coverage per block/tool, not per Editor.js point release.\n\nThere is no pinned single source version: Blok is API-compatible with the Editor.js 2.x `BlockTool` / `InlineTool` contracts, so what matters is **which tools your content uses**, covered below.\n\n### Compatibility Matrix\n\nHow each Editor.js block/tool is handled. \"Auto-migrated at runtime\" means the conversion happens on `render()` with no script needed; \"Codemod\" means the [codemod](./codemod) rewrites saved JSON; \"Drop-in unchanged\" means the data passes through as-is; \"Not bundled\" means Blok ships no equivalent tool, so you must register your own (a ported Editor.js block tool works unchanged — see [Migrating Custom Tools](#migrating-custom-tools)) or the block renders as a preserved stub with a `console.warn` and round-trips untouched on save.\n\n| Editor.js block / tool | Migration status | Notes |\n|------------------------|------------------|-------|\n| `paragraph` | Drop-in unchanged | Bundled as `Blok.Paragraph` (the default block). |\n| `header` | Drop-in unchanged | Bundled as `Blok.Header`. |\n| `list` (nested **and** string-array items) | Auto-migrated at runtime **+** codemod | Each item expands to a flat `list` block. Both list dialects are read: v1 (`data.start`, `item.checked`) and v2 / nested-list (`data.meta.start`, `item.meta.checked`). `meta.counterType` is dropped (warns) — Blok's ordered lists use decimal markers. |\n| `checklist` (standalone tool) | Auto-migrated at runtime **+** codemod | Becomes `list` blocks with checklist style. |\n| `toggleList` | Auto-migrated at runtime **+** codemod → `toggle` | Becomes a `toggle` block (or a toggle `header` when `titleVariant` is set); body blocks flatten into parented children. |\n| `callout` (legacy `body`/`variant` shape) | Auto-migrated at runtime **+** codemod | Legacy `{ title, body, variant, emoji }` → flat `{ emoji, textColor, backgroundColor }`; a non-empty `title` becomes the first child paragraph, then body blocks flatten into parented children. |\n| `image`, `simple-image` | Auto-migrated at runtime **+** codemod | `file.url` (or `simple-image`'s flat `url`) → `url`; `withBorder` → `frame`, `stretched: true` → `size: full`. `withBackground` is dropped (warns). |\n| `linkTool` | Auto-migrated at runtime **+** codemod → `bookmark` | `meta` flattens onto the bookmark shape; `meta.site_name` is dropped (warns). |\n| `delimiter` | Codemod renames type → `divider` | Data format is identical (`{}`). Also recognized at render, so existing data works without the rename. |\n| `quote` | Auto-migrated at runtime **+** codemod | `caption` becomes a trailing `paragraph` sibling; `alignment` is dropped (warns). The quote text/size render unchanged. |\n| `code` | Drop-in unchanged | Passes through to Blok's bundled `code` tool. |\n| `table` (HTML-string cells) | Auto-migrated at runtime **+** codemod | Each non-empty cell becomes a `{ blocks: [ids] }` cell-ref plus child blocks (`parent` = table id). In the editor and `migrate()` with a DOM, lines and lists in the cell become separate `paragraph`/`list` blocks; the codemod (no DOM) keeps the cell as one `paragraph`; empty cells become `{ blocks: [] }`. `withHeadings` is preserved. Cells already in block-ref shape pass through. |\n| `embed` | Drop-in unchanged | Bundled as `Blok.Embed`. `{ service, source, embed, width, height, caption }` map 1:1; iframe providers render correctly (Blok-only fields default). |\n| `raw` | Auto-migrated at runtime **+** codemod → `code` | Blok has no raw tool; `{ html }` becomes a `code` block showing the markup as source verbatim. |\n| `warning` | Auto-migrated at runtime **+** codemod → `callout` | Becomes a ⚠️ orange `callout`; `title` and `message` become child `paragraph` blocks. |\n| `attaches` | Auto-migrated at runtime **+** codemod → `bookmark` | `file.url` + `title` map to a `bookmark`; file metadata (`name`/`size`/`extension`) is dropped (warns). |\n| `marker`, `inlineCode` (inline) | Not bundled | Register your own (port unchanged or wrap with `wrapLegacyInlineTool`). See [Custom Inline Tools](#custom-inline-tools). |\n| Other third-party block tools (`personality`, `button`, audio, …) | Not bundled | No built-in equivalent. Register a tool to render them; otherwise the block is preserved as a stub (warns) and survives a save/load round-trip. |\n\n> **Heads-up:** any block type Blok doesn't recognize and has no registered tool for renders as a stub and logs `Tool «<name>» is not found`. Its data is preserved, so registering the tool later restores the block.\n\n---\n\n## Core Changes\n\n### Class Name\n\n```diff\n- import EditorJS from '@editorjs/editorjs';\n+ import Blok from '@bloklabs/core';\n\n- const editor = new EditorJS({ ... });\n+ const editor = new Blok({ ... });\n```\n\n`Blok` is also exported as the **default export** and under an `EditorJS` alias, so you can keep your existing variable name and only change the import path — a minimal-diff, drop-in migration:\n\n```diff\n- import EditorJS from '@editorjs/editorjs';\n+ import { EditorJS } from '@bloklabs/core';\n\n const editor = new EditorJS({ ... }); // unchanged\n```\n\n`import Blok`, `import { Blok }`, and `import { EditorJS }` all resolve to the same class.\n\n### Default Holder\n\nThe default holder ID changed from `editorjs` to `blok`:\n\n```diff\n- <div id=\"editorjs\"></div>\n+ <div id=\"blok\"></div>\n```\n\nOr specify explicitly:\n\n```javascript\nconst editor = new Blok({\n holder: 'my-editor', // works the same as before\n});\n```\n\n### TypeScript Types\n\n```diff\n- import type { EditorConfig, OutputData } from '@editorjs/editorjs';\n+ import type { BlokConfig, OutputData } from '@bloklabs/core';\n```\n\n---\n\n## Data Attributes\n\nBlok uses `data-blok-*` attributes instead of EditorJS's mixed naming conventions.\n\n| EditorJS | Blok |\n|----------|------|\n| `data-id` | `data-blok-id` |\n| `data-item-name` | `data-blok-item-name` |\n| `data-empty` | `data-blok-empty` |\n| `.ce-block--selected` (class) | `data-blok-selected=\"true\"` |\n| — | `data-blok-component` (tool name) |\n| — | `data-blok-interface` (element type) |\n| — | `data-blok-testid` (testing) |\n| — | `data-blok-opened` (toolbar state) |\n| — | `data-blok-placeholder` |\n| — | `data-blok-stretched` |\n| — | `data-blok-focused` |\n| — | `data-blok-popover-opened` |\n| — | `data-blok-tool` (tool name on rendered element) |\n| — | `data-blok-dragging` (block being dragged) |\n| — | `data-blok-hidden` (hidden state) |\n| — | `data-blok-rtl` (RTL mode) |\n| — | `data-blok-drag-handle` (drag handle element) |\n| — | `data-blok-overlay` (selection overlay) |\n| — | `data-blok-overlay-rectangle` (selection rectangle) |\n\n### Querying Blocks\n\n```diff\n- document.querySelector('[data-id=\"abc123\"]');\n+ document.querySelector('[data-blok-id=\"abc123\"]');\n\n- document.querySelector('[data-item-name=\"bold\"]');\n+ document.querySelector('[data-blok-item-name=\"bold\"]');\n```\n\n---\n\n## CSS Classes\n\nBlok replaces BEM class names with data attributes for selection.\n\n### Editor Wrapper\n\n| EditorJS | Blok |\n|----------|------|\n| `.codex-editor` | `[data-blok-editor]` |\n| `.codex-editor__redactor` | `[data-blok-redactor]` |\n| `.codex-editor--rtl` | `[data-blok-rtl=\"true\"]` |\n\n### Block Elements\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-block` | `[data-blok-element]` |\n| `.ce-block--selected` | `[data-blok-selected=\"true\"]` |\n| `.ce-block--stretched` | `[data-blok-stretched=\"true\"]` |\n| `.ce-block--focused` | `[data-blok-focused=\"true\"]` |\n| `.ce-block__content` | `[data-blok-element-content]` |\n\n### Toolbar\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-toolbar` | `[data-blok-toolbar]` |\n| `.ce-toolbar__plus` | `[data-blok-testid=\"plus-button\"]` |\n| `.ce-toolbar__settings-btn` | `[data-blok-settings-toggler]` |\n| `.ce-toolbar__actions` | `[data-blok-testid=\"toolbar-actions\"]` |\n| `.ce-toolbox` | `[data-blok-toolbox]` |\n| `.ce-toolbox--opened` | `[data-blok-toolbox][data-blok-opened=\"true\"]` |\n\n### Inline Toolbar\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-inline-toolbar` | `[data-blok-testid=\"inline-toolbar\"]` |\n| `.ce-inline-tool` | `[data-blok-testid=\"inline-tool\"]` |\n| `.ce-inline-tool--link` | `[data-blok-testid=\"inline-tool-link\"]` |\n| `.ce-inline-tool--bold` | `[data-blok-testid=\"inline-tool-bold\"]` |\n| `.ce-inline-tool--italic` | `[data-blok-testid=\"inline-tool-italic\"]` |\n\n### Popover\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-popover` | `[data-blok-popover]` |\n| `.ce-popover--opened` | `[data-blok-popover][data-blok-opened=\"true\"]` |\n| `.ce-popover__container` | `[data-blok-popover-container]` |\n| `.ce-popover-item` | `[data-blok-testid=\"popover-item\"]` |\n| `.ce-popover-item--focused` | `[data-blok-focused=\"true\"]` |\n| `.ce-popover-item--confirmation` | `[data-blok-confirmation=\"true\"]` |\n| `.ce-popover-item__icon` | `[data-blok-testid=\"popover-item-icon\"]` |\n| `.ce-popover-item__icon--tool` | `[data-blok-testid=\"popover-item-icon-tool\"]` |\n\n### Tool-Specific Classes\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-paragraph` | `[data-blok-tool=\"paragraph\"]` |\n| `.ce-header` | `[data-blok-tool=\"header\"]` |\n\n### Conversion Toolbar & Settings\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-conversion-toolbar` | `[data-blok-testid=\"conversion-toolbar\"]` |\n| `.ce-conversion-tool` | `[data-blok-testid=\"conversion-tool\"]` |\n| `.ce-settings` | `[data-blok-testid=\"block-settings\"]` |\n| `.ce-tune` | `[data-blok-testid=\"block-tune\"]` |\n\n### Other Elements\n\n| EditorJS | Blok |\n|----------|------|\n| `.ce-stub` | `[data-blok-stub]` |\n| `.ce-drag-handle` | `[data-blok-drag-handle]` |\n| `.ce-ragged-right` | `[data-blok-ragged-right=\"true\"]` |\n\n### CDX List Classes\n\n| EditorJS | Blok |\n|----------|------|\n| `.cdx-list` | `[data-blok-list]` |\n| `.cdx-list__item` | `[data-blok-list-item]` |\n| `.cdx-list--ordered` | `[data-blok-list=\"ordered\"]` |\n| `.cdx-list--unordered` | `[data-blok-list=\"unordered\"]` |\n\n### CDX Utility Classes\n\n| EditorJS | Blok |\n|----------|------|\n| `.cdx-button` | `[data-blok-button]` |\n| `.cdx-input` | `[data-blok-input]` |\n| `.cdx-loader` | `[data-blok-loader]` |\n| `.cdx-search-field` | `[data-blok-search-field]` |\n\n---\n\n## Bundled Tools\n\nBlok includes Header and Paragraph tools. No external packages needed:\n\n```diff\n- import Header from '@editorjs/header';\n- import Paragraph from '@editorjs/paragraph';\n\n+ import Blok from '@bloklabs/core';\n\nconst editor = new Blok({\n tools: {\n- header: Header,\n- paragraph: Paragraph,\n+ header: Blok.Header,\n+ paragraph: Blok.Paragraph, // optional, it's the default\n },\n});\n```\n\n### Tool Configuration\n\nBoth bundled tools accept configuration options:\n\n#### HeaderConfig\n\n```typescript\nimport type { HeaderConfig } from '@bloklabs/core';\n\nconst editor = new Blok({\n tools: {\n header: {\n class: Blok.Header,\n config: {\n placeholder: 'Enter a heading',\n levels: [2, 3, 4], // Restrict to H2-H4 only\n defaultLevel: 2, // Default to H2\n } as HeaderConfig,\n },\n },\n});\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `placeholder` | `string` | `''` | Placeholder text for empty header |\n| `levels` | `number[]` | `[1,2,3,4,5,6]` | Available heading levels (1-6) |\n| `defaultLevel` | `number` | `2` | Default heading level |\n\n#### ParagraphConfig\n\n```typescript\nimport type { ParagraphConfig } from '@bloklabs/core';\n\nconst editor = new Blok({\n tools: {\n paragraph: {\n class: Blok.Paragraph,\n config: {\n placeholder: 'Start typing...',\n preserveBlank: true,\n } as ParagraphConfig,\n },\n },\n});\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `placeholder` | `string` | `''` | Placeholder text for empty paragraph |\n| `preserveBlank` | `boolean` | `false` | Keep empty paragraphs when saving |\n\n### Delimiter → Divider\n\nEditor.js's `@editorjs/delimiter` is replaced by Blok's built-in `divider` tool:\n\n```diff\n- import Delimiter from '@editorjs/delimiter';\n\nconst editor = new Blok({\n tools: {\n- delimiter: Delimiter,\n+ divider: Blok.Divider,\n },\n});\n```\n\n**Saved data migration:** The block type name changed from `\"delimiter\"` to `\"divider\"`. The data format is identical (empty `{}`):\n\n```diff\n {\n- \"type\": \"delimiter\",\n+ \"type\": \"divider\",\n \"data\": {}\n }\n```\n\n> **Note:** Blok automatically recognizes `\"delimiter\"` blocks and renders them as dividers, so existing articles work without data migration. However, renaming the type in your database is recommended for consistency.\n\n---\n\n## Migrating Custom Tools\n\nIf your project ships its own tools, here's what changes and what doesn't.\n\n### Custom Block Tools (port unchanged)\n\n**Custom block tools work as-is.** Blok's `BlockTool` interface keeps the same shape as EditorJS, read by the **same keys**:\n\n- Methods: `render()`, `save()`, `validate()`, `renderSettings()`, `merge()`, `onPaste()`, and the lifecycle hooks `rendered()`, `updated()`, `removed()`, `moved()`.\n- Statics: `toolbox`, `conversionConfig`, `pasteConfig`, `isReadOnlySupported`, `enableLineBreaks`.\n\nIn most cases you can drop your existing block tool into Blok without touching its code.\n\n#### New optional `setReadOnly(state)` method\n\nBlok adds an **optional** `setReadOnly(state: boolean)` method. It is not a breaking change:\n\n- **If you omit it**, read-only toggling still works — Blok falls back to a full re-render of the block (graceful degradation).\n- **If you implement it**, Blok performs a cheaper in-place toggle instead of re-rendering.\n\n```typescript\nclass MyTool {\n // ...render / save / validate as before...\n\n // Optional: enables the cheaper in-place read-only toggle\n setReadOnly(state: boolean): void {\n this.input.contentEditable = state ? 'false' : 'true';\n }\n}\n```\n\n#### New optional statics\n\nTwo new **optional** statics exist for advanced cases; existing tools never need them:\n\n- `titleKey` — a translation key for the tool's toolbox title (i18n).\n- `provides` — lets one class register multiple block types.\n\n### Custom Inline Tools\n\n**This is the one API difference for custom tools.** EditorJS inline tools built a DOM button and used imperative `surround` / `checkState` / `renderActions` / `clear` methods. Blok's `InlineTool` (`types/tools/inline-tool.d.ts`) instead expects `render()` to return a declarative `MenuConfig` object with `onActivate` / `isActive` callbacks.\n\nYou have two paths:\n\n#### Fast path — wrap with `wrapLegacyInlineTool`\n\nTo keep an existing EditorJS inline tool working without a rewrite, wrap its class:\n\n```typescript\nimport { wrapLegacyInlineTool } from '@bloklabs/core';\nimport MarkerTool from './marker-tool'; // your existing EditorJS inline tool\n\nconst editor = new Blok({\n tools: {\n marker: wrapLegacyInlineTool(MarkerTool),\n },\n});\n```\n\nThe shim adapts the legacy contract automatically: it calls your tool's `render()` for the icon, routes `onActivate` → `surround(range)` and `isActive` → `checkState(selection)`, and forwards the static `title` / `shortcut` / `sanitize`. Without wrapping, Blok silently skips a legacy `render()` that returns an `HTMLElement`, so the tool would vanish from the inline toolbar. `renderActions()` / `clear()` are not bridged.\n\n#### Native path — rewrite to `MenuConfig` (recommended)\n\nFor full access to Blok features (nested items, confirmation), port to the native shape:\n\n#### Before (EditorJS)\n\n```typescript\nclass MarkerTool {\n static get isInline() { return true; }\n static get title() { return 'Marker'; }\n\n private button: HTMLButtonElement;\n\n render(): HTMLElement {\n this.button = document.createElement('button');\n this.button.innerHTML = '<svg>...</svg>';\n return this.button;\n }\n\n // Apply / remove formatting on the current range\n surround(range: Range): void {\n document.execCommand('backColor', false, '#ffd700');\n }\n\n // Reflect active state by inspecting the selection\n checkState(selection: Selection): boolean {\n const isActive = /* ...inspect selection... */;\n this.button.classList.toggle('active', isActive);\n return isActive;\n }\n\n renderActions(): HTMLElement { /* extra UI */ }\n clear(): void { /* reset extra UI */ }\n}\n```\n\n#### After (Blok)\n\n```typescript\nimport type { InlineTool, MenuConfig } from '@bloklabs/core';\n\nclass MarkerTool implements InlineTool {\n static isInline = true;\n static title = 'Marker';\n static titleKey = 'marker'; // optional i18n key\n\n // render() now returns a MenuConfig object, not an HTMLElement\n render(): MenuConfig {\n return {\n icon: '<svg>...</svg>',\n name: 'marker',\n // surround(range) logic moves here\n onActivate: () => {\n document.execCommand('backColor', false, '#ffd700');\n },\n // checkState(selection) logic moves here\n isActive: () => {\n const selection = window.getSelection();\n return selection ? /* ...inspect selection... */ false : false;\n },\n };\n }\n}\n```\n\n> See `src/components/inline-tools/inline-tool-bold.ts` for Blok's canonical inline tool. Its `render()` returns a `MenuConfig` with `onActivate` (apply/remove bold) and `isActive` (read the selection).\n\n#### Mapping reference\n\n| EditorJS | Blok |\n|----------|------|\n| `render()` returns an `HTMLElement` button | `render()` returns a `MenuConfig` `{ icon, name, onActivate, isActive }` |\n| `surround(range)` | logic moves into `onActivate()` |\n| `checkState(selection)` | logic moves into `isActive()` |\n| `renderActions()` / `clear()` | **removed** — no direct equivalent; use `MenuConfig` `children` for nested items |\n| `static get isInline()` / `static get title()` | still apply (plus optional `titleKey` for i18n) |\n\n#### Mark-shaped tools — skip the DOM plumbing entirely (`api.marks`)\n\nIf your inline tool wraps text in one element (a highlight `<span>`, a colour\n`<mark>`, a comment anchor), you no longer need to write any range logic.\nDescribe the wrapper as a `MarkSpec` and drive it through `api.marks`\n(`has` / `find` / `read` / `apply` / `remove` / `toggle`, all range-aware):\n\n```typescript\nclass HighlightTool {\n static isInline = true;\n static title = 'Highlight';\n static sanitize = markSanitizerConfig(HIGHLIGHT); // derived, stays in sync\n\n constructor({ api }) {\n this.api = api;\n }\n\n render() {\n return {\n icon: '<svg>…</svg>',\n name: 'highlight',\n onActivate: () => this.api.marks.toggle(HIGHLIGHT),\n isActive: () => this.api.marks.has(HIGHLIGHT),\n };\n }\n}\n\nconst HIGHLIGHT = { tag: 'span', className: 'my-highlight' };\n```\n\n(React consumers get the same thing as a single spec field:\n`createReactInlineTool({ …, mark: { tag: 'span', className: 'my-highlight' } })`.)\n\n**Behavioural note when porting hand-rolled range code onto `api.marks`.** The\nengine is *more correct* than typical hand-rolled implementations on\nselections that span a wrapper boundary — expect these deltas:\n\nBefore (typical hand-rolled tool):\n\n- A selection covering half a wrapper plus outside text unwrapped or re-wrapped\n the WHOLE wrapper (formatting outside the selection was lost).\n- Toggling \"off\" on a partially covered wrapper removed the whole run.\n- `Ctrl+A` selections silently dropped trailing spaces from the formatted run\n (Chromium/WebKit place the range end before trailing whitespace).\n\nAfter (`api.marks`):\n\n- Partially covered wrappers are split at the selection boundaries; text\n outside the selection keeps its formatting.\n- `apply` and `remove` extend the range over browser-excluded trailing\n whitespace, so the visible selection and the formatted run agree (and\n toggling off never strands a whitespace-only wrapper).\n- Two specs sharing a wrapper family (same tag/classes, different declared\n style property — e.g. text colour and background) compose on ONE element\n instead of nesting or cancelling each other.\n- Legacy tag variants can be declared as the SAME mark via `aliasTags`\n (e.g. `{ tag: 'strong', aliasTags: ['b'] }`): alias-tagged wrappers are\n recognized, split and stripped like canonical ones, while newly created\n wrappers always use `tag`. Blok's own Bold (`strong`+`b`) and Italic\n (`i`+`em`) run on exactly this.\n\n---\n\n## Saved Content / Data Migration\n\nExisting EditorJS output data keeps working. Blok converts legacy block shapes to its own format **automatically when the block renders** — no migration script needed.\n\n### Auto-Migrated on Load\n\nThese EditorJS block types and data shapes are recognized and converted on `render()`:\n\n| EditorJS block / shape | Becomes in Blok |\n|------------------------|-----------------|\n| `list` (nested items **and** string-array items) | Blok `list` |\n| `checklist` (standalone tool) | Blok `list` (checklist style) |\n| `image` `{ file: { url }, withBorder, stretched }` | Blok `image` |\n| `table` (HTML-string cells) | Blok `table` |\n| `header` | Blok `header` |\n| `code` | Blok `code` |\n| `delimiter` | Blok `divider` |\n| `linkTool` | Blok `bookmark` |\n\nNo action is required — load your existing data and it renders correctly.\n\n### Dropped Fields\n\nA few EditorJS fields have no Blok equivalent and are **dropped** on load. The block still renders; only these specific fields are lost:\n\n| Block | Dropped field(s) |\n|-------|------------------|\n| `quote` | `caption`, `alignment` |\n| `image` | `withBackground` |\n| `linkTool` | `meta.site_name` |\n| `list` | `meta.counterType` (ordered-list marker style) |\n\nWhen migration drops one of these, Blok emits a `console.warn` (prefixed `[Blok migration]`) naming the block type and field, so the loss is visible in the console rather than silent. If you depend on any of these, capture them before migrating.\n\nFor an offline batch upgrade, take delivery of that report instead of reading the console — every dropped field arrives as data:\n\n```javascript\nimport { migrate } from '@bloklabs/core/migrate';\n\nconst { data, report } = migrate(storedDocument);\n\nreport.lossyFields; // [{ blockType: 'linkTool', field: 'meta.site_name', verb: 'dropped' }, …]\n```\n\n### Migrating Explicitly (`@bloklabs/core/migrate`)\n\nThe same transform the renderer runs at load is exported, so you can upgrade persisted records without opening an editor:\n\n```javascript\nimport {\n migrate,\n migrateLegacyBlocks,\n migrateLegacyOutputData,\n needsLegacyMigration,\n matchLegacyRule,\n LEGACY_GRAMMAR,\n} from '@bloklabs/core/migrate';\n```\n\nTwo kinds of rule compose here, and `migrate(data, options)` runs them in the one correct order:\n\n1. **Data rules** (`migrations`) rewrite a single block's `data` — old shape → new shape, keyed by block type. They run **first**.\n2. **Grammar rules** (`rules`) restructure the tree: change a block's `type`, split one block into several, absorb following siblings, recurse into container bodies.\n\nThat order is load-bearing, because data rules are keyed by block **type** and the grammar rewrites types (`linkTool` → `bookmark`) and explodes containers into many blocks. Run them the other way round and a rule keyed on a legacy type never fires — the type it named no longer exists — and the block stays silently unmigrated. Practically: data rules shape the grammar's **input** (repair a legacy field so a built-in rule can detect it); the grammar owns the **output** for types it rewrites, so extra fields a data rule adds are not merged into the target shape.\n\n```javascript\nlet n = 0;\nconst { data, report } = migrate(storedDocument, {\n migrations: { myCard: (d) => ('name' in d ? { ...d, title: d.name } : d) },\n rules: [alertRule],\n // Supplying generateId makes the migration PURE: the same document migrates\n // to an equal result every time, so you can compare stored vs. migrated.\n generateId: () => `blk-${n++}`,\n});\n```\n\n#### Teaching it a legacy shape Blok doesn't know\n\nA grammar entry reuses the whole interpreter — container recursion, orphan re-parenting, 1:N splits, id minting — so you never re-implement the dispatch loop. Host entries are matched **before** the built-in table, so they can also override a built-in mapping.\n\n```javascript\nconst alertRule = {\n legacyType: 'alert',\n targetType: 'callout',\n cardinality: '1:N',\n contributesNesting: true,\n lossyFields: [],\n docNote: '`alert` → `callout` + message paragraph.',\n detect: (block) => block.type === 'alert',\n expand: (block, ctx) => {\n const calloutId = block.id ?? ctx.generateId();\n const childId = ctx.generateId();\n\n return [\n { id: calloutId, type: 'callout', data: { emoji: '🚨' }, content: [childId] },\n { id: childId, type: 'paragraph', data: { text: block.data.message }, parent: calloutId },\n ];\n },\n};\n```\n\nFor a legacy container that stores its body as a **count of following siblings** (common in Editor.js-era tools), return `{ blocks, consumed }` — the interpreter skips exactly that many siblings, and `consumed` is clamped to what remains, so a truncated document can't over-consume:\n\n```javascript\nexpand: (block, ctx, { siblings, index }) => {\n const id = block.id ?? ctx.generateId();\n const followers = siblings.slice(index + 1, index + 1 + block.data.items);\n const children = followers.map((child) => ({ ...child, id: child.id ?? ctx.generateId(), parent: id }));\n\n return {\n blocks: [{ id, type: 'toggle', data: { text: block.data.text }, content: children.map((c) => c.id) }, ...children],\n consumed: followers.length,\n };\n}\n```\n\n`matchLegacyRule(block, options?)` returns the entry claiming a single block (or `null`) — use it to dispatch per block instead of re-scanning the table each time. `LEGACY_GRAMMAR` is the built-in table itself, readable for coverage introspection.\n\n---\n\n## Rich Text Is Saved as Segments\n\n**Breaking.** Rich text fields such as `paragraph.text` used to save as an inline HTML string. They now save as an array of runs, called segments. There is no flag to turn this off.\n\n```javascript\nconst data = await editor.save();\n// data.blocks[0].data.text\n//=> [{ text: 'Hello ' }, { text: 'world', marks: { bold: true } }]\n```\n\nThis breaks you if your code reads `text` as a string. That includes server code that reads stored documents, search indexing, and previews. Read on for what to change.\n\n### The Shape\n\nThe types are `RichText`, `RichTextSegment` and `RichTextMarks`, exported from `@bloklabs/core`.\n\n- A text run is `{ text, marks? }`. Line breaks are `\"\\n\"`.\n- `marks` holds `bold`, `italic`, `underline`, `strikethrough`, `code`, `sup`, `sub` and `highlight` (a plain `<mark>`), each `true`.\n- `color` and `background` are separate keys. Each is a Blok preset name such as `'red'`, or any CSS colour.\n- `link` is `{ href, rel?, target? }`.\n- A custom inline tool's tag is stored as `\"tag:<name>\": { <attribute>: <value> }`.\n- An inline object is `{ embed: { equation: { expression } } }`, `{ embed: { page: { id } } }` or `{ embed: { html } }`. The `html` embed keeps markup that has no mark, such as an `<img>`, verbatim.\n\nThese fields become segments: `text` on `paragraph`, `header`, `list`, `toggle` and `quote`. Table cells and plain-text fields such as captions stay strings.\n\n### What Returns Segments\n\nEvery place that hands saved data to your code returns segments:\n\n- `editor.save()`, `onSave` and `onSubmit`.\n- `BlockAPI.save()`, including `target.save()` inside `onChange`.\n- `getBlockData` from `useBlocks` in the React, Vue and Angular adapters. Its `.data` can be a new object on each call, so do not use it as a memo dependency.\n- The value `importMarkdown()` returns.\n- `htmlToBlocks()` from `@bloklabs/core/view` and `markdownToBlocks()` from `@bloklabs/core/markdown`.\n- The C# `FromHtmlAsync` and `FromMarkdownAsync`.\n- Collaboration: what the editor saves, what the server writes to your document endpoint, and `GET /sync/{doc}/state`.\n\nWith `dataModel: 'legacy'` (or `'auto'` with legacy input), the legacy shapes hold segments too: list `items[].content`, `toggleList.title` and the blocks in `body.blocks`. Blok and `@bloklabs/core/view` read them back without loss.\n\n`exportMarkdown()` is unchanged. `@bloklabs/core/view` (`blocksToHtml`, plain text, markdown, outline) reads both shapes in built-in block types. A custom block type's fields reach your renderer as stored. The C# server runtime reads both shapes.\n\n### What Stays HTML\n\n- `BlockAPI.preservedData` and `target.preservedData` in `onChange`. They are an internal snapshot.\n- The database `rowPages` `body` and `acceptedBody` sent to your host. Store and echo them unchanged.\n- `extractTexts()` returns HTML strings. `injectTexts()` writes each field back in the shape it found.\n- Fields of custom block tools that do not declare `static richTextFields`.\n\n### Input Takes Both Shapes\n\nBlok reads HTML and segments on every input path, and it will keep reading HTML. That covers the `data` config, `render()`, `blocks.insert()`, `insertMany()`, `update()`, `splitBlock()` and `convert()`. You do not have to convert stored documents before you upgrade.\n\nAn old document turns into segments the next time the editor saves it. Until then your database holds both shapes. To convert everything at once, see [Converting Stored Documents](#converting-stored-documents).\n\nA mark key Blok does not know, such as `\"acme:x\"`, is dropped on input with one console warning per key. A `tag:<name>` mark survives only if an enabled inline tool allows that tag.\n\n### Custom Block Tools\n\nA custom block tool takes part only when it lists its rich fields in `static richTextFields`:\n\n```javascript\nclass MyCard {\n static richTextFields = ['title'];\n}\n```\n\nWithout it, the tool's fields stay as the tool stores them, on input and on output. A sanitize rule does not count.\n\nA tool that reads another block through `BlockAPI.save()` receives segments. For a `database-row` block, that includes the nested documents in `properties.*.blocks`. Blok's built-in tools handle this. Check any third-party tool that reads other blocks.\n\nThe tool class itself still reads and writes HTML. Its constructor, `merge()`, `validate()` and `save()` see HTML strings. Blok converts at the edge.\n\nIf you run the C# collaboration server, list the same fields in `BlokServerOptions.RichTextFields`, for example `{ \"my-card\": [\"title\"] }`. List exactly what the client declares, or the two write the field in different shapes.\n\n### Types\n\n- `ParagraphData`, `HeaderData`, `ListData`, `ToggleData` and `QuoteData` now type `text` as `RichText`. These are the saved shapes.\n- `ParagraphToolData`, `HeaderToolData`, `ListToolData`, `ToggleToolData` and `QuoteToolData` type `text` as `string`. Use them in a tool's constructor, `merge()`, `validate()` and `save()`.\n- For data you write by hand as input, use the `*ToolData` types or `BlockToolData`. Input accepts HTML and segments.\n\n`isBlockType` and `blocksOfType` narrow to the `RichText` types. That is only true for documents saved by this version, or converted with `migrateToRichText`. Convert an older document before a typed read.\n\n### Controlled Components\n\nFeed the `onSave` output back into the editor, not your original HTML.\n\n- Original HTML re-renders once, because Blok turns it into segments.\n- A raw hex colour and a link without `target` or `rel` also re-render once, because Blok normalises them on render.\n- After that the output is stable and the caret stays put.\n\nAfter a round trip, the HTML spelling is canonical. For example `<em>` becomes `<i>` and `&nbsp;` becomes a plain U+00A0 character. The meaning does not change.\n\n### Collaboration\n\nCollaboration rooms move to format 2, which stores rich text as formatted text.\n\n- Upgrade the client and the server together. An old client gets `unsupported-format`.\n- A format-1 room is converted the first time the new server opens it. Each conversion step gets up to 60 seconds. Building a room from an HTML host record gets the same 60 seconds. If one fails, the server answers \"unavailable\" and waits longer before each new try.\n- The client retries the handshake by itself. If the room is still not ready after about 33 seconds (3 tries of 10 seconds), the session ends with `handshake-timeout`. This can happen on the first open of a very large document. Reload to try again.\n- Offline edits made in format 1 are set aside and never sent.\n- The tab sync protocol changed too, so all tabs need the new version.\n- A third-party collaboration server must speak format 2.\n\nOne limitation is accepted. When two people format overlapping text at the same time, one person's formatting can be lost.\n\n### Converting Stored Documents\n\n`migrateToRichText` converts the HTML fields of a stored document offline. Run it to convert your database in bulk, and before typed reads such as `blocksOfType`:\n\n```javascript\nimport { migrateToRichText, richTextToHtml, richTextToPlainText } from '@bloklabs/core/migrate';\n\nconst converted = migrateToRichText(storedDocument, {\n onLossy: ({ blockId, blockType, field, reason }) => log(blockId, blockType, field, reason),\n});\n\nrichTextToHtml([{ text: 'Hi ' }, { text: 'there', marks: { bold: true } }]);\n//=> 'Hi <strong>there</strong>'\nrichTextToPlainText([{ text: 'Hi ' }, { text: 'there', marks: { bold: true } }]);\n//=> 'Hi there'\n```\n\n- It converts Editor.js shapes too: list items, `toggleList` and callout titles and bodies, quote captions and warnings. You can run `migrate()` before or after it. Table string cells, `raw.html` and `attaches.title` stay strings.\n- It converts only built-in block types. A custom tool's fields are left as they are.\n- Fields that already hold segments pass through, so running it twice is safe.\n- `onLossy` reports `'html-embed'` for markup kept as an `{ embed: { html } }` segment, and `'custom-mark'` for an unknown tag kept as a `tag:<name>` mark.\n- It runs in Node without a DOM. The `@bloklabs/core/migrate` bundle now includes parse5, about 70 KB gzip.\n\nTo display segments outside the editor, use `blocksToHtml` from `@bloklabs/core/view`. It sanitizes its output.\n\n`richTextToHtml` does not sanitize. It is a raw converter. Segments can come from collaborators, the server or a host, so do not put its output into a page as is. `richTextToPlainText` returns plain text.\n\n---\n\n## Configuration Defaults\n\n| Option | EditorJS | Blok |\n|--------|----------|------|\n| `holder` | `\"editorjs\"` | `\"blok\"` |\n| `defaultBlock` | `\"paragraph\"` | `\"paragraph\"` |\n\n---\n\n## New API Methods\n\nBlok exposes shorthand methods directly on the instance:\n\n```javascript\nconst editor = new Blok({ ... });\n\n// Shorthand methods\nawait editor.save(); // Same as editor.saver.save()\neditor.clear(); // Same as editor.blocks.clear()\nawait editor.render(data);// Same as editor.blocks.render(data)\neditor.focus(); // Same as editor.caret.focus()\n\n// Event methods\neditor.on('change', handler); // Same as editor.events.on()\neditor.off('change', handler); // Same as editor.events.off()\neditor.emit('custom', data); // Same as editor.events.emit()\n```\n\n---\n\n## DOM Selectors\n\nBlok uses consistent `data-blok-interface` attributes for major UI components:\n\n```javascript\n// Find the editor wrapper\nconst editorWrapper = document.querySelector('[data-blok-interface=\"blok\"]');\n\n// Find inline toolbar\nconst inlineToolbar = document.querySelector('[data-blok-interface=\"inline-toolbar\"]');\n\n// Find tooltip\nconst tooltip = document.querySelector('[data-blok-interface=\"tooltip\"]');\n```\n\nThese selectors are stable and recommended for programmatic access to Blok's UI components.\n\n### Key Changes\n\n1. **Prefix custom classes** with `blok-` instead of `ce-` or `cdx-`\n2. **Use `data-blok-*`** attributes instead of `data-*`\n\n---\n\n## E2E Test Selectors\n\nUpdate your test selectors to use Blok's `data-blok-testid` attributes:\n\n| EditorJS | Blok |\n|----------|------|\n| `[data-cy=editorjs]` | `[data-blok-testid=\"blok-editor\"]` |\n| `.ce-block` | `[data-blok-element]` |\n| `.ce-block__content` | `[data-blok-element-content]` |\n| `.ce-toolbar` | `[data-blok-toolbar]` |\n| `.ce-toolbar__plus` | `[data-blok-testid=\"plus-button\"]` |\n| `.ce-toolbar__settings-btn` | `[data-blok-settings-toggler]` |\n| `.ce-toolbar__actions` | `[data-blok-testid=\"toolbar-actions\"]` |\n| `.ce-toolbox` | `[data-blok-toolbox]` |\n| `.ce-inline-toolbar` | `[data-blok-testid=\"inline-toolbar\"]` |\n| `.ce-popover` | `[data-blok-popover]` |\n| `.ce-popover-item` | `[data-blok-testid=\"popover-item\"]` |\n| `[data-item-name=\"...\"]` | `[data-blok-item-name=\"...\"]` |\n\n### Playwright Example\n\n```diff\n// EditorJS\n- await page.locator('[data-cy=editorjs] .ce-block').click();\n- await page.locator('.ce-toolbar__settings-btn').click();\n- await page.locator('[data-item-name=\"delete\"]').click();\n\n// Blok\n+ await page.locator('[data-blok-element]').click();\n+ await page.locator('[data-blok-settings-toggler]').click();\n+ await page.locator('[data-blok-item-name=\"delete\"]').click();\n```\n\n### Additional Test Selectors\n\n| Element | Selector |\n|---------|----------|\n| Block tunes popover | `[data-blok-testid=\"block-tunes-popover\"]` |\n| Popover search input | `[data-blok-testid=\"popover-search-input\"]` |\n| Popover item title | `[data-blok-testid=\"popover-item-title\"]` |\n| Popover overlay | `[data-blok-testid=\"popover-overlay\"]` |\n| Inline tool input | `[data-blok-testid=\"inline-tool-input\"]` |\n| Tooltip | `[data-blok-testid=\"tooltip\"]` |\n| Redactor | `[data-blok-testid=\"redactor\"]` |\n| Toolbox | `[data-blok-testid=\"toolbox\"]` |\n| Toolbox popover | `[data-blok-testid=\"toolbox-popover\"]` |\n| Selection overlay | `[data-blok-testid=\"overlay\"]` |\n| Selection rectangle | `[data-blok-testid=\"overlay-rectangle\"]` |\n\n---\n\n### State Selectors\n\n| State | Selector |\n|-------|----------|\n| Toolbar opened | `[data-blok-opened=\"true\"]` |\n| Block selected | `[data-blok-selected=\"true\"]` |\n| Popover opened | `[data-blok-popover-opened=\"true\"]` |\n| Item focused | `[data-blok-focused=\"true\"]` |\n| Item disabled | `[data-blok-disabled=\"true\"]` |\n| Nested popover | `[data-blok-nested=\"true\"]` |\n| Toolbox opened | `[data-blok-toolbox-opened=\"true\"]` |\n| Block dragging | `[data-blok-dragging=\"true\"]` |\n| Element hidden | `[data-blok-hidden=\"true\"]` |\n| RTL mode | `[data-blok-rtl=\"true\"]` |\n\n---\n\n## Quick Checklist\n\n- [ ] Replace `new EditorJS` with `new Blok`\n- [ ] Update imports from `@editorjs/*` to `@bloklabs/core`\n- [ ] Change holder from `editorjs` to `blok` (or specify explicitly)\n- [ ] Use bundled tools: `Blok.Header`, `Blok.Paragraph`\n- [ ] Replace `@editorjs/delimiter` with built-in `Blok.Divider`\n- [ ] Update `EditorConfig` type to `BlokConfig`\n- [ ] Replace `data-id` with `data-blok-id` in queries\n- [ ] Replace `data-item-name` with `data-blok-item-name`\n- [ ] Replace `.ce-*` selectors with `[data-blok-*]` attributes\n- [ ] Replace `.cdx-*` selectors with `[data-blok-*]` attributes\n- [ ] Update E2E test selectors\n", n = (e) => [
4
4
  "# Blok Migration Guide (for LLM-assisted migration)",
5
5
  "",
@@ -43,13 +43,13 @@ var t = "# Migrating from EditorJS to Blok\n\nThis guide covers the breaking cha
43
43
  switch (s) {
44
44
  case "convert-html": {
45
45
  o(new (await (import("jsdom"))).JSDOM("").window);
46
- let { convertHtml: e } = await import("./convert-html-RCkTt7d_.mjs");
46
+ let { convertHtml: e } = await import("./convert-html-BATOFBHq.mjs");
47
47
  r(e((await import("node:fs")).readFileSync(0, "utf-8")), c);
48
48
  break;
49
49
  }
50
50
  case "convert-gdocs": {
51
51
  o(new (await (import("jsdom"))).JSDOM("").window);
52
- let { convertGdocs: e } = await import("./convert-gdocs-ZqRLNT9X.mjs");
52
+ let { convertGdocs: e } = await import("./convert-gdocs-D4ZDpR0c.mjs");
53
53
  r(e((await import("node:fs")).readFileSync(0, "utf-8")), c);
54
54
  break;
55
55
  }
@@ -60,5 +60,4 @@ var t = "# Migrating from EditorJS to Blok\n\nThis guide covers the breaking cha
60
60
  case null: process.stdout.write(i);
61
61
  }
62
62
  };
63
- //#endregion
64
63
  export { s as run };
@@ -1,13 +1,12 @@
1
- import { i as e, n as t, o as n, r, t as i } from "./block-builder-Cg5bL2dS.mjs";
2
- //#region src/cli/commands/convert-gdocs/index.ts
1
+ import { i as e, n as t, o as n, r, t as i } from "./block-builder-DZMbuM0Z.mjs";
2
+ // src/cli/commands/convert-gdocs/index.ts
3
3
  function a(a) {
4
4
  let o = n(e(a)), s = new DOMParser().parseFromString(o, "text/html").body;
5
5
  r(s), t(s);
6
6
  let c = {
7
- version: "1.16.0",
7
+ version: "2.0.0",
8
8
  blocks: i(s)
9
9
  };
10
10
  return JSON.stringify(c);
11
11
  }
12
- //#endregion
13
12
  export { a as convertGdocs };
@@ -0,0 +1,12 @@
1
+ import { i as e, n as t, o as n, r, t as i } from "./block-builder-DZMbuM0Z.mjs";
2
+ // src/cli/commands/convert-gdocs/index.ts
3
+ function a(a) {
4
+ let o = n(e(a)), s = new DOMParser().parseFromString(o, "text/html").body;
5
+ r(s), t(s);
6
+ let c = {
7
+ version: "1.16.1",
8
+ blocks: i(s)
9
+ };
10
+ return JSON.stringify(c);
11
+ }
12
+ export { a as convertGdocs };
@@ -1,13 +1,12 @@
1
- import { a as e, i as t, n, r, s as i, t as a } from "./block-builder-Cg5bL2dS.mjs";
2
- //#region src/cli/commands/convert-html/index.ts
1
+ import { a as e, i as t, n, r, s as i, t as a } from "./block-builder-DZMbuM0Z.mjs";
2
+ // src/cli/commands/convert-html/index.ts
3
3
  function o(o) {
4
4
  let s = new DOMParser().parseFromString(t(o), "text/html").body;
5
5
  s.querySelectorAll("td, th").forEach(e), r(s), n(s), i(s);
6
6
  let c = {
7
- version: "1.16.0",
7
+ version: "2.0.0",
8
8
  blocks: a(s)
9
9
  };
10
10
  return JSON.stringify(c);
11
11
  }
12
- //#endregion
13
12
  export { o as convertHtml };
@@ -0,0 +1,12 @@
1
+ import { a as e, i as t, n, r, s as i, t as a } from "./block-builder-DZMbuM0Z.mjs";
2
+ // src/cli/commands/convert-html/index.ts
3
+ function o(o) {
4
+ let s = new DOMParser().parseFromString(t(o), "text/html").body;
5
+ s.querySelectorAll("td, th").forEach(e), r(s), n(s), i(s);
6
+ let c = {
7
+ version: "1.16.1",
8
+ blocks: a(s)
9
+ };
10
+ return JSON.stringify(c);
11
+ }
12
+ export { o as convertHtml };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bloklabs/cli",
3
- "version": "1.16.0",
3
+ "version": "2.0.0",
4
4
  "description": "CLI tools for Blok — convert HTML to Blok JSON, generate migration guides",
5
5
  "bin": {
6
6
  "blok-cli": "bin/blok-cli.mjs"