@bettercms-ai/kit 2.0.0-next.1 → 2.0.0-next.2

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 (37) hide show
  1. package/dist/adapters/lossy.d.ts +4 -0
  2. package/dist/adapters/lossy.js +8 -2
  3. package/dist/adapters/lossy.js.map +1 -1
  4. package/dist/adapters/schema.js +28 -0
  5. package/dist/adapters/schema.js.map +1 -1
  6. package/dist/controls/rich-text/blocks.d.ts +6 -0
  7. package/dist/controls/rich-text/blocks.js +187 -0
  8. package/dist/controls/rich-text/blocks.js.map +1 -1
  9. package/dist/controls/rich-text/catalog.d.ts +2 -5
  10. package/dist/controls/rich-text/catalog.js +104 -6
  11. package/dist/controls/rich-text/catalog.js.map +1 -1
  12. package/dist/controls/rich-text/codec.d.ts +10 -1
  13. package/dist/controls/rich-text/codec.js +7 -0
  14. package/dist/controls/rich-text/codec.js.map +1 -1
  15. package/dist/controls/rich-text/control.js +1 -0
  16. package/dist/controls/rich-text/control.js.map +1 -1
  17. package/dist/controls/rich-text/dom.d.ts +79 -0
  18. package/dist/controls/rich-text/dom.js +159 -7
  19. package/dist/controls/rich-text/dom.js.map +1 -1
  20. package/dist/controls/rich-text/editor.d.ts +4 -2
  21. package/dist/controls/rich-text/editor.js +93 -3
  22. package/dist/controls/rich-text/editor.js.map +1 -1
  23. package/dist/controls/rich-text/ops.js +6 -0
  24. package/dist/controls/rich-text/ops.js.map +1 -1
  25. package/dist/controls/rich-text/toc.d.ts +3 -2
  26. package/dist/controls/rich-text/toc.js +1 -2
  27. package/dist/controls/rich-text/toc.js.map +1 -1
  28. package/dist/core/core.d.ts +1 -0
  29. package/dist/core/core.js +2 -0
  30. package/dist/core/core.js.map +1 -1
  31. package/dist/core/facets.d.ts +8 -0
  32. package/dist/core/facets.js +8 -0
  33. package/dist/core/facets.js.map +1 -1
  34. package/dist/library/kit-catalog.json +4 -2
  35. package/dist/schema/types.d.ts +8 -0
  36. package/dist/schema/types.js.map +1 -1
  37. package/package.json +2 -2
@@ -59,6 +59,38 @@ var IMAGE_SIZES = [
59
59
  "l",
60
60
  "full"
61
61
  ];
62
+ /**
63
+ * RT9: an image's framing as production writes it on the `<img>` (richtext richTextImageFrame): `data-src` the stored
64
+ * url when the drawn one carries the crop, `data-crop` "top,right,bottom,left" and `data-focal` "x,y", each 0–1.
65
+ */
66
+ function readImageFrame(img) {
67
+ const src = img?.getAttribute("data-src") ?? img?.getAttribute("src") ?? "";
68
+ const nums = (raw, n) => {
69
+ const v = (raw ?? "").split(",").map((x) => Number(x));
70
+ return raw && v.length === n && v.every((x) => Number.isFinite(x) && x >= 0 && x <= 1) ? v : null;
71
+ };
72
+ const c = nums(img?.getAttribute("data-crop"), 4);
73
+ const f = nums(img?.getAttribute("data-focal"), 2);
74
+ return {
75
+ src,
76
+ ...c && c[0] + c[2] < 1 && c[1] + c[3] < 1 ? { crop: {
77
+ top: c[0],
78
+ right: c[1],
79
+ bottom: c[2],
80
+ left: c[3]
81
+ } } : {},
82
+ ...f ? { focal: {
83
+ x: f[0],
84
+ y: f[1]
85
+ } } : {}
86
+ };
87
+ }
88
+ var frameAttrs = (crop, focal) => `${crop ? ` data-crop="${[
89
+ crop.top,
90
+ crop.right,
91
+ crop.bottom,
92
+ crop.left
93
+ ].join(",")}"` : ""}${focal ? ` data-focal="${focal.x},${focal.y}"` : ""}`;
62
94
  var CALLOUT_TONES = [
63
95
  "info",
64
96
  "tip",
@@ -152,7 +184,12 @@ var ISLAND_KINDS = [
152
184
  "video",
153
185
  "embed",
154
186
  "file",
155
- "component"
187
+ "component",
188
+ "gallery",
189
+ "toc",
190
+ "columns",
191
+ "author",
192
+ "related"
156
193
  ];
157
194
  function defaultModel(kind, extra) {
158
195
  const base = {
@@ -183,7 +220,8 @@ function defaultModel(kind, extra) {
183
220
  video: () => ({ url: "" }),
184
221
  embed: () => ({
185
222
  url: "",
186
- title: ""
223
+ title: "",
224
+ labelled: true
187
225
  }),
188
226
  file: () => ({
189
227
  url: "",
@@ -192,6 +230,31 @@ function defaultModel(kind, extra) {
192
230
  component: () => ({
193
231
  id: rid("ci"),
194
232
  overrides: "{}"
233
+ }),
234
+ gallery: () => ({
235
+ layout: "grid",
236
+ images: []
237
+ }),
238
+ toc: () => ({
239
+ title: "In this article",
240
+ depth: "3"
241
+ }),
242
+ columns: () => ({
243
+ gap: "m",
244
+ cols: [{
245
+ id: rid("col"),
246
+ html: ""
247
+ }, {
248
+ id: rid("col"),
249
+ html: ""
250
+ }]
251
+ }),
252
+ author: () => ({ author: "" }),
253
+ related: () => ({
254
+ title: "Related stories",
255
+ mode: "topic",
256
+ count: 3,
257
+ posts: []
195
258
  })
196
259
  };
197
260
  if (kind === "table") return {
@@ -230,7 +293,7 @@ function renderStatic(kind, model) {
230
293
  switch (kind) {
231
294
  case "image": {
232
295
  const m = model;
233
- const img = m.src ? `<img src="${escAttr(m.src)}"${attr("alt", m.alt)}>` : "";
296
+ const img = m.src ? `<img src="${escAttr(m.src)}"${attr("alt", m.alt)}${frameAttrs(m.crop, m.focal)}>` : "";
234
297
  const linked = img && m.href !== void 0 ? `<a href="${escAttr(m.href)}">${img}</a>` : img;
235
298
  const cap = m.caption !== void 0 ? `<figcaption>${esc(m.caption)}</figcaption>` : "";
236
299
  return `<figure data-rt="image"${attr("data-align", m.align)}${attr("data-size", m.size)}${xAttr(m.extra)}>${linked}${cap}</figure>`;
@@ -266,7 +329,29 @@ function renderStatic(kind, model) {
266
329
  }
267
330
  case "embed": {
268
331
  const m = model;
269
- return `<figure data-rt="embed"${attr("data-url", m.url)}${attr("data-title", m.title)}${attr("data-aspect", m.aspect)}${xAttr(m.extra)}>${m.url ? `<a href="${escAttr(m.url)}">${esc(m.title || m.url)}</a>` : ""}</figure>`;
332
+ return `<figure data-rt="embed"${attr("data-url", m.url)}${attr("data-title", m.title)}${m.labelled ? attr("aria-label", m.title ?? "") : ""}${attr("data-aspect", m.aspect)}${xAttr(m.extra)}>${m.url ? `<a href="${escAttr(m.url)}">${esc(m.title || m.url)}</a>` : ""}</figure>`;
333
+ }
334
+ case "gallery": {
335
+ const m = model;
336
+ const items = m.images.map((x) => `<li><figure>${x.src ? `<img src="${escAttr(x.src)}" alt="${escAttr(x.alt ?? "")}"${frameAttrs(x.crop, x.focal)}>` : ""}${x.caption ? `<figcaption>${esc(x.caption)}</figcaption>` : ""}</figure></li>`).join("");
337
+ return `<figure data-rt="gallery"${attr("data-layout", m.layout)}${xAttr(m.extra)}><ul>${items}</ul></figure>`;
338
+ }
339
+ case "toc": {
340
+ const m = model;
341
+ const title = (m.title ?? "").trim();
342
+ return `<nav data-rt="toc"${attr("data-depth", m.depth)} aria-label="${escAttr(title || "Table of contents")}"${xAttr(m.extra)}>${title ? `<p data-title>${esc(title)}</p>` : ""}<ol></ol></nav>`;
343
+ }
344
+ case "columns": {
345
+ const m = model;
346
+ return `<div data-rt="columns"${attr("data-gap", m.gap)} data-cols="${m.cols.length}"${xAttr(m.extra)}>${m.cols.map((c) => `<div data-col="true">${c.html}</div>`).join("")}</div>`;
347
+ }
348
+ case "author": {
349
+ const m = model;
350
+ return `<aside data-rt="author"${attr("data-author", m.author)}${xAttr(m.extra)}></aside>`;
351
+ }
352
+ case "related": {
353
+ const m = model;
354
+ return `<aside data-rt="related"${attr("data-mode", m.mode)}${attr("data-count", m.count)} data-posts="${escAttr(m.posts.join(","))}"${xAttr(m.extra)}>${m.title ? `<p data-title>${esc(m.title)}</p>` : ""}</aside>`;
270
355
  }
271
356
  case "file": {
272
357
  const m = model;
@@ -740,8 +825,11 @@ function parseIsland(kind, el) {
740
825
  const img = el.querySelector("img");
741
826
  const a = img?.parentElement?.tagName === "A" ? img.parentElement : null;
742
827
  const cap = el.querySelector(":scope > figcaption");
743
- const m = { src: img?.getAttribute("src") ?? "" };
828
+ const frame = readImageFrame(img);
829
+ const m = { src: frame.src };
744
830
  if (img?.hasAttribute("alt")) m.alt = img.getAttribute("alt");
831
+ if (frame.crop) m.crop = frame.crop;
832
+ if (frame.focal) m.focal = frame.focal;
745
833
  if (cap) m.caption = (cap.textContent ?? "").replace(/\u200b/g, "");
746
834
  const align = oneOf(IMAGE_ALIGNS, attrOf(el, "data-align"));
747
835
  const size = oneOf(IMAGE_SIZES, attrOf(el, "data-size"));
@@ -815,11 +903,75 @@ function parseIsland(kind, el) {
815
903
  case "embed": {
816
904
  const m = {};
817
905
  const url = attrOf(el, "data-url");
818
- const title = attrOf(el, "data-title");
906
+ const title = attrOf(el, "data-title") ?? attrOf(el, "aria-label");
819
907
  const aspect = attrOf(el, "data-aspect");
820
908
  if (url !== void 0) m.url = url;
821
909
  if (title !== void 0) m.title = title;
822
910
  if (aspect !== void 0 && EMBED_ASPECT_VALUES.includes(aspect)) m.aspect = aspect;
911
+ if (el.hasAttribute("aria-label")) m.labelled = true;
912
+ const extra = attrOf(el, "data-extra");
913
+ if (extra) m.extra = extra;
914
+ return m;
915
+ }
916
+ case "gallery": {
917
+ const m = { images: [] };
918
+ const layout = attrOf(el, "data-layout");
919
+ if (layout === "grid" || layout === "carousel") m.layout = layout;
920
+ m.images = Array.from(el.querySelectorAll("li")).map((li) => {
921
+ const img = li.querySelector("img");
922
+ const caption = li.querySelector("figcaption")?.textContent ?? void 0;
923
+ const frame = readImageFrame(img);
924
+ return {
925
+ _key: rid("g"),
926
+ src: frame.src,
927
+ ...img ? { alt: img.getAttribute("alt") ?? "" } : {},
928
+ ...caption ? { caption } : {},
929
+ ...frame.crop ? { crop: frame.crop } : {},
930
+ ...frame.focal ? { focal: frame.focal } : {}
931
+ };
932
+ });
933
+ const extra = attrOf(el, "data-extra");
934
+ if (extra) m.extra = extra;
935
+ return m;
936
+ }
937
+ case "toc": {
938
+ const m = {};
939
+ const title = el.querySelector("[data-title]")?.textContent?.trim();
940
+ if (title) m.title = title;
941
+ const depth = attrOf(el, "data-depth");
942
+ if (depth === "2" || depth === "3") m.depth = depth;
943
+ const extra = attrOf(el, "data-extra");
944
+ if (extra) m.extra = extra;
945
+ return m;
946
+ }
947
+ case "columns": {
948
+ const m = { cols: Array.from(el.children).filter((c) => c.hasAttribute("data-col")).map((c) => ({
949
+ id: rid("col"),
950
+ html: c.innerHTML
951
+ })) };
952
+ const gap = attrOf(el, "data-gap");
953
+ if (gap === "s" || gap === "m" || gap === "l") m.gap = gap;
954
+ const extra = attrOf(el, "data-extra");
955
+ if (extra) m.extra = extra;
956
+ return m;
957
+ }
958
+ case "author": {
959
+ const m = {};
960
+ const author = attrOf(el, "data-author");
961
+ if (author !== void 0) m.author = author;
962
+ const extra = attrOf(el, "data-extra");
963
+ if (extra) m.extra = extra;
964
+ return m;
965
+ }
966
+ case "related": {
967
+ const m = { posts: (attrOf(el, "data-posts") ?? "").split(",").map((x) => x.trim()).filter(Boolean) };
968
+ const title = (attrOf(el, "data-title") ?? el.querySelector("[data-title]")?.textContent ?? "").trim();
969
+ if (title) m.title = title;
970
+ const mode = attrOf(el, "data-mode");
971
+ if (mode === "topic" || mode === "newest" || mode === "manual") m.mode = mode;
972
+ else if (mode === "auto") m.mode = "topic";
973
+ const count = Number(attrOf(el, "data-count"));
974
+ if (Number.isInteger(count) && count > 0) m.count = count;
823
975
  const extra = attrOf(el, "data-extra");
824
976
  if (extra) m.extra = extra;
825
977
  return m;
@@ -1023,6 +1175,6 @@ function rangeRect(r, fallback) {
1023
1175
  }
1024
1176
  var isEmptyHtml = (html) => !html.replace(/<(?!img|hr|figure|table|details|div data-rt)[^>]*>/g, "").replace(/&nbsp;/g, "").trim();
1025
1177
  //#endregion
1026
- export { CALLOUT_TONES, IMAGE_ALIGNS, IMAGE_SIZES, ISLAND_KINDS, STORED_COLUMN_TYPES, TEXT_BLOCKS, cloneModel, closestBlock, colAttrs, colSettingsOf, columnSettings, createIsland, defaultModel, detectEmbed, detectKind, editingHost, editorRoundTrip, esc, escAttr, hydrate, isEmptyHtml, isValueType, parasToText, parseIsland, parseStoredTable, placeCaret, rangeRect, renderStatic, renderStoredTable, rid, sanitize, serialize, textBeforeCaret, textToParas, topBlock, typeAttrOf, typedCellView, typedValueOf, typedWords };
1178
+ export { CALLOUT_TONES, IMAGE_ALIGNS, IMAGE_SIZES, ISLAND_KINDS, STORED_COLUMN_TYPES, TEXT_BLOCKS, cloneModel, closestBlock, colAttrs, colSettingsOf, columnSettings, createIsland, defaultModel, detectEmbed, detectKind, editingHost, editorRoundTrip, esc, escAttr, hydrate, isEmptyHtml, isValueType, parasToText, parseIsland, parseStoredTable, placeCaret, rangeRect, readImageFrame, renderStatic, renderStoredTable, rid, sanitize, serialize, textBeforeCaret, textToParas, topBlock, typeAttrOf, typedCellView, typedValueOf, typedWords };
1027
1179
 
1028
1180
  //# sourceMappingURL=dom.js.map