@sybilion/uilib 1.3.36 → 1.3.38
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/Chat.types.js +6 -1
- package/dist/esm/components/ui/Chat/ChatChrome/ChatChrome.js +7 -10
- package/dist/esm/components/ui/Chat/ChatPrompt/ChatPrompt.js +2 -1
- package/dist/esm/components/ui/Chat/ChatPrompt/useChatPromptEditor.js +6 -2
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +2 -1
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +33 -15
- package/dist/esm/index.js +1 -1
- package/dist/esm/tiptap/slash-mention/createSlashMentionExtension.js +48 -14
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +6 -1
- package/dist/esm/types/src/components/ui/Chat/Chat.types.test.d.ts +1 -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 +3 -6
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/ChatPrompt.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.d.ts +3 -2
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/ChatSheet.d.ts +1 -1
- package/dist/esm/types/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.d.ts +5 -2
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +1 -1
- package/dist/esm/types/src/docs/docsHeaderActions.d.ts +2 -1
- package/dist/esm/types/src/tiptap/slash-mention/createSlashMentionExtension.d.ts +3 -3
- package/dist/esm/types/src/tiptap/slash-mention/index.d.ts +1 -1
- package/dist/esm/types/src/tiptap/slash-mention/types.d.ts +9 -1
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.types.test.ts +32 -0
- package/src/components/ui/Chat/Chat.types.ts +13 -1
- package/src/components/ui/Chat/ChatChrome/ChatChrome.tsx +24 -46
- package/src/components/ui/Chat/ChatChrome/ChatChrome.types.ts +6 -8
- package/src/components/ui/Chat/ChatPrompt/ChatPrompt.tsx +2 -0
- package/src/components/ui/Chat/ChatPrompt/useChatPromptEditor.ts +11 -2
- package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +2 -0
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +147 -109
- package/src/components/ui/Chat/index.ts +5 -1
- package/src/docs/docsHeaderActions.tsx +3 -2
- package/src/docs/pages/ChatAttachmentsDropzonePage.tsx +0 -5
- package/src/docs/pages/ChatPage.tsx +0 -5
- package/src/docs/pages/ChatSlashCommandsPage.tsx +43 -14
- package/src/docs/pages/ChatUserCsvAttachmentPage.tsx +0 -5
- package/src/tiptap/slash-mention/createSlashMentionExtension.ts +65 -11
- package/src/tiptap/slash-mention/index.ts +1 -0
- package/src/tiptap/slash-mention/types.ts +10 -1
|
@@ -6,5 +6,10 @@ var MessageRole;
|
|
|
6
6
|
})(MessageRole || (MessageRole = {}));
|
|
7
7
|
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
8
8
|
const GENERATING_DASHBOARD_SYSTEM_TEXT = 'Generating dashboard…';
|
|
9
|
+
/** Slash command id that triggers dashboard generation when `onGenerateDashboard` is set. */
|
|
10
|
+
const GENERATE_DASHBOARD_SLASH_COMMAND_ID = 'generate-dashboard';
|
|
11
|
+
function isGenerateDashboardSlashMessage(message) {
|
|
12
|
+
return message.trim() === `/${GENERATE_DASHBOARD_SLASH_COMMAND_ID}`;
|
|
13
|
+
}
|
|
9
14
|
|
|
10
|
-
export { GENERATING_DASHBOARD_SYSTEM_TEXT, MessageRole };
|
|
15
|
+
export { GENERATE_DASHBOARD_SLASH_COMMAND_ID, GENERATING_DASHBOARD_SYSTEM_TEXT, MessageRole, isGenerateDashboardSlashMessage };
|
|
@@ -4,7 +4,7 @@ import { useMemo, useState, useCallback, useEffect } from 'react';
|
|
|
4
4
|
import { displayLabelForBranchKeyFromMessages, humanizeBranchKey } from '../ChatMessage/presetScript.js';
|
|
5
5
|
import { TextShimmer } from '../../TextShimmer/TextShimmer.js';
|
|
6
6
|
import { Scroll } from '@homecode/ui';
|
|
7
|
-
import { X, PaperPlaneRightIcon
|
|
7
|
+
import { X, PaperPlaneRightIcon } from '@phosphor-icons/react';
|
|
8
8
|
import { Button } from '../../Button/Button.js';
|
|
9
9
|
import { DropZone } from '../../DropZone/DropZone.js';
|
|
10
10
|
import { PanelResizeHandle } from '../../Sidebar/Sidebar.js';
|
|
@@ -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,
|
|
17
|
+
function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart, showSyntheticBranchButtons, unusedBranchKeys, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments = false, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, 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]);
|
|
@@ -73,14 +73,11 @@ function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPr
|
|
|
73
73
|
: undefined, onScriptContinue: isLast && scriptContinueLabel
|
|
74
74
|
? onScriptContinue
|
|
75
75
|
: undefined, renderMessageChart: renderMessageChart }, msg.id));
|
|
76
|
-
}),
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
: null, isScriptComplete &&
|
|
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, placeholder: promptPlaceholder, slashCommandItems: slashCommandItems, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
|
|
76
|
+
}), showSyntheticBranchButtons ? (jsx("div", { className: S.branchRow, children: unusedBranchKeys.map(key => {
|
|
77
|
+
const label = displayLabelForBranchKeyFromMessages(key, messages) ??
|
|
78
|
+
humanizeBranchKey(key);
|
|
79
|
+
return (jsx("span", { className: S.branchBtnWrap, children: jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: isLoading, onClick: () => onQuickReply(key, label), children: [jsx(PaperPlaneRightIcon, {}), label] }) }, key));
|
|
80
|
+
}) })) : 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, onSlashItemCommand: onSlashItemCommand, attachmentAccept: attachmentsDropzoneEnabled ? attachmentAccept : undefined, onAttachmentFiles: attachmentsDropzoneEnabled ? handleAttachmentFiles : undefined })] })] })] })] }));
|
|
84
81
|
}
|
|
85
82
|
|
|
86
83
|
export { ChatChrome };
|
|
@@ -6,13 +6,14 @@ import { ChatPromptAttachments } from './ChatPromptAttachments.js';
|
|
|
6
6
|
import { ChatPromptComposer } from './ChatPromptComposer.js';
|
|
7
7
|
import { useChatPromptEditor } from './useChatPromptEditor.js';
|
|
8
8
|
|
|
9
|
-
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }) {
|
|
9
|
+
function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, onSlashItemCommand, attachments = [], onRemoveAttachment, disabled = false, attachmentAccept, onAttachmentFiles, }) {
|
|
10
10
|
const attachmentsCount = attachments.length;
|
|
11
11
|
const emitSubmitRef = useRef(() => { });
|
|
12
12
|
const { editor, trimmedMessage, resetAfterSend, handleComposerKeyDown } = useChatPromptEditor({
|
|
13
13
|
disabled,
|
|
14
14
|
placeholder,
|
|
15
15
|
slashCommandItems,
|
|
16
|
+
onSlashItemCommand,
|
|
16
17
|
prefillMessage,
|
|
17
18
|
attachmentsCount,
|
|
18
19
|
onEnterSubmit: () => emitSubmitRef.current(),
|
|
@@ -7,8 +7,10 @@ import StarterKit from '@tiptap/starter-kit';
|
|
|
7
7
|
import { chatPromptSafeEditorDom, syncChatPromptComposerHeight } from './ChatPrompt.helpers.js';
|
|
8
8
|
import { CHAT_PROMPT_EMPTY_DOC, chatPromptParagraphDoc } from './chatPromptDoc.js';
|
|
9
9
|
|
|
10
|
-
function useChatPromptEditor({ disabled, placeholder, slashCommandItems, prefillMessage, attachmentsCount = 0, onEnterSubmit, }) {
|
|
10
|
+
function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlashItemCommand, prefillMessage, attachmentsCount = 0, onEnterSubmit, }) {
|
|
11
11
|
const slashOpenRef = useRef(false);
|
|
12
|
+
const onSlashItemCommandRef = useRef(onSlashItemCommand);
|
|
13
|
+
onSlashItemCommandRef.current = onSlashItemCommand;
|
|
12
14
|
const suggestionActiveUpdater = useCallback((active) => {
|
|
13
15
|
slashOpenRef.current = active;
|
|
14
16
|
}, []);
|
|
@@ -48,7 +50,9 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, prefill
|
|
|
48
50
|
if (slashItemsStable.length > 0) {
|
|
49
51
|
exts.push(createSlashMentionExtension({
|
|
50
52
|
items: slashItemsStable,
|
|
53
|
+
suggestionPlacement: 'above',
|
|
51
54
|
onSuggestionUiActiveChange: suggestionActiveUpdater,
|
|
55
|
+
onItemCommand: ctx => onSlashItemCommandRef.current?.(ctx) === true,
|
|
52
56
|
}));
|
|
53
57
|
}
|
|
54
58
|
return exts;
|
|
@@ -85,7 +89,7 @@ function useChatPromptEditor({ disabled, placeholder, slashCommandItems, prefill
|
|
|
85
89
|
setPlainDraft(ed.getText());
|
|
86
90
|
},
|
|
87
91
|
onCreate: bindEditorDom,
|
|
88
|
-
}, [extensions,
|
|
92
|
+
}, [extensions, bindEditorDom, ariaLabelComposer]);
|
|
89
93
|
useEffect(() => {
|
|
90
94
|
if (!editor)
|
|
91
95
|
return;
|
|
@@ -4,7 +4,7 @@ import { Button } from '../../Button/Button.js';
|
|
|
4
4
|
import { ChatChrome } from '../ChatChrome/ChatChrome.js';
|
|
5
5
|
import { useChatPanelChromeModel } from './useChatPanelChromeModel.js';
|
|
6
6
|
|
|
7
|
-
function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, inline = false, }) {
|
|
7
|
+
function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, inline = false, }) {
|
|
8
8
|
const model = useChatPanelChromeModel({
|
|
9
9
|
embedAsPage: inline,
|
|
10
10
|
presets,
|
|
@@ -17,6 +17,7 @@ function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, r
|
|
|
17
17
|
allowedAttachments,
|
|
18
18
|
allowPdfAttachments,
|
|
19
19
|
onAttachmentsDropped,
|
|
20
|
+
slashCommandItems,
|
|
20
21
|
});
|
|
21
22
|
if (actionsRef) {
|
|
22
23
|
actionsRef.current = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
3
|
-
import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT } from '../Chat.types.js';
|
|
3
|
+
import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT, GENERATE_DASHBOARD_SLASH_COMMAND_ID, isGenerateDashboardSlashMessage } from '../Chat.types.js';
|
|
4
4
|
import { isGraphIntakeAssistantStepComplete, matchUserTextToQuickReply, isPresetScriptGraph, branchesFromPresetScriptGraph, parseScriptLine, textHasQuickReplyMarkers, branchKeysUsedFromChatHistory, branchKeysUsedByUserMessages, extractQuickReplyLabelKeyPairsFromText, entryBranchKeyBeforeLastAssistant } from '../ChatMessage/presetScript.js';
|
|
5
5
|
import { buildChatSendMessagePayload, displayTextFromSendPayload } from '../buildChatSendMessagePayload.js';
|
|
6
6
|
import { usedPresetIdsFromMessages, formatChatTranscript } from '../chat-preset-utils.js';
|
|
@@ -22,7 +22,7 @@ const CHAT_NAV_COLLAPSE_BREAKPOINT_PX = 1400;
|
|
|
22
22
|
const CHAT_QUERY_PARAM = 'chat';
|
|
23
23
|
const CHAT_OPEN_VALUE = 'open';
|
|
24
24
|
const PROMPT_QUERY_PARAM = 'prompt';
|
|
25
|
-
function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, }) {
|
|
25
|
+
function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, }) {
|
|
26
26
|
const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
|
|
27
27
|
const isMobile = useIsMobile();
|
|
28
28
|
const { chatPanelContainer, isOpen: sidebarNavOpen, setOpen: setSidebarNavOpen, chatWidthPx, setChatWidthPx, getShellWidth, setChatPanelOpen, } = useSidebar();
|
|
@@ -58,7 +58,7 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
58
58
|
const [quickReplyBranchesByChat, setQuickReplyBranchesByChat] = useState({});
|
|
59
59
|
const [usedScriptBranchKeysByChat, setUsedScriptBranchKeysByChat] = useState({});
|
|
60
60
|
const [intakeByChatId, setIntakeByChatId] = useState({});
|
|
61
|
-
/** Preset intake finished for this session
|
|
61
|
+
/** Preset intake finished for this session (e.g. `onScriptComplete` callback). */
|
|
62
62
|
const [scriptCompleteByChatId, setScriptCompleteByChatId] = useState({});
|
|
63
63
|
const [generatingDashboard, setGeneratingDashboard] = useState(false);
|
|
64
64
|
const scriptAdvanceLockRef = useRef(false);
|
|
@@ -712,10 +712,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
712
712
|
lastMsg?.role === MessageRole.ASSISTANT &&
|
|
713
713
|
!linearScriptActive &&
|
|
714
714
|
(!graphActive || (!lastHasQuickMarkers && unusedBranchKeys.length === 0));
|
|
715
|
-
const isScriptComplete = Boolean(currentChatId && scriptCompleteByChatId[currentChatId]);
|
|
716
|
-
/** Branch row also when intake is done but all branches used (Generate Dashboard only). */
|
|
717
|
-
const showBranchActionsRow = showSyntheticBranchButtons ||
|
|
718
|
-
(isScriptComplete && Boolean(onGenerateDashboard) && !generatingDashboard);
|
|
719
715
|
const handleGenerateDashboard = useCallback(async () => {
|
|
720
716
|
if (!currentChatId || !onGenerateDashboard)
|
|
721
717
|
return;
|
|
@@ -743,6 +739,33 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
743
739
|
addMessage,
|
|
744
740
|
removeMessageById,
|
|
745
741
|
]);
|
|
742
|
+
const onSlashItemCommand = useCallback(({ item }) => {
|
|
743
|
+
if (item.id !== GENERATE_DASHBOARD_SLASH_COMMAND_ID ||
|
|
744
|
+
!onGenerateDashboard) {
|
|
745
|
+
return false;
|
|
746
|
+
}
|
|
747
|
+
if (generatingDashboard) {
|
|
748
|
+
return true;
|
|
749
|
+
}
|
|
750
|
+
queueMicrotask(() => {
|
|
751
|
+
void handleGenerateDashboard();
|
|
752
|
+
});
|
|
753
|
+
return true;
|
|
754
|
+
}, [onGenerateDashboard, generatingDashboard, handleGenerateDashboard]);
|
|
755
|
+
const onPromptSubmitWithSlashCommands = useCallback(async (message, attachments) => {
|
|
756
|
+
if (isGenerateDashboardSlashMessage(message) &&
|
|
757
|
+
onGenerateDashboard &&
|
|
758
|
+
!generatingDashboard) {
|
|
759
|
+
void handleGenerateDashboard();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
return handlePromptSubmit(message, attachments);
|
|
763
|
+
}, [
|
|
764
|
+
handlePromptSubmit,
|
|
765
|
+
onGenerateDashboard,
|
|
766
|
+
generatingDashboard,
|
|
767
|
+
handleGenerateDashboard,
|
|
768
|
+
]);
|
|
746
769
|
const onDragChatWidth = useCallback((rawPx) => setChatWidthPx(rawPx, { persist: false }), [setChatWidthPx]);
|
|
747
770
|
const onDragChatComplete = useCallback((finalRawPx) => setChatWidthPx(finalRawPx, { persist: true }), [setChatWidthPx]);
|
|
748
771
|
const chromeProps = {
|
|
@@ -766,26 +789,21 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
766
789
|
scriptContinueLabel,
|
|
767
790
|
onScriptContinue,
|
|
768
791
|
renderMessageChart,
|
|
769
|
-
showBranchActionsRow,
|
|
770
792
|
showSyntheticBranchButtons,
|
|
771
793
|
unusedBranchKeys,
|
|
772
|
-
isScriptComplete,
|
|
773
|
-
onGenerateDashboard,
|
|
774
|
-
generatingDashboard,
|
|
775
|
-
onGenerateDashboardClick: () => {
|
|
776
|
-
void handleGenerateDashboard();
|
|
777
|
-
},
|
|
778
794
|
showInlinePresets,
|
|
779
795
|
isLastMessageFromUser,
|
|
780
796
|
scrollRef,
|
|
781
797
|
effectiveScopeId,
|
|
782
|
-
onPromptSubmit:
|
|
798
|
+
onPromptSubmit: onPromptSubmitWithSlashCommands,
|
|
783
799
|
onChatDeleted: endLocalDemoFlow,
|
|
784
800
|
promptPrefill: promptLinkPrefill,
|
|
785
801
|
emptyState: resolvedEmptyState,
|
|
786
802
|
allowedAttachments,
|
|
787
803
|
allowPdfAttachments,
|
|
788
804
|
onAttachmentsDropped,
|
|
805
|
+
slashCommandItems,
|
|
806
|
+
onSlashItemCommand,
|
|
789
807
|
};
|
|
790
808
|
const toggleOpen = () => onOpenChange(!isOpen);
|
|
791
809
|
return {
|
package/dist/esm/index.js
CHANGED
|
@@ -32,7 +32,7 @@ export { useChatPanelChromeModel } from './components/ui/Chat/ChatSheet/useChatP
|
|
|
32
32
|
export { ChatMessage } from './components/ui/Chat/ChatMessage/ChatMessage.js';
|
|
33
33
|
export { ChatPrompt } from './components/ui/Chat/ChatPrompt/ChatPrompt.js';
|
|
34
34
|
export { ChatPresets } from './components/ui/Chat/ChatPresets/ChatPresets.js';
|
|
35
|
-
export { MessageRole } from './components/ui/Chat/Chat.types.js';
|
|
35
|
+
export { GENERATE_DASHBOARD_SLASH_COMMAND_ID, MessageRole, isGenerateDashboardSlashMessage } from './components/ui/Chat/Chat.types.js';
|
|
36
36
|
export { CsvIcon } from './components/icons/CsvIcon/CsvIcon.js';
|
|
37
37
|
export { Checkbox } from './components/ui/Checkbox/Checkbox.js';
|
|
38
38
|
export { Confirm } from './components/ui/Confirm/Confirm.js';
|
|
@@ -4,17 +4,30 @@ import { ReactRenderer } from '@tiptap/react';
|
|
|
4
4
|
import { SlashSuggestionList } from './SlashSuggestionList.js';
|
|
5
5
|
import { filterSlashItems } from './defaultChatSlashItems.js';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const SUGGESTION_GAP_PX = 4;
|
|
8
|
+
function placeSlashSuggestionPopup(popupElement, clientRect, placement) {
|
|
9
|
+
if (!clientRect)
|
|
10
|
+
return;
|
|
11
|
+
const el = popupElement;
|
|
12
|
+
const popupHeight = el.getBoundingClientRect().height;
|
|
13
|
+
const spaceBelow = window.innerHeight - clientRect.bottom - SUGGESTION_GAP_PX;
|
|
14
|
+
const spaceAbove = clientRect.top - SUGGESTION_GAP_PX;
|
|
15
|
+
let showAbove = placement === 'above';
|
|
16
|
+
if (placement === 'auto') {
|
|
17
|
+
showAbove = spaceBelow < popupHeight && spaceAbove >= spaceBelow;
|
|
18
|
+
}
|
|
19
|
+
const top = showAbove
|
|
20
|
+
? Math.max(SUGGESTION_GAP_PX, clientRect.top - popupHeight - SUGGESTION_GAP_PX)
|
|
21
|
+
: clientRect.bottom + SUGGESTION_GAP_PX;
|
|
22
|
+
el.style.left = `${clientRect.left}px`;
|
|
23
|
+
el.style.top = `${top}px`;
|
|
24
|
+
}
|
|
25
|
+
function slashMentionSuggestionRender(uiRef, placement = 'below') {
|
|
8
26
|
let popup = null;
|
|
9
27
|
const place = (props) => {
|
|
10
28
|
if (!popup?.element)
|
|
11
29
|
return;
|
|
12
|
-
|
|
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`;
|
|
30
|
+
placeSlashSuggestionPopup(popup.element, props.clientRect?.() ?? null, placement);
|
|
18
31
|
};
|
|
19
32
|
return {
|
|
20
33
|
onStart: props => {
|
|
@@ -33,6 +46,7 @@ function slashMentionSuggestionRender(uiRef) {
|
|
|
33
46
|
popup.element.style.zIndex = '10002';
|
|
34
47
|
document.body.append(popup.element);
|
|
35
48
|
place(props);
|
|
49
|
+
requestAnimationFrame(() => place(props));
|
|
36
50
|
},
|
|
37
51
|
onUpdate: props => {
|
|
38
52
|
if (!popup)
|
|
@@ -43,6 +57,7 @@ function slashMentionSuggestionRender(uiRef) {
|
|
|
43
57
|
listHandleRef: uiRef,
|
|
44
58
|
});
|
|
45
59
|
place(props);
|
|
60
|
+
requestAnimationFrame(() => place(props));
|
|
46
61
|
},
|
|
47
62
|
onExit: () => {
|
|
48
63
|
popup?.destroy();
|
|
@@ -53,6 +68,28 @@ function slashMentionSuggestionRender(uiRef) {
|
|
|
53
68
|
onKeyDown: ({ event }) => uiRef.current?.onKeyboardEvent(event) ?? false,
|
|
54
69
|
};
|
|
55
70
|
}
|
|
71
|
+
function clearSlashTriggerEditor(editor, range) {
|
|
72
|
+
if (editor.isDestroyed)
|
|
73
|
+
return;
|
|
74
|
+
try {
|
|
75
|
+
editor.chain().focus().deleteRange(range).clearContent().run();
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Editor view may be tearing down during suggestion exit.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function collapseEditorSelectionEnd(editor) {
|
|
82
|
+
if (editor.isDestroyed)
|
|
83
|
+
return;
|
|
84
|
+
try {
|
|
85
|
+
editor.view?.dom?.ownerDocument?.defaultView
|
|
86
|
+
?.getSelection?.()
|
|
87
|
+
?.collapseToEnd();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// view.dom throws when editor is not mounted
|
|
91
|
+
}
|
|
92
|
+
}
|
|
56
93
|
function insertDefaultMention(editor, range, props, slashChar) {
|
|
57
94
|
const nodeAfter = editor.view.state.selection.$to.nodeAfter;
|
|
58
95
|
const extend = nodeAfter?.text?.startsWith(' ') ? 1 : 0;
|
|
@@ -88,7 +125,7 @@ function allowSlashTrigger({ state, range, }) {
|
|
|
88
125
|
const before = state.doc.textBetween(range.from - 1, range.from);
|
|
89
126
|
return /\s/.test(before);
|
|
90
127
|
}
|
|
91
|
-
function createSlashMentionExtension({ items: resolvedItems, slashChar = '/', pluginKey, onItemCommand, onSuggestionUiActiveChange, }) {
|
|
128
|
+
function createSlashMentionExtension({ items: resolvedItems, slashChar = '/', pluginKey, onItemCommand, suggestionPlacement = 'below', onSuggestionUiActiveChange, }) {
|
|
92
129
|
const uiRef = {
|
|
93
130
|
current: null,
|
|
94
131
|
};
|
|
@@ -120,18 +157,15 @@ function createSlashMentionExtension({ items: resolvedItems, slashChar = '/', pl
|
|
|
120
157
|
command: ({ editor, range, props }) => {
|
|
121
158
|
const item = props;
|
|
122
159
|
if (onItemCommand?.({ editor, range, item }) === true) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
?.getSelection?.()
|
|
126
|
-
?.collapseToEnd();
|
|
127
|
-
});
|
|
160
|
+
clearSlashTriggerEditor(editor, range);
|
|
161
|
+
queueMicrotask(() => collapseEditorSelectionEnd(editor));
|
|
128
162
|
return null;
|
|
129
163
|
}
|
|
130
164
|
insertDefaultMention(editor, range, item, slashChar);
|
|
131
165
|
return null;
|
|
132
166
|
},
|
|
133
167
|
render: () => {
|
|
134
|
-
const menu = slashMentionSuggestionRender(uiRef);
|
|
168
|
+
const menu = slashMentionSuggestionRender(uiRef, suggestionPlacement);
|
|
135
169
|
return {
|
|
136
170
|
...menu,
|
|
137
171
|
onStart: props => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
-
import type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
2
|
+
import type { SlashCommandItem, SlashOnItemCommand } from '#uilib/tiptap/slash-mention/types';
|
|
3
3
|
import type { PresetScriptGraph } from './ChatMessage/presetScript';
|
|
4
4
|
export declare enum MessageRole {
|
|
5
5
|
USER = "user",
|
|
@@ -8,6 +8,9 @@ export declare enum MessageRole {
|
|
|
8
8
|
}
|
|
9
9
|
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
10
10
|
export declare const GENERATING_DASHBOARD_SYSTEM_TEXT = "Generating dashboard\u2026";
|
|
11
|
+
/** Slash command id that triggers dashboard generation when `onGenerateDashboard` is set. */
|
|
12
|
+
export declare const GENERATE_DASHBOARD_SLASH_COMMAND_ID = "generate-dashboard";
|
|
13
|
+
export declare function isGenerateDashboardSlashMessage(message: string): boolean;
|
|
11
14
|
/** USER-only: text file attached to a message; shown as downloadable file row(s). */
|
|
12
15
|
export type UserTextFileAttachment = {
|
|
13
16
|
displayName: string;
|
|
@@ -84,6 +87,8 @@ export interface ChatPromptProps {
|
|
|
84
87
|
onAttachmentFiles?: (files: File[]) => void;
|
|
85
88
|
/** Slash menu (`/`); omit or pass empty to disable Mention trigger (no default items). */
|
|
86
89
|
slashCommandItems?: SlashCommandItem[];
|
|
90
|
+
/** Custom slash pick handler; return true to skip default mention insert. */
|
|
91
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
87
92
|
}
|
|
88
93
|
export interface ChatMessageProps {
|
|
89
94
|
role: MessageRole;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChatChromeProps } from './ChatChrome.types';
|
|
2
|
-
export declare function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart,
|
|
2
|
+
export declare function ChatChrome({ showResizeHandle, resizeHandle, onClose, isEmpty, renderPresets, messages, onQuickReply, suppressedQuickReplyKeys, isLoading, scriptContinueLabel, onScriptContinue, renderMessageChart, showSyntheticBranchButtons, unusedBranchKeys, showInlinePresets, isLastMessageFromUser, scrollRef, effectiveScopeId, onPromptSubmit, onChatDeleted, promptPrefill, footerClassName, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, promptPlaceholder, }: ChatChromeProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,7 +2,7 @@ import type { RefObject } from 'react';
|
|
|
2
2
|
import type { ChatAttachmentDropItem, Message } from '#uilib/components/ui/Chat/Chat.types';
|
|
3
3
|
import type { ChatEmptyStateProps } from '#uilib/components/ui/Chat/ChatEmptyState/ChatEmptyState.types';
|
|
4
4
|
import type { ChatPresetsLayout } from '#uilib/components/ui/Chat/ChatPresets';
|
|
5
|
-
import type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
5
|
+
import type { SlashCommandItem, SlashOnItemCommand } from '#uilib/tiptap/slash-mention/types';
|
|
6
6
|
import type { ScrollRef } from '@homecode/ui';
|
|
7
7
|
export type ChatChromeResizeHandleConfig = {
|
|
8
8
|
isActive: boolean;
|
|
@@ -26,13 +26,8 @@ export interface ChatChromeProps {
|
|
|
26
26
|
scriptContinueLabel: string | undefined;
|
|
27
27
|
onScriptContinue: (() => void) | undefined;
|
|
28
28
|
renderMessageChart?: () => React.ReactNode;
|
|
29
|
-
showBranchActionsRow: boolean;
|
|
30
29
|
showSyntheticBranchButtons: boolean;
|
|
31
30
|
unusedBranchKeys: string[];
|
|
32
|
-
isScriptComplete: boolean;
|
|
33
|
-
onGenerateDashboard: ((transcript: string) => void | Promise<void>) | undefined;
|
|
34
|
-
generatingDashboard: boolean;
|
|
35
|
-
onGenerateDashboardClick: () => void;
|
|
36
31
|
showInlinePresets: boolean;
|
|
37
32
|
isLastMessageFromUser: boolean;
|
|
38
33
|
scrollRef: RefObject<ScrollRef | null>;
|
|
@@ -51,6 +46,8 @@ export interface ChatChromeProps {
|
|
|
51
46
|
onAttachmentsDropped?: (items: ChatAttachmentDropItem[]) => void | Promise<void>;
|
|
52
47
|
/** Slash menu (`/`), forwarded to `Chat.Prompt`; omit or pass empty list to disable slash palette. */
|
|
53
48
|
slashCommandItems?: SlashCommandItem[];
|
|
49
|
+
/** Custom slash pick handler; forwarded to `Chat.Prompt`. */
|
|
50
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
54
51
|
/** Composer placeholder forwarded to `Chat.Prompt`. */
|
|
55
52
|
promptPlaceholder?: string;
|
|
56
53
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ChatPromptProps } from '../Chat.types';
|
|
2
|
-
export declare function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, attachments, onRemoveAttachment, disabled, attachmentAccept, onAttachmentFiles, }: ChatPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ChatPrompt({ onSubmit, placeholder, className, footer, prefillMessage, slashCommandItems, onSlashItemCommand, attachments, onRemoveAttachment, disabled, attachmentAccept, onAttachmentFiles, }: ChatPromptProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type KeyboardEvent as ReactKeyboardEvent } from 'react';
|
|
2
|
-
import type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
2
|
+
import type { SlashCommandItem, SlashOnItemCommand } from '#uilib/tiptap/slash-mention/types';
|
|
3
3
|
import type { Editor } from '@tiptap/core';
|
|
4
4
|
export type UseChatPromptEditorOptions = {
|
|
5
5
|
disabled: boolean;
|
|
6
6
|
placeholder?: string;
|
|
7
7
|
slashCommandItems?: SlashCommandItem[];
|
|
8
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
8
9
|
prefillMessage?: string | null;
|
|
9
10
|
/** Staged attachment count — Enter-to-send when text empty but files present. */
|
|
10
11
|
attachmentsCount?: number;
|
|
@@ -17,4 +18,4 @@ export type UseChatPromptEditorResult = {
|
|
|
17
18
|
resetAfterSend: () => void;
|
|
18
19
|
handleComposerKeyDown: (event: ReactKeyboardEvent) => void;
|
|
19
20
|
};
|
|
20
|
-
export declare function useChatPromptEditor({ disabled, placeholder, slashCommandItems, prefillMessage, attachmentsCount, onEnterSubmit, }: UseChatPromptEditorOptions): UseChatPromptEditorResult;
|
|
21
|
+
export declare function useChatPromptEditor({ disabled, placeholder, slashCommandItems, onSlashItemCommand, prefillMessage, attachmentsCount, onEnterSubmit, }: UseChatPromptEditorOptions): UseChatPromptEditorResult;
|
|
@@ -19,4 +19,4 @@ export interface ChatSheetProps extends Omit<UseChatPanelChromeModelInput, 'embe
|
|
|
19
19
|
*/
|
|
20
20
|
inline?: boolean;
|
|
21
21
|
}
|
|
22
|
-
export declare function ChatSheet({ triggerLabel, triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, inline, }: ChatSheetProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare function ChatSheet({ triggerLabel, triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, inline, }: ChatSheetProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ChatPreset, type ScriptCompletePayload } from '#uilib/components/ui/Chat/Chat.types';
|
|
2
|
+
import type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
2
3
|
import type { ChatChromeProps } from '../ChatChrome';
|
|
3
4
|
import type { ChatAttachmentDropItem } from '../ChatChrome/ChatChrome.types';
|
|
4
5
|
import type { ChatEmptyStateConfig } from '../ChatEmptyState/ChatEmptyState.types';
|
|
@@ -11,7 +12,7 @@ export type UseChatPanelChromeModelInput = {
|
|
|
11
12
|
onMessage?: (message: string) => void;
|
|
12
13
|
/** Fires when a preset script has no further `[Label|branchKey]` steps (graph leaf or linear script end). */
|
|
13
14
|
onScriptComplete?: (payload: ScriptCompletePayload) => void;
|
|
14
|
-
/**
|
|
15
|
+
/** Generate dashboard from chat transcript (e.g. via `/generate-dashboard` slash command). */
|
|
15
16
|
onGenerateDashboard?: (transcript: string) => void | Promise<void>;
|
|
16
17
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
17
18
|
renderMessageChart?: () => React.ReactNode;
|
|
@@ -22,6 +23,8 @@ export type UseChatPanelChromeModelInput = {
|
|
|
22
23
|
/** When true, PDF drops are accepted and parsed to plain text. */
|
|
23
24
|
allowPdfAttachments?: boolean;
|
|
24
25
|
onAttachmentsDropped?: (items: ChatAttachmentDropItem[]) => void | Promise<void>;
|
|
26
|
+
/** Slash menu (`/`) in the composer; omit or pass empty to disable. */
|
|
27
|
+
slashCommandItems?: SlashCommandItem[];
|
|
25
28
|
};
|
|
26
29
|
export type UseChatPanelChromeModelResult = {
|
|
27
30
|
chromeProps: ChatChromeProps;
|
|
@@ -31,4 +34,4 @@ export type UseChatPanelChromeModelResult = {
|
|
|
31
34
|
newChat: () => void;
|
|
32
35
|
chatPanelContainer: HTMLElement | null;
|
|
33
36
|
};
|
|
34
|
-
export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
|
|
37
|
+
export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, onGenerateDashboard, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
|
|
@@ -14,6 +14,6 @@ export { ChatPrompt } from './ChatPrompt';
|
|
|
14
14
|
export { ChatPresets } from './ChatPresets';
|
|
15
15
|
export type { ChatEmptyStateConfig, ChatEmptyStateContext, ChatEmptyStateProps, } from './ChatEmptyState/ChatEmptyState.types';
|
|
16
16
|
export type { Chat as ChatType, ChatAttachmentDropItem, ChatSendMessagePayload, ChatProps, ChatPreset as ChatPresetType, Message, UserTextFileAttachment, } from './Chat.types';
|
|
17
|
-
export { MessageRole } from './Chat.types';
|
|
17
|
+
export { GENERATE_DASHBOARD_SLASH_COMMAND_ID, isGenerateDashboardSlashMessage, MessageRole, } from './Chat.types';
|
|
18
18
|
export type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
19
19
|
export { CsvIcon } from '../../icons/CsvIcon/CsvIcon';
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { ChatSheetProps } from '#uilib/components/ui/Chat';
|
|
2
|
+
export declare function DocsHeaderActions(props: ChatSheetProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { MutableRefObject } from 'react';
|
|
2
2
|
import type { SuggestionProps } from '@tiptap/suggestion';
|
|
3
3
|
import { type SlashSuggestionListHandle } from './SlashSuggestionList';
|
|
4
|
-
import type { CreateSlashMentionExtensionOptions, SlashCommandItem } from './types';
|
|
5
|
-
export declare function slashMentionSuggestionRender(uiRef: MutableRefObject<SlashSuggestionListHandle | null
|
|
4
|
+
import type { CreateSlashMentionExtensionOptions, SlashCommandItem, SlashSuggestionPlacement } from './types';
|
|
5
|
+
export declare function slashMentionSuggestionRender(uiRef: MutableRefObject<SlashSuggestionListHandle | null>, placement?: SlashSuggestionPlacement): {
|
|
6
6
|
onStart?: (props: SuggestionProps<SlashCommandItem, SlashCommandItem>) => void;
|
|
7
7
|
onUpdate?: (props: SuggestionProps<SlashCommandItem, SlashCommandItem>) => void;
|
|
8
8
|
onExit?: (props: SuggestionProps<SlashCommandItem, SlashCommandItem>) => void;
|
|
@@ -18,4 +18,4 @@ export declare function slashMentionSuggestionRender(uiRef: MutableRefObject<Sla
|
|
|
18
18
|
export type CreateSlashMentionExtensionConfiguredOptions = CreateSlashMentionExtensionOptions & {
|
|
19
19
|
onSuggestionUiActiveChange?: (active: boolean) => void;
|
|
20
20
|
};
|
|
21
|
-
export declare function createSlashMentionExtension({ items: resolvedItems, slashChar, pluginKey, onItemCommand, onSuggestionUiActiveChange, }: CreateSlashMentionExtensionConfiguredOptions): import("@tiptap/core").Node<import("@tiptap/extension-mention").MentionOptions<any, import("@tiptap/extension-mention").MentionNodeAttrs>, any>;
|
|
21
|
+
export declare function createSlashMentionExtension({ items: resolvedItems, slashChar, pluginKey, onItemCommand, suggestionPlacement, onSuggestionUiActiveChange, }: CreateSlashMentionExtensionConfiguredOptions): import("@tiptap/core").Node<import("@tiptap/extension-mention").MentionOptions<any, import("@tiptap/extension-mention").MentionNodeAttrs>, any>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { SlashCommandItem, SlashOnItemCommand, SlashItemCommandContext, CreateSlashMentionExtensionOptions, } from './types';
|
|
1
|
+
export type { SlashCommandItem, SlashOnItemCommand, SlashItemCommandContext, SlashSuggestionPlacement, CreateSlashMentionExtensionOptions, } from './types';
|
|
2
2
|
export { DEFAULT_CHAT_SLASH_ITEMS, filterSlashItems, } from './defaultChatSlashItems';
|
|
3
3
|
export { createSlashMentionExtension, slashMentionSuggestionRender, } from './createSlashMentionExtension';
|
|
4
4
|
export type { CreateSlashMentionExtensionConfiguredOptions } from './createSlashMentionExtension';
|
|
@@ -10,9 +10,12 @@ export type SlashItemCommandContext = {
|
|
|
10
10
|
item: SlashCommandItem;
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
|
-
* If provided, run
|
|
13
|
+
* If provided, run when a slash item is picked. Return true to skip mention insert
|
|
14
|
+
* (extension clears the trigger text from the composer).
|
|
14
15
|
*/
|
|
15
16
|
export type SlashOnItemCommand = (ctx: SlashItemCommandContext) => boolean;
|
|
17
|
+
/** Where the slash palette opens relative to the caret. */
|
|
18
|
+
export type SlashSuggestionPlacement = 'below' | 'above' | 'auto';
|
|
16
19
|
export type CreateSlashMentionExtensionOptions = {
|
|
17
20
|
/** Items shown in the slash menu (filtered by query after `/`). */
|
|
18
21
|
items: SlashCommandItem[];
|
|
@@ -22,4 +25,9 @@ export type CreateSlashMentionExtensionOptions = {
|
|
|
22
25
|
pluginKey?: import('@tiptap/pm/state').PluginKey;
|
|
23
26
|
/** Custom handler (e.g. insert a block node instead of a mention). */
|
|
24
27
|
onItemCommand?: SlashOnItemCommand;
|
|
28
|
+
/**
|
|
29
|
+
* Palette position vs caret. Default `below`.
|
|
30
|
+
* Use `above` for bottom-anchored composers (chat prompt); `auto` flips by viewport space.
|
|
31
|
+
*/
|
|
32
|
+
suggestionPlacement?: SlashSuggestionPlacement;
|
|
25
33
|
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GENERATE_DASHBOARD_SLASH_COMMAND_ID,
|
|
3
|
+
isGenerateDashboardSlashMessage,
|
|
4
|
+
} from './Chat.types';
|
|
5
|
+
|
|
6
|
+
describe('isGenerateDashboardSlashMessage', () => {
|
|
7
|
+
it('matches exact slash command', () => {
|
|
8
|
+
expect(
|
|
9
|
+
isGenerateDashboardSlashMessage(
|
|
10
|
+
`/${GENERATE_DASHBOARD_SLASH_COMMAND_ID}`,
|
|
11
|
+
),
|
|
12
|
+
).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('trims surrounding whitespace', () => {
|
|
16
|
+
expect(
|
|
17
|
+
isGenerateDashboardSlashMessage(
|
|
18
|
+
` /${GENERATE_DASHBOARD_SLASH_COMMAND_ID} `,
|
|
19
|
+
),
|
|
20
|
+
).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('rejects plain text and partial commands', () => {
|
|
24
|
+
expect(isGenerateDashboardSlashMessage('generate-dashboard')).toBe(false);
|
|
25
|
+
expect(isGenerateDashboardSlashMessage('/generate')).toBe(false);
|
|
26
|
+
expect(
|
|
27
|
+
isGenerateDashboardSlashMessage(
|
|
28
|
+
`/ ${GENERATE_DASHBOARD_SLASH_COMMAND_ID}`,
|
|
29
|
+
),
|
|
30
|
+
).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
SlashCommandItem,
|
|
5
|
+
SlashOnItemCommand,
|
|
6
|
+
} from '#uilib/tiptap/slash-mention/types';
|
|
4
7
|
|
|
5
8
|
import type { PresetScriptGraph } from './ChatMessage/presetScript';
|
|
6
9
|
|
|
@@ -13,6 +16,13 @@ export enum MessageRole {
|
|
|
13
16
|
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
14
17
|
export const GENERATING_DASHBOARD_SYSTEM_TEXT = 'Generating dashboard…';
|
|
15
18
|
|
|
19
|
+
/** Slash command id that triggers dashboard generation when `onGenerateDashboard` is set. */
|
|
20
|
+
export const GENERATE_DASHBOARD_SLASH_COMMAND_ID = 'generate-dashboard';
|
|
21
|
+
|
|
22
|
+
export function isGenerateDashboardSlashMessage(message: string): boolean {
|
|
23
|
+
return message.trim() === `/${GENERATE_DASHBOARD_SLASH_COMMAND_ID}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
/** USER-only: text file attached to a message; shown as downloadable file row(s). */
|
|
17
27
|
export type UserTextFileAttachment = {
|
|
18
28
|
displayName: string;
|
|
@@ -96,6 +106,8 @@ export interface ChatPromptProps {
|
|
|
96
106
|
onAttachmentFiles?: (files: File[]) => void;
|
|
97
107
|
/** Slash menu (`/`); omit or pass empty to disable Mention trigger (no default items). */
|
|
98
108
|
slashCommandItems?: SlashCommandItem[];
|
|
109
|
+
/** Custom slash pick handler; return true to skip default mention insert. */
|
|
110
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
export interface ChatMessageProps {
|