@bettercms-ai/kit 2.0.0 → 2.0.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.
- package/dist/adapters/lossy.d.ts +14 -6
- package/dist/adapters/lossy.js +23 -11
- package/dist/adapters/lossy.js.map +1 -1
- package/dist/adapters/prod-types.d.ts +46 -14
- package/dist/adapters/prod-types.js +37 -35
- package/dist/adapters/prod-types.js.map +1 -1
- package/dist/adapters/schema.d.ts +39 -0
- package/dist/adapters/schema.js +281 -82
- package/dist/adapters/schema.js.map +1 -1
- package/dist/context.d.ts +39 -0
- package/dist/context.js +40 -1
- package/dist/context.js.map +1 -1
- package/dist/controls/form-picker-register.d.ts +7 -1
- package/dist/controls/form-picker-register.js +1 -5
- package/dist/controls/form-picker-register.js.map +1 -1
- package/dist/controls/icon.d.ts +1 -1
- package/dist/controls/icon.js +1 -1
- package/dist/controls/icon.js.map +1 -1
- package/dist/controls/link.d.ts +11 -4
- package/dist/controls/link.js +120 -202
- package/dist/controls/link.js.map +1 -1
- package/dist/controls/location.js +3 -8
- package/dist/controls/location.js.map +1 -1
- package/dist/controls/media.d.ts +0 -14
- package/dist/controls/media.js +52 -95
- package/dist/controls/media.js.map +1 -1
- package/dist/controls/reference-services.d.ts +23 -2
- package/dist/controls/reference.d.ts +2 -2
- package/dist/controls/reference.js +79 -28
- package/dist/controls/reference.js.map +1 -1
- package/dist/controls/rich-text/article-shell.d.ts +145 -0
- package/dist/controls/rich-text/article-shell.js +514 -0
- package/dist/controls/rich-text/article-shell.js.map +1 -0
- package/dist/controls/rich-text/author.d.ts +25 -0
- package/dist/controls/rich-text/author.js +107 -0
- package/dist/controls/rich-text/author.js.map +1 -0
- package/dist/controls/rich-text/blocks.d.ts +19 -2
- package/dist/controls/rich-text/blocks.js +275 -638
- package/dist/controls/rich-text/blocks.js.map +1 -1
- package/dist/controls/rich-text/button.d.ts +23 -0
- package/dist/controls/rich-text/button.js +81 -0
- package/dist/controls/rich-text/button.js.map +1 -0
- package/dist/controls/rich-text/catalog.d.ts +2 -5
- package/dist/controls/rich-text/catalog.js +121 -7
- package/dist/controls/rich-text/catalog.js.map +1 -1
- package/dist/controls/rich-text/checklist.d.ts +24 -0
- package/dist/controls/rich-text/checklist.js +115 -0
- package/dist/controls/rich-text/checklist.js.map +1 -0
- package/dist/controls/rich-text/codec.d.ts +15 -1
- package/dist/controls/rich-text/codec.js +11 -1
- package/dist/controls/rich-text/codec.js.map +1 -1
- package/dist/controls/rich-text/columns.d.ts +41 -0
- package/dist/controls/rich-text/columns.js +181 -0
- package/dist/controls/rich-text/columns.js.map +1 -0
- package/dist/controls/rich-text/control.js +1 -0
- package/dist/controls/rich-text/control.js.map +1 -1
- package/dist/controls/rich-text/dom.d.ts +84 -4
- package/dist/controls/rich-text/dom.js +171 -10
- package/dist/controls/rich-text/dom.js.map +1 -1
- package/dist/controls/rich-text/editor.d.ts +4 -2
- package/dist/controls/rich-text/editor.js +199 -26
- package/dist/controls/rich-text/editor.js.map +1 -1
- package/dist/controls/rich-text/embed.d.ts +28 -0
- package/dist/controls/rich-text/embed.js +210 -0
- package/dist/controls/rich-text/embed.js.map +1 -0
- package/dist/controls/rich-text/footnote.d.ts +41 -0
- package/dist/controls/rich-text/footnote.js +185 -0
- package/dist/controls/rich-text/footnote.js.map +1 -0
- package/dist/controls/rich-text/gallery.d.ts +28 -0
- package/dist/controls/rich-text/gallery.js +167 -0
- package/dist/controls/rich-text/gallery.js.map +1 -0
- package/dist/controls/rich-text/link-form.d.ts +13 -1
- package/dist/controls/rich-text/link-form.js +41 -7
- package/dist/controls/rich-text/link-form.js.map +1 -1
- package/dist/controls/rich-text/ops.js +6 -0
- package/dist/controls/rich-text/ops.js.map +1 -1
- package/dist/controls/rich-text/related.d.ts +49 -0
- package/dist/controls/rich-text/related.js +233 -0
- package/dist/controls/rich-text/related.js.map +1 -0
- package/dist/controls/rich-text/toc.d.ts +47 -0
- package/dist/controls/rich-text/toc.js +160 -0
- package/dist/controls/rich-text/toc.js.map +1 -0
- package/dist/controls/slot-register.d.ts +6 -1
- package/dist/controls/slot-register.js +1 -4
- package/dist/controls/slot-register.js.map +1 -1
- package/dist/core/core.d.ts +11 -3
- package/dist/core/core.js +24 -6
- package/dist/core/core.js.map +1 -1
- package/dist/core/facets.d.ts +16 -0
- package/dist/core/facets.js +17 -1
- package/dist/core/facets.js.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/ledgers/exclusions.v1.js +141 -0
- package/dist/ledgers/exclusions.v1.js.map +1 -0
- package/dist/library/coverage.d.ts +13 -1
- package/dist/library/coverage.js +12 -1
- package/dist/library/coverage.js.map +1 -1
- package/dist/library/kit-catalog.json +158 -154
- package/dist/schema/types.d.ts +17 -0
- package/dist/schema/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing/coverage-extras.d.ts +8 -0
- package/dist/testing/coverage-extras.js +44 -0
- package/dist/testing/coverage-extras.js.map +1 -0
- package/dist/testing/exclusions.d.ts +30 -0
- package/dist/testing/exclusions.js +13 -0
- package/dist/testing/exclusions.js.map +1 -0
- package/dist/ui/autocomplete.d.ts +29 -1
- package/dist/ui/autocomplete.js +102 -47
- package/dist/ui/autocomplete.js.map +1 -1
- package/dist/ui/button.js +3 -5
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/overlay.d.ts +4 -1
- package/dist/ui/overlay.js +2 -1
- package/dist/ui/overlay.js.map +1 -1
- package/dist/ui/toast.js +1 -1
- package/package.json +8 -2
|
@@ -4,19 +4,24 @@ import { Button } from "../../ui/button.js";
|
|
|
4
4
|
import { shortcutHintsOn } from "../../ui/shortcut-hint.js";
|
|
5
5
|
import { Menu, MenuContent, MenuItem, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Tooltip } from "../../ui/overlay.js";
|
|
6
6
|
import { Textarea } from "../../ui/input.js";
|
|
7
|
+
import { LinkPopoverBody } from "../link.js";
|
|
7
8
|
import { ExpandedEditorModal } from "../../ui/expanded-editor-modal.js";
|
|
8
9
|
import { COMPONENT_INSERT_TOAST } from "../component-services.js";
|
|
9
10
|
import { eventKey } from "../../lib/key-event.js";
|
|
10
11
|
import { closestBlock, createIsland, defaultModel, editingHost, esc, hydrate, isEmptyHtml, placeCaret, rangeRect, rid, sanitize, serialize, textBeforeCaret, topBlock } from "./dom.js";
|
|
11
|
-
import {
|
|
12
|
+
import { applyTextLink, fromLinkValue, textLinkOf, toLinkValue } from "./link-form.js";
|
|
13
|
+
import { createOutlineStore, outlineOf } from "./toc.js";
|
|
12
14
|
import { CatalogComponentsProvider, InsertMenuContent, SlashMenuPanel, blockAllowed, catalogKey, noteRecent, useCatalog } from "./catalog.js";
|
|
13
15
|
import { openComponentPanel } from "./component-panel.js";
|
|
14
16
|
import { Island } from "./blocks.js";
|
|
17
|
+
import { duplicateBlock, ensureLineAfter, insertBasic, insertIsland, liveInlineCode, liveMarkdown, moveBlock, restrictHtml, turnInto } from "./ops.js";
|
|
18
|
+
import { ButtonLinkPanel, applyButton, buttonAt, buttonInitial, insertButton, removeButton } from "./button.js";
|
|
19
|
+
import { checklistShortcut, insertChecklist, syncTaskA11y, toggleTaskAtPointer, toggleTaskFromKey } from "./checklist.js";
|
|
15
20
|
import { ALL_FEATURES } from "./codec.js";
|
|
16
21
|
import { htmlToMarkdown, looksLikeMarkdown, markdownToHtml } from "./markdown.js";
|
|
17
|
-
import { duplicateBlock, ensureLineAfter, insertBasic, insertIsland, liveInlineCode, liveMarkdown, moveBlock, restrictHtml, turnInto } from "./ops.js";
|
|
18
22
|
import { EMPTY_FMT, FormatToolbar, MARK_FEATURE, stylesFor } from "./toolbar.js";
|
|
19
23
|
import { SelectionBubble } from "./floating.js";
|
|
24
|
+
import { FN_MARK, FOOTNOTE_POPOVER_CLASS, FOOTNOTE_REFUSED, FootnoteEditor, FootnotesList, backToMarker, caretAfterMarker, insertFootnote, renumberFootnotes } from "./footnote.js";
|
|
20
25
|
import * as React from "react";
|
|
21
26
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
22
27
|
import { IconArrowBarToDown, IconArrowBarToUp, IconCopy, IconGripVertical, IconPlus, IconTransform, IconTrash } from "@tabler/icons-react";
|
|
@@ -112,11 +117,23 @@ function insertHtmlAtCaret(html) {
|
|
|
112
117
|
s.addRange(r);
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
|
-
var NOT_COUNTED =
|
|
120
|
+
var NOT_COUNTED = [
|
|
121
|
+
"[data-rt=component]",
|
|
122
|
+
"[data-rt=pullquote]",
|
|
123
|
+
"[data-rt=takeaways]",
|
|
124
|
+
"[data-rt=toc]",
|
|
125
|
+
"[data-rt=related]",
|
|
126
|
+
"[data-rt=author]",
|
|
127
|
+
"[data-rt=embed]",
|
|
128
|
+
"[data-rt=video]",
|
|
129
|
+
"[data-rt=file]",
|
|
130
|
+
"[data-rt=footnotes]",
|
|
131
|
+
"sup[data-rt=fn]"
|
|
132
|
+
].join(",");
|
|
116
133
|
var WORDS = (s) => s.replace(/\u200b/g, "").trim().split(/\s+/).filter(Boolean).length;
|
|
117
134
|
function countWords(html) {
|
|
118
135
|
if (!html) return 0;
|
|
119
|
-
if (!/data-rt="(?:component|pullquote|takeaways|embed|video|file)"/.test(html) || typeof DOMParser === "undefined") return WORDS(html.replace(
|
|
136
|
+
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(/ |&[a-z]+;/g, " "));
|
|
120
137
|
const doc = new DOMParser().parseFromString(`<body>${html}</body>`, "text/html");
|
|
121
138
|
doc.body.querySelectorAll(NOT_COUNTED).forEach((n) => n.remove());
|
|
122
139
|
doc.body.querySelectorAll("p,li,h1,h2,h3,h4,h5,h6,td,th,figcaption,summary,pre,blockquote,div").forEach((n) => n.append(" "));
|
|
@@ -128,7 +145,7 @@ var countComponents = (html) => (html.match(/data-rt="component"/g) ?? []).lengt
|
|
|
128
145
|
var bodyCountLabel = (html) => {
|
|
129
146
|
const w = countWords(html);
|
|
130
147
|
const c = countComponents(html);
|
|
131
|
-
return `${w.toLocaleString("en-US")} ${w === 1 ? "word" : "words"}${c ? ` · ${c} ${c === 1 ? "
|
|
148
|
+
return `${w.toLocaleString("en-US")} ${w === 1 ? "word" : "words"}${c ? ` · ${c} ${c === 1 ? "component" : "components"}` : ""}`;
|
|
132
149
|
};
|
|
133
150
|
function insertTextAtCaret(text) {
|
|
134
151
|
if (exec("insertText", text)) return;
|
|
@@ -161,7 +178,7 @@ function slashPlacement(caret, viewportH) {
|
|
|
161
178
|
maxHeight: Math.max(SLASH_MIN_H, Math.min(SLASH_MENU_H, flipUp ? above : below))
|
|
162
179
|
};
|
|
163
180
|
}
|
|
164
|
-
function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, features = ALL_FEATURES, single, allowNewTab = true, id, invalid, variant = "document", expandCrumbs, expandedOpen, onExpandedOpenChange, inModal, hideFullscreen, maxHeight, autoFocus, onComment, components }) {
|
|
181
|
+
function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, features = ALL_FEATURES, references, single, allowNewTab = true, id, invalid, variant = "document", expandCrumbs, expandedOpen, onExpandedOpenChange, inModal, hideFullscreen, maxHeight, autoFocus, onComment, components }) {
|
|
165
182
|
const hints = shortcutHintsOn();
|
|
166
183
|
const ph = placeholder ?? (hints ? "Type '/' for commands" : "Write something…");
|
|
167
184
|
const dense = variant === "panel";
|
|
@@ -249,6 +266,11 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
249
266
|
const t = window.setTimeout(() => setNotice(null), 8e3);
|
|
250
267
|
return () => window.clearTimeout(t);
|
|
251
268
|
}, [notice]);
|
|
269
|
+
/** V2 article-blocks.tsx:45-66: one live outline per editor, so every table of contents follows the headings */
|
|
270
|
+
const outline = React.useRef(createOutlineStore());
|
|
271
|
+
/** V2 rich-text-editor.tsx:271-276: the body's markers in text order, and the one whose note is open */
|
|
272
|
+
const [footnotes, setFootnotes] = React.useState([]);
|
|
273
|
+
const [fnEdit, setFnEdit] = React.useState(null);
|
|
252
274
|
const syncIslands = React.useCallback(() => {
|
|
253
275
|
const el = ref.current;
|
|
254
276
|
if (!el) return setIslands((p) => p.length ? [] : p);
|
|
@@ -272,7 +294,12 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
272
294
|
const el = ref.current;
|
|
273
295
|
if (!el) return;
|
|
274
296
|
el.innerHTML = sanitize(html || "") || "<p><br></p>";
|
|
297
|
+
el.querySelectorAll(":scope > ol[data-rt=\"footnotes\"]").forEach((n) => n.remove());
|
|
298
|
+
el.querySelectorAll(FN_MARK).forEach((m) => m.setAttribute("contenteditable", "false"));
|
|
275
299
|
hydrate(el);
|
|
300
|
+
syncTaskA11y(el);
|
|
301
|
+
setFootnotes(renumberFootnotes(el));
|
|
302
|
+
outline.current.set(outlineOf(el));
|
|
276
303
|
known.current = html;
|
|
277
304
|
setEmpty(isEmptyHtml(html));
|
|
278
305
|
setWords(countWords(html));
|
|
@@ -283,6 +310,9 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
283
310
|
const el = ref.current;
|
|
284
311
|
if (!el) return;
|
|
285
312
|
syncIslands();
|
|
313
|
+
syncTaskA11y(el);
|
|
314
|
+
outline.current.set(outlineOf(el));
|
|
315
|
+
setFootnotes(renumberFootnotes(el));
|
|
286
316
|
const html = serialize(el);
|
|
287
317
|
setEmpty(isEmptyHtml(html) && !el.querySelector(":scope > [data-rt-island], :scope > hr"));
|
|
288
318
|
setWords(countWords(html));
|
|
@@ -568,7 +598,25 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
568
598
|
}
|
|
569
599
|
if (!it.id || !blockAllowed(it.id, allows)) return;
|
|
570
600
|
noteRecent(it);
|
|
571
|
-
|
|
601
|
+
if (it.id === "footnote") {
|
|
602
|
+
const m = insertFootnote(el);
|
|
603
|
+
if (!m) return void notify(FOOTNOTE_REFUSED.title, { description: FOOTNOTE_REFUSED.description });
|
|
604
|
+
emit();
|
|
605
|
+
openFootnote(m, true);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (it.id === "task") insertChecklist(el);
|
|
609
|
+
else if (it.id === "button") {
|
|
610
|
+
const a = insertButton(el);
|
|
611
|
+
emit();
|
|
612
|
+
refresh();
|
|
613
|
+
openButton(a);
|
|
614
|
+
return;
|
|
615
|
+
} else if (it.id === "related" && references?.topic === false) insertIsland(el, "related", {
|
|
616
|
+
...defaultModel("related"),
|
|
617
|
+
mode: "newest"
|
|
618
|
+
});
|
|
619
|
+
else insertBasic(el, it.id);
|
|
572
620
|
emit();
|
|
573
621
|
refresh();
|
|
574
622
|
};
|
|
@@ -645,6 +693,11 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
645
693
|
}
|
|
646
694
|
const mod = e.metaKey || e.ctrlKey;
|
|
647
695
|
const k = eventKey(e);
|
|
696
|
+
if (!mod && !e.altKey && !readOnly && ref.current && toggleTaskFromKey(ref.current, e.key)) {
|
|
697
|
+
e.preventDefault();
|
|
698
|
+
emit();
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
648
701
|
if (mod && !e.altKey && k === "k") {
|
|
649
702
|
e.preventDefault();
|
|
650
703
|
saveSel();
|
|
@@ -750,6 +803,11 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
750
803
|
hist.current.textAt = Date.now();
|
|
751
804
|
hist.current.future = [];
|
|
752
805
|
}
|
|
806
|
+
if (!single && ne.inputType === "insertText" && allows("checklist") && checklistShortcut(ref.current, ne.data)) {
|
|
807
|
+
setSlash(null);
|
|
808
|
+
emit();
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
753
811
|
if (!single && ne.inputType === "insertText" && (ne.data === " " || ne.data === "-") && liveMarkdown(ref.current, ne.data, allows)) {
|
|
754
812
|
setSlash(null);
|
|
755
813
|
emit();
|
|
@@ -789,6 +847,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
789
847
|
restoreSel();
|
|
790
848
|
const s = window.getSelection();
|
|
791
849
|
const anchor = (s?.anchorNode?.nodeType === 1 ? s.anchorNode : s?.anchorNode?.parentElement)?.closest("a") ?? null;
|
|
850
|
+
if (anchor && ref.current && buttonAt(ref.current, anchor)) return openButton(anchor);
|
|
792
851
|
if (!anchor && (!s || s.isCollapsed || !s.toString().trim())) {
|
|
793
852
|
notify("Select some text first", { description: hints ? "Then press ⌘K to link it." : "Then link it." });
|
|
794
853
|
return;
|
|
@@ -798,26 +857,66 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
798
857
|
setLink({
|
|
799
858
|
rect,
|
|
800
859
|
existing: anchor,
|
|
801
|
-
initial: anchor ? {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
860
|
+
initial: anchor ? textLinkOf(anchor) : { href: "" }
|
|
861
|
+
});
|
|
862
|
+
};
|
|
863
|
+
const openFootnote = (m, fresh = false) => {
|
|
864
|
+
if (readOnly) return;
|
|
865
|
+
setBubble(null);
|
|
866
|
+
setFnEdit({
|
|
867
|
+
el: m,
|
|
868
|
+
rect: m.getBoundingClientRect(),
|
|
869
|
+
fresh
|
|
870
|
+
});
|
|
871
|
+
};
|
|
872
|
+
const removeFootnote = (m) => {
|
|
873
|
+
setFnEdit(null);
|
|
874
|
+
m.remove();
|
|
875
|
+
emit();
|
|
876
|
+
};
|
|
877
|
+
/** closed: a new footnote with no note is taken back out (V2), else the caret goes back after its marker */
|
|
878
|
+
const closeFootnote = () => {
|
|
879
|
+
const f = fnEdit;
|
|
880
|
+
setFnEdit(null);
|
|
881
|
+
if (!f || !ref.current) return;
|
|
882
|
+
if (f.fresh && !(f.el.getAttribute("data-note") ?? "").trim()) return removeFootnote(f.el);
|
|
883
|
+
caretAfterMarker(ref.current, f.el);
|
|
884
|
+
};
|
|
885
|
+
/** V2 rich-text-editor.tsx:865-873: a button's popover, its label above the link form */
|
|
886
|
+
const openButton = (a) => {
|
|
887
|
+
if (readOnly) return;
|
|
888
|
+
const { link: initial, label } = buttonInitial(a);
|
|
889
|
+
setBubble(null);
|
|
890
|
+
setLink({
|
|
891
|
+
rect: a.getBoundingClientRect(),
|
|
892
|
+
existing: a,
|
|
893
|
+
initial,
|
|
894
|
+
button: {
|
|
895
|
+
a,
|
|
896
|
+
label
|
|
897
|
+
}
|
|
805
898
|
});
|
|
806
899
|
};
|
|
900
|
+
const saveButton = (v, label) => {
|
|
901
|
+
const b = link?.button;
|
|
902
|
+
setLink(null);
|
|
903
|
+
if (!b) return;
|
|
904
|
+
applyButton(b.a, v, label);
|
|
905
|
+
emit();
|
|
906
|
+
};
|
|
807
907
|
const saveLink = (v) => {
|
|
808
908
|
const l = link;
|
|
809
909
|
setLink(null);
|
|
810
910
|
if (!l) return;
|
|
811
911
|
if (l.existing) {
|
|
812
|
-
l.existing
|
|
813
|
-
if (v.newTab) l.existing.setAttribute("target", "_blank");
|
|
814
|
-
else l.existing.removeAttribute("target");
|
|
912
|
+
applyTextLink(l.existing, v);
|
|
815
913
|
emit();
|
|
816
914
|
return;
|
|
817
915
|
}
|
|
818
916
|
run(() => {
|
|
819
917
|
const text = window.getSelection()?.toString() || v.href;
|
|
820
|
-
|
|
918
|
+
const attr = (s) => esc(s).replace(/"/g, """);
|
|
919
|
+
insertHtmlAtCaret(`<a href="${attr(v.href)}"${v.newTab ? ` target="_blank"` : ""}${v.rel ? ` rel="${attr(v.rel)}"` : ""}>${esc(text)}</a>`);
|
|
821
920
|
});
|
|
822
921
|
};
|
|
823
922
|
const aiRewrite = (preset) => {
|
|
@@ -1072,7 +1171,8 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1072
1171
|
moveBlock: moveBlock$1,
|
|
1073
1172
|
islandUndo,
|
|
1074
1173
|
islandRedo,
|
|
1075
|
-
startDrag
|
|
1174
|
+
startDrag,
|
|
1175
|
+
allows
|
|
1076
1176
|
});
|
|
1077
1177
|
api.current = {
|
|
1078
1178
|
emit,
|
|
@@ -1085,7 +1185,8 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1085
1185
|
moveBlock: moveBlock$1,
|
|
1086
1186
|
islandUndo,
|
|
1087
1187
|
islandRedo,
|
|
1088
|
-
startDrag
|
|
1188
|
+
startDrag,
|
|
1189
|
+
allows
|
|
1089
1190
|
};
|
|
1090
1191
|
const islandCtx = React.useMemo(() => ({
|
|
1091
1192
|
readOnly,
|
|
@@ -1100,8 +1201,15 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1100
1201
|
move: (x, dir) => api.current.moveBlock(x, dir),
|
|
1101
1202
|
undo: () => api.current.islandUndo(),
|
|
1102
1203
|
redo: () => api.current.islandRedo(),
|
|
1103
|
-
drag: (x, e) => api.current.startDrag(x, e)
|
|
1104
|
-
|
|
1204
|
+
drag: (x, e) => api.current.startDrag(x, e),
|
|
1205
|
+
allows: (f) => api.current.allows(f),
|
|
1206
|
+
outline: outline.current,
|
|
1207
|
+
references
|
|
1208
|
+
}), [
|
|
1209
|
+
readOnly,
|
|
1210
|
+
dense,
|
|
1211
|
+
references
|
|
1212
|
+
]);
|
|
1105
1213
|
const commentSel = onComment ? () => {
|
|
1106
1214
|
restoreSel();
|
|
1107
1215
|
const q = window.getSelection()?.toString().trim();
|
|
@@ -1305,6 +1413,22 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1305
1413
|
onKeyDown,
|
|
1306
1414
|
onKeyUp: refresh,
|
|
1307
1415
|
onMouseUp: refresh,
|
|
1416
|
+
onMouseDown: (e) => {
|
|
1417
|
+
if (readOnly || !ref.current || !toggleTaskAtPointer(ref.current, e.target, e.clientX)) return;
|
|
1418
|
+
e.preventDefault();
|
|
1419
|
+
emit();
|
|
1420
|
+
},
|
|
1421
|
+
onClick: (e) => {
|
|
1422
|
+
const fn = e.target.closest?.(FN_MARK);
|
|
1423
|
+
if (fn && ref.current?.contains(fn) && !fn.closest("[data-rt-island]") && !readOnly) {
|
|
1424
|
+
e.preventDefault();
|
|
1425
|
+
return openFootnote(fn);
|
|
1426
|
+
}
|
|
1427
|
+
const a = ref.current ? buttonAt(ref.current, e.target) : null;
|
|
1428
|
+
if (!a || readOnly) return;
|
|
1429
|
+
e.preventDefault();
|
|
1430
|
+
openButton(a);
|
|
1431
|
+
},
|
|
1308
1432
|
onPaste: (e) => {
|
|
1309
1433
|
if (readOnly) return;
|
|
1310
1434
|
if (Array.from(e.clipboardData.files).filter((f) => f.type.startsWith("image/")).length) {
|
|
@@ -1352,7 +1476,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1352
1476
|
notify("That drop is not an image from the media library");
|
|
1353
1477
|
}
|
|
1354
1478
|
},
|
|
1355
|
-
className: cn(RT_PROSE, dense && "text-kit-sm leading-[1.6] [&_h1:not([data-rt-isolate]_*)]:text-kit-xl [&_h1:not([data-rt-isolate]_*)]:leading-7 [&_h2:not([data-rt-isolate]_*)]:text-kit-lg [&_h2:not([data-rt-isolate]_*)]:leading-7 [&_h3:not([data-rt-isolate]_*)]:text-kit-md [&_h4:not([data-rt-isolate]_*)]:text-kit-base [&_h5:not([data-rt-isolate]_*)]:text-kit-sm [&_h6:not([data-rt-isolate]_*)]:text-kit-xs", "peer relative outline-none focus-visible:shadow-none [&_*:not([data-rt-isolate]_*)]:outline-none", "[&>p[data-hint]]:before:pointer-events-none [&>p[data-hint]]:before:float-left [&>p[data-hint]]:before:h-0 [&>p[data-hint]]:before:text-kit-fg-4 [&>p[data-hint]]:before:content-[attr(data-hint)]", "[&_a[data-button]:not([data-rt-isolate]_*)]:cursor-pointer [&_ul[data-task]>li:not([data-rt-isolate]_*)]:before:cursor-pointer", "[&_sup[data-rt=fn]]:mx-px [&_sup[data-rt=fn]]:cursor-pointer [&_sup[data-rt=fn]]:rounded-kit-sm [&_sup[data-rt=fn]]:px-0.5 [&_sup[data-rt=fn]]:text-[0.72em] [&_sup[data-rt=fn]]:font-semibold [&_sup[data-rt=fn]]:text-kit-accent-fg [&_sup[data-rt=fn]:hover]:bg-kit-accent-soft", dense ? "px-2.5 py-2" : "px-4 py-3.5", minH)
|
|
1479
|
+
className: cn(RT_PROSE, dense && "text-kit-sm leading-[1.6] [&_h1:not([data-rt-isolate]_*)]:text-kit-xl [&_h1:not([data-rt-isolate]_*)]:leading-7 [&_h2:not([data-rt-isolate]_*)]:text-kit-lg [&_h2:not([data-rt-isolate]_*)]:leading-7 [&_h3:not([data-rt-isolate]_*)]:text-kit-md [&_h4:not([data-rt-isolate]_*)]:text-kit-base [&_h5:not([data-rt-isolate]_*)]:text-kit-sm [&_h6:not([data-rt-isolate]_*)]:text-kit-xs", "peer relative outline-none focus-visible:shadow-none [&_*:not([data-rt-isolate]_*)]:outline-none", "[&>p[data-hint]]:before:pointer-events-none [&>p[data-hint]]:before:float-left [&>p[data-hint]]:before:h-0 [&>p[data-hint]]:before:text-kit-fg-4 [&>p[data-hint]]:before:content-[attr(data-hint)]", "[&_a[data-button]:not([data-rt-isolate]_*)]:cursor-pointer [&_ul[data-task]>li:not([data-rt-isolate]_*)]:before:cursor-pointer", "[&_sup[data-rt=fn]]:mx-px [&_sup[data-rt=fn]]:cursor-pointer [&_sup[data-rt=fn]]:rounded-kit-sm [&_sup[data-rt=fn]]:px-0.5 [&_sup[data-rt=fn]]:text-[0.72em] [&_sup[data-rt=fn]]:leading-[0] [&_sup[data-rt=fn]]:font-semibold [&_sup[data-rt=fn]]:text-kit-accent-fg [&_sup[data-rt=fn]:hover]:bg-kit-accent-soft", dense ? "px-2.5 py-2" : "px-4 py-3.5", minH)
|
|
1356
1480
|
}),
|
|
1357
1481
|
empty ? /* @__PURE__ */ jsx("div", {
|
|
1358
1482
|
"aria-hidden": true,
|
|
@@ -1481,6 +1605,13 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1481
1605
|
}), el, key))
|
|
1482
1606
|
]
|
|
1483
1607
|
}),
|
|
1608
|
+
/* @__PURE__ */ jsx(FootnotesList, {
|
|
1609
|
+
notes: footnotes,
|
|
1610
|
+
readOnly,
|
|
1611
|
+
density: dense ? "dense" : "default",
|
|
1612
|
+
onEdit: (m) => openFootnote(m),
|
|
1613
|
+
onBack: (m) => ref.current && backToMarker(ref.current, m)
|
|
1614
|
+
}),
|
|
1484
1615
|
/* @__PURE__ */ jsxs("div", {
|
|
1485
1616
|
className: "flex h-7 items-center justify-end gap-3 rounded-b-kit-lg border-t border-kit-line-subtle px-3 text-kit-2xs text-kit-fg-3",
|
|
1486
1617
|
children: [notice ? /* @__PURE__ */ jsxs("span", {
|
|
@@ -1506,7 +1637,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1506
1637
|
words.toLocaleString("en-US"),
|
|
1507
1638
|
" ",
|
|
1508
1639
|
words === 1 ? "word" : "words",
|
|
1509
|
-
comps ? ` · ${comps} ${comps === 1 ? "
|
|
1640
|
+
comps ? ` · ${comps} ${comps === 1 ? "component" : "components"}` : "",
|
|
1510
1641
|
" · ",
|
|
1511
1642
|
Math.max(1, Math.round(words / 220)),
|
|
1512
1643
|
" min read"
|
|
@@ -1562,6 +1693,35 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1562
1693
|
maxHeight: slashMaxH
|
|
1563
1694
|
})
|
|
1564
1695
|
})] }), document.body) : null,
|
|
1696
|
+
/* @__PURE__ */ jsxs(Popover, {
|
|
1697
|
+
open: !!fnEdit,
|
|
1698
|
+
onOpenChange: (o) => !o && closeFootnote(),
|
|
1699
|
+
children: [/* @__PURE__ */ jsx(PopoverAnchor, { render: /* @__PURE__ */ jsx("span", {
|
|
1700
|
+
className: "pointer-events-none fixed",
|
|
1701
|
+
style: fnEdit ? {
|
|
1702
|
+
top: fnEdit.rect.bottom,
|
|
1703
|
+
left: fnEdit.rect.left,
|
|
1704
|
+
width: Math.max(1, fnEdit.rect.width),
|
|
1705
|
+
height: 1
|
|
1706
|
+
} : {
|
|
1707
|
+
top: 0,
|
|
1708
|
+
left: 0
|
|
1709
|
+
}
|
|
1710
|
+
}) }), /* @__PURE__ */ jsx(PopoverContent, {
|
|
1711
|
+
className: FOOTNOTE_POPOVER_CLASS,
|
|
1712
|
+
finalFocus: false,
|
|
1713
|
+
children: fnEdit ? /* @__PURE__ */ jsx(FootnoteEditor, {
|
|
1714
|
+
n: footnotes.findIndex((f) => f.el === fnEdit.el) + 1,
|
|
1715
|
+
note: fnEdit.el.getAttribute("data-note") ?? "",
|
|
1716
|
+
onChange: (v) => {
|
|
1717
|
+
fnEdit.el.setAttribute("data-note", v);
|
|
1718
|
+
emit();
|
|
1719
|
+
},
|
|
1720
|
+
onRemove: () => removeFootnote(fnEdit.el),
|
|
1721
|
+
onDone: closeFootnote
|
|
1722
|
+
}, footnotes.findIndex((f) => f.el === fnEdit.el)) : null
|
|
1723
|
+
})]
|
|
1724
|
+
}),
|
|
1565
1725
|
/* @__PURE__ */ jsxs(Popover, {
|
|
1566
1726
|
open: !!link,
|
|
1567
1727
|
onOpenChange: (o) => !o && setLink(null),
|
|
@@ -1579,12 +1739,24 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1579
1739
|
}) }), /* @__PURE__ */ jsx(PopoverContent, {
|
|
1580
1740
|
className: "w-[340px] rounded-kit-lg p-3",
|
|
1581
1741
|
finalFocus: false,
|
|
1582
|
-
children: link ? /* @__PURE__ */ jsx(
|
|
1742
|
+
children: link?.button ? /* @__PURE__ */ jsx(ButtonLinkPanel, {
|
|
1743
|
+
initial: link.initial,
|
|
1744
|
+
label: link.button.label,
|
|
1745
|
+
allowNewTab,
|
|
1746
|
+
onSave: saveButton,
|
|
1747
|
+
onCancel: () => setLink(null),
|
|
1748
|
+
onDetach: () => {
|
|
1749
|
+
const a = link.button.a;
|
|
1750
|
+
setLink(null);
|
|
1751
|
+
if (ref.current) removeButton(ref.current, a);
|
|
1752
|
+
emit();
|
|
1753
|
+
}
|
|
1754
|
+
}) : link ? /* @__PURE__ */ jsx("div", {
|
|
1583
1755
|
className: "flex flex-col gap-2",
|
|
1584
|
-
children: /* @__PURE__ */ jsx(
|
|
1585
|
-
initial: link.initial,
|
|
1586
|
-
allowNewTab,
|
|
1587
|
-
onSave: saveLink,
|
|
1756
|
+
children: /* @__PURE__ */ jsx(LinkPopoverBody, {
|
|
1757
|
+
initial: toLinkValue(link.initial),
|
|
1758
|
+
hideNewTab: !allowNewTab,
|
|
1759
|
+
onSave: (v) => saveLink(fromLinkValue(v, allowNewTab)),
|
|
1588
1760
|
onCancel: () => setLink(null),
|
|
1589
1761
|
onDetach: link.existing ? () => {
|
|
1590
1762
|
const a = link.existing;
|
|
@@ -1609,6 +1781,7 @@ function RichTextEditor({ value, onChange, readOnly, placeholder, docKey, featur
|
|
|
1609
1781
|
placeholder,
|
|
1610
1782
|
docKey: `${docKey}:expanded`,
|
|
1611
1783
|
features,
|
|
1784
|
+
references,
|
|
1612
1785
|
single,
|
|
1613
1786
|
allowNewTab,
|
|
1614
1787
|
expandCrumbs,
|