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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/adapters/lossy.d.ts +5 -1
  2. package/dist/adapters/lossy.js +10 -5
  3. package/dist/adapters/lossy.js.map +1 -1
  4. package/dist/adapters/prod-types.d.ts +9 -22
  5. package/dist/adapters/prod-types.js +36 -35
  6. package/dist/adapters/prod-types.js.map +1 -1
  7. package/dist/adapters/schema.js +56 -39
  8. package/dist/adapters/schema.js.map +1 -1
  9. package/dist/context.d.ts +39 -0
  10. package/dist/context.js +40 -1
  11. package/dist/context.js.map +1 -1
  12. package/dist/controls/link.js +112 -196
  13. package/dist/controls/link.js.map +1 -1
  14. package/dist/controls/location.js +3 -8
  15. package/dist/controls/location.js.map +1 -1
  16. package/dist/controls/media.d.ts +0 -14
  17. package/dist/controls/media.js +52 -95
  18. package/dist/controls/media.js.map +1 -1
  19. package/dist/controls/rich-text/article-shell.d.ts +145 -0
  20. package/dist/controls/rich-text/article-shell.js +510 -0
  21. package/dist/controls/rich-text/article-shell.js.map +1 -0
  22. package/dist/controls/rich-text/author.d.ts +25 -0
  23. package/dist/controls/rich-text/author.js +104 -0
  24. package/dist/controls/rich-text/author.js.map +1 -0
  25. package/dist/controls/rich-text/blocks.d.ts +13 -2
  26. package/dist/controls/rich-text/blocks.js +86 -638
  27. package/dist/controls/rich-text/blocks.js.map +1 -1
  28. package/dist/controls/rich-text/button.d.ts +23 -0
  29. package/dist/controls/rich-text/button.js +85 -0
  30. package/dist/controls/rich-text/button.js.map +1 -0
  31. package/dist/controls/rich-text/catalog.d.ts +3 -3
  32. package/dist/controls/rich-text/catalog.js +20 -4
  33. package/dist/controls/rich-text/catalog.js.map +1 -1
  34. package/dist/controls/rich-text/checklist.d.ts +24 -0
  35. package/dist/controls/rich-text/checklist.js +115 -0
  36. package/dist/controls/rich-text/checklist.js.map +1 -0
  37. package/dist/controls/rich-text/codec.d.ts +1 -1
  38. package/dist/controls/rich-text/codec.js +4 -1
  39. package/dist/controls/rich-text/codec.js.map +1 -1
  40. package/dist/controls/rich-text/columns.d.ts +41 -0
  41. package/dist/controls/rich-text/columns.js +181 -0
  42. package/dist/controls/rich-text/columns.js.map +1 -0
  43. package/dist/controls/rich-text/dom.d.ts +5 -4
  44. package/dist/controls/rich-text/dom.js +13 -4
  45. package/dist/controls/rich-text/dom.js.map +1 -1
  46. package/dist/controls/rich-text/editor.js +92 -10
  47. package/dist/controls/rich-text/editor.js.map +1 -1
  48. package/dist/controls/rich-text/embed.d.ts +28 -0
  49. package/dist/controls/rich-text/embed.js +210 -0
  50. package/dist/controls/rich-text/embed.js.map +1 -0
  51. package/dist/controls/rich-text/footnote.d.ts +41 -0
  52. package/dist/controls/rich-text/footnote.js +185 -0
  53. package/dist/controls/rich-text/footnote.js.map +1 -0
  54. package/dist/controls/rich-text/gallery.d.ts +28 -0
  55. package/dist/controls/rich-text/gallery.js +167 -0
  56. package/dist/controls/rich-text/gallery.js.map +1 -0
  57. package/dist/controls/rich-text/link-form.d.ts +3 -1
  58. package/dist/controls/rich-text/link-form.js +2 -2
  59. package/dist/controls/rich-text/link-form.js.map +1 -1
  60. package/dist/controls/rich-text/related.d.ts +36 -0
  61. package/dist/controls/rich-text/related.js +213 -0
  62. package/dist/controls/rich-text/related.js.map +1 -0
  63. package/dist/controls/rich-text/toc.d.ts +46 -0
  64. package/dist/controls/rich-text/toc.js +161 -0
  65. package/dist/controls/rich-text/toc.js.map +1 -0
  66. package/dist/core/core.d.ts +10 -3
  67. package/dist/core/core.js +22 -6
  68. package/dist/core/core.js.map +1 -1
  69. package/dist/core/facets.d.ts +8 -0
  70. package/dist/core/facets.js +9 -1
  71. package/dist/core/facets.js.map +1 -1
  72. package/dist/core/index.js +2 -2
  73. package/dist/index.js +4 -4
  74. package/dist/library/kit-catalog.json +45 -44
  75. package/dist/schema/types.d.ts +7 -0
  76. package/dist/schema/types.js.map +1 -1
  77. package/dist/styles.css +1 -1
  78. package/dist/ui/autocomplete.d.ts +29 -1
  79. package/dist/ui/autocomplete.js +102 -47
  80. package/dist/ui/autocomplete.js.map +1 -1
  81. package/dist/ui/button.js +3 -5
  82. package/dist/ui/button.js.map +1 -1
  83. package/dist/ui/index.js +2 -2
  84. package/dist/ui/toast.js +1 -1
  85. package/package.json +3 -1
@@ -12,9 +12,11 @@ import { LinkForm } from "./link-form.js";
12
12
  import { CatalogComponentsProvider, InsertMenuContent, SlashMenuPanel, blockAllowed, catalogKey, noteRecent, useCatalog } from "./catalog.js";
13
13
  import { openComponentPanel } from "./component-panel.js";
14
14
  import { Island } from "./blocks.js";
15
+ import { duplicateBlock, ensureLineAfter, insertBasic, insertIsland, liveInlineCode, liveMarkdown, moveBlock, restrictHtml, turnInto } from "./ops.js";
16
+ import { ButtonLinkPanel, applyButton, buttonAt, buttonInitial, insertButton, removeButton } from "./button.js";
17
+ import { checklistShortcut, insertChecklist, syncTaskA11y, toggleTaskAtPointer, toggleTaskFromKey } from "./checklist.js";
15
18
  import { ALL_FEATURES } from "./codec.js";
16
19
  import { htmlToMarkdown, looksLikeMarkdown, markdownToHtml } from "./markdown.js";
17
- import { duplicateBlock, ensureLineAfter, insertBasic, insertIsland, liveInlineCode, liveMarkdown, moveBlock, restrictHtml, turnInto } from "./ops.js";
18
20
  import { EMPTY_FMT, FormatToolbar, MARK_FEATURE, stylesFor } from "./toolbar.js";
19
21
  import { SelectionBubble } from "./floating.js";
20
22
  import * as React from "react";
@@ -112,11 +114,23 @@ function insertHtmlAtCaret(html) {
112
114
  s.addRange(r);
113
115
  }
114
116
  }
115
- var NOT_COUNTED = "[data-rt=component],[data-rt=pullquote],[data-rt=takeaways],[data-rt=embed],[data-rt=video],[data-rt=file]";
117
+ var NOT_COUNTED = [
118
+ "[data-rt=component]",
119
+ "[data-rt=pullquote]",
120
+ "[data-rt=takeaways]",
121
+ "[data-rt=toc]",
122
+ "[data-rt=related]",
123
+ "[data-rt=author]",
124
+ "[data-rt=embed]",
125
+ "[data-rt=video]",
126
+ "[data-rt=file]",
127
+ "[data-rt=footnotes]",
128
+ "sup[data-rt=fn]"
129
+ ].join(",");
116
130
  var WORDS = (s) => s.replace(/\u200b/g, "").trim().split(/\s+/).filter(Boolean).length;
117
131
  function countWords(html) {
118
132
  if (!html) return 0;
119
- if (!/data-rt="(?:component|pullquote|takeaways|embed|video|file)"/.test(html) || typeof DOMParser === "undefined") return WORDS(html.replace(/<\/(p|li|h[1-6]|blockquote|pre|figcaption|td|th|summary|div)>|<br\s*\/?>/g, " ").replace(/<[^>]+>/g, "").replace(/&nbsp;|&[a-z]+;/g, " "));
133
+ if (!/data-rt="(?:component|pullquote|takeaways|toc|related|author|embed|video|file|footnotes|fn)"/.test(html) || typeof DOMParser === "undefined") return WORDS(html.replace(/<[^>]+>/g, " ").replace(/&nbsp;|&[a-z]+;/g, " "));
120
134
  const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
121
135
  doc.body.querySelectorAll(NOT_COUNTED).forEach((n) => n.remove());
122
136
  doc.body.querySelectorAll("p,li,h1,h2,h3,h4,h5,h6,td,th,figcaption,summary,pre,blockquote,div").forEach((n) => n.append(" "));
@@ -128,7 +142,7 @@ var countComponents = (html) => (html.match(/data-rt="component"/g) ?? []).lengt
128
142
  var bodyCountLabel = (html) => {
129
143
  const w = countWords(html);
130
144
  const c = countComponents(html);
131
- return `${w.toLocaleString("en-US")} ${w === 1 ? "word" : "words"}${c ? ` · ${c} ${c === 1 ? "block" : "blocks"}` : ""}`;
145
+ return `${w.toLocaleString("en-US")} ${w === 1 ? "word" : "words"}${c ? ` · ${c} ${c === 1 ? "component" : "components"}` : ""}`;
132
146
  };
133
147
  function insertTextAtCaret(text) {
134
148
  if (exec("insertText", text)) return;
@@ -273,6 +287,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
273
287
  if (!el) return;
274
288
  el.innerHTML = sanitize(html || "") || "<p><br></p>";
275
289
  hydrate(el);
290
+ syncTaskA11y(el);
276
291
  known.current = html;
277
292
  setEmpty(isEmptyHtml(html));
278
293
  setWords(countWords(html));
@@ -283,6 +298,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
283
298
  const el = ref.current;
284
299
  if (!el) return;
285
300
  syncIslands();
301
+ syncTaskA11y(el);
286
302
  const html = serialize(el);
287
303
  setEmpty(isEmptyHtml(html) && !el.querySelector(":scope > [data-rt-island], :scope > hr"));
288
304
  setWords(countWords(html));
@@ -568,7 +584,14 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
568
584
  }
569
585
  if (!it.id || !blockAllowed(it.id, allows)) return;
570
586
  noteRecent(it);
571
- insertBasic(el, it.id);
587
+ if (it.id === "task") insertChecklist(el);
588
+ else if (it.id === "button") {
589
+ const a = insertButton(el);
590
+ emit();
591
+ refresh();
592
+ openButton(a);
593
+ return;
594
+ } else insertBasic(el, it.id);
572
595
  emit();
573
596
  refresh();
574
597
  };
@@ -645,6 +668,11 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
645
668
  }
646
669
  const mod = e.metaKey || e.ctrlKey;
647
670
  const k = eventKey(e);
671
+ if (!mod && !e.altKey && !readOnly && ref.current && toggleTaskFromKey(ref.current, e.key)) {
672
+ e.preventDefault();
673
+ emit();
674
+ return;
675
+ }
648
676
  if (mod && !e.altKey && k === "k") {
649
677
  e.preventDefault();
650
678
  saveSel();
@@ -750,6 +778,11 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
750
778
  hist.current.textAt = Date.now();
751
779
  hist.current.future = [];
752
780
  }
781
+ if (!single && ne.inputType === "insertText" && allows("checklist") && checklistShortcut(ref.current, ne.data)) {
782
+ setSlash(null);
783
+ emit();
784
+ return;
785
+ }
753
786
  if (!single && ne.inputType === "insertText" && (ne.data === " " || ne.data === "-") && liveMarkdown(ref.current, ne.data, allows)) {
754
787
  setSlash(null);
755
788
  emit();
@@ -789,6 +822,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
789
822
  restoreSel();
790
823
  const s = window.getSelection();
791
824
  const anchor = (s?.anchorNode?.nodeType === 1 ? s.anchorNode : s?.anchorNode?.parentElement)?.closest("a") ?? null;
825
+ if (anchor && ref.current && buttonAt(ref.current, anchor)) return openButton(anchor);
792
826
  if (!anchor && (!s || s.isCollapsed || !s.toString().trim())) {
793
827
  notify("Select some text first", { description: hints ? "Then press ⌘K to link it." : "Then link it." });
794
828
  return;
@@ -804,6 +838,28 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
804
838
  } : { href: "" }
805
839
  });
806
840
  };
841
+ /** V2 rich-text-editor.tsx:865-873: a button's popover, its label above the link form */
842
+ const openButton = (a) => {
843
+ if (readOnly) return;
844
+ const { link: initial, label } = buttonInitial(a);
845
+ setBubble(null);
846
+ setLink({
847
+ rect: a.getBoundingClientRect(),
848
+ existing: a,
849
+ initial,
850
+ button: {
851
+ a,
852
+ label
853
+ }
854
+ });
855
+ };
856
+ const saveButton = (v, label) => {
857
+ const b = link?.button;
858
+ setLink(null);
859
+ if (!b) return;
860
+ applyButton(b.a, v, label);
861
+ emit();
862
+ };
807
863
  const saveLink = (v) => {
808
864
  const l = link;
809
865
  setLink(null);
@@ -1072,7 +1128,8 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
1072
1128
  moveBlock: moveBlock$1,
1073
1129
  islandUndo,
1074
1130
  islandRedo,
1075
- startDrag
1131
+ startDrag,
1132
+ allows
1076
1133
  });
1077
1134
  api.current = {
1078
1135
  emit,
@@ -1085,7 +1142,8 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
1085
1142
  moveBlock: moveBlock$1,
1086
1143
  islandUndo,
1087
1144
  islandRedo,
1088
- startDrag
1145
+ startDrag,
1146
+ allows
1089
1147
  };
1090
1148
  const islandCtx = React.useMemo(() => ({
1091
1149
  readOnly,
@@ -1100,7 +1158,8 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
1100
1158
  move: (x, dir) => api.current.moveBlock(x, dir),
1101
1159
  undo: () => api.current.islandUndo(),
1102
1160
  redo: () => api.current.islandRedo(),
1103
- drag: (x, e) => api.current.startDrag(x, e)
1161
+ drag: (x, e) => api.current.startDrag(x, e),
1162
+ allows: (f) => api.current.allows(f)
1104
1163
  }), [readOnly, dense]);
1105
1164
  const commentSel = onComment ? () => {
1106
1165
  restoreSel();
@@ -1305,6 +1364,17 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
1305
1364
  onKeyDown,
1306
1365
  onKeyUp: refresh,
1307
1366
  onMouseUp: refresh,
1367
+ onMouseDown: (e) => {
1368
+ if (readOnly || !ref.current || !toggleTaskAtPointer(ref.current, e.target, e.clientX)) return;
1369
+ e.preventDefault();
1370
+ emit();
1371
+ },
1372
+ onClick: (e) => {
1373
+ const a = ref.current ? buttonAt(ref.current, e.target) : null;
1374
+ if (!a || readOnly) return;
1375
+ e.preventDefault();
1376
+ openButton(a);
1377
+ },
1308
1378
  onPaste: (e) => {
1309
1379
  if (readOnly) return;
1310
1380
  if (Array.from(e.clipboardData.files).filter((f) => f.type.startsWith("image/")).length) {
@@ -1506,7 +1576,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
1506
1576
  words.toLocaleString("en-US"),
1507
1577
  " ",
1508
1578
  words === 1 ? "word" : "words",
1509
- comps ? ` · ${comps} ${comps === 1 ? "block" : "blocks"}` : "",
1579
+ comps ? ` · ${comps} ${comps === 1 ? "component" : "components"}` : "",
1510
1580
  " · ",
1511
1581
  Math.max(1, Math.round(words / 220)),
1512
1582
  " min read"
@@ -1579,7 +1649,19 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
1579
1649
  }) }), /* @__PURE__ */ jsx(PopoverContent, {
1580
1650
  className: "w-[340px] rounded-kit-lg p-3",
1581
1651
  finalFocus: false,
1582
- children: link ? /* @__PURE__ */ jsx("div", {
1652
+ children: link?.button ? /* @__PURE__ */ jsx(ButtonLinkPanel, {
1653
+ initial: link.initial,
1654
+ label: link.button.label,
1655
+ allowNewTab,
1656
+ onSave: saveButton,
1657
+ onCancel: () => setLink(null),
1658
+ onDetach: () => {
1659
+ const a = link.button.a;
1660
+ setLink(null);
1661
+ if (ref.current) removeButton(ref.current, a);
1662
+ emit();
1663
+ }
1664
+ }) : link ? /* @__PURE__ */ jsx("div", {
1583
1665
  className: "flex flex-col gap-2",
1584
1666
  children: /* @__PURE__ */ jsx(LinkForm, {
1585
1667
  initial: link.initial,