composeai 0.1.5 → 0.1.8
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 +931 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +188 -20
- package/dist/index.d.ts +188 -20
- package/dist/index.js +933 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/composer.css +293 -9
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createContext, forwardRef, useMemo, useState, useRef, useCallback, useEffect, useContext,
|
|
1
|
+
import { createContext, forwardRef, useMemo, useState, useRef, useCallback, useEffect, useContext, useId, isValidElement, cloneElement, useLayoutEffect } from 'react';
|
|
2
2
|
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
|
3
3
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
4
|
-
import { ParagraphNode, $getRoot, $createParagraphNode, ElementNode, $createTextNode, TextNode, addClassNamesToElement, $isElementNode, $isTextNode, $isParagraphNode,
|
|
4
|
+
import { ParagraphNode, $getRoot, $createParagraphNode, ElementNode, $createTextNode, TextNode, addClassNamesToElement, $isElementNode, $isTextNode, $isParagraphNode, $getSelection, $isRangeSelection, KEY_ENTER_COMMAND, COMMAND_PRIORITY_HIGH, PASTE_COMMAND, KEY_BACKSPACE_COMMAND, COMMAND_PRIORITY_LOW, KEY_TAB_COMMAND, KEY_ESCAPE_COMMAND, $applyNodeReplacement, $isLineBreakNode } from 'lexical';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
|
|
7
7
|
import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin';
|
|
@@ -393,6 +393,13 @@ var IconAttach = makeIcon(
|
|
|
393
393
|
/* @__PURE__ */ 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" })
|
|
394
394
|
] })
|
|
395
395
|
);
|
|
396
|
+
var IconPlus = makeIcon(
|
|
397
|
+
"IconPlus",
|
|
398
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
399
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5v14" }),
|
|
400
|
+
/* @__PURE__ */ jsx("path", { d: "M5 12h14" })
|
|
401
|
+
] })
|
|
402
|
+
);
|
|
396
403
|
var IconImage = makeIcon(
|
|
397
404
|
"IconImage",
|
|
398
405
|
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -481,6 +488,7 @@ var DEFAULT_ICONS = {
|
|
|
481
488
|
send: IconSend,
|
|
482
489
|
stop: IconStop,
|
|
483
490
|
attach: IconAttach,
|
|
491
|
+
plus: IconPlus,
|
|
484
492
|
image: IconImage,
|
|
485
493
|
voice: IconVoice,
|
|
486
494
|
voiceRecording: IconVoiceRecording,
|
|
@@ -506,6 +514,8 @@ var DEFAULT_FEATURES = {
|
|
|
506
514
|
voice: true,
|
|
507
515
|
mermaid: true,
|
|
508
516
|
web: true,
|
|
517
|
+
// No consumer-defined actions by default.
|
|
518
|
+
custom: [],
|
|
509
519
|
// Off by default — ghost autocomplete is an opt-in input affordance that
|
|
510
520
|
// only makes sense when the consumer has a curated list of completions.
|
|
511
521
|
ghostedAutoComplete: false
|
|
@@ -537,6 +547,7 @@ function normalizeFeatures(features) {
|
|
|
537
547
|
voice: features.voice ?? DEFAULT_FEATURES.voice,
|
|
538
548
|
mermaid: features.mermaid ?? DEFAULT_FEATURES.mermaid,
|
|
539
549
|
web: features.web ?? DEFAULT_FEATURES.web,
|
|
550
|
+
custom: features.custom ?? DEFAULT_FEATURES.custom,
|
|
540
551
|
ghostedAutoComplete: features.ghostedAutoComplete ?? DEFAULT_FEATURES.ghostedAutoComplete
|
|
541
552
|
};
|
|
542
553
|
}
|
|
@@ -552,6 +563,7 @@ function ComposerProvider({
|
|
|
552
563
|
closeMenusOnOutsideClick = true,
|
|
553
564
|
attachmentOptions,
|
|
554
565
|
mode = "markdown",
|
|
566
|
+
variant = "compact",
|
|
555
567
|
multiline = true,
|
|
556
568
|
submitOnEnter = true,
|
|
557
569
|
smartNewline = true,
|
|
@@ -730,6 +742,7 @@ function ComposerProvider({
|
|
|
730
742
|
attachmentOptions: normalizedAttachmentOptions,
|
|
731
743
|
markdownMode,
|
|
732
744
|
mode,
|
|
745
|
+
variant,
|
|
733
746
|
multiline,
|
|
734
747
|
submitOnEnter,
|
|
735
748
|
smartNewline,
|
|
@@ -762,6 +775,7 @@ function ComposerProvider({
|
|
|
762
775
|
normalizedAttachmentOptions,
|
|
763
776
|
markdownMode,
|
|
764
777
|
mode,
|
|
778
|
+
variant,
|
|
765
779
|
multiline,
|
|
766
780
|
submitOnEnter,
|
|
767
781
|
smartNewline,
|
|
@@ -786,8 +800,11 @@ function useComposerContext() {
|
|
|
786
800
|
}
|
|
787
801
|
function EditorShell({
|
|
788
802
|
placeholder,
|
|
803
|
+
animated,
|
|
789
804
|
mode,
|
|
805
|
+
variant,
|
|
790
806
|
multiline,
|
|
807
|
+
expanded,
|
|
791
808
|
header,
|
|
792
809
|
toolbar,
|
|
793
810
|
sendButton,
|
|
@@ -795,11 +812,18 @@ function EditorShell({
|
|
|
795
812
|
}) {
|
|
796
813
|
const { classNames, sx, dir } = useComposerContext();
|
|
797
814
|
const isMarkdown = mode === "markdown";
|
|
798
|
-
const
|
|
815
|
+
const isCompact = variant === "compact";
|
|
816
|
+
const compactInline = isCompact && !expanded;
|
|
817
|
+
const fillEditor = compactInline || !isCompact && !multiline;
|
|
818
|
+
const editorClass = isCompact ? "composer-editor composer-editor--compact" : multiline ? "composer-editor composer-editor--multiline" : "composer-editor composer-editor--inline";
|
|
799
819
|
const editor = slotProps("editor", editorClass, classNames, sx);
|
|
800
820
|
const editorResolved = resolveSx(sx?.editor);
|
|
801
821
|
const placeholderBase = mirrorEditorPadding(editorResolved);
|
|
802
|
-
const placeholderClass =
|
|
822
|
+
const placeholderClass = cn(
|
|
823
|
+
isCompact ? "composer-placeholder composer-placeholder--compact" : multiline ? "composer-placeholder composer-placeholder--multiline" : "composer-placeholder composer-placeholder--inline",
|
|
824
|
+
// Adds the blinking caret after the typewriter text.
|
|
825
|
+
animated && "composer-placeholder--animated"
|
|
826
|
+
);
|
|
803
827
|
const placeholderProps = slotProps(
|
|
804
828
|
"placeholder",
|
|
805
829
|
placeholderClass,
|
|
@@ -814,8 +838,9 @@ function EditorShell({
|
|
|
814
838
|
{
|
|
815
839
|
className: cn(
|
|
816
840
|
"composer-editor-block",
|
|
817
|
-
// Inline: the editor block is the flex child that fills the
|
|
818
|
-
|
|
841
|
+
// Inline + compact: the editor block is the flex child that fills the
|
|
842
|
+
// row between the leading actions and the trailing send cluster.
|
|
843
|
+
fillEditor && "composer-editor-block--inline"
|
|
819
844
|
),
|
|
820
845
|
children: isMarkdown ? /* @__PURE__ */ jsx(
|
|
821
846
|
RichTextPlugin,
|
|
@@ -834,6 +859,32 @@ function EditorShell({
|
|
|
834
859
|
)
|
|
835
860
|
}
|
|
836
861
|
);
|
|
862
|
+
if (isCompact) {
|
|
863
|
+
const actions = toolbar && /* @__PURE__ */ jsx("div", { className: "composer-compact-actions", children: toolbar });
|
|
864
|
+
const sendCluster = sendButton && /* @__PURE__ */ jsx("div", { className: "composer-compact-send", children: sendButton });
|
|
865
|
+
if (expanded) {
|
|
866
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
867
|
+
header,
|
|
868
|
+
editorBlock,
|
|
869
|
+
(actions || sendCluster) && /* @__PURE__ */ jsxs("div", { className: "composer-compact-footer", children: [
|
|
870
|
+
actions ?? /* @__PURE__ */ jsx("span", {}),
|
|
871
|
+
sendCluster
|
|
872
|
+
] }),
|
|
873
|
+
/* @__PURE__ */ jsx(HistoryPlugin, {}),
|
|
874
|
+
footer
|
|
875
|
+
] });
|
|
876
|
+
}
|
|
877
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
878
|
+
header,
|
|
879
|
+
/* @__PURE__ */ jsxs("div", { className: "composer-compact-row", children: [
|
|
880
|
+
actions,
|
|
881
|
+
editorBlock,
|
|
882
|
+
sendCluster
|
|
883
|
+
] }),
|
|
884
|
+
/* @__PURE__ */ jsx(HistoryPlugin, {}),
|
|
885
|
+
footer
|
|
886
|
+
] });
|
|
887
|
+
}
|
|
837
888
|
if (!multiline) {
|
|
838
889
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
839
890
|
header,
|
|
@@ -1224,6 +1275,63 @@ function $createLinkTextNode(text = "", url = "") {
|
|
|
1224
1275
|
function $isLinkTextNode(node) {
|
|
1225
1276
|
return node instanceof LinkTextNode;
|
|
1226
1277
|
}
|
|
1278
|
+
var BASE_CLASS = "composer-code-tok";
|
|
1279
|
+
var CodeTokenNode = class _CodeTokenNode extends TextNode {
|
|
1280
|
+
__codeKind;
|
|
1281
|
+
static getType() {
|
|
1282
|
+
return "composeai-code-token";
|
|
1283
|
+
}
|
|
1284
|
+
static clone(node) {
|
|
1285
|
+
return new _CodeTokenNode(node.__text, node.__codeKind, node.__key);
|
|
1286
|
+
}
|
|
1287
|
+
constructor(text = "", codeKind = "text", key) {
|
|
1288
|
+
super(text, key);
|
|
1289
|
+
this.__codeKind = codeKind;
|
|
1290
|
+
}
|
|
1291
|
+
getCodeKind() {
|
|
1292
|
+
return this.getLatest().__codeKind;
|
|
1293
|
+
}
|
|
1294
|
+
setCodeKind(kind) {
|
|
1295
|
+
const self = this.getWritable();
|
|
1296
|
+
self.__codeKind = kind;
|
|
1297
|
+
return self;
|
|
1298
|
+
}
|
|
1299
|
+
createDOM(config) {
|
|
1300
|
+
const dom = super.createDOM(config);
|
|
1301
|
+
addClassNamesToElement(dom, BASE_CLASS, `${BASE_CLASS}--${this.__codeKind}`);
|
|
1302
|
+
return dom;
|
|
1303
|
+
}
|
|
1304
|
+
updateDOM(prevNode, dom, config) {
|
|
1305
|
+
const updated = super.updateDOM(prevNode, dom, config);
|
|
1306
|
+
if (prevNode.__codeKind !== this.__codeKind) {
|
|
1307
|
+
dom.classList.remove(`${BASE_CLASS}--${prevNode.__codeKind}`);
|
|
1308
|
+
dom.classList.add(`${BASE_CLASS}--${this.__codeKind}`);
|
|
1309
|
+
}
|
|
1310
|
+
return updated;
|
|
1311
|
+
}
|
|
1312
|
+
static importJSON(serializedNode) {
|
|
1313
|
+
return $createCodeTokenNode("", serializedNode.codeKind).updateFromJSON(
|
|
1314
|
+
serializedNode
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
updateFromJSON(serializedNode) {
|
|
1318
|
+
return super.updateFromJSON(serializedNode);
|
|
1319
|
+
}
|
|
1320
|
+
exportJSON() {
|
|
1321
|
+
return {
|
|
1322
|
+
...super.exportJSON(),
|
|
1323
|
+
type: _CodeTokenNode.getType(),
|
|
1324
|
+
version: 1,
|
|
1325
|
+
codeKind: this.__codeKind
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
function $createCodeTokenNode(text = "", codeKind = "text") {
|
|
1330
|
+
return $applyNodeReplacement(new CodeTokenNode(text, codeKind));
|
|
1331
|
+
}
|
|
1332
|
+
function $isCodeTokenNode(node) {
|
|
1333
|
+
return node instanceof CodeTokenNode;
|
|
1334
|
+
}
|
|
1227
1335
|
var HEADING_RE = /^(#{1,6}) /;
|
|
1228
1336
|
var QUOTE_RE = /^> /;
|
|
1229
1337
|
var BULLET_RE = /^[-*+] /;
|
|
@@ -1297,12 +1405,20 @@ function $computeBlockMap() {
|
|
|
1297
1405
|
const map = /* @__PURE__ */ new Map();
|
|
1298
1406
|
const root = $getRoot();
|
|
1299
1407
|
let insideCode = false;
|
|
1408
|
+
let codeLang;
|
|
1300
1409
|
for (const child of root.getChildren()) {
|
|
1301
1410
|
if (!$isParagraphNode(child)) continue;
|
|
1302
|
-
|
|
1411
|
+
let info = $resolveBlockFor(child, insideCode);
|
|
1412
|
+
if (info.kind === "code-fence-open") {
|
|
1413
|
+
insideCode = true;
|
|
1414
|
+
codeLang = info.lang;
|
|
1415
|
+
} else if (info.kind === "code-fence-close") {
|
|
1416
|
+
insideCode = false;
|
|
1417
|
+
codeLang = void 0;
|
|
1418
|
+
} else if (info.kind === "code-line" && codeLang) {
|
|
1419
|
+
info = { ...info, lang: codeLang };
|
|
1420
|
+
}
|
|
1303
1421
|
map.set(child.getKey(), info);
|
|
1304
|
-
if (info.kind === "code-fence-open") insideCode = true;
|
|
1305
|
-
else if (info.kind === "code-fence-close") insideCode = false;
|
|
1306
1422
|
}
|
|
1307
1423
|
return map;
|
|
1308
1424
|
}
|
|
@@ -1508,11 +1624,6 @@ function $isInsideCodeFence() {
|
|
|
1508
1624
|
const block = $detectBlockFor(top);
|
|
1509
1625
|
return block.kind === "code-line" || block.kind === "code-fence-open";
|
|
1510
1626
|
}
|
|
1511
|
-
function $hasMultiLineContent() {
|
|
1512
|
-
const root = $getRoot();
|
|
1513
|
-
if (root.getChildrenSize() > 1) return true;
|
|
1514
|
-
return root.getTextContent().includes("\n");
|
|
1515
|
-
}
|
|
1516
1627
|
function $offsetWithinParagraph(paragraph, point) {
|
|
1517
1628
|
if (point.type === "element") {
|
|
1518
1629
|
const children = paragraph.getChildren();
|
|
@@ -1605,10 +1716,8 @@ function KeyboardPlugin({ onSubmit }) {
|
|
|
1605
1716
|
return true;
|
|
1606
1717
|
}
|
|
1607
1718
|
let inCodeFence = false;
|
|
1608
|
-
let hasMultiLine = false;
|
|
1609
1719
|
editor.getEditorState().read(() => {
|
|
1610
1720
|
inCodeFence = $isInsideCodeFence();
|
|
1611
|
-
hasMultiLine = $hasMultiLineContent();
|
|
1612
1721
|
});
|
|
1613
1722
|
if (inCodeFence) {
|
|
1614
1723
|
if (!multiline) {
|
|
@@ -1630,7 +1739,6 @@ function KeyboardPlugin({ onSubmit }) {
|
|
|
1630
1739
|
return true;
|
|
1631
1740
|
}
|
|
1632
1741
|
}
|
|
1633
|
-
if (smartNewline && hasMultiLine) return false;
|
|
1634
1742
|
if (!submitOnEnter) return false;
|
|
1635
1743
|
return trySubmit(event);
|
|
1636
1744
|
},
|
|
@@ -1760,6 +1868,134 @@ function PasteDropPlugin() {
|
|
|
1760
1868
|
return null;
|
|
1761
1869
|
}
|
|
1762
1870
|
|
|
1871
|
+
// src/plugins/mermaid-highlight.ts
|
|
1872
|
+
var KEYWORDS = /* @__PURE__ */ new Set([
|
|
1873
|
+
"flowchart",
|
|
1874
|
+
"graph",
|
|
1875
|
+
"sequenceDiagram",
|
|
1876
|
+
"classDiagram",
|
|
1877
|
+
"stateDiagram",
|
|
1878
|
+
"stateDiagram-v2",
|
|
1879
|
+
"erDiagram",
|
|
1880
|
+
"journey",
|
|
1881
|
+
"gantt",
|
|
1882
|
+
"pie",
|
|
1883
|
+
"gitGraph",
|
|
1884
|
+
"mindmap",
|
|
1885
|
+
"timeline",
|
|
1886
|
+
"quadrantChart",
|
|
1887
|
+
"requirementDiagram",
|
|
1888
|
+
"C4Context",
|
|
1889
|
+
"subgraph",
|
|
1890
|
+
"end",
|
|
1891
|
+
"direction",
|
|
1892
|
+
"participant",
|
|
1893
|
+
"actor",
|
|
1894
|
+
"note",
|
|
1895
|
+
"loop",
|
|
1896
|
+
"alt",
|
|
1897
|
+
"opt",
|
|
1898
|
+
"par",
|
|
1899
|
+
"and",
|
|
1900
|
+
"else",
|
|
1901
|
+
"rect",
|
|
1902
|
+
"activate",
|
|
1903
|
+
"deactivate",
|
|
1904
|
+
"class",
|
|
1905
|
+
"state",
|
|
1906
|
+
"click",
|
|
1907
|
+
"call",
|
|
1908
|
+
"href",
|
|
1909
|
+
"style",
|
|
1910
|
+
"linkStyle",
|
|
1911
|
+
"classDef",
|
|
1912
|
+
"section",
|
|
1913
|
+
"title",
|
|
1914
|
+
"accTitle",
|
|
1915
|
+
"accDescr",
|
|
1916
|
+
"over",
|
|
1917
|
+
"as",
|
|
1918
|
+
// flowchart directions
|
|
1919
|
+
"TB",
|
|
1920
|
+
"TD",
|
|
1921
|
+
"BT",
|
|
1922
|
+
"RL",
|
|
1923
|
+
"LR"
|
|
1924
|
+
]);
|
|
1925
|
+
var ARROW_CHARS = /* @__PURE__ */ new Set(["-", "=", ".", "<", ">"]);
|
|
1926
|
+
var WS_RE = /\s/;
|
|
1927
|
+
var WORD_RE = /[A-Za-z0-9_]/;
|
|
1928
|
+
var DIGIT_RE = /[0-9]/;
|
|
1929
|
+
function tokenizeMermaidLine(line) {
|
|
1930
|
+
const out = [];
|
|
1931
|
+
const push = (text, kind) => {
|
|
1932
|
+
if (text) out.push({ text, kind });
|
|
1933
|
+
};
|
|
1934
|
+
let i = 0;
|
|
1935
|
+
const n = line.length;
|
|
1936
|
+
while (i < n) {
|
|
1937
|
+
const c = line[i];
|
|
1938
|
+
if (WS_RE.test(c)) {
|
|
1939
|
+
let j = i + 1;
|
|
1940
|
+
while (j < n && WS_RE.test(line[j])) j++;
|
|
1941
|
+
push(line.slice(i, j), "text");
|
|
1942
|
+
i = j;
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
if (c === "%" && line[i + 1] === "%") {
|
|
1946
|
+
push(line.slice(i), "comment");
|
|
1947
|
+
i = n;
|
|
1948
|
+
continue;
|
|
1949
|
+
}
|
|
1950
|
+
if (c === '"') {
|
|
1951
|
+
let j = i + 1;
|
|
1952
|
+
while (j < n && line[j] !== '"') j++;
|
|
1953
|
+
j = Math.min(j + 1, n);
|
|
1954
|
+
push(line.slice(i, j), "string");
|
|
1955
|
+
i = j;
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
if ("[](){}|".includes(c)) {
|
|
1959
|
+
push(c, "punct");
|
|
1960
|
+
i++;
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
if (ARROW_CHARS.has(c)) {
|
|
1964
|
+
let j = i + 1;
|
|
1965
|
+
while (j < n && ARROW_CHARS.has(line[j])) j++;
|
|
1966
|
+
if (j < n && (line[j] === "x" || line[j] === "o") && j - i >= 2) j++;
|
|
1967
|
+
const run = line.slice(i, j);
|
|
1968
|
+
const isArrow = run.length >= 2 || run.includes(">") || run.includes("<");
|
|
1969
|
+
push(run, isArrow ? "arrow" : "punct");
|
|
1970
|
+
i = j;
|
|
1971
|
+
continue;
|
|
1972
|
+
}
|
|
1973
|
+
if (":;,&+*".includes(c)) {
|
|
1974
|
+
push(c, "punct");
|
|
1975
|
+
i++;
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
if (DIGIT_RE.test(c)) {
|
|
1979
|
+
let j = i + 1;
|
|
1980
|
+
while (j < n && /[0-9.]/.test(line[j])) j++;
|
|
1981
|
+
push(line.slice(i, j), "number");
|
|
1982
|
+
i = j;
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
if (WORD_RE.test(c)) {
|
|
1986
|
+
let j = i + 1;
|
|
1987
|
+
while (j < n && WORD_RE.test(line[j])) j++;
|
|
1988
|
+
const word = line.slice(i, j);
|
|
1989
|
+
push(word, KEYWORDS.has(word) ? "keyword" : "ident");
|
|
1990
|
+
i = j;
|
|
1991
|
+
continue;
|
|
1992
|
+
}
|
|
1993
|
+
push(c, "text");
|
|
1994
|
+
i++;
|
|
1995
|
+
}
|
|
1996
|
+
return out;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1763
1999
|
// src/plugins/markdown-tokenizer.ts
|
|
1764
2000
|
var PAIRED_PATTERNS = [
|
|
1765
2001
|
{ open: "**", close: "**", format: "bold" },
|
|
@@ -1871,6 +2107,13 @@ function readCurrentChildren(paragraph) {
|
|
|
1871
2107
|
});
|
|
1872
2108
|
} else if ($isMarkdownTokenNode(child)) {
|
|
1873
2109
|
out.push({ kind: "token", text: child.getTextContent(), format: 0 });
|
|
2110
|
+
} else if ($isCodeTokenNode(child)) {
|
|
2111
|
+
out.push({
|
|
2112
|
+
kind: "code",
|
|
2113
|
+
text: child.getTextContent(),
|
|
2114
|
+
format: 0,
|
|
2115
|
+
codeKind: child.getCodeKind()
|
|
2116
|
+
});
|
|
1874
2117
|
} else if ($isTextNode(child)) {
|
|
1875
2118
|
out.push({
|
|
1876
2119
|
kind: "text",
|
|
@@ -1893,6 +2136,7 @@ function nodesEqual(a, b) {
|
|
|
1893
2136
|
if (aKind !== bKind) return false;
|
|
1894
2137
|
if (ai.text !== bi.text) return false;
|
|
1895
2138
|
if (ai.format !== bi.format) return false;
|
|
2139
|
+
if (ai.codeKind !== bi.codeKind) return false;
|
|
1896
2140
|
}
|
|
1897
2141
|
return true;
|
|
1898
2142
|
}
|
|
@@ -2060,7 +2304,18 @@ function $applyStyling(paragraph, block, mode) {
|
|
|
2060
2304
|
trailingDropForOffsetMap = body.length;
|
|
2061
2305
|
body = "";
|
|
2062
2306
|
} else if (body.length > 0) {
|
|
2063
|
-
|
|
2307
|
+
if (block.kind === "code-line" && block.lang === "mermaid") {
|
|
2308
|
+
for (const tok of tokenizeMermaidLine(body)) {
|
|
2309
|
+
desired.push({
|
|
2310
|
+
kind: "code",
|
|
2311
|
+
text: tok.text,
|
|
2312
|
+
format: 0,
|
|
2313
|
+
codeKind: tok.kind
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
} else {
|
|
2317
|
+
desired.push({ kind: "text", text: body, format: 0 });
|
|
2318
|
+
}
|
|
2064
2319
|
}
|
|
2065
2320
|
} else if (block.kind === "hr") {
|
|
2066
2321
|
if (body.length > 0) {
|
|
@@ -2148,6 +2403,8 @@ function $applyStyling(paragraph, block, mode) {
|
|
|
2148
2403
|
for (const node of desired) {
|
|
2149
2404
|
if (node.kind === "token") {
|
|
2150
2405
|
paragraph.append($createMarkdownTokenNode(node.text));
|
|
2406
|
+
} else if (node.kind === "code") {
|
|
2407
|
+
paragraph.append($createCodeTokenNode(node.text, node.codeKind));
|
|
2151
2408
|
} else if (node.kind === "link") {
|
|
2152
2409
|
const t = $createLinkTextNode(node.text, node.url ?? "");
|
|
2153
2410
|
if (node.format !== 0) t.setFormat(node.format);
|
|
@@ -2409,10 +2666,13 @@ async function loadMermaid() {
|
|
|
2409
2666
|
function svgToDataUri(svg) {
|
|
2410
2667
|
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
|
|
2411
2668
|
}
|
|
2412
|
-
|
|
2669
|
+
var MermaidContext = createContext(null);
|
|
2670
|
+
function useMermaidContext() {
|
|
2671
|
+
return useContext(MermaidContext);
|
|
2672
|
+
}
|
|
2673
|
+
function MermaidProvider({ children }) {
|
|
2413
2674
|
const [editor] = useLexicalComposerContext();
|
|
2414
|
-
const { features,
|
|
2415
|
-
const { sparkle: SparkleIcon } = icons;
|
|
2675
|
+
const { features, renderDiagram } = useComposerContext();
|
|
2416
2676
|
const [diagrams, setDiagrams] = useState([]);
|
|
2417
2677
|
const keepSource = typeof features.mermaid === "object" ? features.mermaid.keepSource !== false : true;
|
|
2418
2678
|
useEffect(() => {
|
|
@@ -2420,10 +2680,10 @@ function MermaidPlugin() {
|
|
|
2420
2680
|
editor.getEditorState().read(() => {
|
|
2421
2681
|
const found = [];
|
|
2422
2682
|
const root = $getRoot();
|
|
2423
|
-
const
|
|
2683
|
+
const children2 = root.getChildren();
|
|
2424
2684
|
let i = 0;
|
|
2425
|
-
while (i <
|
|
2426
|
-
const opener =
|
|
2685
|
+
while (i < children2.length) {
|
|
2686
|
+
const opener = children2[i];
|
|
2427
2687
|
if (!$isParagraphNode(opener) || !FENCE_OPEN_MERMAID.test(opener.getTextContent())) {
|
|
2428
2688
|
i++;
|
|
2429
2689
|
continue;
|
|
@@ -2431,8 +2691,8 @@ function MermaidPlugin() {
|
|
|
2431
2691
|
const paragraphKeys = [opener.getKey()];
|
|
2432
2692
|
const codeLines = [];
|
|
2433
2693
|
let j = i + 1;
|
|
2434
|
-
while (j <
|
|
2435
|
-
const next =
|
|
2694
|
+
while (j < children2.length) {
|
|
2695
|
+
const next = children2[j];
|
|
2436
2696
|
if (!$isParagraphNode(next)) break;
|
|
2437
2697
|
const text = next.getTextContent();
|
|
2438
2698
|
paragraphKeys.push(next.getKey());
|
|
@@ -2496,26 +2756,190 @@ function MermaidPlugin() {
|
|
|
2496
2756
|
hiddenKeysRef.current.clear();
|
|
2497
2757
|
};
|
|
2498
2758
|
}, [editor]);
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
"composer-mermaid",
|
|
2503
|
-
classNames,
|
|
2504
|
-
sx
|
|
2759
|
+
const value = useMemo(
|
|
2760
|
+
() => ({ diagrams, renderDiagram }),
|
|
2761
|
+
[diagrams, renderDiagram]
|
|
2505
2762
|
);
|
|
2763
|
+
return /* @__PURE__ */ jsxs(MermaidContext.Provider, { value, children: [
|
|
2764
|
+
children,
|
|
2765
|
+
/* @__PURE__ */ jsx(MermaidBackground, {})
|
|
2766
|
+
] });
|
|
2767
|
+
}
|
|
2768
|
+
function MermaidBackground() {
|
|
2769
|
+
const ctx = useMermaidContext();
|
|
2770
|
+
if (!ctx || ctx.renderDiagram) return null;
|
|
2771
|
+
return /* @__PURE__ */ jsx(Fragment, { children: ctx.diagrams.map((d) => /* @__PURE__ */ jsx(DiagramBackdrop, { diagram: d }, d.id)) });
|
|
2772
|
+
}
|
|
2773
|
+
function DiagramBackdrop({ diagram }) {
|
|
2774
|
+
const [editor] = useLexicalComposerContext();
|
|
2775
|
+
const { svg } = useDiagramSvg(diagram);
|
|
2776
|
+
const [rect, setRect] = useState(null);
|
|
2777
|
+
const dataUri = useMemo(() => svg ? svgToDataUri(svg) : null, [svg]);
|
|
2778
|
+
useEffect(() => {
|
|
2779
|
+
if (!svg) {
|
|
2780
|
+
setRect(null);
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
const root = editor.getRootElement();
|
|
2784
|
+
const block2 = root?.parentElement;
|
|
2785
|
+
if (!root || !block2) return;
|
|
2786
|
+
let raf = 0;
|
|
2787
|
+
const measure = () => {
|
|
2788
|
+
raf = 0;
|
|
2789
|
+
const blockRect = block2.getBoundingClientRect();
|
|
2790
|
+
let top = Infinity;
|
|
2791
|
+
let left = Infinity;
|
|
2792
|
+
let right = -Infinity;
|
|
2793
|
+
let bottom = -Infinity;
|
|
2794
|
+
let any = false;
|
|
2795
|
+
for (const key of diagram.paragraphKeys) {
|
|
2796
|
+
const el = editor.getElementByKey(key);
|
|
2797
|
+
if (!el) continue;
|
|
2798
|
+
any = true;
|
|
2799
|
+
const r = el.getBoundingClientRect();
|
|
2800
|
+
top = Math.min(top, r.top);
|
|
2801
|
+
left = Math.min(left, r.left);
|
|
2802
|
+
right = Math.max(right, r.right);
|
|
2803
|
+
bottom = Math.max(bottom, r.bottom);
|
|
2804
|
+
}
|
|
2805
|
+
if (!any) {
|
|
2806
|
+
setRect(null);
|
|
2807
|
+
return;
|
|
2808
|
+
}
|
|
2809
|
+
const relTop = top - blockRect.top;
|
|
2810
|
+
const visTop = Math.max(0, relTop);
|
|
2811
|
+
const visBottom = Math.min(blockRect.height, relTop + (bottom - top));
|
|
2812
|
+
if (visBottom <= visTop) {
|
|
2813
|
+
setRect(null);
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2816
|
+
setRect({
|
|
2817
|
+
top: visTop,
|
|
2818
|
+
left: left - blockRect.left,
|
|
2819
|
+
width: right - left,
|
|
2820
|
+
height: visBottom - visTop
|
|
2821
|
+
});
|
|
2822
|
+
};
|
|
2823
|
+
const schedule = () => {
|
|
2824
|
+
if (!raf) raf = requestAnimationFrame(measure);
|
|
2825
|
+
};
|
|
2826
|
+
schedule();
|
|
2827
|
+
const unregister = editor.registerUpdateListener(schedule);
|
|
2828
|
+
root.addEventListener("scroll", schedule, { passive: true });
|
|
2829
|
+
const ro = new ResizeObserver(schedule);
|
|
2830
|
+
ro.observe(root);
|
|
2831
|
+
return () => {
|
|
2832
|
+
if (raf) cancelAnimationFrame(raf);
|
|
2833
|
+
unregister();
|
|
2834
|
+
root.removeEventListener("scroll", schedule);
|
|
2835
|
+
ro.disconnect();
|
|
2836
|
+
};
|
|
2837
|
+
}, [editor, svg, diagram.paragraphKeys]);
|
|
2838
|
+
const block = editor.getRootElement()?.parentElement;
|
|
2839
|
+
if (!dataUri || !rect || !block) return null;
|
|
2840
|
+
return createPortal(
|
|
2841
|
+
/* @__PURE__ */ jsx(
|
|
2842
|
+
"div",
|
|
2843
|
+
{
|
|
2844
|
+
"aria-hidden": true,
|
|
2845
|
+
className: "composer-mermaid-backdrop",
|
|
2846
|
+
style: {
|
|
2847
|
+
top: rect.top,
|
|
2848
|
+
left: rect.left,
|
|
2849
|
+
width: rect.width,
|
|
2850
|
+
height: rect.height,
|
|
2851
|
+
backgroundImage: `url("${dataUri}")`
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
),
|
|
2855
|
+
block
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2858
|
+
function MermaidPreview() {
|
|
2859
|
+
const ctx = useMermaidContext();
|
|
2860
|
+
const { icons, classNames, sx } = useComposerContext();
|
|
2861
|
+
const { sparkle: SparkleIcon } = icons;
|
|
2862
|
+
if (!ctx || ctx.diagrams.length === 0) return null;
|
|
2863
|
+
const preview = slotProps("mermaidPreview", "composer-mermaid", classNames, sx);
|
|
2506
2864
|
return /* @__PURE__ */ jsxs("div", { ...preview, children: [
|
|
2507
2865
|
/* @__PURE__ */ jsxs("div", { className: "composer-mermaid-head", children: [
|
|
2508
2866
|
/* @__PURE__ */ jsx(SparkleIcon, {}),
|
|
2509
2867
|
"Diagram preview"
|
|
2510
2868
|
] }),
|
|
2511
|
-
/* @__PURE__ */ jsx("div", { className: "composer-mermaid-row", children: diagrams.map((d) => /* @__PURE__ */ jsx(
|
|
2512
|
-
|
|
2869
|
+
/* @__PURE__ */ jsx("div", { className: "composer-mermaid-row", children: ctx.diagrams.map((d) => /* @__PURE__ */ jsx(DiagramTile, { diagram: d, renderDiagram: ctx.renderDiagram }, d.id)) })
|
|
2870
|
+
] });
|
|
2871
|
+
}
|
|
2872
|
+
function MermaidQuickAction() {
|
|
2873
|
+
const ctx = useMermaidContext();
|
|
2874
|
+
const { icons, closeMenusOnOutsideClick } = useComposerContext();
|
|
2875
|
+
const { sparkle: SparkleIcon } = icons;
|
|
2876
|
+
const [open, setOpen] = useState(false);
|
|
2877
|
+
const triggerRef = useRef(null);
|
|
2878
|
+
const popoverRef = useRef(null);
|
|
2879
|
+
const menuId = useId();
|
|
2880
|
+
useEffect(() => {
|
|
2881
|
+
if (!open) return;
|
|
2882
|
+
const onPointerDown = (event) => {
|
|
2883
|
+
if (!closeMenusOnOutsideClick) return;
|
|
2884
|
+
const target = event.target;
|
|
2885
|
+
if (!target) return;
|
|
2886
|
+
if (popoverRef.current?.contains(target)) return;
|
|
2887
|
+
if (triggerRef.current?.contains(target)) return;
|
|
2888
|
+
setOpen(false);
|
|
2889
|
+
};
|
|
2890
|
+
const onKeyDown = (event) => {
|
|
2891
|
+
if (event.key === "Escape") {
|
|
2892
|
+
event.preventDefault();
|
|
2893
|
+
setOpen(false);
|
|
2894
|
+
triggerRef.current?.focus();
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2897
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2898
|
+
document.addEventListener("keydown", onKeyDown);
|
|
2899
|
+
return () => {
|
|
2900
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2901
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
2902
|
+
};
|
|
2903
|
+
}, [open, closeMenusOnOutsideClick]);
|
|
2904
|
+
if (!ctx || ctx.diagrams.length === 0) return null;
|
|
2905
|
+
const count = ctx.diagrams.length;
|
|
2906
|
+
return /* @__PURE__ */ jsxs("div", { className: "composer-quick-actions", children: [
|
|
2907
|
+
/* @__PURE__ */ jsxs(
|
|
2908
|
+
"button",
|
|
2513
2909
|
{
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2910
|
+
ref: triggerRef,
|
|
2911
|
+
type: "button",
|
|
2912
|
+
"aria-label": `Diagram preview (${count})`,
|
|
2913
|
+
"aria-haspopup": "dialog",
|
|
2914
|
+
"aria-expanded": open,
|
|
2915
|
+
"aria-controls": open ? menuId : void 0,
|
|
2916
|
+
"data-active": open ? "" : void 0,
|
|
2917
|
+
onClick: () => setOpen((o) => !o),
|
|
2918
|
+
className: "composer-mermaid-trigger",
|
|
2919
|
+
children: [
|
|
2920
|
+
/* @__PURE__ */ jsx(DiagramThumb, { diagram: ctx.diagrams[0], renderDiagram: ctx.renderDiagram }),
|
|
2921
|
+
count > 1 && /* @__PURE__ */ jsx("span", { className: "composer-mermaid-count", children: count })
|
|
2922
|
+
]
|
|
2923
|
+
}
|
|
2924
|
+
),
|
|
2925
|
+
open && /* @__PURE__ */ jsxs(
|
|
2926
|
+
"div",
|
|
2927
|
+
{
|
|
2928
|
+
ref: popoverRef,
|
|
2929
|
+
id: menuId,
|
|
2930
|
+
role: "dialog",
|
|
2931
|
+
"aria-label": "Diagram preview",
|
|
2932
|
+
"data-composer-popover": "open",
|
|
2933
|
+
className: "composer-popover-in composer-mermaid-pop",
|
|
2934
|
+
children: [
|
|
2935
|
+
/* @__PURE__ */ jsxs("div", { className: "composer-mermaid-head", children: [
|
|
2936
|
+
/* @__PURE__ */ jsx(SparkleIcon, {}),
|
|
2937
|
+
"Diagram preview"
|
|
2938
|
+
] }),
|
|
2939
|
+
/* @__PURE__ */ jsx("div", { className: "composer-mermaid-row", children: ctx.diagrams.map((d) => /* @__PURE__ */ jsx(DiagramTile, { diagram: d, renderDiagram: ctx.renderDiagram }, d.id)) })
|
|
2940
|
+
]
|
|
2941
|
+
}
|
|
2942
|
+
)
|
|
2519
2943
|
] });
|
|
2520
2944
|
}
|
|
2521
2945
|
function DiagramTile({ diagram, renderDiagram }) {
|
|
@@ -2537,12 +2961,9 @@ function ConsumerTile({
|
|
|
2537
2961
|
}
|
|
2538
2962
|
return /* @__PURE__ */ jsx("div", { className: "composer-mermaid-tile", children: content });
|
|
2539
2963
|
}
|
|
2540
|
-
function
|
|
2541
|
-
const { icons } = useComposerContext();
|
|
2542
|
-
const { zoom: ZoomIcon } = icons;
|
|
2964
|
+
function useDiagramSvg(diagram) {
|
|
2543
2965
|
const [svg, setSvg] = useState(null);
|
|
2544
2966
|
const [error, setError] = useState(null);
|
|
2545
|
-
const [zoom, setZoom] = useState(false);
|
|
2546
2967
|
const [mermaidMissing, setMermaidMissing] = useState(false);
|
|
2547
2968
|
const renderId = useMemo(
|
|
2548
2969
|
() => `mermaid-${diagram.id}-${Math.random().toString(36).slice(2, 8)}`,
|
|
@@ -2575,6 +2996,13 @@ function MermaidTile({ diagram }) {
|
|
|
2575
2996
|
cancelled = true;
|
|
2576
2997
|
};
|
|
2577
2998
|
}, [diagram.code, renderId]);
|
|
2999
|
+
return { svg, error, mermaidMissing };
|
|
3000
|
+
}
|
|
3001
|
+
function MermaidTile({ diagram }) {
|
|
3002
|
+
const { icons } = useComposerContext();
|
|
3003
|
+
const { zoom: ZoomIcon } = icons;
|
|
3004
|
+
const [zoom, setZoom] = useState(false);
|
|
3005
|
+
const { svg, error, mermaidMissing } = useDiagramSvg(diagram);
|
|
2578
3006
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2579
3007
|
/* @__PURE__ */ jsx(
|
|
2580
3008
|
"button",
|
|
@@ -2610,6 +3038,35 @@ function MermaidTile({ diagram }) {
|
|
|
2610
3038
|
)
|
|
2611
3039
|
] });
|
|
2612
3040
|
}
|
|
3041
|
+
function DiagramThumb({ diagram, renderDiagram }) {
|
|
3042
|
+
if (renderDiagram) {
|
|
3043
|
+
return /* @__PURE__ */ jsx(ConsumerThumb, { diagram, renderDiagram });
|
|
3044
|
+
}
|
|
3045
|
+
return /* @__PURE__ */ jsx(MermaidThumb, { diagram });
|
|
3046
|
+
}
|
|
3047
|
+
function ConsumerThumb({
|
|
3048
|
+
diagram,
|
|
3049
|
+
renderDiagram
|
|
3050
|
+
}) {
|
|
3051
|
+
let content = null;
|
|
3052
|
+
try {
|
|
3053
|
+
content = renderDiagram({ code: diagram.code, language: "mermaid" });
|
|
3054
|
+
} catch {
|
|
3055
|
+
content = null;
|
|
3056
|
+
}
|
|
3057
|
+
return /* @__PURE__ */ jsx("span", { className: "composer-mermaid-thumb", children: content });
|
|
3058
|
+
}
|
|
3059
|
+
function MermaidThumb({ diagram }) {
|
|
3060
|
+
const { svg } = useDiagramSvg(diagram);
|
|
3061
|
+
if (!svg) return /* @__PURE__ */ jsx("span", { className: "composer-mermaid-thumb" });
|
|
3062
|
+
return /* @__PURE__ */ jsx(
|
|
3063
|
+
"span",
|
|
3064
|
+
{
|
|
3065
|
+
className: "composer-mermaid-thumb",
|
|
3066
|
+
dangerouslySetInnerHTML: { __html: svg }
|
|
3067
|
+
}
|
|
3068
|
+
);
|
|
3069
|
+
}
|
|
2613
3070
|
function SlashMenu({
|
|
2614
3071
|
options,
|
|
2615
3072
|
selectedIndex,
|
|
@@ -3872,8 +4329,284 @@ function AttachmentTypePicker({
|
|
|
3872
4329
|
)
|
|
3873
4330
|
] });
|
|
3874
4331
|
}
|
|
4332
|
+
function useCustomActionContext(submit) {
|
|
4333
|
+
const [editor] = useLexicalComposerContext();
|
|
4334
|
+
return useMemo(
|
|
4335
|
+
() => ({
|
|
4336
|
+
insertText: (text) => {
|
|
4337
|
+
editor.update(() => {
|
|
4338
|
+
const sel = $getSelection();
|
|
4339
|
+
if ($isRangeSelection(sel)) sel.insertText(text);
|
|
4340
|
+
});
|
|
4341
|
+
focusEditor(editor);
|
|
4342
|
+
},
|
|
4343
|
+
insertMarkdown: (md) => {
|
|
4344
|
+
editor.update(() => {
|
|
4345
|
+
$insertTextWithParagraphBreaks(md);
|
|
4346
|
+
});
|
|
4347
|
+
focusEditor(editor);
|
|
4348
|
+
},
|
|
4349
|
+
submit
|
|
4350
|
+
}),
|
|
4351
|
+
[editor, submit]
|
|
4352
|
+
);
|
|
4353
|
+
}
|
|
4354
|
+
function CustomActionButtons({ submit }) {
|
|
4355
|
+
const { features, classNames, sx } = useComposerContext();
|
|
4356
|
+
const ctx = useCustomActionContext(submit);
|
|
4357
|
+
const actions = features.custom;
|
|
4358
|
+
const toolbarBtn = slotProps(
|
|
4359
|
+
"toolbarButton",
|
|
4360
|
+
"composer-toolbar-btn",
|
|
4361
|
+
classNames,
|
|
4362
|
+
sx
|
|
4363
|
+
);
|
|
4364
|
+
if (actions.length === 0) return null;
|
|
4365
|
+
return /* @__PURE__ */ jsx(Fragment, { children: actions.map((action, i) => /* @__PURE__ */ jsx(Tooltip, { content: action.title, side: "top", children: /* @__PURE__ */ jsx(
|
|
4366
|
+
"button",
|
|
4367
|
+
{
|
|
4368
|
+
type: "button",
|
|
4369
|
+
"aria-label": action.title,
|
|
4370
|
+
"aria-pressed": action.active,
|
|
4371
|
+
"data-active": action.active ? "" : void 0,
|
|
4372
|
+
disabled: action.disabled,
|
|
4373
|
+
onClick: () => action.onClick(ctx),
|
|
4374
|
+
...toolbarBtn,
|
|
4375
|
+
children: action.icon
|
|
4376
|
+
}
|
|
4377
|
+
) }, action.id ?? i)) });
|
|
4378
|
+
}
|
|
4379
|
+
function QuickActionsMenu({ extras, submit }) {
|
|
4380
|
+
const {
|
|
4381
|
+
features,
|
|
4382
|
+
attachmentsConfig,
|
|
4383
|
+
addFiles,
|
|
4384
|
+
webEnabled,
|
|
4385
|
+
toggleWeb,
|
|
4386
|
+
icons,
|
|
4387
|
+
classNames,
|
|
4388
|
+
sx,
|
|
4389
|
+
closeMenusOnOutsideClick
|
|
4390
|
+
} = useComposerContext();
|
|
4391
|
+
const {
|
|
4392
|
+
plus: PlusIcon,
|
|
4393
|
+
attach: AttachIcon,
|
|
4394
|
+
image: ImageIcon,
|
|
4395
|
+
web: WebIcon
|
|
4396
|
+
} = icons;
|
|
4397
|
+
const [open, setOpen] = useState(false);
|
|
4398
|
+
const triggerRef = useRef(null);
|
|
4399
|
+
const popoverRef = useRef(null);
|
|
4400
|
+
const fileInputRef = useRef(null);
|
|
4401
|
+
const imageInputRef = useRef(null);
|
|
4402
|
+
const menuId = useId();
|
|
4403
|
+
const attachmentsEnabled = !!features.attachments;
|
|
4404
|
+
const showFileBtn = attachmentsEnabled && attachmentsConfig.file !== false;
|
|
4405
|
+
const showImageBtn = attachmentsEnabled && attachmentsConfig.image !== false;
|
|
4406
|
+
const types = showFileBtn && Array.isArray(attachmentsConfig.types) ? attachmentsConfig.types : [];
|
|
4407
|
+
const hasTypePicker = types.length > 0;
|
|
4408
|
+
const customActions = features.custom;
|
|
4409
|
+
const actionCtx = useCustomActionContext(submit ?? (() => {
|
|
4410
|
+
}));
|
|
4411
|
+
const hasAnyAction = showFileBtn || showImageBtn || features.web || customActions.length > 0 || !!extras;
|
|
4412
|
+
const close = useCallback(() => setOpen(false), []);
|
|
4413
|
+
const openFilePicker = useCallback((accept) => {
|
|
4414
|
+
const input = fileInputRef.current;
|
|
4415
|
+
if (!input) return;
|
|
4416
|
+
input.accept = accept ?? attachmentsConfig.accept ?? "";
|
|
4417
|
+
input.click();
|
|
4418
|
+
}, [attachmentsConfig.accept]);
|
|
4419
|
+
useEffect(() => {
|
|
4420
|
+
if (!open) return;
|
|
4421
|
+
const onPointerDown = (event) => {
|
|
4422
|
+
if (!closeMenusOnOutsideClick) return;
|
|
4423
|
+
const target = event.target;
|
|
4424
|
+
if (!target) return;
|
|
4425
|
+
if (popoverRef.current?.contains(target)) return;
|
|
4426
|
+
if (triggerRef.current?.contains(target)) return;
|
|
4427
|
+
close();
|
|
4428
|
+
};
|
|
4429
|
+
const onKeyDown = (event) => {
|
|
4430
|
+
if (event.key === "Escape") {
|
|
4431
|
+
event.preventDefault();
|
|
4432
|
+
close();
|
|
4433
|
+
triggerRef.current?.focus();
|
|
4434
|
+
}
|
|
4435
|
+
};
|
|
4436
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
4437
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4438
|
+
return () => {
|
|
4439
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
4440
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
4441
|
+
};
|
|
4442
|
+
}, [open, closeMenusOnOutsideClick, close]);
|
|
4443
|
+
if (!hasAnyAction) return null;
|
|
4444
|
+
const triggerBtn = slotProps(
|
|
4445
|
+
"toolbarButton",
|
|
4446
|
+
"composer-toolbar-btn",
|
|
4447
|
+
classNames,
|
|
4448
|
+
sx
|
|
4449
|
+
);
|
|
4450
|
+
return /* @__PURE__ */ jsxs("div", { className: "composer-quick-actions", children: [
|
|
4451
|
+
showFileBtn && /* @__PURE__ */ jsx(
|
|
4452
|
+
"input",
|
|
4453
|
+
{
|
|
4454
|
+
ref: fileInputRef,
|
|
4455
|
+
type: "file",
|
|
4456
|
+
multiple: true,
|
|
4457
|
+
accept: attachmentsConfig.accept,
|
|
4458
|
+
hidden: true,
|
|
4459
|
+
onChange: (e) => {
|
|
4460
|
+
const files = e.target.files;
|
|
4461
|
+
if (files) addFiles(Array.from(files));
|
|
4462
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
4463
|
+
}
|
|
4464
|
+
}
|
|
4465
|
+
),
|
|
4466
|
+
showImageBtn && /* @__PURE__ */ jsx(
|
|
4467
|
+
"input",
|
|
4468
|
+
{
|
|
4469
|
+
ref: imageInputRef,
|
|
4470
|
+
type: "file",
|
|
4471
|
+
multiple: true,
|
|
4472
|
+
accept: "image/*",
|
|
4473
|
+
hidden: true,
|
|
4474
|
+
onChange: (e) => {
|
|
4475
|
+
const files = e.target.files;
|
|
4476
|
+
if (files) addFiles(Array.from(files));
|
|
4477
|
+
if (imageInputRef.current) imageInputRef.current.value = "";
|
|
4478
|
+
}
|
|
4479
|
+
}
|
|
4480
|
+
),
|
|
4481
|
+
/* @__PURE__ */ jsx(
|
|
4482
|
+
"button",
|
|
4483
|
+
{
|
|
4484
|
+
ref: triggerRef,
|
|
4485
|
+
type: "button",
|
|
4486
|
+
"aria-label": "Quick actions",
|
|
4487
|
+
"aria-haspopup": "menu",
|
|
4488
|
+
"aria-expanded": open,
|
|
4489
|
+
"aria-controls": open ? menuId : void 0,
|
|
4490
|
+
"data-active": open ? "" : void 0,
|
|
4491
|
+
onClick: () => setOpen((o) => !o),
|
|
4492
|
+
...triggerBtn,
|
|
4493
|
+
children: /* @__PURE__ */ jsx(PlusIcon, {})
|
|
4494
|
+
}
|
|
4495
|
+
),
|
|
4496
|
+
open && /* @__PURE__ */ jsxs(
|
|
4497
|
+
"div",
|
|
4498
|
+
{
|
|
4499
|
+
ref: popoverRef,
|
|
4500
|
+
id: menuId,
|
|
4501
|
+
role: "menu",
|
|
4502
|
+
"aria-label": "Quick actions",
|
|
4503
|
+
"data-composer-popover": "open",
|
|
4504
|
+
className: "composer-popover-in composer-attach-menu composer-quick-menu",
|
|
4505
|
+
children: [
|
|
4506
|
+
showFileBtn && !hasTypePicker && /* @__PURE__ */ jsx(
|
|
4507
|
+
ActionItem,
|
|
4508
|
+
{
|
|
4509
|
+
icon: /* @__PURE__ */ jsx(AttachIcon, {}),
|
|
4510
|
+
label: "Attach file",
|
|
4511
|
+
onClick: () => {
|
|
4512
|
+
close();
|
|
4513
|
+
openFilePicker();
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
),
|
|
4517
|
+
hasTypePicker && types.map((type) => /* @__PURE__ */ jsx(
|
|
4518
|
+
ActionItem,
|
|
4519
|
+
{
|
|
4520
|
+
icon: type.icon ?? /* @__PURE__ */ jsx(AttachIcon, {}),
|
|
4521
|
+
label: type.label,
|
|
4522
|
+
description: type.description,
|
|
4523
|
+
onClick: () => {
|
|
4524
|
+
close();
|
|
4525
|
+
openFilePicker(type.accept);
|
|
4526
|
+
}
|
|
4527
|
+
},
|
|
4528
|
+
type.id
|
|
4529
|
+
)),
|
|
4530
|
+
showImageBtn && /* @__PURE__ */ jsx(
|
|
4531
|
+
ActionItem,
|
|
4532
|
+
{
|
|
4533
|
+
icon: /* @__PURE__ */ jsx(ImageIcon, {}),
|
|
4534
|
+
label: "Add image",
|
|
4535
|
+
onClick: () => {
|
|
4536
|
+
close();
|
|
4537
|
+
imageInputRef.current?.click();
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
),
|
|
4541
|
+
features.web && /* @__PURE__ */ jsx(
|
|
4542
|
+
ActionItem,
|
|
4543
|
+
{
|
|
4544
|
+
icon: /* @__PURE__ */ jsx(WebIcon, {}),
|
|
4545
|
+
label: "Search the web",
|
|
4546
|
+
active: webEnabled,
|
|
4547
|
+
onClick: () => {
|
|
4548
|
+
toggleWeb();
|
|
4549
|
+
close();
|
|
4550
|
+
}
|
|
4551
|
+
}
|
|
4552
|
+
),
|
|
4553
|
+
customActions.map((action, i) => /* @__PURE__ */ jsx(
|
|
4554
|
+
ActionItem,
|
|
4555
|
+
{
|
|
4556
|
+
icon: action.icon,
|
|
4557
|
+
label: action.title,
|
|
4558
|
+
active: action.active,
|
|
4559
|
+
disabled: action.disabled,
|
|
4560
|
+
onClick: () => {
|
|
4561
|
+
action.onClick(actionCtx);
|
|
4562
|
+
close();
|
|
4563
|
+
}
|
|
4564
|
+
},
|
|
4565
|
+
action.id ?? i
|
|
4566
|
+
)),
|
|
4567
|
+
extras && /* @__PURE__ */ jsx("div", { className: "composer-quick-extras", children: extras })
|
|
4568
|
+
]
|
|
4569
|
+
}
|
|
4570
|
+
)
|
|
4571
|
+
] });
|
|
4572
|
+
}
|
|
4573
|
+
function ActionItem({
|
|
4574
|
+
icon,
|
|
4575
|
+
label,
|
|
4576
|
+
description,
|
|
4577
|
+
active,
|
|
4578
|
+
disabled,
|
|
4579
|
+
onClick
|
|
4580
|
+
}) {
|
|
4581
|
+
return /* @__PURE__ */ jsxs(
|
|
4582
|
+
"button",
|
|
4583
|
+
{
|
|
4584
|
+
role: "menuitem",
|
|
4585
|
+
type: "button",
|
|
4586
|
+
"aria-pressed": active,
|
|
4587
|
+
"data-active": active ? "" : void 0,
|
|
4588
|
+
disabled,
|
|
4589
|
+
onClick,
|
|
4590
|
+
className: cn("composer-attach-item"),
|
|
4591
|
+
children: [
|
|
4592
|
+
/* @__PURE__ */ jsx("span", { className: "composer-attach-item-icon", children: icon }),
|
|
4593
|
+
/* @__PURE__ */ jsx("span", { className: "composer-attach-item-label", children: label }),
|
|
4594
|
+
description ? /* @__PURE__ */ jsx("span", { className: "composer-attach-item-desc", children: description }) : null
|
|
4595
|
+
]
|
|
4596
|
+
}
|
|
4597
|
+
);
|
|
4598
|
+
}
|
|
3875
4599
|
var TOOLBAR_BTN_BASE = "composer-toolbar-btn";
|
|
3876
|
-
function Toolbar({ extras }) {
|
|
4600
|
+
function Toolbar({ extras, variant = "full", submit }) {
|
|
4601
|
+
if (variant === "compact") {
|
|
4602
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4603
|
+
/* @__PURE__ */ jsx(QuickActionsMenu, { extras, submit }),
|
|
4604
|
+
/* @__PURE__ */ jsx(MermaidQuickAction, {})
|
|
4605
|
+
] });
|
|
4606
|
+
}
|
|
4607
|
+
return /* @__PURE__ */ jsx(FullToolbar, { extras, submit });
|
|
4608
|
+
}
|
|
4609
|
+
function FullToolbar({ extras, submit }) {
|
|
3877
4610
|
const {
|
|
3878
4611
|
features,
|
|
3879
4612
|
attachmentsConfig,
|
|
@@ -3972,6 +4705,8 @@ function Toolbar({ extras }) {
|
|
|
3972
4705
|
]
|
|
3973
4706
|
}
|
|
3974
4707
|
),
|
|
4708
|
+
/* @__PURE__ */ jsx(CustomActionButtons, { submit: submit ?? (() => {
|
|
4709
|
+
}) }),
|
|
3975
4710
|
extras
|
|
3976
4711
|
] });
|
|
3977
4712
|
}
|
|
@@ -4065,7 +4800,6 @@ function HintBar({ hint }) {
|
|
|
4065
4800
|
const {
|
|
4066
4801
|
multiline,
|
|
4067
4802
|
submitOnEnter,
|
|
4068
|
-
smartNewline,
|
|
4069
4803
|
focusShortcut,
|
|
4070
4804
|
classNames,
|
|
4071
4805
|
sx
|
|
@@ -4079,16 +4813,6 @@ function HintBar({ hint }) {
|
|
|
4079
4813
|
" to send."
|
|
4080
4814
|
] });
|
|
4081
4815
|
}
|
|
4082
|
-
if (smartNewline && submitOnEnter) {
|
|
4083
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4084
|
-
"Press ",
|
|
4085
|
-
/* @__PURE__ */ jsx(Key, { children: "Enter" }),
|
|
4086
|
-
" to send a single line,",
|
|
4087
|
-
" ",
|
|
4088
|
-
/* @__PURE__ */ jsx(Key, { children: "\u2318/Ctrl + Enter" }),
|
|
4089
|
-
" to send once you've started a new line."
|
|
4090
|
-
] });
|
|
4091
|
-
}
|
|
4092
4816
|
if (!submitOnEnter) {
|
|
4093
4817
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4094
4818
|
"Press ",
|
|
@@ -4105,7 +4829,7 @@ function HintBar({ hint }) {
|
|
|
4105
4829
|
/* @__PURE__ */ jsx(Key, { children: "Shift + Enter" }),
|
|
4106
4830
|
" for newline."
|
|
4107
4831
|
] });
|
|
4108
|
-
}, [multiline, submitOnEnter
|
|
4832
|
+
}, [multiline, submitOnEnter]);
|
|
4109
4833
|
const focusHint = useMemo(() => {
|
|
4110
4834
|
if (!focusShortcut) return null;
|
|
4111
4835
|
const label = formatShortcut(focusShortcut);
|
|
@@ -4208,6 +4932,107 @@ function useComposerHandle(ref, onSubmit) {
|
|
|
4208
4932
|
};
|
|
4209
4933
|
}, [editor, ref, onSubmit, addFiles]);
|
|
4210
4934
|
}
|
|
4935
|
+
var DEFAULT_TIMING = {
|
|
4936
|
+
typeSpeed: 55,
|
|
4937
|
+
deleteSpeed: 28,
|
|
4938
|
+
holdDuration: 1800,
|
|
4939
|
+
pauseDuration: 450
|
|
4940
|
+
};
|
|
4941
|
+
function useAnimatedPlaceholder(phrases, enabled, options) {
|
|
4942
|
+
const [state, setState] = useState({
|
|
4943
|
+
text: "",
|
|
4944
|
+
active: true
|
|
4945
|
+
});
|
|
4946
|
+
const optsRef = useRef({});
|
|
4947
|
+
optsRef.current = options ?? {};
|
|
4948
|
+
const active = enabled && Array.isArray(phrases) && phrases.length > 0;
|
|
4949
|
+
const key = active ? phrases.join("\u241F") : "";
|
|
4950
|
+
const loop = !!options?.loop;
|
|
4951
|
+
const phrasesRef = useRef(phrases);
|
|
4952
|
+
phrasesRef.current = phrases;
|
|
4953
|
+
useEffect(() => {
|
|
4954
|
+
if (!active) {
|
|
4955
|
+
setState({ text: "", active: true });
|
|
4956
|
+
return;
|
|
4957
|
+
}
|
|
4958
|
+
const list = phrasesRef.current;
|
|
4959
|
+
const lastIdx = list.length - 1;
|
|
4960
|
+
const reduceMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
4961
|
+
if (reduceMotion) {
|
|
4962
|
+
setState({ text: list[0], active: false });
|
|
4963
|
+
return;
|
|
4964
|
+
}
|
|
4965
|
+
let timer;
|
|
4966
|
+
let phraseIdx = 0;
|
|
4967
|
+
let charIdx = 0;
|
|
4968
|
+
let phase = "typing";
|
|
4969
|
+
const tick = () => {
|
|
4970
|
+
const timing = { ...DEFAULT_TIMING, ...optsRef.current };
|
|
4971
|
+
const { typeSpeed, deleteSpeed, holdDuration, pauseDuration } = timing;
|
|
4972
|
+
const settleTo = optsRef.current.settleTo;
|
|
4973
|
+
const current = list[phraseIdx];
|
|
4974
|
+
const isLast = phraseIdx === lastIdx;
|
|
4975
|
+
switch (phase) {
|
|
4976
|
+
case "typing": {
|
|
4977
|
+
charIdx += 1;
|
|
4978
|
+
setState({ text: current.slice(0, charIdx), active: true });
|
|
4979
|
+
if (charIdx >= current.length) {
|
|
4980
|
+
phase = "holding";
|
|
4981
|
+
timer = setTimeout(tick, holdDuration);
|
|
4982
|
+
} else {
|
|
4983
|
+
timer = setTimeout(tick, typeSpeed);
|
|
4984
|
+
}
|
|
4985
|
+
break;
|
|
4986
|
+
}
|
|
4987
|
+
case "holding": {
|
|
4988
|
+
if (!loop && isLast) {
|
|
4989
|
+
if (settleTo !== void 0) {
|
|
4990
|
+
phase = "settling";
|
|
4991
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
4992
|
+
} else {
|
|
4993
|
+
setState({ text: current, active: false });
|
|
4994
|
+
}
|
|
4995
|
+
} else {
|
|
4996
|
+
phase = "deleting";
|
|
4997
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
4998
|
+
}
|
|
4999
|
+
break;
|
|
5000
|
+
}
|
|
5001
|
+
case "deleting": {
|
|
5002
|
+
charIdx -= 1;
|
|
5003
|
+
setState({ text: current.slice(0, Math.max(0, charIdx)), active: true });
|
|
5004
|
+
if (charIdx <= 0) {
|
|
5005
|
+
phase = "pausing";
|
|
5006
|
+
timer = setTimeout(tick, pauseDuration);
|
|
5007
|
+
} else {
|
|
5008
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
5009
|
+
}
|
|
5010
|
+
break;
|
|
5011
|
+
}
|
|
5012
|
+
case "pausing": {
|
|
5013
|
+
phraseIdx = phraseIdx >= lastIdx ? 0 : phraseIdx + 1;
|
|
5014
|
+
charIdx = 0;
|
|
5015
|
+
phase = "typing";
|
|
5016
|
+
timer = setTimeout(tick, typeSpeed);
|
|
5017
|
+
break;
|
|
5018
|
+
}
|
|
5019
|
+
case "settling": {
|
|
5020
|
+
charIdx -= 1;
|
|
5021
|
+
if (charIdx <= 0) {
|
|
5022
|
+
setState({ text: settleTo ?? "", active: false });
|
|
5023
|
+
} else {
|
|
5024
|
+
setState({ text: current.slice(0, charIdx), active: true });
|
|
5025
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
5026
|
+
}
|
|
5027
|
+
break;
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
};
|
|
5031
|
+
timer = setTimeout(tick, DEFAULT_TIMING.typeSpeed);
|
|
5032
|
+
return () => clearTimeout(timer);
|
|
5033
|
+
}, [active, key, loop]);
|
|
5034
|
+
return active ? state : null;
|
|
5035
|
+
}
|
|
4211
5036
|
|
|
4212
5037
|
// src/internal/shortcut.ts
|
|
4213
5038
|
var MODIFIERS = /* @__PURE__ */ new Set([
|
|
@@ -4285,6 +5110,7 @@ function matchesShortcut(parsed, event) {
|
|
|
4285
5110
|
var Composer = forwardRef(function Composer2(props, ref) {
|
|
4286
5111
|
const {
|
|
4287
5112
|
placeholder = "Send a message\u2026",
|
|
5113
|
+
animatedPlaceholder,
|
|
4288
5114
|
onSend,
|
|
4289
5115
|
onStop,
|
|
4290
5116
|
isStreaming,
|
|
@@ -4303,6 +5129,7 @@ var Composer = forwardRef(function Composer2(props, ref) {
|
|
|
4303
5129
|
toolbarExtras,
|
|
4304
5130
|
closeMenusOnOutsideClick = true,
|
|
4305
5131
|
mode = "markdown",
|
|
5132
|
+
variant = "compact",
|
|
4306
5133
|
multiline = true,
|
|
4307
5134
|
submitOnEnter = true,
|
|
4308
5135
|
smartNewline = true,
|
|
@@ -4313,6 +5140,7 @@ var Composer = forwardRef(function Composer2(props, ref) {
|
|
|
4313
5140
|
attachmentOptions,
|
|
4314
5141
|
dir
|
|
4315
5142
|
} = props;
|
|
5143
|
+
const hasPlaceholder = props.placeholder != null;
|
|
4316
5144
|
const tokenStyle = useMemo(() => {
|
|
4317
5145
|
const derived = color ? deriveColorTokens(color) : null;
|
|
4318
5146
|
if (!derived && !tokens) return void 0;
|
|
@@ -4331,6 +5159,7 @@ var Composer = forwardRef(function Composer2(props, ref) {
|
|
|
4331
5159
|
closeMenusOnOutsideClick,
|
|
4332
5160
|
attachmentOptions,
|
|
4333
5161
|
mode,
|
|
5162
|
+
variant,
|
|
4334
5163
|
multiline,
|
|
4335
5164
|
submitOnEnter,
|
|
4336
5165
|
smartNewline,
|
|
@@ -4355,6 +5184,8 @@ var Composer = forwardRef(function Composer2(props, ref) {
|
|
|
4355
5184
|
ComposerCard,
|
|
4356
5185
|
{
|
|
4357
5186
|
placeholder,
|
|
5187
|
+
animatedPlaceholder,
|
|
5188
|
+
hasPlaceholder,
|
|
4358
5189
|
initialValue,
|
|
4359
5190
|
handleRef: ref,
|
|
4360
5191
|
onSend,
|
|
@@ -4365,6 +5196,7 @@ var Composer = forwardRef(function Composer2(props, ref) {
|
|
|
4365
5196
|
isStreaming: !!isStreaming,
|
|
4366
5197
|
toolbarExtras,
|
|
4367
5198
|
mode,
|
|
5199
|
+
variant,
|
|
4368
5200
|
multiline
|
|
4369
5201
|
}
|
|
4370
5202
|
),
|
|
@@ -4385,11 +5217,14 @@ var RICH_NODES = [
|
|
|
4385
5217
|
MarkdownTokenNode,
|
|
4386
5218
|
BlockParagraphNode,
|
|
4387
5219
|
LinkTextNode,
|
|
5220
|
+
CodeTokenNode,
|
|
4388
5221
|
BLOCK_PARAGRAPH_REPLACEMENT
|
|
4389
5222
|
];
|
|
4390
5223
|
var PLAIN_NODES = [MentionNode];
|
|
4391
5224
|
function ComposerCard({
|
|
4392
5225
|
placeholder,
|
|
5226
|
+
animatedPlaceholder,
|
|
5227
|
+
hasPlaceholder,
|
|
4393
5228
|
initialValue,
|
|
4394
5229
|
handleRef,
|
|
4395
5230
|
onSend,
|
|
@@ -4400,6 +5235,7 @@ function ComposerCard({
|
|
|
4400
5235
|
isStreaming,
|
|
4401
5236
|
toolbarExtras,
|
|
4402
5237
|
mode,
|
|
5238
|
+
variant,
|
|
4403
5239
|
multiline
|
|
4404
5240
|
}) {
|
|
4405
5241
|
const { webEnabled, isDraggingFiles, classNames, sx } = useComposerContext();
|
|
@@ -4420,7 +5256,8 @@ function ComposerCard({
|
|
|
4420
5256
|
"div",
|
|
4421
5257
|
{
|
|
4422
5258
|
"data-composer-root": "",
|
|
4423
|
-
"data-composer-
|
|
5259
|
+
"data-composer-variant": variant,
|
|
5260
|
+
"data-composer-inline": variant === "full" && !multiline ? "" : void 0,
|
|
4424
5261
|
"data-composer-web": webEnabled ? "" : void 0,
|
|
4425
5262
|
"data-composer-dragging": isDraggingFiles ? "" : void 0,
|
|
4426
5263
|
...card,
|
|
@@ -4449,7 +5286,10 @@ function ComposerCard({
|
|
|
4449
5286
|
ComposerInner,
|
|
4450
5287
|
{
|
|
4451
5288
|
placeholder,
|
|
5289
|
+
animatedPlaceholder,
|
|
5290
|
+
hasPlaceholder,
|
|
4452
5291
|
mode,
|
|
5292
|
+
variant,
|
|
4453
5293
|
multiline,
|
|
4454
5294
|
handleRef,
|
|
4455
5295
|
onSend,
|
|
@@ -4468,7 +5308,10 @@ function ComposerCard({
|
|
|
4468
5308
|
}
|
|
4469
5309
|
function ComposerInner({
|
|
4470
5310
|
placeholder,
|
|
5311
|
+
animatedPlaceholder,
|
|
5312
|
+
hasPlaceholder,
|
|
4471
5313
|
mode,
|
|
5314
|
+
variant,
|
|
4472
5315
|
multiline,
|
|
4473
5316
|
handleRef,
|
|
4474
5317
|
onSend,
|
|
@@ -4496,6 +5339,9 @@ function ComposerInner({
|
|
|
4496
5339
|
const [hasText, setHasText] = useState(
|
|
4497
5340
|
!!initialValue && initialValue.trim().length > 0
|
|
4498
5341
|
);
|
|
5342
|
+
const [isMultiLine, setIsMultiLine] = useState(
|
|
5343
|
+
!!initialValue && initialValue.includes("\n")
|
|
5344
|
+
);
|
|
4499
5345
|
const onSendRef = useRef(onSend);
|
|
4500
5346
|
onSendRef.current = onSend;
|
|
4501
5347
|
const refocusOnSubmitRef = useRef(refocusOnSubmit);
|
|
@@ -4574,8 +5420,10 @@ function ComposerInner({
|
|
|
4574
5420
|
useEffect(() => {
|
|
4575
5421
|
return editor.registerUpdateListener(() => {
|
|
4576
5422
|
editor.getEditorState().read(() => {
|
|
4577
|
-
const
|
|
4578
|
-
|
|
5423
|
+
const root = $getRoot();
|
|
5424
|
+
const text = root.getTextContent();
|
|
5425
|
+
setHasText(text.trim().length > 0);
|
|
5426
|
+
setIsMultiLine(root.getChildrenSize() > 1 || text.includes("\n"));
|
|
4579
5427
|
});
|
|
4580
5428
|
});
|
|
4581
5429
|
}, [editor]);
|
|
@@ -4591,8 +5439,17 @@ function ComposerInner({
|
|
|
4591
5439
|
}
|
|
4592
5440
|
});
|
|
4593
5441
|
}, [editor, registerRunPrompt, submit]);
|
|
4594
|
-
const
|
|
4595
|
-
const
|
|
5442
|
+
const isCompact = variant === "compact";
|
|
5443
|
+
const animatedPhrases = Array.isArray(animatedPlaceholder) ? animatedPlaceholder : animatedPlaceholder?.phrases;
|
|
5444
|
+
const animatedLoop = Array.isArray(animatedPlaceholder) ? false : !!animatedPlaceholder?.loop;
|
|
5445
|
+
const animatedFrame = useAnimatedPlaceholder(animatedPhrases, !hasText, {
|
|
5446
|
+
loop: animatedLoop,
|
|
5447
|
+
settleTo: hasPlaceholder ? placeholder : void 0
|
|
5448
|
+
});
|
|
5449
|
+
const effectivePlaceholder = animatedFrame?.text ?? placeholder;
|
|
5450
|
+
const mermaidActive = multiline && mode === "markdown" && !!features.mermaid;
|
|
5451
|
+
const toolbarSlot = /* @__PURE__ */ jsx(Toolbar, { extras: toolbarExtras, variant, submit });
|
|
5452
|
+
const sendButton = /* @__PURE__ */ jsx(
|
|
4596
5453
|
SendButton,
|
|
4597
5454
|
{
|
|
4598
5455
|
canSend: (
|
|
@@ -4605,38 +5462,38 @@ function ComposerInner({
|
|
|
4605
5462
|
onStop
|
|
4606
5463
|
}
|
|
4607
5464
|
);
|
|
4608
|
-
|
|
5465
|
+
const sendButtonSlot = isCompact && features.voice ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5466
|
+
/* @__PURE__ */ jsx(VoiceButton, {}),
|
|
5467
|
+
sendButton
|
|
5468
|
+
] }) : sendButton;
|
|
5469
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4609
5470
|
/* @__PURE__ */ jsx(
|
|
4610
5471
|
EditorShell,
|
|
4611
5472
|
{
|
|
4612
|
-
placeholder,
|
|
5473
|
+
placeholder: effectivePlaceholder,
|
|
5474
|
+
animated: animatedFrame?.active ?? false,
|
|
4613
5475
|
mode,
|
|
5476
|
+
variant,
|
|
4614
5477
|
multiline,
|
|
5478
|
+
expanded: isCompact && isMultiLine,
|
|
4615
5479
|
header: /* @__PURE__ */ jsx(AttachmentTray, {}),
|
|
4616
5480
|
toolbar: toolbarSlot,
|
|
4617
5481
|
sendButton: sendButtonSlot,
|
|
4618
|
-
footer:
|
|
5482
|
+
footer: mermaidActive && !isCompact ? /* @__PURE__ */ jsx(MermaidPreview, {}) : null
|
|
4619
5483
|
}
|
|
4620
5484
|
),
|
|
4621
5485
|
/* @__PURE__ */ jsx(KeyboardPlugin, { onSubmit: submit }),
|
|
4622
5486
|
/* @__PURE__ */ jsx(AutoFocusPlugin, { enabled: !!autoFocus }),
|
|
4623
5487
|
/* @__PURE__ */ jsx(PasteDropPlugin, {}),
|
|
4624
5488
|
markdownEnabled && /* @__PURE__ */ jsx(MarkdownPlugin, {}),
|
|
4625
|
-
features.slashCommands &&
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
onSubmit: submit
|
|
4630
|
-
}
|
|
4631
|
-
),
|
|
5489
|
+
features.slashCommands && // One typeahead per config — a single config or an array of them, so
|
|
5490
|
+
// consumers can register several trigger symbols (each with its own
|
|
5491
|
+
// action menu) at once. Keyed by trigger; give each a distinct symbol.
|
|
5492
|
+
(Array.isArray(features.slashCommands) ? features.slashCommands : [features.slashCommands]).map((cfg, i) => /* @__PURE__ */ jsx(SlashCommandPlugin, { config: cfg, onSubmit: submit }, cfg.trigger ?? `slash-${i}`)),
|
|
4632
5493
|
features.mentions && /* @__PURE__ */ jsx(MentionPlugin, { config: features.mentions }),
|
|
4633
5494
|
features.ghostedAutoComplete && /* @__PURE__ */ jsx(GhostedAutoCompletePlugin, { config: features.ghostedAutoComplete })
|
|
4634
5495
|
] });
|
|
4635
|
-
}
|
|
4636
|
-
function MermaidSlot() {
|
|
4637
|
-
const { features, mode } = useComposerContext();
|
|
4638
|
-
if (mode !== "markdown" || !features.mermaid) return null;
|
|
4639
|
-
return /* @__PURE__ */ jsx(MermaidPlugin, {});
|
|
5496
|
+
return mermaidActive ? /* @__PURE__ */ jsx(MermaidProvider, { children: content }) : content;
|
|
4640
5497
|
}
|
|
4641
5498
|
function SuggestionRow({ items, onSelect, className }) {
|
|
4642
5499
|
const { icons } = useComposerContext();
|