@sybilion/uilib 1.3.33 → 1.3.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +2 -2
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.helpers.js +18 -4
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +39 -52
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.styl.js +2 -2
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPromptComposer.js +25 -0
- package/dist/esm/components/ui/Chat/ChatPrompt/chatPromptDoc.js +17 -0
- package/dist/esm/components/ui/Chat/ChatPrompt/useChatPromptEditor.js +163 -0
- package/dist/esm/components/ui/Tooltip/Tooltip.styl.js +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/tiptap/slash-mention/SlashSuggestionList.js +53 -0
- package/dist/esm/tiptap/slash-mention/SlashSuggestionList.styl.js +7 -0
- package/dist/esm/tiptap/slash-mention/createSlashMentionExtension.js +151 -0
- package/dist/esm/tiptap/slash-mention/defaultChatSlashItems.js +12 -0
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +3 -0
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatChrome/ChatChrome.types.d.ts +5 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.helpers.d.ts +6 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.d.ts +13 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/chatPromptDoc.d.ts +3 -0
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.d.ts +20 -0
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -0
- package/dist/esm/types/src/components/ui/Input/Input.d.ts +1 -1
- package/dist/esm/types/src/docs/pages/ChatSlashCommandsPage.d.ts +1 -0
- package/dist/esm/types/src/index.d.ts +1 -0
- package/dist/esm/types/src/tiptap/slash-mention/SlashSuggestionList.d.ts +12 -0
- package/dist/esm/types/src/tiptap/slash-mention/createSlashMentionExtension.d.ts +21 -0
- package/dist/esm/types/src/tiptap/slash-mention/defaultChatSlashItems.d.ts +4 -0
- package/dist/esm/types/src/tiptap/slash-mention/index.d.ts +5 -0
- package/dist/esm/types/src/tiptap/slash-mention/types.d.ts +25 -0
- package/package.json +15 -1
- package/src/components/ui/Chat/Chat.types.ts +4 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +4 -0
- package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +5 -0
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.helpers.ts +43 -0
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl +33 -5
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.styl.d.ts +2 -1
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +50 -106
- package/src/components/ui/Chat/ChatPrompt/ChatPromptComposer.tsx +93 -0
- package/src/components/ui/Chat/ChatPrompt/chatPromptDoc.ts +18 -0
- package/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.ts +214 -0
- package/src/components/ui/Chat/index.ts +1 -0
- package/src/components/ui/Tooltip/Tooltip.styl +1 -0
- package/src/docs/pages/ChatSlashCommandsPage.tsx +139 -0
- package/src/docs/pages/TooltipPage.tsx +1 -1
- package/src/docs/registry.ts +6 -0
- package/src/index.ts +1 -0
- package/src/tiptap/slash-mention/SlashSuggestionList.styl +48 -0
- package/src/tiptap/slash-mention/SlashSuggestionList.styl.d.ts +11 -0
- package/src/tiptap/slash-mention/SlashSuggestionList.tsx +109 -0
- package/src/tiptap/slash-mention/createSlashMentionExtension.ts +217 -0
- package/src/tiptap/slash-mention/defaultChatSlashItems.ts +18 -0
- package/src/tiptap/slash-mention/index.ts +16 -0
- package/src/tiptap/slash-mention/types.ts +29 -0
|
@@ -14,7 +14,7 @@ import { filterToTextAttachments, isAttachmentsDropzoneEnabled, buildAcceptAttr
|
|
|
14
14
|
import { extractChatAttachmentItems } from '../chatAttachmentExtract.js';
|
|
15
15
|
import S from './ChatChrome.styl.js';
|
|
16
16
|
|
|
17
|
-
function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart, showBranchActionsRow, showSyntheticBranchButtons, unusedBranchKeys, isScriptComplete, onGenerateDashboard, generatingDashboard, onGenerateDashboardClick, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments = false, onAttachmentsDropped, }) {
|
|
17
|
+
function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart, showBranchActionsRow, showSyntheticBranchButtons, unusedBranchKeys, isScriptComplete, onGenerateDashboard, generatingDashboard, onGenerateDashboardClick, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments = false, onAttachmentsDropped, slashCommandItems, promptPlaceholder, }) {
|
|
18
18
|
const filteredAllowedAttachments = useMemo(() => filterToTextAttachments(allowedAttachments), [allowedAttachments]);
|
|
19
19
|
const attachmentsDropzoneEnabled = isAttachmentsDropzoneEnabled(allowedAttachments, allowPdfAttachments);
|
|
20
20
|
const attachmentAccept = useMemo(() => buildAcceptAttr(filteredAllowedAttachments, allowPdfAttachments), [filteredAllowedAttachments, allowPdfAttachments]);
|
|
@@ -80,7 +80,7 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
80
80
|
})
|
|
81
81
|
: null, isScriptComplete &&
|
|
82
82
|
onGenerateDashboard &&
|
|
83
|
-
!generatingDashboard ? (jsxs(Button, { type: "button", variant: "default", size: "lg", disabled: isLoading, onClick: onGenerateDashboardClick, children: [jsx(ChartLineIcon, {}), "Generate Dashboard"] })) : null] })), showInlinePresets && renderPresets('inline'), isLoading && isLastMessageFromUser && (jsx(TextShimmer, { duration: 1, spread: 5, className: S.loader, children: "Thinking..." }))] }) })), jsxs("div", { className: cn(S.footer, footerClassName), children: [isEmpty ? (jsx("div", { className: S.notice, children: "Forecast Assistant can make mistakes." })) : null, jsx(Chat.Prompt, { onSubmit: handlePromptSubmitWithAttachments, disabled: promptBusy, attachments: pendingAttachments, onRemoveAttachment: handleRemoveAttachment, prefillMessage: promptPrefill ?? undefined, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
|
|
83
|
+
!generatingDashboard ? (jsxs(Button, { type: "button", variant: "default", size: "lg", disabled: isLoading, onClick: onGenerateDashboardClick, children: [jsx(ChartLineIcon, {}), "Generate Dashboard"] })) : null] })), showInlinePresets && renderPresets('inline'), isLoading && isLastMessageFromUser && (jsx(TextShimmer, { duration: 1, spread: 5, className: S.loader, children: "Thinking..." }))] }) })), jsxs("div", { className: cn(S.footer, footerClassName), children: [isEmpty ? (jsx("div", { className: S.notice, children: "Forecast Assistant can make mistakes." })) : null, jsx(Chat.Prompt, { onSubmit: handlePromptSubmitWithAttachments, disabled: promptBusy, attachments: pendingAttachments, onRemoveAttachment: handleRemoveAttachment, prefillMessage: promptPrefill ?? undefined, placeholder: promptPlaceholder, slashCommandItems: slashCommandItems, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
export { ChatChrome };
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
/** Keep in sync with `ChatPrompt.styl` `INPUT_MAX_HEIGHT` / `min-height`. */
|
|
2
2
|
const PROMPT_INPUT_MAX_HEIGHT_PX = 200;
|
|
3
3
|
const PROMPT_INPUT_MIN_HEIGHT_PX = 40;
|
|
4
|
+
/** ProseMirror `view.dom` exists only after TipTap mounts `<EditorContent />`; accessing `.view` can throw earlier. */
|
|
5
|
+
function chatPromptSafeEditorDom(editor) {
|
|
6
|
+
if (!editor || editor.isDestroyed)
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
const dom = editor.view?.dom;
|
|
11
|
+
return dom instanceof HTMLElement ? dom : null;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
function chatPromptTextareaFloorPx(cs) {
|
|
5
18
|
const padY = (Number.parseFloat(cs.paddingTop) || 0) +
|
|
6
19
|
(Number.parseFloat(cs.paddingBottom) || 0);
|
|
@@ -10,12 +23,13 @@ function chatPromptTextareaFloorPx(cs) {
|
|
|
10
23
|
: Number.parseFloat(cs.lineHeight) || fs * 1.25;
|
|
11
24
|
return Math.max(PROMPT_INPUT_MIN_HEIGHT_PX, Math.ceil(linePx + padY));
|
|
12
25
|
}
|
|
13
|
-
/**
|
|
14
|
-
function
|
|
26
|
+
/** Same sizing rules for TipTap `.ProseMirror` root (`text` mirrors doc plain text length for empty-state). */
|
|
27
|
+
function syncChatPromptComposerHeight(el, text) {
|
|
15
28
|
const floor = chatPromptTextareaFloorPx(getComputedStyle(el));
|
|
16
29
|
el.style.overflowY = 'hidden';
|
|
17
30
|
let contentHeight;
|
|
18
|
-
|
|
31
|
+
const empty = text.trim() === '';
|
|
32
|
+
if (empty) {
|
|
19
33
|
contentHeight = floor;
|
|
20
34
|
}
|
|
21
35
|
else {
|
|
@@ -28,4 +42,4 @@ function syncChatPromptTextareaHeight(el, message) {
|
|
|
28
42
|
contentHeight > PROMPT_INPUT_MAX_HEIGHT_PX ? 'auto' : 'hidden';
|
|
29
43
|
}
|
|
30
44
|
|
|
31
|
-
export { PROMPT_INPUT_MAX_HEIGHT_PX, PROMPT_INPUT_MIN_HEIGHT_PX, chatPromptTextareaFloorPx,
|
|
45
|
+
export { PROMPT_INPUT_MAX_HEIGHT_PX, PROMPT_INPUT_MIN_HEIGHT_PX, chatPromptSafeEditorDom, chatPromptTextareaFloorPx, syncChatPromptComposerHeight };
|
|
@@ -1,60 +1,47 @@
|
|
|
1
|
-
import { jsxs, jsx
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import cn from 'classnames';
|
|
3
|
-
import {
|
|
4
|
-
import useEvent from '../../../../hooks/useEvent.js';
|
|
5
|
-
import { PaperclipIcon, SendHorizontalIcon } from 'lucide-react';
|
|
6
|
-
import { Button } from '../../Button/Button.js';
|
|
7
|
-
import { Input } from '../../Input/Input.js';
|
|
8
|
-
import { syncChatPromptTextareaHeight } from './ChatPrompt.helpers.js';
|
|
3
|
+
import { useRef, useCallback } from 'react';
|
|
9
4
|
import S from './ChatPrompt.styl.js';
|
|
10
5
|
import { ChatPromptAttachments } from './ChatPromptAttachments.js';
|
|
6
|
+
import { ChatPromptComposer } from './ChatPromptComposer.js';
|
|
7
|
+
import { useChatPromptEditor } from './useChatPromptEditor.js';
|
|
11
8
|
|
|
12
|
-
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }) {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}, [message]);
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
if (prefillMessage != null && prefillMessage !== '') {
|
|
25
|
-
setMessage(prefillMessage);
|
|
26
|
-
}
|
|
27
|
-
}, [prefillMessage]);
|
|
28
|
-
const handleSubmit = (e) => {
|
|
29
|
-
const trimmedMessage = message.trim();
|
|
30
|
-
const hasAttachments = attachments.length > 0;
|
|
31
|
-
if (trimmedMessage || hasAttachments) {
|
|
32
|
-
e.preventDefault();
|
|
33
|
-
onSubmit(trimmedMessage, hasAttachments ? attachments : undefined);
|
|
34
|
-
setMessage('');
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const handleFileInputChange = (e) => {
|
|
38
|
-
const files = Array.from(e.target.files ?? []);
|
|
39
|
-
e.target.value = '';
|
|
40
|
-
if (files.length > 0) {
|
|
41
|
-
onAttachmentFiles?.(files);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
useEvent({
|
|
45
|
-
event: 'keydown',
|
|
46
|
-
callback: (e) => {
|
|
47
|
-
if (e.key === 'Enter' && !(e.metaKey || e.shiftKey || e.ctrlKey)) {
|
|
48
|
-
e.preventDefault();
|
|
49
|
-
handleSubmit(e);
|
|
50
|
-
setMessage('');
|
|
51
|
-
}
|
|
52
|
-
},
|
|
9
|
+
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }) {
|
|
10
|
+
const attachmentsCount = attachments.length;
|
|
11
|
+
const emitSubmitRef = useRef(() => { });
|
|
12
|
+
const { editor, trimmedMessage, resetAfterSend, handleComposerKeyDown } = useChatPromptEditor({
|
|
13
|
+
disabled,
|
|
14
|
+
placeholder,
|
|
15
|
+
slashCommandItems,
|
|
16
|
+
prefillMessage,
|
|
17
|
+
attachmentsCount,
|
|
18
|
+
onEnterSubmit: () => emitSubmitRef.current(),
|
|
53
19
|
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
20
|
+
const emitSubmitAndClear = useCallback(() => {
|
|
21
|
+
if (!editor)
|
|
22
|
+
return;
|
|
23
|
+
if (!trimmedMessage && attachmentsCount === 0)
|
|
24
|
+
return;
|
|
25
|
+
const msg = trimmedMessage;
|
|
26
|
+
resetAfterSend();
|
|
27
|
+
onSubmit(msg, attachmentsCount > 0 ? attachments : undefined);
|
|
28
|
+
}, [
|
|
29
|
+
attachments,
|
|
30
|
+
attachmentsCount,
|
|
31
|
+
editor,
|
|
32
|
+
onSubmit,
|
|
33
|
+
resetAfterSend,
|
|
34
|
+
trimmedMessage,
|
|
35
|
+
]);
|
|
36
|
+
emitSubmitRef.current = emitSubmitAndClear;
|
|
37
|
+
const handleSubmitForm = useCallback((e) => {
|
|
38
|
+
e?.preventDefault();
|
|
39
|
+
emitSubmitAndClear();
|
|
40
|
+
}, [emitSubmitAndClear]);
|
|
41
|
+
if (!editor) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
return (jsxs("form", { onSubmit: handleSubmitForm, className: cn(S.root, className), children: [jsx(ChatPromptAttachments, { attachments: attachments, onRemove: index => onRemoveAttachment?.(index), disabled: disabled }), jsx(ChatPromptComposer, { editor: editor, disabled: disabled, trimmedMessage: trimmedMessage, attachments: attachments, attachmentAccept: attachmentAccept, onAttachmentFiles: onAttachmentFiles, onComposerKeyDown: handleComposerKeyDown }), footer] }));
|
|
58
45
|
}
|
|
59
46
|
|
|
60
47
|
export { ChatPrompt };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatPrompt_root__5G5bq{align-items:stretch;display:flex;flex-direction:column;gap:var(--p-2);padding:var(--p-6);padding-top:var(--p-5);position:relative}.ChatPrompt_composer__H3c3N{align-items:flex-end;display:flex;flex-direction:row;gap:var(--p-3);position:relative;width:100%}.ChatPrompt_fileInput__xdgPn{display:none}.ChatPrompt_attachButton__gi-qF{align-self:flex-end;flex-shrink:0}.
|
|
4
|
-
var S = {"root":"ChatPrompt_root__5G5bq","composer":"ChatPrompt_composer__H3c3N","fileInput":"ChatPrompt_fileInput__xdgPn","attachButton":"ChatPrompt_attachButton__gi-qF","
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.ChatPrompt_root__5G5bq{align-items:stretch;display:flex;flex-direction:column;gap:var(--p-2);padding:var(--p-6);padding-top:var(--p-5);position:relative}.ChatPrompt_composer__H3c3N{align-items:flex-end;display:flex;flex-direction:row;gap:var(--p-3);position:relative;width:100%}.ChatPrompt_fileInput__xdgPn{display:none}.ChatPrompt_attachButton__gi-qF{align-self:flex-end;flex-shrink:0}.ChatPrompt_editorWrap__Q7gat{align-self:stretch;flex:1;min-width:0}.ChatPrompt_editorMount__Phh4D{background:transparent;border:none;border-radius:0!important;box-shadow:none!important;display:flex;flex:1;flex-direction:column;max-height:200px;min-height:40px;min-width:0;padding:0!important}.ChatPrompt_editorMount__Phh4D:focus-within{box-shadow:none!important}.ChatPrompt_editorMount__Phh4D .ProseMirror{border:none!important;box-shadow:none!important;flex:1;margin:0;max-height:200px!important;min-height:40px!important;outline:none!important;overflow-x:hidden!important;overflow-y:auto!important;padding:var(--p-2) 0 0!important;resize:none!important;white-space:pre-wrap;word-break:break-word}.ChatPrompt_editorMount__Phh4D .ProseMirror p.is-empty:before{color:var(--muted-foreground);content:attr(data-placeholder);float:left;height:0;pointer-events:none}.ChatPrompt_submitColumn__0rY1R{align-items:center;display:flex;flex-direction:column;flex-shrink:0;justify-content:flex-end}.ChatPrompt_submitColumn__0rY1R>button:focus{box-shadow:0 0 0 2px var(--brand-color-500)!important}.ChatPrompt_submitColumn__0rY1R>button:first-child{border:none;position:relative;transition:all .2s}.ChatPrompt_submitColumn__0rY1R>button:first-child:focus{transform:scale(1.2)}.ChatPrompt_submitColumn__0rY1R>button:first-child:before{bottom:-100%;content:\"\";left:-100%;position:absolute;right:-100%;top:-100%}.ChatPrompt_attachments__KG-fG{display:flex;flex-wrap:wrap;gap:var(--p-2);margin-bottom:var(--p-2)}.ChatPrompt_attachmentItem__QJk7J{flex:1 1 300px;max-width:300px;min-width:0}";
|
|
4
|
+
var S = {"root":"ChatPrompt_root__5G5bq","composer":"ChatPrompt_composer__H3c3N","fileInput":"ChatPrompt_fileInput__xdgPn","attachButton":"ChatPrompt_attachButton__gi-qF","editorWrap":"ChatPrompt_editorWrap__Q7gat","editorMount":"ChatPrompt_editorMount__Phh4D","submitColumn":"ChatPrompt_submitColumn__0rY1R","attachments":"ChatPrompt_attachments__KG-fG","attachmentItem":"ChatPrompt_attachmentItem__QJk7J"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
|
7
7
|
export { S as default };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useRef, useCallback } from 'react';
|
|
3
|
+
import { EditorContent } from '@tiptap/react';
|
|
4
|
+
import { PaperclipIcon, SendHorizontalIcon } from 'lucide-react';
|
|
5
|
+
import { Button } from '../../Button/Button.js';
|
|
6
|
+
import S from './ChatPrompt.styl.js';
|
|
7
|
+
|
|
8
|
+
function ChatPromptComposer({ editor, disabled, trimmedMessage, attachments, attachmentAccept, onAttachmentFiles, onComposerKeyDown, }) {
|
|
9
|
+
const fileInputRef = useRef(null);
|
|
10
|
+
const showAttachButton = Boolean(attachmentAccept && onAttachmentFiles);
|
|
11
|
+
const handleFileInputChange = useCallback((e) => {
|
|
12
|
+
const files = Array.from(e.target.files ?? []);
|
|
13
|
+
e.target.value = '';
|
|
14
|
+
if (files.length > 0) {
|
|
15
|
+
onAttachmentFiles?.(files);
|
|
16
|
+
}
|
|
17
|
+
}, [onAttachmentFiles]);
|
|
18
|
+
const canSubmit = Boolean(trimmedMessage || attachments.length > 0);
|
|
19
|
+
return (jsxs("div", { className: S.composer, children: [showAttachButton ? (jsxs(Fragment, { children: [jsx("input", { ref: fileInputRef, type: "file", accept: attachmentAccept, multiple: true, className: S.fileInput, disabled: disabled, onChange: handleFileInputChange }), jsx(Button, { type: "button", variant: "ghost", icon: true, size: "sm", className: S.attachButton, "aria-label": "Attach file", disabled: disabled, onClick: e => {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
fileInputRef.current?.click();
|
|
22
|
+
}, children: jsx(PaperclipIcon, { size: 16 }) })] })) : null, jsx("div", { className: S.editorWrap, onKeyDown: onComposerKeyDown, children: jsx(EditorContent, { editor: editor, className: S.editorMount }) }), jsx("div", { className: S.submitColumn, children: jsx(Button, { type: "submit", size: "sm", disabled: disabled || !canSubmit, children: jsx(SendHorizontalIcon, { size: 16 }) }) })] }));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { ChatPromptComposer };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const CHAT_PROMPT_EMPTY_DOC = {
|
|
2
|
+
type: 'doc',
|
|
3
|
+
content: [{ type: 'paragraph' }],
|
|
4
|
+
};
|
|
5
|
+
function chatPromptParagraphDoc(text) {
|
|
6
|
+
return {
|
|
7
|
+
type: 'doc',
|
|
8
|
+
content: [
|
|
9
|
+
{
|
|
10
|
+
type: 'paragraph',
|
|
11
|
+
content: text ? [{ type: 'text', text }] : [],
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { CHAT_PROMPT_EMPTY_DOC, chatPromptParagraphDoc };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { useRef, useCallback, useMemo, useState, useEffect, useLayoutEffect } from 'react';
|
|
2
|
+
import { DEFAULT_CHAT_SLASH_ITEMS } from '../../../../tiptap/slash-mention/defaultChatSlashItems.js';
|
|
3
|
+
import { createSlashMentionExtension } from '../../../../tiptap/slash-mention/createSlashMentionExtension.js';
|
|
4
|
+
import { Placeholder } from '@tiptap/extensions';
|
|
5
|
+
import { useEditor } from '@tiptap/react';
|
|
6
|
+
import StarterKit from '@tiptap/starter-kit';
|
|
7
|
+
import { chatPromptSafeEditorDom, syncChatPromptComposerHeight } from './ChatPrompt.helpers.js';
|
|
8
|
+
import { CHAT_PROMPT_EMPTY_DOC, chatPromptParagraphDoc } from './chatPromptDoc.js';
|
|
9
|
+
|
|
10
|
+
function useChatPromptEditor({ disabled, placeholder, slashCommandItems, prefillMessage, attachmentsCount = 0, onEnterSubmit, }) {
|
|
11
|
+
const slashOpenRef = useRef(false);
|
|
12
|
+
const suggestionActiveUpdater = useCallback((active) => {
|
|
13
|
+
slashOpenRef.current = active;
|
|
14
|
+
}, []);
|
|
15
|
+
const slashFinger = JSON.stringify(slashCommandItems ?? null);
|
|
16
|
+
const slashItemsStable = useMemo(() => {
|
|
17
|
+
return slashCommandItems ?? DEFAULT_CHAT_SLASH_ITEMS;
|
|
18
|
+
}, [slashCommandItems, slashFinger]);
|
|
19
|
+
const placeholderText = useMemo(() => {
|
|
20
|
+
if (placeholder === undefined || placeholder === null) {
|
|
21
|
+
return 'Type a message…';
|
|
22
|
+
}
|
|
23
|
+
const t = String(placeholder).trim();
|
|
24
|
+
return t === '' ? 'Type a message…' : t;
|
|
25
|
+
}, [placeholder]);
|
|
26
|
+
const ariaLabelComposer = useMemo(() => {
|
|
27
|
+
if (placeholder === undefined || placeholder === null)
|
|
28
|
+
return 'Message';
|
|
29
|
+
const t = String(placeholder).trim();
|
|
30
|
+
return t === '' ? 'Message' : t;
|
|
31
|
+
}, [placeholder]);
|
|
32
|
+
const extensions = useMemo(() => {
|
|
33
|
+
const exts = [
|
|
34
|
+
StarterKit.configure({
|
|
35
|
+
heading: false,
|
|
36
|
+
bulletList: false,
|
|
37
|
+
orderedList: false,
|
|
38
|
+
blockquote: false,
|
|
39
|
+
codeBlock: false,
|
|
40
|
+
horizontalRule: false,
|
|
41
|
+
}),
|
|
42
|
+
Placeholder.configure({
|
|
43
|
+
placeholder: placeholderText,
|
|
44
|
+
showOnlyWhenEditable: true,
|
|
45
|
+
showOnlyCurrent: false,
|
|
46
|
+
}),
|
|
47
|
+
];
|
|
48
|
+
if (slashItemsStable.length > 0) {
|
|
49
|
+
exts.push(createSlashMentionExtension({
|
|
50
|
+
items: slashItemsStable,
|
|
51
|
+
onSuggestionUiActiveChange: suggestionActiveUpdater,
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
return exts;
|
|
55
|
+
}, [slashItemsStable, placeholderText, suggestionActiveUpdater]);
|
|
56
|
+
const [plainDraft, setPlainDraft] = useState('');
|
|
57
|
+
const editorDomRef = useRef(null);
|
|
58
|
+
const bindEditorDom = useCallback(({ editor }) => {
|
|
59
|
+
setPlainDraft(editor.getText());
|
|
60
|
+
queueMicrotask(() => {
|
|
61
|
+
const dom = chatPromptSafeEditorDom(editor);
|
|
62
|
+
if (!dom)
|
|
63
|
+
return;
|
|
64
|
+
editorDomRef.current = dom;
|
|
65
|
+
syncChatPromptComposerHeight(dom, editor.getText());
|
|
66
|
+
});
|
|
67
|
+
}, []);
|
|
68
|
+
const trimmedMessage = plainDraft.trim();
|
|
69
|
+
const editor = useEditor({
|
|
70
|
+
extensions,
|
|
71
|
+
content: CHAT_PROMPT_EMPTY_DOC,
|
|
72
|
+
editable: !disabled,
|
|
73
|
+
immediatelyRender: true,
|
|
74
|
+
editorProps: {
|
|
75
|
+
attributes: {
|
|
76
|
+
spellcheck: 'true',
|
|
77
|
+
'aria-label': ariaLabelComposer,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
onTransaction: ({ editor: ed }) => {
|
|
81
|
+
const dom = chatPromptSafeEditorDom(ed);
|
|
82
|
+
if (dom) {
|
|
83
|
+
syncChatPromptComposerHeight(dom, ed.getText());
|
|
84
|
+
}
|
|
85
|
+
setPlainDraft(ed.getText());
|
|
86
|
+
},
|
|
87
|
+
onCreate: bindEditorDom,
|
|
88
|
+
}, [extensions, disabled, bindEditorDom, ariaLabelComposer]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (!editor)
|
|
91
|
+
return;
|
|
92
|
+
editor.setEditable(!disabled);
|
|
93
|
+
}, [editor, disabled]);
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (!editor)
|
|
96
|
+
return;
|
|
97
|
+
const bind = () => {
|
|
98
|
+
editorDomRef.current = chatPromptSafeEditorDom(editor);
|
|
99
|
+
};
|
|
100
|
+
bind();
|
|
101
|
+
let rafId = null;
|
|
102
|
+
if (typeof window !== 'undefined') {
|
|
103
|
+
rafId = window.requestAnimationFrame(bind);
|
|
104
|
+
}
|
|
105
|
+
return () => {
|
|
106
|
+
if (rafId != null)
|
|
107
|
+
cancelAnimationFrame(rafId);
|
|
108
|
+
};
|
|
109
|
+
}, [editor]);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!editor || prefillMessage == null || prefillMessage === '')
|
|
112
|
+
return;
|
|
113
|
+
editor.commands.setContent(chatPromptParagraphDoc(prefillMessage));
|
|
114
|
+
const t = editor.getText();
|
|
115
|
+
setPlainDraft(t);
|
|
116
|
+
queueMicrotask(() => {
|
|
117
|
+
const dom = chatPromptSafeEditorDom(editor);
|
|
118
|
+
if (dom)
|
|
119
|
+
syncChatPromptComposerHeight(dom, t);
|
|
120
|
+
});
|
|
121
|
+
}, [editor, prefillMessage]);
|
|
122
|
+
useLayoutEffect(() => {
|
|
123
|
+
if (!editor)
|
|
124
|
+
return;
|
|
125
|
+
const dom = chatPromptSafeEditorDom(editor);
|
|
126
|
+
if (!dom)
|
|
127
|
+
return;
|
|
128
|
+
syncChatPromptComposerHeight(dom, plainDraft);
|
|
129
|
+
}, [editor, plainDraft]);
|
|
130
|
+
const onEnterSubmitRef = useRef(onEnterSubmit);
|
|
131
|
+
onEnterSubmitRef.current = onEnterSubmit;
|
|
132
|
+
const resetAfterSend = useCallback(() => {
|
|
133
|
+
if (!editor)
|
|
134
|
+
return;
|
|
135
|
+
editor.commands.clearContent();
|
|
136
|
+
queueMicrotask(() => {
|
|
137
|
+
const dom = chatPromptSafeEditorDom(editor);
|
|
138
|
+
if (dom)
|
|
139
|
+
syncChatPromptComposerHeight(dom, '');
|
|
140
|
+
});
|
|
141
|
+
setPlainDraft('');
|
|
142
|
+
}, [editor]);
|
|
143
|
+
const handleComposerKeyDown = useCallback((e) => {
|
|
144
|
+
if (!(e.key === 'Enter' && !e.shiftKey && !e.metaKey && !e.ctrlKey))
|
|
145
|
+
return;
|
|
146
|
+
if (!editorDomRef.current?.contains(e.target))
|
|
147
|
+
return;
|
|
148
|
+
if (slashOpenRef.current)
|
|
149
|
+
return;
|
|
150
|
+
if (!trimmedMessage && attachmentsCount === 0)
|
|
151
|
+
return;
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
onEnterSubmitRef.current();
|
|
154
|
+
}, [attachmentsCount, trimmedMessage]);
|
|
155
|
+
return {
|
|
156
|
+
editor,
|
|
157
|
+
trimmedMessage,
|
|
158
|
+
resetAfterSend,
|
|
159
|
+
handleComposerKeyDown,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export { useChatPromptEditor };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styleInject from 'style-inject';
|
|
2
2
|
|
|
3
|
-
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.Tooltip_tooltipContent__b3pS-{backdrop-filter:blur(10px);background-color:var(--popover);border:1px solid var(--border);border-radius:var(--p-3);box-shadow:0 10px 15px -3px rgba(0,0,0,.1);color:var(--popover-foreground);font-size:12px;padding:6px 12px;text-wrap:balance;transform-origin:var(--radix-tooltip-content-transform-origin);width:-moz-fit-content;width:fit-content;word-break:break-word;z-index:50}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open],.Tooltip_tooltipContent__b3pS-[data-state=instant-open],.Tooltip_tooltipContent__b3pS-[data-state=open]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=closed]{animation:Tooltip_fade-out__UOBET .1s ease-in,Tooltip_zoom-out__fodOk .1s ease-in}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=bottom],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=bottom],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=bottom]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-top-2__8uuS- .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=left],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=left],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=left]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-right-2__Uu79F .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=right],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=right],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=right]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-left-2__23kHm .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=top],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=top],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=top]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-bottom-2__O-Aa8 .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=bottom],.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=left],.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=right],.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=top]{animation:Tooltip_fade-out__UOBET .1s ease-in,Tooltip_zoom-out__fodOk .1s ease-in}.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=delayed-open],.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=instant-open],.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=open]{animation:Tooltip_fade-in__ZQqZv .15s ease-out}.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=closed]{animation:Tooltip_fade-out__UOBET .1s ease-in}.Tooltip_tooltipContentOverTrigger__VQAU3{box-sizing:border-box;height:auto}.Tooltip_tooltipArrow__87DVL{background-color:var(--popover);border-bottom:1px solid var(--border);border-left-width:1px;border-left:0 solid var(--border);border-radius:2px;border-right:1px solid var(--border);border-top-width:1px;border-top:0 solid var(--border);fill:var(--popover);height:10px;transform:translateY(calc(-50% + .5px)) rotate(45deg);width:10px;z-index:50}@keyframes Tooltip_fade-in__ZQqZv{0%{opacity:0}to{opacity:1}}@keyframes Tooltip_fade-out__UOBET{0%{opacity:1}to{opacity:0}}@keyframes Tooltip_zoom-in__SbWQb{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes Tooltip_zoom-out__fodOk{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@keyframes Tooltip_slide-in-from-top-2__8uuS-{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes Tooltip_slide-in-from-right-2__Uu79F{0%{opacity:0;transform:translateX(.5rem)}to{opacity:1;transform:translateX(0)}}@keyframes Tooltip_slide-in-from-left-2__23kHm{0%{opacity:0;transform:translateX(-.5rem)}to{opacity:1;transform:translateX(0)}}@keyframes Tooltip_slide-in-from-bottom-2__O-Aa8{0%{opacity:0;transform:translateY(.5rem)}to{opacity:1;transform:translateY(0)}}";
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.Tooltip_tooltipContent__b3pS-{backdrop-filter:blur(10px);background-color:var(--popover);border:1px solid var(--border);border-radius:var(--p-3);box-shadow:0 10px 15px -3px rgba(0,0,0,.1);color:var(--popover-foreground);font-size:12px;min-width:100%;padding:6px 12px;text-wrap:balance;transform-origin:var(--radix-tooltip-content-transform-origin);width:-moz-fit-content;width:fit-content;word-break:break-word;z-index:50}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open],.Tooltip_tooltipContent__b3pS-[data-state=instant-open],.Tooltip_tooltipContent__b3pS-[data-state=open]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=closed]{animation:Tooltip_fade-out__UOBET .1s ease-in,Tooltip_zoom-out__fodOk .1s ease-in}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=bottom],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=bottom],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=bottom]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-top-2__8uuS- .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=left],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=left],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=left]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-right-2__Uu79F .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=right],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=right],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=right]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-left-2__23kHm .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=delayed-open][data-side=top],.Tooltip_tooltipContent__b3pS-[data-state=instant-open][data-side=top],.Tooltip_tooltipContent__b3pS-[data-state=open][data-side=top]{animation:Tooltip_fade-in__ZQqZv .15s ease-out,Tooltip_zoom-in__SbWQb .15s ease-out,Tooltip_slide-in-from-bottom-2__O-Aa8 .15s ease-out}.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=bottom],.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=left],.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=right],.Tooltip_tooltipContent__b3pS-[data-state=closed][data-side=top]{animation:Tooltip_fade-out__UOBET .1s ease-in,Tooltip_zoom-out__fodOk .1s ease-in}.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=delayed-open],.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=instant-open],.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=open]{animation:Tooltip_fade-in__ZQqZv .15s ease-out}.Tooltip_tooltipContent__b3pS-.Tooltip_tooltipContentOverTrigger__VQAU3[data-state=closed]{animation:Tooltip_fade-out__UOBET .1s ease-in}.Tooltip_tooltipContentOverTrigger__VQAU3{box-sizing:border-box;height:auto}.Tooltip_tooltipArrow__87DVL{background-color:var(--popover);border-bottom:1px solid var(--border);border-left-width:1px;border-left:0 solid var(--border);border-radius:2px;border-right:1px solid var(--border);border-top-width:1px;border-top:0 solid var(--border);fill:var(--popover);height:10px;transform:translateY(calc(-50% + .5px)) rotate(45deg);width:10px;z-index:50}@keyframes Tooltip_fade-in__ZQqZv{0%{opacity:0}to{opacity:1}}@keyframes Tooltip_fade-out__UOBET{0%{opacity:1}to{opacity:0}}@keyframes Tooltip_zoom-in__SbWQb{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}@keyframes Tooltip_zoom-out__fodOk{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.95)}}@keyframes Tooltip_slide-in-from-top-2__8uuS-{0%{opacity:0;transform:translateY(-.5rem)}to{opacity:1;transform:translateY(0)}}@keyframes Tooltip_slide-in-from-right-2__Uu79F{0%{opacity:0;transform:translateX(.5rem)}to{opacity:1;transform:translateX(0)}}@keyframes Tooltip_slide-in-from-left-2__23kHm{0%{opacity:0;transform:translateX(-.5rem)}to{opacity:1;transform:translateX(0)}}@keyframes Tooltip_slide-in-from-bottom-2__O-Aa8{0%{opacity:0;transform:translateY(.5rem)}to{opacity:1;transform:translateY(0)}}";
|
|
4
4
|
var S = {"tooltipContent":"Tooltip_tooltipContent__b3pS-","fade-in":"Tooltip_fade-in__ZQqZv","zoom-in":"Tooltip_zoom-in__SbWQb","fade-out":"Tooltip_fade-out__UOBET","zoom-out":"Tooltip_zoom-out__fodOk","slide-in-from-top-2":"Tooltip_slide-in-from-top-2__8uuS-","slide-in-from-right-2":"Tooltip_slide-in-from-right-2__Uu79F","slide-in-from-left-2":"Tooltip_slide-in-from-left-2__23kHm","slide-in-from-bottom-2":"Tooltip_slide-in-from-bottom-2__O-Aa8","tooltipContentOverTrigger":"Tooltip_tooltipContentOverTrigger__VQAU3","tooltipArrow":"Tooltip_tooltipArrow__87DVL"};
|
|
5
5
|
styleInject(css_248z);
|
|
6
6
|
|
package/dist/esm/index.js
CHANGED
|
@@ -7,6 +7,8 @@ export { SybilionAuthProvider, createSybilionApiFetch, getSybilionApiOriginFromS
|
|
|
7
7
|
export { SYBILION_AUTH_LOGIN_PATH, normalizeApiBaseUrl } from './sybilion-auth/authPaths.js';
|
|
8
8
|
export { exchangeAuth0AccessTokenForSybilionJwt } from './sybilion-auth/exchangeSybilionToken.js';
|
|
9
9
|
export { ChatContext, ChatProvider, isChatEmpty, outboundPendingKey, useChat, useChatOutboundPending, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat, useIsChatLoading, useSyncChatPanelBusy } from './contexts/chat-context.js';
|
|
10
|
+
export { DEFAULT_CHAT_SLASH_ITEMS, filterSlashItems } from './tiptap/slash-mention/defaultChatSlashItems.js';
|
|
11
|
+
export { createSlashMentionExtension, slashMentionSuggestionRender } from './tiptap/slash-mention/createSlashMentionExtension.js';
|
|
10
12
|
export { AnalysesSelector } from './components/ui/AnalysesSelector/AnalysesSelector.js';
|
|
11
13
|
export { AnalysisLineIcon } from './components/ui/AnalysisLineIcon/AnalysisLineIcon.js';
|
|
12
14
|
export { AppHeaderHost, AppHeaderPortal } from './components/ui/AppHeader/AppHeader.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import { useState, useEffect, useCallback, useImperativeHandle } from 'react';
|
|
4
|
+
import S from './SlashSuggestionList.styl.js';
|
|
5
|
+
|
|
6
|
+
function SlashSuggestionListInner({ items, command, listHandleRef, }) {
|
|
7
|
+
const [selected, setSelected] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
setSelected(s => Math.min(s, Math.max(items.length - 1, 0)));
|
|
10
|
+
}, [items.length]);
|
|
11
|
+
const safeSel = Math.min(selected, Math.max(items.length - 1, 0));
|
|
12
|
+
const onPick = useCallback((index) => {
|
|
13
|
+
const item = items[index];
|
|
14
|
+
if (!item)
|
|
15
|
+
return;
|
|
16
|
+
command(item);
|
|
17
|
+
}, [command, items]);
|
|
18
|
+
const onKeyboardEvent = useCallback((event) => {
|
|
19
|
+
if (items.length === 0)
|
|
20
|
+
return false;
|
|
21
|
+
if (event.key === 'ArrowDown') {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
setSelected(s => Math.min(s + 1, items.length - 1));
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (event.key === 'ArrowUp') {
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
setSelected(s => Math.max(s - 1, 0));
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
if (event.key === 'Enter') {
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
const max = Math.max(items.length - 1, 0);
|
|
34
|
+
const idx = Math.min(selected, max);
|
|
35
|
+
const item = items[idx];
|
|
36
|
+
if (item)
|
|
37
|
+
command(item);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}, [command, items, selected]);
|
|
42
|
+
useImperativeHandle(listHandleRef, () => ({ onKeyboardEvent }), [
|
|
43
|
+
onKeyboardEvent,
|
|
44
|
+
]);
|
|
45
|
+
return (jsx("div", { className: S.root, role: "listbox", "aria-label": "Slash commands", children: items.map((item, idx) => (jsxs("button", { type: "button", role: "option", "aria-selected": idx === safeSel, className: cn(S.item, idx === safeSel && S.itemHighlighted), onMouseDown: e => e.preventDefault(), onMouseEnter: () => setSelected(idx), onClick: () => onPick(idx), children: [jsxs("span", { className: S.itemLabel, children: ["/", item.label] }), item.description ? (jsx("span", { className: S.itemDesc, children: item.description })) : null] }, `${item.id}-${idx}`))) }));
|
|
46
|
+
}
|
|
47
|
+
function SlashSuggestionList(props) {
|
|
48
|
+
if (props.items.length === 0)
|
|
49
|
+
return null;
|
|
50
|
+
return (jsx(SlashSuggestionListInner, { items: props.items, command: props.command, listHandleRef: props.listHandleRef }));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { SlashSuggestionList };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import styleInject from 'style-inject';
|
|
2
|
+
|
|
3
|
+
var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.SlashSuggestionList_root__9KCb1{background:var(--popover,var(--card));border:1px solid var(--border);border-radius:8px;border-radius:var(--radius-lg,8px);box-shadow:0 8px 24px rgba(0,0,0,.12);color:var(--foreground);display:flex;flex-direction:column;font-size:var(--text-sm);list-style:none;margin:0;max-height:40vh;max-width:90vw;min-width:220px;overflow-y:auto;padding:var(--p-1)}.dark .SlashSuggestionList_root__9KCb1{box-shadow:0 8px 24px rgba(0,0,0,.5)}.SlashSuggestionList_item__9JBeY{align-items:flex-start;background:transparent;border:none;border-radius:6px;border-radius:var(--radius-md,6px);color:inherit;cursor:pointer;display:flex;flex-direction:column;margin:0;padding:var(--p-1) var(--p-2);text-align:left;width:100%}.SlashSuggestionList_itemHighlighted__cStZr,.SlashSuggestionList_item__9JBeY:hover{background:var(--muted)}.SlashSuggestionList_itemLabel__OMaov{font-weight:600}.SlashSuggestionList_itemDesc__fAdcA{color:var(--muted-foreground);font-size:var(--text-xs)}";
|
|
4
|
+
var S = {"root":"SlashSuggestionList_root__9KCb1","item":"SlashSuggestionList_item__9JBeY","itemHighlighted":"SlashSuggestionList_itemHighlighted__cStZr","itemLabel":"SlashSuggestionList_itemLabel__OMaov","itemDesc":"SlashSuggestionList_itemDesc__fAdcA"};
|
|
5
|
+
styleInject(css_248z);
|
|
6
|
+
|
|
7
|
+
export { S as default };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { mergeAttributes } from '@tiptap/core';
|
|
2
|
+
import Mention from '@tiptap/extension-mention';
|
|
3
|
+
import { ReactRenderer } from '@tiptap/react';
|
|
4
|
+
import { SlashSuggestionList } from './SlashSuggestionList.js';
|
|
5
|
+
import { filterSlashItems } from './defaultChatSlashItems.js';
|
|
6
|
+
|
|
7
|
+
function slashMentionSuggestionRender(uiRef) {
|
|
8
|
+
let popup = null;
|
|
9
|
+
const place = (props) => {
|
|
10
|
+
if (!popup?.element)
|
|
11
|
+
return;
|
|
12
|
+
const rect = props.clientRect?.();
|
|
13
|
+
if (!rect)
|
|
14
|
+
return;
|
|
15
|
+
const el = popup.element;
|
|
16
|
+
el.style.left = `${rect.left}px`;
|
|
17
|
+
el.style.top = `${rect.bottom + 4}px`;
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
onStart: props => {
|
|
21
|
+
uiRef.current = null;
|
|
22
|
+
popup?.destroy?.();
|
|
23
|
+
popup = new ReactRenderer(SlashSuggestionList, {
|
|
24
|
+
editor: props.editor,
|
|
25
|
+
props: {
|
|
26
|
+
items: props.items,
|
|
27
|
+
command: props.command,
|
|
28
|
+
listHandleRef: uiRef,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
popup.element.style.position = 'fixed';
|
|
32
|
+
popup.element.style.margin = '0';
|
|
33
|
+
popup.element.style.zIndex = '10002';
|
|
34
|
+
document.body.append(popup.element);
|
|
35
|
+
place(props);
|
|
36
|
+
},
|
|
37
|
+
onUpdate: props => {
|
|
38
|
+
if (!popup)
|
|
39
|
+
return;
|
|
40
|
+
popup.updateProps({
|
|
41
|
+
items: props.items,
|
|
42
|
+
command: props.command,
|
|
43
|
+
listHandleRef: uiRef,
|
|
44
|
+
});
|
|
45
|
+
place(props);
|
|
46
|
+
},
|
|
47
|
+
onExit: () => {
|
|
48
|
+
popup?.destroy();
|
|
49
|
+
popup?.element?.remove?.();
|
|
50
|
+
popup = null;
|
|
51
|
+
uiRef.current = null;
|
|
52
|
+
},
|
|
53
|
+
onKeyDown: ({ event }) => uiRef.current?.onKeyboardEvent(event) ?? false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function insertDefaultMention(editor, range, props, slashChar) {
|
|
57
|
+
const nodeAfter = editor.view.state.selection.$to.nodeAfter;
|
|
58
|
+
const extend = nodeAfter?.text?.startsWith(' ') ? 1 : 0;
|
|
59
|
+
const adjusted = extend ? { ...range, to: range.to + extend } : range;
|
|
60
|
+
editor
|
|
61
|
+
.chain()
|
|
62
|
+
.focus()
|
|
63
|
+
.insertContentAt(adjusted, [
|
|
64
|
+
{
|
|
65
|
+
type: 'mention',
|
|
66
|
+
attrs: {
|
|
67
|
+
id: props.id,
|
|
68
|
+
label: props.label,
|
|
69
|
+
mentionSuggestionChar: slashChar,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{ type: 'text', text: ' ' },
|
|
73
|
+
])
|
|
74
|
+
.run();
|
|
75
|
+
queueMicrotask(() => {
|
|
76
|
+
editor.view.dom.ownerDocument?.defaultView
|
|
77
|
+
?.getSelection?.()
|
|
78
|
+
?.collapseToEnd();
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function allowSlashTrigger({ state, range, }) {
|
|
82
|
+
const type = state.schema.nodes['mention'];
|
|
83
|
+
const $from = state.doc.resolve(range.from);
|
|
84
|
+
if (!type || !$from.parent.type.contentMatch.matchType(type))
|
|
85
|
+
return false;
|
|
86
|
+
if ($from.parentOffset === 0)
|
|
87
|
+
return true;
|
|
88
|
+
const before = state.doc.textBetween(range.from - 1, range.from);
|
|
89
|
+
return /\s/.test(before);
|
|
90
|
+
}
|
|
91
|
+
function createSlashMentionExtension({ items: resolvedItems, slashChar = '/', pluginKey, onItemCommand, onSuggestionUiActiveChange, }) {
|
|
92
|
+
const uiRef = {
|
|
93
|
+
current: null,
|
|
94
|
+
};
|
|
95
|
+
return Mention.configure({
|
|
96
|
+
renderText({ node }) {
|
|
97
|
+
return `/${node.attrs.id}`;
|
|
98
|
+
},
|
|
99
|
+
renderHTML({ options, node, suggestion }) {
|
|
100
|
+
const suggestionChar = suggestion?.char ?? slashChar;
|
|
101
|
+
const id = typeof node.attrs.id === 'string'
|
|
102
|
+
? node.attrs.id
|
|
103
|
+
: String(node.attrs.id ?? '');
|
|
104
|
+
return [
|
|
105
|
+
'span',
|
|
106
|
+
mergeAttributes({
|
|
107
|
+
'data-type': 'mention',
|
|
108
|
+
'data-slash-command': id,
|
|
109
|
+
class: 'slash-mention',
|
|
110
|
+
}, options.HTMLAttributes),
|
|
111
|
+
`${suggestionChar}${id}`,
|
|
112
|
+
];
|
|
113
|
+
},
|
|
114
|
+
suggestion: {
|
|
115
|
+
char: slashChar,
|
|
116
|
+
pluginKey,
|
|
117
|
+
allowedPrefixes: [' ', '\n', '\t', '\r'],
|
|
118
|
+
allow: props => allowSlashTrigger(props),
|
|
119
|
+
items: ({ query }) => Promise.resolve(filterSlashItems(resolvedItems, query)),
|
|
120
|
+
command: ({ editor, range, props }) => {
|
|
121
|
+
const item = props;
|
|
122
|
+
if (onItemCommand?.({ editor, range, item }) === true) {
|
|
123
|
+
queueMicrotask(() => {
|
|
124
|
+
editor.view.dom.ownerDocument?.defaultView
|
|
125
|
+
?.getSelection?.()
|
|
126
|
+
?.collapseToEnd();
|
|
127
|
+
});
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
insertDefaultMention(editor, range, item, slashChar);
|
|
131
|
+
return null;
|
|
132
|
+
},
|
|
133
|
+
render: () => {
|
|
134
|
+
const menu = slashMentionSuggestionRender(uiRef);
|
|
135
|
+
return {
|
|
136
|
+
...menu,
|
|
137
|
+
onStart: props => {
|
|
138
|
+
onSuggestionUiActiveChange?.(true);
|
|
139
|
+
menu.onStart?.(props);
|
|
140
|
+
},
|
|
141
|
+
onExit: props => {
|
|
142
|
+
menu.onExit?.(props);
|
|
143
|
+
onSuggestionUiActiveChange?.(false);
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { createSlashMentionExtension, slashMentionSuggestionRender };
|