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