composeai 0.1.4 → 0.1.7
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/README.md +25 -1
- package/dist/index.cjs +817 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -18
- package/dist/index.d.ts +141 -18
- package/dist/index.js +819 -78
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/composer.css +262 -9
package/dist/index.cjs
CHANGED
|
@@ -395,6 +395,13 @@ var IconAttach = makeIcon(
|
|
|
395
395
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m16 6-8.414 8.586a2 2 0 0 0 0 2.828 2 2 0 0 0 2.828 0l8.414-8.586a4 4 0 0 0 0-5.656 4 4 0 0 0-5.656 0l-8.415 8.585a6 6 0 1 0 8.486 8.486" })
|
|
396
396
|
] })
|
|
397
397
|
);
|
|
398
|
+
var IconPlus = makeIcon(
|
|
399
|
+
"IconPlus",
|
|
400
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14" }),
|
|
402
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14" })
|
|
403
|
+
] })
|
|
404
|
+
);
|
|
398
405
|
var IconImage = makeIcon(
|
|
399
406
|
"IconImage",
|
|
400
407
|
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -483,6 +490,7 @@ var DEFAULT_ICONS = {
|
|
|
483
490
|
send: IconSend,
|
|
484
491
|
stop: IconStop,
|
|
485
492
|
attach: IconAttach,
|
|
493
|
+
plus: IconPlus,
|
|
486
494
|
image: IconImage,
|
|
487
495
|
voice: IconVoice,
|
|
488
496
|
voiceRecording: IconVoiceRecording,
|
|
@@ -508,6 +516,8 @@ var DEFAULT_FEATURES = {
|
|
|
508
516
|
voice: true,
|
|
509
517
|
mermaid: true,
|
|
510
518
|
web: true,
|
|
519
|
+
// No consumer-defined actions by default.
|
|
520
|
+
custom: [],
|
|
511
521
|
// Off by default — ghost autocomplete is an opt-in input affordance that
|
|
512
522
|
// only makes sense when the consumer has a curated list of completions.
|
|
513
523
|
ghostedAutoComplete: false
|
|
@@ -539,6 +549,7 @@ function normalizeFeatures(features) {
|
|
|
539
549
|
voice: features.voice ?? DEFAULT_FEATURES.voice,
|
|
540
550
|
mermaid: features.mermaid ?? DEFAULT_FEATURES.mermaid,
|
|
541
551
|
web: features.web ?? DEFAULT_FEATURES.web,
|
|
552
|
+
custom: features.custom ?? DEFAULT_FEATURES.custom,
|
|
542
553
|
ghostedAutoComplete: features.ghostedAutoComplete ?? DEFAULT_FEATURES.ghostedAutoComplete
|
|
543
554
|
};
|
|
544
555
|
}
|
|
@@ -554,6 +565,7 @@ function ComposerProvider({
|
|
|
554
565
|
closeMenusOnOutsideClick = true,
|
|
555
566
|
attachmentOptions,
|
|
556
567
|
mode = "markdown",
|
|
568
|
+
variant = "compact",
|
|
557
569
|
multiline = true,
|
|
558
570
|
submitOnEnter = true,
|
|
559
571
|
smartNewline = true,
|
|
@@ -732,6 +744,7 @@ function ComposerProvider({
|
|
|
732
744
|
attachmentOptions: normalizedAttachmentOptions,
|
|
733
745
|
markdownMode,
|
|
734
746
|
mode,
|
|
747
|
+
variant,
|
|
735
748
|
multiline,
|
|
736
749
|
submitOnEnter,
|
|
737
750
|
smartNewline,
|
|
@@ -764,6 +777,7 @@ function ComposerProvider({
|
|
|
764
777
|
normalizedAttachmentOptions,
|
|
765
778
|
markdownMode,
|
|
766
779
|
mode,
|
|
780
|
+
variant,
|
|
767
781
|
multiline,
|
|
768
782
|
submitOnEnter,
|
|
769
783
|
smartNewline,
|
|
@@ -789,7 +803,9 @@ function useComposerContext() {
|
|
|
789
803
|
function EditorShell({
|
|
790
804
|
placeholder,
|
|
791
805
|
mode,
|
|
806
|
+
variant,
|
|
792
807
|
multiline,
|
|
808
|
+
expanded,
|
|
793
809
|
header,
|
|
794
810
|
toolbar,
|
|
795
811
|
sendButton,
|
|
@@ -797,11 +813,14 @@ function EditorShell({
|
|
|
797
813
|
}) {
|
|
798
814
|
const { classNames, sx, dir } = useComposerContext();
|
|
799
815
|
const isMarkdown = mode === "markdown";
|
|
800
|
-
const
|
|
816
|
+
const isCompact = variant === "compact";
|
|
817
|
+
const compactInline = isCompact && !expanded;
|
|
818
|
+
const fillEditor = compactInline || !isCompact && !multiline;
|
|
819
|
+
const editorClass = isCompact ? "composer-editor composer-editor--compact" : multiline ? "composer-editor composer-editor--multiline" : "composer-editor composer-editor--inline";
|
|
801
820
|
const editor = slotProps("editor", editorClass, classNames, sx);
|
|
802
821
|
const editorResolved = resolveSx(sx?.editor);
|
|
803
822
|
const placeholderBase = mirrorEditorPadding(editorResolved);
|
|
804
|
-
const placeholderClass = multiline ? "composer-placeholder composer-placeholder--multiline" : "composer-placeholder composer-placeholder--inline";
|
|
823
|
+
const placeholderClass = isCompact ? "composer-placeholder composer-placeholder--compact" : multiline ? "composer-placeholder composer-placeholder--multiline" : "composer-placeholder composer-placeholder--inline";
|
|
805
824
|
const placeholderProps = slotProps(
|
|
806
825
|
"placeholder",
|
|
807
826
|
placeholderClass,
|
|
@@ -816,8 +835,9 @@ function EditorShell({
|
|
|
816
835
|
{
|
|
817
836
|
className: cn(
|
|
818
837
|
"composer-editor-block",
|
|
819
|
-
// Inline: the editor block is the flex child that fills the
|
|
820
|
-
|
|
838
|
+
// Inline + compact: the editor block is the flex child that fills the
|
|
839
|
+
// row between the leading actions and the trailing send cluster.
|
|
840
|
+
fillEditor && "composer-editor-block--inline"
|
|
821
841
|
),
|
|
822
842
|
children: isMarkdown ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
823
843
|
LexicalRichTextPlugin.RichTextPlugin,
|
|
@@ -836,6 +856,32 @@ function EditorShell({
|
|
|
836
856
|
)
|
|
837
857
|
}
|
|
838
858
|
);
|
|
859
|
+
if (isCompact) {
|
|
860
|
+
const actions = toolbar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-compact-actions", children: toolbar });
|
|
861
|
+
const sendCluster = sendButton && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-compact-send", children: sendButton });
|
|
862
|
+
if (expanded) {
|
|
863
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
864
|
+
header,
|
|
865
|
+
editorBlock,
|
|
866
|
+
(actions || sendCluster) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "composer-compact-footer", children: [
|
|
867
|
+
actions ?? /* @__PURE__ */ jsxRuntime.jsx("span", {}),
|
|
868
|
+
sendCluster
|
|
869
|
+
] }),
|
|
870
|
+
/* @__PURE__ */ jsxRuntime.jsx(LexicalHistoryPlugin.HistoryPlugin, {}),
|
|
871
|
+
footer
|
|
872
|
+
] });
|
|
873
|
+
}
|
|
874
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
875
|
+
header,
|
|
876
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "composer-compact-row", children: [
|
|
877
|
+
actions,
|
|
878
|
+
editorBlock,
|
|
879
|
+
sendCluster
|
|
880
|
+
] }),
|
|
881
|
+
/* @__PURE__ */ jsxRuntime.jsx(LexicalHistoryPlugin.HistoryPlugin, {}),
|
|
882
|
+
footer
|
|
883
|
+
] });
|
|
884
|
+
}
|
|
839
885
|
if (!multiline) {
|
|
840
886
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
841
887
|
header,
|
|
@@ -1226,6 +1272,63 @@ function $createLinkTextNode(text = "", url = "") {
|
|
|
1226
1272
|
function $isLinkTextNode(node) {
|
|
1227
1273
|
return node instanceof LinkTextNode;
|
|
1228
1274
|
}
|
|
1275
|
+
var BASE_CLASS = "composer-code-tok";
|
|
1276
|
+
var CodeTokenNode = class _CodeTokenNode extends lexical.TextNode {
|
|
1277
|
+
__codeKind;
|
|
1278
|
+
static getType() {
|
|
1279
|
+
return "composeai-code-token";
|
|
1280
|
+
}
|
|
1281
|
+
static clone(node) {
|
|
1282
|
+
return new _CodeTokenNode(node.__text, node.__codeKind, node.__key);
|
|
1283
|
+
}
|
|
1284
|
+
constructor(text = "", codeKind = "text", key) {
|
|
1285
|
+
super(text, key);
|
|
1286
|
+
this.__codeKind = codeKind;
|
|
1287
|
+
}
|
|
1288
|
+
getCodeKind() {
|
|
1289
|
+
return this.getLatest().__codeKind;
|
|
1290
|
+
}
|
|
1291
|
+
setCodeKind(kind) {
|
|
1292
|
+
const self = this.getWritable();
|
|
1293
|
+
self.__codeKind = kind;
|
|
1294
|
+
return self;
|
|
1295
|
+
}
|
|
1296
|
+
createDOM(config) {
|
|
1297
|
+
const dom = super.createDOM(config);
|
|
1298
|
+
lexical.addClassNamesToElement(dom, BASE_CLASS, `${BASE_CLASS}--${this.__codeKind}`);
|
|
1299
|
+
return dom;
|
|
1300
|
+
}
|
|
1301
|
+
updateDOM(prevNode, dom, config) {
|
|
1302
|
+
const updated = super.updateDOM(prevNode, dom, config);
|
|
1303
|
+
if (prevNode.__codeKind !== this.__codeKind) {
|
|
1304
|
+
dom.classList.remove(`${BASE_CLASS}--${prevNode.__codeKind}`);
|
|
1305
|
+
dom.classList.add(`${BASE_CLASS}--${this.__codeKind}`);
|
|
1306
|
+
}
|
|
1307
|
+
return updated;
|
|
1308
|
+
}
|
|
1309
|
+
static importJSON(serializedNode) {
|
|
1310
|
+
return $createCodeTokenNode("", serializedNode.codeKind).updateFromJSON(
|
|
1311
|
+
serializedNode
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
updateFromJSON(serializedNode) {
|
|
1315
|
+
return super.updateFromJSON(serializedNode);
|
|
1316
|
+
}
|
|
1317
|
+
exportJSON() {
|
|
1318
|
+
return {
|
|
1319
|
+
...super.exportJSON(),
|
|
1320
|
+
type: _CodeTokenNode.getType(),
|
|
1321
|
+
version: 1,
|
|
1322
|
+
codeKind: this.__codeKind
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
function $createCodeTokenNode(text = "", codeKind = "text") {
|
|
1327
|
+
return lexical.$applyNodeReplacement(new CodeTokenNode(text, codeKind));
|
|
1328
|
+
}
|
|
1329
|
+
function $isCodeTokenNode(node) {
|
|
1330
|
+
return node instanceof CodeTokenNode;
|
|
1331
|
+
}
|
|
1229
1332
|
var HEADING_RE = /^(#{1,6}) /;
|
|
1230
1333
|
var QUOTE_RE = /^> /;
|
|
1231
1334
|
var BULLET_RE = /^[-*+] /;
|
|
@@ -1299,12 +1402,20 @@ function $computeBlockMap() {
|
|
|
1299
1402
|
const map = /* @__PURE__ */ new Map();
|
|
1300
1403
|
const root = lexical.$getRoot();
|
|
1301
1404
|
let insideCode = false;
|
|
1405
|
+
let codeLang;
|
|
1302
1406
|
for (const child of root.getChildren()) {
|
|
1303
1407
|
if (!lexical.$isParagraphNode(child)) continue;
|
|
1304
|
-
|
|
1408
|
+
let info = $resolveBlockFor(child, insideCode);
|
|
1409
|
+
if (info.kind === "code-fence-open") {
|
|
1410
|
+
insideCode = true;
|
|
1411
|
+
codeLang = info.lang;
|
|
1412
|
+
} else if (info.kind === "code-fence-close") {
|
|
1413
|
+
insideCode = false;
|
|
1414
|
+
codeLang = void 0;
|
|
1415
|
+
} else if (info.kind === "code-line" && codeLang) {
|
|
1416
|
+
info = { ...info, lang: codeLang };
|
|
1417
|
+
}
|
|
1305
1418
|
map.set(child.getKey(), info);
|
|
1306
|
-
if (info.kind === "code-fence-open") insideCode = true;
|
|
1307
|
-
else if (info.kind === "code-fence-close") insideCode = false;
|
|
1308
1419
|
}
|
|
1309
1420
|
return map;
|
|
1310
1421
|
}
|
|
@@ -1399,7 +1510,8 @@ function wrapByFormat(text, format) {
|
|
|
1399
1510
|
if (format & FORMAT_BIT.strike) out = `~~${out}~~`;
|
|
1400
1511
|
return out;
|
|
1401
1512
|
}
|
|
1402
|
-
function toMarkdown(editor) {
|
|
1513
|
+
function toMarkdown(editor, opts) {
|
|
1514
|
+
const linkedMention = opts?.linkedMention === true;
|
|
1403
1515
|
return editor.getEditorState().read(() => {
|
|
1404
1516
|
const root = lexical.$getRoot();
|
|
1405
1517
|
let usingLive = true;
|
|
@@ -1424,7 +1536,13 @@ function toMarkdown(editor) {
|
|
|
1424
1536
|
let out = "";
|
|
1425
1537
|
for (const child of paragraph.getChildren()) {
|
|
1426
1538
|
if ($isMentionNode(child)) {
|
|
1427
|
-
|
|
1539
|
+
const prefix = child.getMentionPrefix();
|
|
1540
|
+
const label = child.getMentionLabel();
|
|
1541
|
+
if (linkedMention) {
|
|
1542
|
+
out += `[${prefix}${label}](mention:${child.getMentionId()})`;
|
|
1543
|
+
} else {
|
|
1544
|
+
out += `${prefix}${label}`;
|
|
1545
|
+
}
|
|
1428
1546
|
continue;
|
|
1429
1547
|
}
|
|
1430
1548
|
if (lexical.$isLineBreakNode(child)) {
|
|
@@ -1503,11 +1621,6 @@ function $isInsideCodeFence() {
|
|
|
1503
1621
|
const block = $detectBlockFor(top);
|
|
1504
1622
|
return block.kind === "code-line" || block.kind === "code-fence-open";
|
|
1505
1623
|
}
|
|
1506
|
-
function $hasMultiLineContent() {
|
|
1507
|
-
const root = lexical.$getRoot();
|
|
1508
|
-
if (root.getChildrenSize() > 1) return true;
|
|
1509
|
-
return root.getTextContent().includes("\n");
|
|
1510
|
-
}
|
|
1511
1624
|
function $offsetWithinParagraph(paragraph, point) {
|
|
1512
1625
|
if (point.type === "element") {
|
|
1513
1626
|
const children = paragraph.getChildren();
|
|
@@ -1600,10 +1713,8 @@ function KeyboardPlugin({ onSubmit }) {
|
|
|
1600
1713
|
return true;
|
|
1601
1714
|
}
|
|
1602
1715
|
let inCodeFence = false;
|
|
1603
|
-
let hasMultiLine = false;
|
|
1604
1716
|
editor.getEditorState().read(() => {
|
|
1605
1717
|
inCodeFence = $isInsideCodeFence();
|
|
1606
|
-
hasMultiLine = $hasMultiLineContent();
|
|
1607
1718
|
});
|
|
1608
1719
|
if (inCodeFence) {
|
|
1609
1720
|
if (!multiline) {
|
|
@@ -1625,7 +1736,6 @@ function KeyboardPlugin({ onSubmit }) {
|
|
|
1625
1736
|
return true;
|
|
1626
1737
|
}
|
|
1627
1738
|
}
|
|
1628
|
-
if (smartNewline && hasMultiLine) return false;
|
|
1629
1739
|
if (!submitOnEnter) return false;
|
|
1630
1740
|
return trySubmit(event);
|
|
1631
1741
|
},
|
|
@@ -1755,6 +1865,134 @@ function PasteDropPlugin() {
|
|
|
1755
1865
|
return null;
|
|
1756
1866
|
}
|
|
1757
1867
|
|
|
1868
|
+
// src/plugins/mermaid-highlight.ts
|
|
1869
|
+
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
1870
|
+
"flowchart",
|
|
1871
|
+
"graph",
|
|
1872
|
+
"sequenceDiagram",
|
|
1873
|
+
"classDiagram",
|
|
1874
|
+
"stateDiagram",
|
|
1875
|
+
"stateDiagram-v2",
|
|
1876
|
+
"erDiagram",
|
|
1877
|
+
"journey",
|
|
1878
|
+
"gantt",
|
|
1879
|
+
"pie",
|
|
1880
|
+
"gitGraph",
|
|
1881
|
+
"mindmap",
|
|
1882
|
+
"timeline",
|
|
1883
|
+
"quadrantChart",
|
|
1884
|
+
"requirementDiagram",
|
|
1885
|
+
"C4Context",
|
|
1886
|
+
"subgraph",
|
|
1887
|
+
"end",
|
|
1888
|
+
"direction",
|
|
1889
|
+
"participant",
|
|
1890
|
+
"actor",
|
|
1891
|
+
"note",
|
|
1892
|
+
"loop",
|
|
1893
|
+
"alt",
|
|
1894
|
+
"opt",
|
|
1895
|
+
"par",
|
|
1896
|
+
"and",
|
|
1897
|
+
"else",
|
|
1898
|
+
"rect",
|
|
1899
|
+
"activate",
|
|
1900
|
+
"deactivate",
|
|
1901
|
+
"class",
|
|
1902
|
+
"state",
|
|
1903
|
+
"click",
|
|
1904
|
+
"call",
|
|
1905
|
+
"href",
|
|
1906
|
+
"style",
|
|
1907
|
+
"linkStyle",
|
|
1908
|
+
"classDef",
|
|
1909
|
+
"section",
|
|
1910
|
+
"title",
|
|
1911
|
+
"accTitle",
|
|
1912
|
+
"accDescr",
|
|
1913
|
+
"over",
|
|
1914
|
+
"as",
|
|
1915
|
+
// flowchart directions
|
|
1916
|
+
"TB",
|
|
1917
|
+
"TD",
|
|
1918
|
+
"BT",
|
|
1919
|
+
"RL",
|
|
1920
|
+
"LR"
|
|
1921
|
+
]);
|
|
1922
|
+
var ARROW_CHARS = /* @__PURE__ */ new Set(["-", "=", ".", "<", ">"]);
|
|
1923
|
+
var WS_RE = /\s/;
|
|
1924
|
+
var WORD_RE = /[A-Za-z0-9_]/;
|
|
1925
|
+
var DIGIT_RE = /[0-9]/;
|
|
1926
|
+
function tokenizeMermaidLine(line) {
|
|
1927
|
+
const out = [];
|
|
1928
|
+
const push = (text, kind) => {
|
|
1929
|
+
if (text) out.push({ text, kind });
|
|
1930
|
+
};
|
|
1931
|
+
let i = 0;
|
|
1932
|
+
const n = line.length;
|
|
1933
|
+
while (i < n) {
|
|
1934
|
+
const c = line[i];
|
|
1935
|
+
if (WS_RE.test(c)) {
|
|
1936
|
+
let j = i + 1;
|
|
1937
|
+
while (j < n && WS_RE.test(line[j])) j++;
|
|
1938
|
+
push(line.slice(i, j), "text");
|
|
1939
|
+
i = j;
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
if (c === "%" && line[i + 1] === "%") {
|
|
1943
|
+
push(line.slice(i), "comment");
|
|
1944
|
+
i = n;
|
|
1945
|
+
continue;
|
|
1946
|
+
}
|
|
1947
|
+
if (c === '"') {
|
|
1948
|
+
let j = i + 1;
|
|
1949
|
+
while (j < n && line[j] !== '"') j++;
|
|
1950
|
+
j = Math.min(j + 1, n);
|
|
1951
|
+
push(line.slice(i, j), "string");
|
|
1952
|
+
i = j;
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
if ("[](){}|".includes(c)) {
|
|
1956
|
+
push(c, "punct");
|
|
1957
|
+
i++;
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
if (ARROW_CHARS.has(c)) {
|
|
1961
|
+
let j = i + 1;
|
|
1962
|
+
while (j < n && ARROW_CHARS.has(line[j])) j++;
|
|
1963
|
+
if (j < n && (line[j] === "x" || line[j] === "o") && j - i >= 2) j++;
|
|
1964
|
+
const run = line.slice(i, j);
|
|
1965
|
+
const isArrow = run.length >= 2 || run.includes(">") || run.includes("<");
|
|
1966
|
+
push(run, isArrow ? "arrow" : "punct");
|
|
1967
|
+
i = j;
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
if (":;,&+*".includes(c)) {
|
|
1971
|
+
push(c, "punct");
|
|
1972
|
+
i++;
|
|
1973
|
+
continue;
|
|
1974
|
+
}
|
|
1975
|
+
if (DIGIT_RE.test(c)) {
|
|
1976
|
+
let j = i + 1;
|
|
1977
|
+
while (j < n && /[0-9.]/.test(line[j])) j++;
|
|
1978
|
+
push(line.slice(i, j), "number");
|
|
1979
|
+
i = j;
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
if (WORD_RE.test(c)) {
|
|
1983
|
+
let j = i + 1;
|
|
1984
|
+
while (j < n && WORD_RE.test(line[j])) j++;
|
|
1985
|
+
const word = line.slice(i, j);
|
|
1986
|
+
push(word, KEYWORDS.has(word) ? "keyword" : "ident");
|
|
1987
|
+
i = j;
|
|
1988
|
+
continue;
|
|
1989
|
+
}
|
|
1990
|
+
push(c, "text");
|
|
1991
|
+
i++;
|
|
1992
|
+
}
|
|
1993
|
+
return out;
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1758
1996
|
// src/plugins/markdown-tokenizer.ts
|
|
1759
1997
|
var PAIRED_PATTERNS = [
|
|
1760
1998
|
{ open: "**", close: "**", format: "bold" },
|
|
@@ -1866,6 +2104,13 @@ function readCurrentChildren(paragraph) {
|
|
|
1866
2104
|
});
|
|
1867
2105
|
} else if ($isMarkdownTokenNode(child)) {
|
|
1868
2106
|
out.push({ kind: "token", text: child.getTextContent(), format: 0 });
|
|
2107
|
+
} else if ($isCodeTokenNode(child)) {
|
|
2108
|
+
out.push({
|
|
2109
|
+
kind: "code",
|
|
2110
|
+
text: child.getTextContent(),
|
|
2111
|
+
format: 0,
|
|
2112
|
+
codeKind: child.getCodeKind()
|
|
2113
|
+
});
|
|
1869
2114
|
} else if (lexical.$isTextNode(child)) {
|
|
1870
2115
|
out.push({
|
|
1871
2116
|
kind: "text",
|
|
@@ -1888,6 +2133,7 @@ function nodesEqual(a, b) {
|
|
|
1888
2133
|
if (aKind !== bKind) return false;
|
|
1889
2134
|
if (ai.text !== bi.text) return false;
|
|
1890
2135
|
if (ai.format !== bi.format) return false;
|
|
2136
|
+
if (ai.codeKind !== bi.codeKind) return false;
|
|
1891
2137
|
}
|
|
1892
2138
|
return true;
|
|
1893
2139
|
}
|
|
@@ -2055,7 +2301,18 @@ function $applyStyling(paragraph, block, mode) {
|
|
|
2055
2301
|
trailingDropForOffsetMap = body.length;
|
|
2056
2302
|
body = "";
|
|
2057
2303
|
} else if (body.length > 0) {
|
|
2058
|
-
|
|
2304
|
+
if (block.kind === "code-line" && block.lang === "mermaid") {
|
|
2305
|
+
for (const tok of tokenizeMermaidLine(body)) {
|
|
2306
|
+
desired.push({
|
|
2307
|
+
kind: "code",
|
|
2308
|
+
text: tok.text,
|
|
2309
|
+
format: 0,
|
|
2310
|
+
codeKind: tok.kind
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
} else {
|
|
2314
|
+
desired.push({ kind: "text", text: body, format: 0 });
|
|
2315
|
+
}
|
|
2059
2316
|
}
|
|
2060
2317
|
} else if (block.kind === "hr") {
|
|
2061
2318
|
if (body.length > 0) {
|
|
@@ -2143,6 +2400,8 @@ function $applyStyling(paragraph, block, mode) {
|
|
|
2143
2400
|
for (const node of desired) {
|
|
2144
2401
|
if (node.kind === "token") {
|
|
2145
2402
|
paragraph.append($createMarkdownTokenNode(node.text));
|
|
2403
|
+
} else if (node.kind === "code") {
|
|
2404
|
+
paragraph.append($createCodeTokenNode(node.text, node.codeKind));
|
|
2146
2405
|
} else if (node.kind === "link") {
|
|
2147
2406
|
const t = $createLinkTextNode(node.text, node.url ?? "");
|
|
2148
2407
|
if (node.format !== 0) t.setFormat(node.format);
|
|
@@ -2404,10 +2663,13 @@ async function loadMermaid() {
|
|
|
2404
2663
|
function svgToDataUri(svg) {
|
|
2405
2664
|
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
|
2406
2665
|
}
|
|
2407
|
-
|
|
2666
|
+
var MermaidContext = react.createContext(null);
|
|
2667
|
+
function useMermaidContext() {
|
|
2668
|
+
return react.useContext(MermaidContext);
|
|
2669
|
+
}
|
|
2670
|
+
function MermaidProvider({ children }) {
|
|
2408
2671
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
2409
|
-
const { features,
|
|
2410
|
-
const { sparkle: SparkleIcon } = icons;
|
|
2672
|
+
const { features, renderDiagram } = useComposerContext();
|
|
2411
2673
|
const [diagrams, setDiagrams] = react.useState([]);
|
|
2412
2674
|
const keepSource = typeof features.mermaid === "object" ? features.mermaid.keepSource !== false : true;
|
|
2413
2675
|
react.useEffect(() => {
|
|
@@ -2415,10 +2677,10 @@ function MermaidPlugin() {
|
|
|
2415
2677
|
editor.getEditorState().read(() => {
|
|
2416
2678
|
const found = [];
|
|
2417
2679
|
const root = lexical.$getRoot();
|
|
2418
|
-
const
|
|
2680
|
+
const children2 = root.getChildren();
|
|
2419
2681
|
let i = 0;
|
|
2420
|
-
while (i <
|
|
2421
|
-
const opener =
|
|
2682
|
+
while (i < children2.length) {
|
|
2683
|
+
const opener = children2[i];
|
|
2422
2684
|
if (!lexical.$isParagraphNode(opener) || !FENCE_OPEN_MERMAID.test(opener.getTextContent())) {
|
|
2423
2685
|
i++;
|
|
2424
2686
|
continue;
|
|
@@ -2426,8 +2688,8 @@ function MermaidPlugin() {
|
|
|
2426
2688
|
const paragraphKeys = [opener.getKey()];
|
|
2427
2689
|
const codeLines = [];
|
|
2428
2690
|
let j = i + 1;
|
|
2429
|
-
while (j <
|
|
2430
|
-
const next =
|
|
2691
|
+
while (j < children2.length) {
|
|
2692
|
+
const next = children2[j];
|
|
2431
2693
|
if (!lexical.$isParagraphNode(next)) break;
|
|
2432
2694
|
const text = next.getTextContent();
|
|
2433
2695
|
paragraphKeys.push(next.getKey());
|
|
@@ -2491,26 +2753,190 @@ function MermaidPlugin() {
|
|
|
2491
2753
|
hiddenKeysRef.current.clear();
|
|
2492
2754
|
};
|
|
2493
2755
|
}, [editor]);
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
"composer-mermaid",
|
|
2498
|
-
classNames,
|
|
2499
|
-
sx
|
|
2756
|
+
const value = react.useMemo(
|
|
2757
|
+
() => ({ diagrams, renderDiagram }),
|
|
2758
|
+
[diagrams, renderDiagram]
|
|
2500
2759
|
);
|
|
2760
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MermaidContext.Provider, { value, children: [
|
|
2761
|
+
children,
|
|
2762
|
+
/* @__PURE__ */ jsxRuntime.jsx(MermaidBackground, {})
|
|
2763
|
+
] });
|
|
2764
|
+
}
|
|
2765
|
+
function MermaidBackground() {
|
|
2766
|
+
const ctx = useMermaidContext();
|
|
2767
|
+
if (!ctx || ctx.renderDiagram) return null;
|
|
2768
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: ctx.diagrams.map((d) => /* @__PURE__ */ jsxRuntime.jsx(DiagramBackdrop, { diagram: d }, d.id)) });
|
|
2769
|
+
}
|
|
2770
|
+
function DiagramBackdrop({ diagram }) {
|
|
2771
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
2772
|
+
const { svg } = useDiagramSvg(diagram);
|
|
2773
|
+
const [rect, setRect] = react.useState(null);
|
|
2774
|
+
const dataUri = react.useMemo(() => svg ? svgToDataUri(svg) : null, [svg]);
|
|
2775
|
+
react.useEffect(() => {
|
|
2776
|
+
if (!svg) {
|
|
2777
|
+
setRect(null);
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
const root = editor.getRootElement();
|
|
2781
|
+
const block2 = root?.parentElement;
|
|
2782
|
+
if (!root || !block2) return;
|
|
2783
|
+
let raf = 0;
|
|
2784
|
+
const measure = () => {
|
|
2785
|
+
raf = 0;
|
|
2786
|
+
const blockRect = block2.getBoundingClientRect();
|
|
2787
|
+
let top = Infinity;
|
|
2788
|
+
let left = Infinity;
|
|
2789
|
+
let right = -Infinity;
|
|
2790
|
+
let bottom = -Infinity;
|
|
2791
|
+
let any = false;
|
|
2792
|
+
for (const key of diagram.paragraphKeys) {
|
|
2793
|
+
const el = editor.getElementByKey(key);
|
|
2794
|
+
if (!el) continue;
|
|
2795
|
+
any = true;
|
|
2796
|
+
const r = el.getBoundingClientRect();
|
|
2797
|
+
top = Math.min(top, r.top);
|
|
2798
|
+
left = Math.min(left, r.left);
|
|
2799
|
+
right = Math.max(right, r.right);
|
|
2800
|
+
bottom = Math.max(bottom, r.bottom);
|
|
2801
|
+
}
|
|
2802
|
+
if (!any) {
|
|
2803
|
+
setRect(null);
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
const relTop = top - blockRect.top;
|
|
2807
|
+
const visTop = Math.max(0, relTop);
|
|
2808
|
+
const visBottom = Math.min(blockRect.height, relTop + (bottom - top));
|
|
2809
|
+
if (visBottom <= visTop) {
|
|
2810
|
+
setRect(null);
|
|
2811
|
+
return;
|
|
2812
|
+
}
|
|
2813
|
+
setRect({
|
|
2814
|
+
top: visTop,
|
|
2815
|
+
left: left - blockRect.left,
|
|
2816
|
+
width: right - left,
|
|
2817
|
+
height: visBottom - visTop
|
|
2818
|
+
});
|
|
2819
|
+
};
|
|
2820
|
+
const schedule = () => {
|
|
2821
|
+
if (!raf) raf = requestAnimationFrame(measure);
|
|
2822
|
+
};
|
|
2823
|
+
schedule();
|
|
2824
|
+
const unregister = editor.registerUpdateListener(schedule);
|
|
2825
|
+
root.addEventListener("scroll", schedule, { passive: true });
|
|
2826
|
+
const ro = new ResizeObserver(schedule);
|
|
2827
|
+
ro.observe(root);
|
|
2828
|
+
return () => {
|
|
2829
|
+
if (raf) cancelAnimationFrame(raf);
|
|
2830
|
+
unregister();
|
|
2831
|
+
root.removeEventListener("scroll", schedule);
|
|
2832
|
+
ro.disconnect();
|
|
2833
|
+
};
|
|
2834
|
+
}, [editor, svg, diagram.paragraphKeys]);
|
|
2835
|
+
const block = editor.getRootElement()?.parentElement;
|
|
2836
|
+
if (!dataUri || !rect || !block) return null;
|
|
2837
|
+
return reactDom.createPortal(
|
|
2838
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2839
|
+
"div",
|
|
2840
|
+
{
|
|
2841
|
+
"aria-hidden": true,
|
|
2842
|
+
className: "composer-mermaid-backdrop",
|
|
2843
|
+
style: {
|
|
2844
|
+
top: rect.top,
|
|
2845
|
+
left: rect.left,
|
|
2846
|
+
width: rect.width,
|
|
2847
|
+
height: rect.height,
|
|
2848
|
+
backgroundImage: `url("${dataUri}")`
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
),
|
|
2852
|
+
block
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2855
|
+
function MermaidPreview() {
|
|
2856
|
+
const ctx = useMermaidContext();
|
|
2857
|
+
const { icons, classNames, sx } = useComposerContext();
|
|
2858
|
+
const { sparkle: SparkleIcon } = icons;
|
|
2859
|
+
if (!ctx || ctx.diagrams.length === 0) return null;
|
|
2860
|
+
const preview = slotProps("mermaidPreview", "composer-mermaid", classNames, sx);
|
|
2501
2861
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ...preview, children: [
|
|
2502
2862
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "composer-mermaid-head", children: [
|
|
2503
2863
|
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, {}),
|
|
2504
2864
|
"Diagram preview"
|
|
2505
2865
|
] }),
|
|
2506
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-mermaid-row", children: diagrams.map((d) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2507
|
-
|
|
2866
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-mermaid-row", children: ctx.diagrams.map((d) => /* @__PURE__ */ jsxRuntime.jsx(DiagramTile, { diagram: d, renderDiagram: ctx.renderDiagram }, d.id)) })
|
|
2867
|
+
] });
|
|
2868
|
+
}
|
|
2869
|
+
function MermaidQuickAction() {
|
|
2870
|
+
const ctx = useMermaidContext();
|
|
2871
|
+
const { icons, closeMenusOnOutsideClick } = useComposerContext();
|
|
2872
|
+
const { sparkle: SparkleIcon } = icons;
|
|
2873
|
+
const [open, setOpen] = react.useState(false);
|
|
2874
|
+
const triggerRef = react.useRef(null);
|
|
2875
|
+
const popoverRef = react.useRef(null);
|
|
2876
|
+
const menuId = react.useId();
|
|
2877
|
+
react.useEffect(() => {
|
|
2878
|
+
if (!open) return;
|
|
2879
|
+
const onPointerDown = (event) => {
|
|
2880
|
+
if (!closeMenusOnOutsideClick) return;
|
|
2881
|
+
const target = event.target;
|
|
2882
|
+
if (!target) return;
|
|
2883
|
+
if (popoverRef.current?.contains(target)) return;
|
|
2884
|
+
if (triggerRef.current?.contains(target)) return;
|
|
2885
|
+
setOpen(false);
|
|
2886
|
+
};
|
|
2887
|
+
const onKeyDown = (event) => {
|
|
2888
|
+
if (event.key === "Escape") {
|
|
2889
|
+
event.preventDefault();
|
|
2890
|
+
setOpen(false);
|
|
2891
|
+
triggerRef.current?.focus();
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2895
|
+
document.addEventListener("keydown", onKeyDown);
|
|
2896
|
+
return () => {
|
|
2897
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2898
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
2899
|
+
};
|
|
2900
|
+
}, [open, closeMenusOnOutsideClick]);
|
|
2901
|
+
if (!ctx || ctx.diagrams.length === 0) return null;
|
|
2902
|
+
const count = ctx.diagrams.length;
|
|
2903
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "composer-quick-actions", children: [
|
|
2904
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2905
|
+
"button",
|
|
2508
2906
|
{
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2907
|
+
ref: triggerRef,
|
|
2908
|
+
type: "button",
|
|
2909
|
+
"aria-label": `Diagram preview (${count})`,
|
|
2910
|
+
"aria-haspopup": "dialog",
|
|
2911
|
+
"aria-expanded": open,
|
|
2912
|
+
"aria-controls": open ? menuId : void 0,
|
|
2913
|
+
"data-active": open ? "" : void 0,
|
|
2914
|
+
onClick: () => setOpen((o) => !o),
|
|
2915
|
+
className: "composer-mermaid-trigger",
|
|
2916
|
+
children: [
|
|
2917
|
+
/* @__PURE__ */ jsxRuntime.jsx(DiagramThumb, { diagram: ctx.diagrams[0], renderDiagram: ctx.renderDiagram }),
|
|
2918
|
+
count > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "composer-mermaid-count", children: count })
|
|
2919
|
+
]
|
|
2920
|
+
}
|
|
2921
|
+
),
|
|
2922
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2923
|
+
"div",
|
|
2924
|
+
{
|
|
2925
|
+
ref: popoverRef,
|
|
2926
|
+
id: menuId,
|
|
2927
|
+
role: "dialog",
|
|
2928
|
+
"aria-label": "Diagram preview",
|
|
2929
|
+
"data-composer-popover": "open",
|
|
2930
|
+
className: "composer-popover-in composer-mermaid-pop",
|
|
2931
|
+
children: [
|
|
2932
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "composer-mermaid-head", children: [
|
|
2933
|
+
/* @__PURE__ */ jsxRuntime.jsx(SparkleIcon, {}),
|
|
2934
|
+
"Diagram preview"
|
|
2935
|
+
] }),
|
|
2936
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-mermaid-row", children: ctx.diagrams.map((d) => /* @__PURE__ */ jsxRuntime.jsx(DiagramTile, { diagram: d, renderDiagram: ctx.renderDiagram }, d.id)) })
|
|
2937
|
+
]
|
|
2938
|
+
}
|
|
2939
|
+
)
|
|
2514
2940
|
] });
|
|
2515
2941
|
}
|
|
2516
2942
|
function DiagramTile({ diagram, renderDiagram }) {
|
|
@@ -2532,12 +2958,9 @@ function ConsumerTile({
|
|
|
2532
2958
|
}
|
|
2533
2959
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-mermaid-tile", children: content });
|
|
2534
2960
|
}
|
|
2535
|
-
function
|
|
2536
|
-
const { icons } = useComposerContext();
|
|
2537
|
-
const { zoom: ZoomIcon } = icons;
|
|
2961
|
+
function useDiagramSvg(diagram) {
|
|
2538
2962
|
const [svg, setSvg] = react.useState(null);
|
|
2539
2963
|
const [error, setError] = react.useState(null);
|
|
2540
|
-
const [zoom, setZoom] = react.useState(false);
|
|
2541
2964
|
const [mermaidMissing, setMermaidMissing] = react.useState(false);
|
|
2542
2965
|
const renderId = react.useMemo(
|
|
2543
2966
|
() => `mermaid-${diagram.id}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -2570,6 +2993,13 @@ function MermaidTile({ diagram }) {
|
|
|
2570
2993
|
cancelled = true;
|
|
2571
2994
|
};
|
|
2572
2995
|
}, [diagram.code, renderId]);
|
|
2996
|
+
return { svg, error, mermaidMissing };
|
|
2997
|
+
}
|
|
2998
|
+
function MermaidTile({ diagram }) {
|
|
2999
|
+
const { icons } = useComposerContext();
|
|
3000
|
+
const { zoom: ZoomIcon } = icons;
|
|
3001
|
+
const [zoom, setZoom] = react.useState(false);
|
|
3002
|
+
const { svg, error, mermaidMissing } = useDiagramSvg(diagram);
|
|
2573
3003
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2574
3004
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2575
3005
|
"button",
|
|
@@ -2605,6 +3035,35 @@ function MermaidTile({ diagram }) {
|
|
|
2605
3035
|
)
|
|
2606
3036
|
] });
|
|
2607
3037
|
}
|
|
3038
|
+
function DiagramThumb({ diagram, renderDiagram }) {
|
|
3039
|
+
if (renderDiagram) {
|
|
3040
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ConsumerThumb, { diagram, renderDiagram });
|
|
3041
|
+
}
|
|
3042
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MermaidThumb, { diagram });
|
|
3043
|
+
}
|
|
3044
|
+
function ConsumerThumb({
|
|
3045
|
+
diagram,
|
|
3046
|
+
renderDiagram
|
|
3047
|
+
}) {
|
|
3048
|
+
let content = null;
|
|
3049
|
+
try {
|
|
3050
|
+
content = renderDiagram({ code: diagram.code, language: "mermaid" });
|
|
3051
|
+
} catch {
|
|
3052
|
+
content = null;
|
|
3053
|
+
}
|
|
3054
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "composer-mermaid-thumb", children: content });
|
|
3055
|
+
}
|
|
3056
|
+
function MermaidThumb({ diagram }) {
|
|
3057
|
+
const { svg } = useDiagramSvg(diagram);
|
|
3058
|
+
if (!svg) return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "composer-mermaid-thumb" });
|
|
3059
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3060
|
+
"span",
|
|
3061
|
+
{
|
|
3062
|
+
className: "composer-mermaid-thumb",
|
|
3063
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
3064
|
+
}
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
2608
3067
|
function SlashMenu({
|
|
2609
3068
|
options,
|
|
2610
3069
|
selectedIndex,
|
|
@@ -3867,8 +4326,284 @@ function AttachmentTypePicker({
|
|
|
3867
4326
|
)
|
|
3868
4327
|
] });
|
|
3869
4328
|
}
|
|
4329
|
+
function useCustomActionContext(submit) {
|
|
4330
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
4331
|
+
return react.useMemo(
|
|
4332
|
+
() => ({
|
|
4333
|
+
insertText: (text) => {
|
|
4334
|
+
editor.update(() => {
|
|
4335
|
+
const sel = lexical.$getSelection();
|
|
4336
|
+
if (lexical.$isRangeSelection(sel)) sel.insertText(text);
|
|
4337
|
+
});
|
|
4338
|
+
focusEditor(editor);
|
|
4339
|
+
},
|
|
4340
|
+
insertMarkdown: (md) => {
|
|
4341
|
+
editor.update(() => {
|
|
4342
|
+
$insertTextWithParagraphBreaks(md);
|
|
4343
|
+
});
|
|
4344
|
+
focusEditor(editor);
|
|
4345
|
+
},
|
|
4346
|
+
submit
|
|
4347
|
+
}),
|
|
4348
|
+
[editor, submit]
|
|
4349
|
+
);
|
|
4350
|
+
}
|
|
4351
|
+
function CustomActionButtons({ submit }) {
|
|
4352
|
+
const { features, classNames, sx } = useComposerContext();
|
|
4353
|
+
const ctx = useCustomActionContext(submit);
|
|
4354
|
+
const actions = features.custom;
|
|
4355
|
+
const toolbarBtn = slotProps(
|
|
4356
|
+
"toolbarButton",
|
|
4357
|
+
"composer-toolbar-btn",
|
|
4358
|
+
classNames,
|
|
4359
|
+
sx
|
|
4360
|
+
);
|
|
4361
|
+
if (actions.length === 0) return null;
|
|
4362
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: actions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: action.title, side: "top", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4363
|
+
"button",
|
|
4364
|
+
{
|
|
4365
|
+
type: "button",
|
|
4366
|
+
"aria-label": action.title,
|
|
4367
|
+
"aria-pressed": action.active,
|
|
4368
|
+
"data-active": action.active ? "" : void 0,
|
|
4369
|
+
disabled: action.disabled,
|
|
4370
|
+
onClick: () => action.onClick(ctx),
|
|
4371
|
+
...toolbarBtn,
|
|
4372
|
+
children: action.icon
|
|
4373
|
+
}
|
|
4374
|
+
) }, action.id ?? i)) });
|
|
4375
|
+
}
|
|
4376
|
+
function QuickActionsMenu({ extras, submit }) {
|
|
4377
|
+
const {
|
|
4378
|
+
features,
|
|
4379
|
+
attachmentsConfig,
|
|
4380
|
+
addFiles,
|
|
4381
|
+
webEnabled,
|
|
4382
|
+
toggleWeb,
|
|
4383
|
+
icons,
|
|
4384
|
+
classNames,
|
|
4385
|
+
sx,
|
|
4386
|
+
closeMenusOnOutsideClick
|
|
4387
|
+
} = useComposerContext();
|
|
4388
|
+
const {
|
|
4389
|
+
plus: PlusIcon,
|
|
4390
|
+
attach: AttachIcon,
|
|
4391
|
+
image: ImageIcon,
|
|
4392
|
+
web: WebIcon
|
|
4393
|
+
} = icons;
|
|
4394
|
+
const [open, setOpen] = react.useState(false);
|
|
4395
|
+
const triggerRef = react.useRef(null);
|
|
4396
|
+
const popoverRef = react.useRef(null);
|
|
4397
|
+
const fileInputRef = react.useRef(null);
|
|
4398
|
+
const imageInputRef = react.useRef(null);
|
|
4399
|
+
const menuId = react.useId();
|
|
4400
|
+
const attachmentsEnabled = !!features.attachments;
|
|
4401
|
+
const showFileBtn = attachmentsEnabled && attachmentsConfig.file !== false;
|
|
4402
|
+
const showImageBtn = attachmentsEnabled && attachmentsConfig.image !== false;
|
|
4403
|
+
const types = showFileBtn && Array.isArray(attachmentsConfig.types) ? attachmentsConfig.types : [];
|
|
4404
|
+
const hasTypePicker = types.length > 0;
|
|
4405
|
+
const customActions = features.custom;
|
|
4406
|
+
const actionCtx = useCustomActionContext(submit ?? (() => {
|
|
4407
|
+
}));
|
|
4408
|
+
const hasAnyAction = showFileBtn || showImageBtn || features.web || customActions.length > 0 || !!extras;
|
|
4409
|
+
const close = react.useCallback(() => setOpen(false), []);
|
|
4410
|
+
const openFilePicker = react.useCallback((accept) => {
|
|
4411
|
+
const input = fileInputRef.current;
|
|
4412
|
+
if (!input) return;
|
|
4413
|
+
input.accept = accept ?? attachmentsConfig.accept ?? "";
|
|
4414
|
+
input.click();
|
|
4415
|
+
}, [attachmentsConfig.accept]);
|
|
4416
|
+
react.useEffect(() => {
|
|
4417
|
+
if (!open) return;
|
|
4418
|
+
const onPointerDown = (event) => {
|
|
4419
|
+
if (!closeMenusOnOutsideClick) return;
|
|
4420
|
+
const target = event.target;
|
|
4421
|
+
if (!target) return;
|
|
4422
|
+
if (popoverRef.current?.contains(target)) return;
|
|
4423
|
+
if (triggerRef.current?.contains(target)) return;
|
|
4424
|
+
close();
|
|
4425
|
+
};
|
|
4426
|
+
const onKeyDown = (event) => {
|
|
4427
|
+
if (event.key === "Escape") {
|
|
4428
|
+
event.preventDefault();
|
|
4429
|
+
close();
|
|
4430
|
+
triggerRef.current?.focus();
|
|
4431
|
+
}
|
|
4432
|
+
};
|
|
4433
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
4434
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4435
|
+
return () => {
|
|
4436
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
4437
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
4438
|
+
};
|
|
4439
|
+
}, [open, closeMenusOnOutsideClick, close]);
|
|
4440
|
+
if (!hasAnyAction) return null;
|
|
4441
|
+
const triggerBtn = slotProps(
|
|
4442
|
+
"toolbarButton",
|
|
4443
|
+
"composer-toolbar-btn",
|
|
4444
|
+
classNames,
|
|
4445
|
+
sx
|
|
4446
|
+
);
|
|
4447
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "composer-quick-actions", children: [
|
|
4448
|
+
showFileBtn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4449
|
+
"input",
|
|
4450
|
+
{
|
|
4451
|
+
ref: fileInputRef,
|
|
4452
|
+
type: "file",
|
|
4453
|
+
multiple: true,
|
|
4454
|
+
accept: attachmentsConfig.accept,
|
|
4455
|
+
hidden: true,
|
|
4456
|
+
onChange: (e) => {
|
|
4457
|
+
const files = e.target.files;
|
|
4458
|
+
if (files) addFiles(Array.from(files));
|
|
4459
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
),
|
|
4463
|
+
showImageBtn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4464
|
+
"input",
|
|
4465
|
+
{
|
|
4466
|
+
ref: imageInputRef,
|
|
4467
|
+
type: "file",
|
|
4468
|
+
multiple: true,
|
|
4469
|
+
accept: "image/*",
|
|
4470
|
+
hidden: true,
|
|
4471
|
+
onChange: (e) => {
|
|
4472
|
+
const files = e.target.files;
|
|
4473
|
+
if (files) addFiles(Array.from(files));
|
|
4474
|
+
if (imageInputRef.current) imageInputRef.current.value = "";
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
),
|
|
4478
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4479
|
+
"button",
|
|
4480
|
+
{
|
|
4481
|
+
ref: triggerRef,
|
|
4482
|
+
type: "button",
|
|
4483
|
+
"aria-label": "Quick actions",
|
|
4484
|
+
"aria-haspopup": "menu",
|
|
4485
|
+
"aria-expanded": open,
|
|
4486
|
+
"aria-controls": open ? menuId : void 0,
|
|
4487
|
+
"data-active": open ? "" : void 0,
|
|
4488
|
+
onClick: () => setOpen((o) => !o),
|
|
4489
|
+
...triggerBtn,
|
|
4490
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(PlusIcon, {})
|
|
4491
|
+
}
|
|
4492
|
+
),
|
|
4493
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4494
|
+
"div",
|
|
4495
|
+
{
|
|
4496
|
+
ref: popoverRef,
|
|
4497
|
+
id: menuId,
|
|
4498
|
+
role: "menu",
|
|
4499
|
+
"aria-label": "Quick actions",
|
|
4500
|
+
"data-composer-popover": "open",
|
|
4501
|
+
className: "composer-popover-in composer-attach-menu composer-quick-menu",
|
|
4502
|
+
children: [
|
|
4503
|
+
showFileBtn && !hasTypePicker && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4504
|
+
ActionItem,
|
|
4505
|
+
{
|
|
4506
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(AttachIcon, {}),
|
|
4507
|
+
label: "Attach file",
|
|
4508
|
+
onClick: () => {
|
|
4509
|
+
close();
|
|
4510
|
+
openFilePicker();
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
),
|
|
4514
|
+
hasTypePicker && types.map((type) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4515
|
+
ActionItem,
|
|
4516
|
+
{
|
|
4517
|
+
icon: type.icon ?? /* @__PURE__ */ jsxRuntime.jsx(AttachIcon, {}),
|
|
4518
|
+
label: type.label,
|
|
4519
|
+
description: type.description,
|
|
4520
|
+
onClick: () => {
|
|
4521
|
+
close();
|
|
4522
|
+
openFilePicker(type.accept);
|
|
4523
|
+
}
|
|
4524
|
+
},
|
|
4525
|
+
type.id
|
|
4526
|
+
)),
|
|
4527
|
+
showImageBtn && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4528
|
+
ActionItem,
|
|
4529
|
+
{
|
|
4530
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(ImageIcon, {}),
|
|
4531
|
+
label: "Add image",
|
|
4532
|
+
onClick: () => {
|
|
4533
|
+
close();
|
|
4534
|
+
imageInputRef.current?.click();
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
),
|
|
4538
|
+
features.web && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4539
|
+
ActionItem,
|
|
4540
|
+
{
|
|
4541
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(WebIcon, {}),
|
|
4542
|
+
label: "Search the web",
|
|
4543
|
+
active: webEnabled,
|
|
4544
|
+
onClick: () => {
|
|
4545
|
+
toggleWeb();
|
|
4546
|
+
close();
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
),
|
|
4550
|
+
customActions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4551
|
+
ActionItem,
|
|
4552
|
+
{
|
|
4553
|
+
icon: action.icon,
|
|
4554
|
+
label: action.title,
|
|
4555
|
+
active: action.active,
|
|
4556
|
+
disabled: action.disabled,
|
|
4557
|
+
onClick: () => {
|
|
4558
|
+
action.onClick(actionCtx);
|
|
4559
|
+
close();
|
|
4560
|
+
}
|
|
4561
|
+
},
|
|
4562
|
+
action.id ?? i
|
|
4563
|
+
)),
|
|
4564
|
+
extras && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "composer-quick-extras", children: extras })
|
|
4565
|
+
]
|
|
4566
|
+
}
|
|
4567
|
+
)
|
|
4568
|
+
] });
|
|
4569
|
+
}
|
|
4570
|
+
function ActionItem({
|
|
4571
|
+
icon,
|
|
4572
|
+
label,
|
|
4573
|
+
description,
|
|
4574
|
+
active,
|
|
4575
|
+
disabled,
|
|
4576
|
+
onClick
|
|
4577
|
+
}) {
|
|
4578
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4579
|
+
"button",
|
|
4580
|
+
{
|
|
4581
|
+
role: "menuitem",
|
|
4582
|
+
type: "button",
|
|
4583
|
+
"aria-pressed": active,
|
|
4584
|
+
"data-active": active ? "" : void 0,
|
|
4585
|
+
disabled,
|
|
4586
|
+
onClick,
|
|
4587
|
+
className: cn("composer-attach-item"),
|
|
4588
|
+
children: [
|
|
4589
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "composer-attach-item-icon", children: icon }),
|
|
4590
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "composer-attach-item-label", children: label }),
|
|
4591
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "composer-attach-item-desc", children: description }) : null
|
|
4592
|
+
]
|
|
4593
|
+
}
|
|
4594
|
+
);
|
|
4595
|
+
}
|
|
3870
4596
|
var TOOLBAR_BTN_BASE = "composer-toolbar-btn";
|
|
3871
|
-
function Toolbar({ extras }) {
|
|
4597
|
+
function Toolbar({ extras, variant = "full", submit }) {
|
|
4598
|
+
if (variant === "compact") {
|
|
4599
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4600
|
+
/* @__PURE__ */ jsxRuntime.jsx(QuickActionsMenu, { extras, submit }),
|
|
4601
|
+
/* @__PURE__ */ jsxRuntime.jsx(MermaidQuickAction, {})
|
|
4602
|
+
] });
|
|
4603
|
+
}
|
|
4604
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FullToolbar, { extras, submit });
|
|
4605
|
+
}
|
|
4606
|
+
function FullToolbar({ extras, submit }) {
|
|
3872
4607
|
const {
|
|
3873
4608
|
features,
|
|
3874
4609
|
attachmentsConfig,
|
|
@@ -3967,6 +4702,8 @@ function Toolbar({ extras }) {
|
|
|
3967
4702
|
]
|
|
3968
4703
|
}
|
|
3969
4704
|
),
|
|
4705
|
+
/* @__PURE__ */ jsxRuntime.jsx(CustomActionButtons, { submit: submit ?? (() => {
|
|
4706
|
+
}) }),
|
|
3970
4707
|
extras
|
|
3971
4708
|
] });
|
|
3972
4709
|
}
|
|
@@ -4060,7 +4797,6 @@ function HintBar({ hint }) {
|
|
|
4060
4797
|
const {
|
|
4061
4798
|
multiline,
|
|
4062
4799
|
submitOnEnter,
|
|
4063
|
-
smartNewline,
|
|
4064
4800
|
focusShortcut,
|
|
4065
4801
|
classNames,
|
|
4066
4802
|
sx
|
|
@@ -4074,16 +4810,6 @@ function HintBar({ hint }) {
|
|
|
4074
4810
|
" to send."
|
|
4075
4811
|
] });
|
|
4076
4812
|
}
|
|
4077
|
-
if (smartNewline && submitOnEnter) {
|
|
4078
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4079
|
-
"Press ",
|
|
4080
|
-
/* @__PURE__ */ jsxRuntime.jsx(Key, { children: "Enter" }),
|
|
4081
|
-
" to send a single line,",
|
|
4082
|
-
" ",
|
|
4083
|
-
/* @__PURE__ */ jsxRuntime.jsx(Key, { children: "\u2318/Ctrl + Enter" }),
|
|
4084
|
-
" to send once you've started a new line."
|
|
4085
|
-
] });
|
|
4086
|
-
}
|
|
4087
4813
|
if (!submitOnEnter) {
|
|
4088
4814
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4089
4815
|
"Press ",
|
|
@@ -4100,7 +4826,7 @@ function HintBar({ hint }) {
|
|
|
4100
4826
|
/* @__PURE__ */ jsxRuntime.jsx(Key, { children: "Shift + Enter" }),
|
|
4101
4827
|
" for newline."
|
|
4102
4828
|
] });
|
|
4103
|
-
}, [multiline, submitOnEnter
|
|
4829
|
+
}, [multiline, submitOnEnter]);
|
|
4104
4830
|
const focusHint = react.useMemo(() => {
|
|
4105
4831
|
if (!focusShortcut) return null;
|
|
4106
4832
|
const label = formatShortcut(focusShortcut);
|
|
@@ -4298,6 +5024,7 @@ var Composer = react.forwardRef(function Composer2(props, ref) {
|
|
|
4298
5024
|
toolbarExtras,
|
|
4299
5025
|
closeMenusOnOutsideClick = true,
|
|
4300
5026
|
mode = "markdown",
|
|
5027
|
+
variant = "compact",
|
|
4301
5028
|
multiline = true,
|
|
4302
5029
|
submitOnEnter = true,
|
|
4303
5030
|
smartNewline = true,
|
|
@@ -4326,6 +5053,7 @@ var Composer = react.forwardRef(function Composer2(props, ref) {
|
|
|
4326
5053
|
closeMenusOnOutsideClick,
|
|
4327
5054
|
attachmentOptions,
|
|
4328
5055
|
mode,
|
|
5056
|
+
variant,
|
|
4329
5057
|
multiline,
|
|
4330
5058
|
submitOnEnter,
|
|
4331
5059
|
smartNewline,
|
|
@@ -4360,6 +5088,7 @@ var Composer = react.forwardRef(function Composer2(props, ref) {
|
|
|
4360
5088
|
isStreaming: !!isStreaming,
|
|
4361
5089
|
toolbarExtras,
|
|
4362
5090
|
mode,
|
|
5091
|
+
variant,
|
|
4363
5092
|
multiline
|
|
4364
5093
|
}
|
|
4365
5094
|
),
|
|
@@ -4380,6 +5109,7 @@ var RICH_NODES = [
|
|
|
4380
5109
|
MarkdownTokenNode,
|
|
4381
5110
|
BlockParagraphNode,
|
|
4382
5111
|
LinkTextNode,
|
|
5112
|
+
CodeTokenNode,
|
|
4383
5113
|
BLOCK_PARAGRAPH_REPLACEMENT
|
|
4384
5114
|
];
|
|
4385
5115
|
var PLAIN_NODES = [MentionNode];
|
|
@@ -4395,6 +5125,7 @@ function ComposerCard({
|
|
|
4395
5125
|
isStreaming,
|
|
4396
5126
|
toolbarExtras,
|
|
4397
5127
|
mode,
|
|
5128
|
+
variant,
|
|
4398
5129
|
multiline
|
|
4399
5130
|
}) {
|
|
4400
5131
|
const { webEnabled, isDraggingFiles, classNames, sx } = useComposerContext();
|
|
@@ -4415,7 +5146,8 @@ function ComposerCard({
|
|
|
4415
5146
|
"div",
|
|
4416
5147
|
{
|
|
4417
5148
|
"data-composer-root": "",
|
|
4418
|
-
"data-composer-
|
|
5149
|
+
"data-composer-variant": variant,
|
|
5150
|
+
"data-composer-inline": variant === "full" && !multiline ? "" : void 0,
|
|
4419
5151
|
"data-composer-web": webEnabled ? "" : void 0,
|
|
4420
5152
|
"data-composer-dragging": isDraggingFiles ? "" : void 0,
|
|
4421
5153
|
...card,
|
|
@@ -4445,6 +5177,7 @@ function ComposerCard({
|
|
|
4445
5177
|
{
|
|
4446
5178
|
placeholder,
|
|
4447
5179
|
mode,
|
|
5180
|
+
variant,
|
|
4448
5181
|
multiline,
|
|
4449
5182
|
handleRef,
|
|
4450
5183
|
onSend,
|
|
@@ -4464,6 +5197,7 @@ function ComposerCard({
|
|
|
4464
5197
|
function ComposerInner({
|
|
4465
5198
|
placeholder,
|
|
4466
5199
|
mode,
|
|
5200
|
+
variant,
|
|
4467
5201
|
multiline,
|
|
4468
5202
|
handleRef,
|
|
4469
5203
|
onSend,
|
|
@@ -4491,6 +5225,9 @@ function ComposerInner({
|
|
|
4491
5225
|
const [hasText, setHasText] = react.useState(
|
|
4492
5226
|
!!initialValue && initialValue.trim().length > 0
|
|
4493
5227
|
);
|
|
5228
|
+
const [isMultiLine, setIsMultiLine] = react.useState(
|
|
5229
|
+
!!initialValue && initialValue.includes("\n")
|
|
5230
|
+
);
|
|
4494
5231
|
const onSendRef = react.useRef(onSend);
|
|
4495
5232
|
onSendRef.current = onSend;
|
|
4496
5233
|
const refocusOnSubmitRef = react.useRef(refocusOnSubmit);
|
|
@@ -4499,9 +5236,10 @@ function ComposerInner({
|
|
|
4499
5236
|
if (isStreaming) return;
|
|
4500
5237
|
if (uploadsBlocking) return;
|
|
4501
5238
|
let payload = null;
|
|
5239
|
+
const linkedMention = typeof features.mentions === "object" && !!features.mentions.linkedMention;
|
|
4502
5240
|
editor.getEditorState().read(() => {
|
|
4503
5241
|
const { text, mentions } = collectPlainAndMentions(editor);
|
|
4504
|
-
const markdown = toMarkdown(editor);
|
|
5242
|
+
const markdown = toMarkdown(editor, { linkedMention });
|
|
4505
5243
|
const trimmed = text.trim();
|
|
4506
5244
|
if (!trimmed) {
|
|
4507
5245
|
if (attachments.length === 0) return;
|
|
@@ -4568,8 +5306,10 @@ function ComposerInner({
|
|
|
4568
5306
|
react.useEffect(() => {
|
|
4569
5307
|
return editor.registerUpdateListener(() => {
|
|
4570
5308
|
editor.getEditorState().read(() => {
|
|
4571
|
-
const
|
|
4572
|
-
|
|
5309
|
+
const root = lexical.$getRoot();
|
|
5310
|
+
const text = root.getTextContent();
|
|
5311
|
+
setHasText(text.trim().length > 0);
|
|
5312
|
+
setIsMultiLine(root.getChildrenSize() > 1 || text.includes("\n"));
|
|
4573
5313
|
});
|
|
4574
5314
|
});
|
|
4575
5315
|
}, [editor]);
|
|
@@ -4585,8 +5325,10 @@ function ComposerInner({
|
|
|
4585
5325
|
}
|
|
4586
5326
|
});
|
|
4587
5327
|
}, [editor, registerRunPrompt, submit]);
|
|
4588
|
-
const
|
|
4589
|
-
const
|
|
5328
|
+
const isCompact = variant === "compact";
|
|
5329
|
+
const mermaidActive = multiline && mode === "markdown" && !!features.mermaid;
|
|
5330
|
+
const toolbarSlot = /* @__PURE__ */ jsxRuntime.jsx(Toolbar, { extras: toolbarExtras, variant, submit });
|
|
5331
|
+
const sendButton = /* @__PURE__ */ jsxRuntime.jsx(
|
|
4590
5332
|
SendButton,
|
|
4591
5333
|
{
|
|
4592
5334
|
canSend: (
|
|
@@ -4599,38 +5341,37 @@ function ComposerInner({
|
|
|
4599
5341
|
onStop
|
|
4600
5342
|
}
|
|
4601
5343
|
);
|
|
4602
|
-
|
|
5344
|
+
const sendButtonSlot = isCompact && features.voice ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5345
|
+
/* @__PURE__ */ jsxRuntime.jsx(VoiceButton, {}),
|
|
5346
|
+
sendButton
|
|
5347
|
+
] }) : sendButton;
|
|
5348
|
+
const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4603
5349
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4604
5350
|
EditorShell,
|
|
4605
5351
|
{
|
|
4606
5352
|
placeholder,
|
|
4607
5353
|
mode,
|
|
5354
|
+
variant,
|
|
4608
5355
|
multiline,
|
|
5356
|
+
expanded: isCompact && isMultiLine,
|
|
4609
5357
|
header: /* @__PURE__ */ jsxRuntime.jsx(AttachmentTray, {}),
|
|
4610
5358
|
toolbar: toolbarSlot,
|
|
4611
5359
|
sendButton: sendButtonSlot,
|
|
4612
|
-
footer:
|
|
5360
|
+
footer: mermaidActive && !isCompact ? /* @__PURE__ */ jsxRuntime.jsx(MermaidPreview, {}) : null
|
|
4613
5361
|
}
|
|
4614
5362
|
),
|
|
4615
5363
|
/* @__PURE__ */ jsxRuntime.jsx(KeyboardPlugin, { onSubmit: submit }),
|
|
4616
5364
|
/* @__PURE__ */ jsxRuntime.jsx(AutoFocusPlugin, { enabled: !!autoFocus }),
|
|
4617
5365
|
/* @__PURE__ */ jsxRuntime.jsx(PasteDropPlugin, {}),
|
|
4618
5366
|
markdownEnabled && /* @__PURE__ */ jsxRuntime.jsx(MarkdownPlugin, {}),
|
|
4619
|
-
features.slashCommands &&
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
onSubmit: submit
|
|
4624
|
-
}
|
|
4625
|
-
),
|
|
5367
|
+
features.slashCommands && // One typeahead per config — a single config or an array of them, so
|
|
5368
|
+
// consumers can register several trigger symbols (each with its own
|
|
5369
|
+
// action menu) at once. Keyed by trigger; give each a distinct symbol.
|
|
5370
|
+
(Array.isArray(features.slashCommands) ? features.slashCommands : [features.slashCommands]).map((cfg, i) => /* @__PURE__ */ jsxRuntime.jsx(SlashCommandPlugin, { config: cfg, onSubmit: submit }, cfg.trigger ?? `slash-${i}`)),
|
|
4626
5371
|
features.mentions && /* @__PURE__ */ jsxRuntime.jsx(MentionPlugin, { config: features.mentions }),
|
|
4627
5372
|
features.ghostedAutoComplete && /* @__PURE__ */ jsxRuntime.jsx(GhostedAutoCompletePlugin, { config: features.ghostedAutoComplete })
|
|
4628
5373
|
] });
|
|
4629
|
-
}
|
|
4630
|
-
function MermaidSlot() {
|
|
4631
|
-
const { features, mode } = useComposerContext();
|
|
4632
|
-
if (mode !== "markdown" || !features.mermaid) return null;
|
|
4633
|
-
return /* @__PURE__ */ jsxRuntime.jsx(MermaidPlugin, {});
|
|
5374
|
+
return mermaidActive ? /* @__PURE__ */ jsxRuntime.jsx(MermaidProvider, { children: content }) : content;
|
|
4634
5375
|
}
|
|
4635
5376
|
function SuggestionRow({ items, onSelect, className }) {
|
|
4636
5377
|
const { icons } = useComposerContext();
|