@sybilion/uilib 1.3.38 → 1.3.39
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 +1 -8
- package/dist/esm/components/ui/Chat/ChatMessage/ChatMessage.js +2 -3
- package/dist/esm/components/ui/Chat/ChatSheet/ChatSheet.js +2 -2
- package/dist/esm/components/ui/Chat/ChatSheet/useChatPanelChromeModel.js +7 -54
- package/dist/esm/index.js +2 -2
- package/dist/esm/types/src/components/ui/Chat/Chat.types.d.ts +0 -5
- 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 +4 -4
- package/dist/esm/types/src/components/ui/Chat/index.d.ts +3 -3
- package/dist/esm/types/src/tiptap/slash-mention/types.d.ts +3 -2
- package/package.json +1 -1
- package/src/components/ui/Chat/Chat.types.ts +0 -10
- package/src/components/ui/Chat/ChatMessage/ChatMessage.tsx +2 -13
- package/src/components/ui/Chat/ChatSheet/ChatSheet.tsx +2 -2
- package/src/components/ui/Chat/ChatSheet/useChatPanelChromeModel.tsx +11 -75
- package/src/components/ui/Chat/index.ts +10 -7
- package/src/docs/pages/ChatSlashCommandsPage.tsx +0 -3
- package/src/tiptap/slash-mention/types.ts +3 -2
- package/dist/esm/types/src/components/ui/Chat/Chat.types.test.d.ts +0 -1
- package/src/components/ui/Chat/Chat.types.test.ts +0 -32
|
@@ -4,12 +4,5 @@ var MessageRole;
|
|
|
4
4
|
MessageRole["ASSISTANT"] = "assistant";
|
|
5
5
|
MessageRole["SYSTEM"] = "system";
|
|
6
6
|
})(MessageRole || (MessageRole = {}));
|
|
7
|
-
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
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
|
-
}
|
|
14
7
|
|
|
15
|
-
export {
|
|
8
|
+
export { MessageRole };
|
|
@@ -3,8 +3,7 @@ import cn from 'classnames';
|
|
|
3
3
|
import { InteractiveContent } from '../../InteractiveContent/InteractiveContent.js';
|
|
4
4
|
import 'lucide-react';
|
|
5
5
|
import '../../InteractiveContent/InteractiveContent.styl.js';
|
|
6
|
-
import {
|
|
7
|
-
import { MessageRole, GENERATING_DASHBOARD_SYSTEM_TEXT } from '../Chat.types.js';
|
|
6
|
+
import { MessageRole } from '../Chat.types.js';
|
|
8
7
|
import { userTextFileAttachmentsFromMessage } from '../userTextFileAttachments.js';
|
|
9
8
|
import { AgentMessageContent } from './AgentMessageContent.js';
|
|
10
9
|
import S from './ChatMessage.styl.js';
|
|
@@ -16,7 +15,7 @@ function ChatMessage({ role, text, userTextFileAttachments, onQuickReply, suppre
|
|
|
16
15
|
});
|
|
17
16
|
const isAssistant = role === MessageRole.ASSISTANT;
|
|
18
17
|
const isSystem = role === MessageRole.SYSTEM;
|
|
19
|
-
return (jsx("div", { className: cn(S.root, S[`role-${role}`]), children: isSystem ? (jsx("div", { className: S.text, children: text
|
|
18
|
+
return (jsx("div", { className: cn(S.root, S[`role-${role}`]), children: isSystem ? (jsx("div", { className: S.text, children: text })) : isAssistant ? (jsx(AgentMessageContent, { text: text, onQuickReply: onQuickReply, suppressedQuickReplyKeys: suppressedQuickReplyKeys, quickReplyDisabled: quickReplyDisabled, isLastMessage: isLastMessage, scriptContinue: scriptContinue, onScriptContinue: onScriptContinue, renderMessageChart: renderMessageChart })) : (jsxs("div", { className: S.userColumn, children: [jsx("div", { className: S.text, children: jsx(InteractiveContent, { text: text }) }), fileAttachments.map(attachment => (jsx(UserTextFileAttachmentBubble, { attachment: attachment }, `${attachment.displayName}:${attachment.filename}`)))] })) }));
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export { ChatMessage };
|
|
@@ -4,20 +4,20 @@ 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,
|
|
7
|
+
function ChatSheet({ triggerLabel = 'Open Chat', triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, inline = false, }) {
|
|
8
8
|
const model = useChatPanelChromeModel({
|
|
9
9
|
embedAsPage: inline,
|
|
10
10
|
presets,
|
|
11
11
|
scopeId,
|
|
12
12
|
onMessage,
|
|
13
13
|
onScriptComplete,
|
|
14
|
-
onGenerateDashboard,
|
|
15
14
|
renderMessageChart,
|
|
16
15
|
emptyState,
|
|
17
16
|
allowedAttachments,
|
|
18
17
|
allowPdfAttachments,
|
|
19
18
|
onAttachmentsDropped,
|
|
20
19
|
slashCommandItems,
|
|
20
|
+
onSlashItemCommand,
|
|
21
21
|
});
|
|
22
22
|
if (actionsRef) {
|
|
23
23
|
actionsRef.current = {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
3
|
-
import { MessageRole
|
|
3
|
+
import { MessageRole } 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
|
-
import { usedPresetIdsFromMessages
|
|
6
|
+
import { usedPresetIdsFromMessages } from '../chat-preset-utils.js';
|
|
7
7
|
import { useChatsForScopeId, useChat, useChatOutboundPending, useSyncChatPanelBusy, isChatEmpty } from '../../../../contexts/chat-context.js';
|
|
8
8
|
import useEvent from '../../../../hooks/useEvent.js';
|
|
9
9
|
import { useIsMobile } from '../../../../hooks/useIsMobile.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,
|
|
25
|
+
function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, }) {
|
|
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();
|
|
@@ -60,7 +60,6 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
60
60
|
const [intakeByChatId, setIntakeByChatId] = useState({});
|
|
61
61
|
/** Preset intake finished for this session (e.g. `onScriptComplete` callback). */
|
|
62
62
|
const [scriptCompleteByChatId, setScriptCompleteByChatId] = useState({});
|
|
63
|
-
const [generatingDashboard, setGeneratingDashboard] = useState(false);
|
|
64
63
|
const scriptAdvanceLockRef = useRef(false);
|
|
65
64
|
const quickReplyLockRef = useRef(false);
|
|
66
65
|
const scrollRef = useRef(null);
|
|
@@ -712,60 +711,14 @@ function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onS
|
|
|
712
711
|
lastMsg?.role === MessageRole.ASSISTANT &&
|
|
713
712
|
!linearScriptActive &&
|
|
714
713
|
(!graphActive || (!lastHasQuickMarkers && unusedBranchKeys.length === 0));
|
|
715
|
-
const handleGenerateDashboard = useCallback(async () => {
|
|
716
|
-
if (!currentChatId || !onGenerateDashboard)
|
|
717
|
-
return;
|
|
718
|
-
const transcript = formatChatTranscript((chat?.messages ?? []).filter(m => m.role !== MessageRole.SYSTEM));
|
|
719
|
-
setGeneratingDashboard(true);
|
|
720
|
-
const systemPlaceholderId = addMessage(currentChatId, MessageRole.SYSTEM, GENERATING_DASHBOARD_SYSTEM_TEXT);
|
|
721
|
-
try {
|
|
722
|
-
await Promise.resolve(onGenerateDashboard(transcript));
|
|
723
|
-
setScriptCompleteByChatId(prev => {
|
|
724
|
-
const next = { ...prev };
|
|
725
|
-
delete next[currentChatId];
|
|
726
|
-
return next;
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
|
-
finally {
|
|
730
|
-
if (systemPlaceholderId) {
|
|
731
|
-
removeMessageById(currentChatId, systemPlaceholderId);
|
|
732
|
-
}
|
|
733
|
-
setGeneratingDashboard(false);
|
|
734
|
-
}
|
|
735
|
-
}, [
|
|
736
|
-
currentChatId,
|
|
737
|
-
onGenerateDashboard,
|
|
738
|
-
chat?.messages,
|
|
739
|
-
addMessage,
|
|
740
|
-
removeMessageById,
|
|
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
714
|
const onPromptSubmitWithSlashCommands = useCallback(async (message, attachments) => {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
void handleGenerateDashboard();
|
|
715
|
+
const trimmed = message.trim();
|
|
716
|
+
const slashItem = slashCommandItems?.find(item => trimmed === `/${item.id}`);
|
|
717
|
+
if (slashItem && onSlashItemCommand?.({ item: slashItem }) === true) {
|
|
760
718
|
return;
|
|
761
719
|
}
|
|
762
720
|
return handlePromptSubmit(message, attachments);
|
|
763
|
-
}, [
|
|
764
|
-
handlePromptSubmit,
|
|
765
|
-
onGenerateDashboard,
|
|
766
|
-
generatingDashboard,
|
|
767
|
-
handleGenerateDashboard,
|
|
768
|
-
]);
|
|
721
|
+
}, [handlePromptSubmit, slashCommandItems, onSlashItemCommand]);
|
|
769
722
|
const onDragChatWidth = useCallback((rawPx) => setChatWidthPx(rawPx, { persist: false }), [setChatWidthPx]);
|
|
770
723
|
const onDragChatComplete = useCallback((finalRawPx) => setChatWidthPx(finalRawPx, { persist: true }), [setChatWidthPx]);
|
|
771
724
|
const chromeProps = {
|
package/dist/esm/index.js
CHANGED
|
@@ -22,7 +22,7 @@ export { ChartLegend, ChartTooltip } from './components/ui/Chart/Chart.js';
|
|
|
22
22
|
export { THEMES } from './components/ui/Chart/Chart.types.js';
|
|
23
23
|
export { ChartAreaInteractive, chartConfig } from './components/ui/ChartAreaInteractive/ChartAreaInteractive.js';
|
|
24
24
|
export { Chat } from './components/ui/Chat/Chat.js';
|
|
25
|
-
export { usedPresetIdsFromMessages } from './components/ui/Chat/chat-preset-utils.js';
|
|
25
|
+
export { formatChatTranscript, usedPresetIdsFromMessages } from './components/ui/Chat/chat-preset-utils.js';
|
|
26
26
|
export { ChatChrome } from './components/ui/Chat/ChatChrome/ChatChrome.js';
|
|
27
27
|
export { TEXT_ATTACHMENT_ACCEPT_PARTS, filterToTextAttachments } from './components/ui/Chat/chatAttachmentAccept.js';
|
|
28
28
|
export { buildChatSendMessagePayload, displayTextFromSendPayload, normalizeUserTextFileAttachments } from './components/ui/Chat/buildChatSendMessagePayload.js';
|
|
@@ -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 {
|
|
35
|
+
export { MessageRole } 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';
|
|
@@ -6,11 +6,6 @@ export declare enum MessageRole {
|
|
|
6
6
|
ASSISTANT = "assistant",
|
|
7
7
|
SYSTEM = "system"
|
|
8
8
|
}
|
|
9
|
-
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
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;
|
|
14
9
|
/** USER-only: text file attached to a message; shown as downloadable file row(s). */
|
|
15
10
|
export type UserTextFileAttachment = {
|
|
16
11
|
displayName: string;
|
|
@@ -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,
|
|
22
|
+
export declare function ChatSheet({ triggerLabel, triggerAriaLabel, actionsRef, renderTrigger, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, inline, }: ChatSheetProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +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
|
+
import type { SlashCommandItem, SlashOnItemCommand } from '#uilib/tiptap/slash-mention/types';
|
|
3
3
|
import type { ChatChromeProps } from '../ChatChrome';
|
|
4
4
|
import type { ChatAttachmentDropItem } from '../ChatChrome/ChatChrome.types';
|
|
5
5
|
import type { ChatEmptyStateConfig } from '../ChatEmptyState/ChatEmptyState.types';
|
|
@@ -12,8 +12,6 @@ export type UseChatPanelChromeModelInput = {
|
|
|
12
12
|
onMessage?: (message: string) => void;
|
|
13
13
|
/** Fires when a preset script has no further `[Label|branchKey]` steps (graph leaf or linear script end). */
|
|
14
14
|
onScriptComplete?: (payload: ScriptCompletePayload) => void;
|
|
15
|
-
/** Generate dashboard from chat transcript (e.g. via `/generate-dashboard` slash command). */
|
|
16
|
-
onGenerateDashboard?: (transcript: string) => void | Promise<void>;
|
|
17
15
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
18
16
|
renderMessageChart?: () => React.ReactNode;
|
|
19
17
|
/** Forwarded to `ChatChrome` when the thread is empty. */
|
|
@@ -25,6 +23,8 @@ export type UseChatPanelChromeModelInput = {
|
|
|
25
23
|
onAttachmentsDropped?: (items: ChatAttachmentDropItem[]) => void | Promise<void>;
|
|
26
24
|
/** Slash menu (`/`) in the composer; omit or pass empty to disable. */
|
|
27
25
|
slashCommandItems?: SlashCommandItem[];
|
|
26
|
+
/** Custom slash command handler (palette pick or Enter on `/id`). */
|
|
27
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
28
28
|
};
|
|
29
29
|
export type UseChatPanelChromeModelResult = {
|
|
30
30
|
chromeProps: ChatChromeProps;
|
|
@@ -34,4 +34,4 @@ export type UseChatPanelChromeModelResult = {
|
|
|
34
34
|
newChat: () => void;
|
|
35
35
|
chatPanelContainer: HTMLElement | null;
|
|
36
36
|
};
|
|
37
|
-
export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete,
|
|
37
|
+
export declare function useChatPanelChromeModel({ embedAsPage, presets, scopeId, onMessage, onScriptComplete, renderMessageChart, emptyState, allowedAttachments, allowPdfAttachments, onAttachmentsDropped, slashCommandItems, onSlashItemCommand, }: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Chat } from './Chat';
|
|
2
|
-
export { usedPresetIdsFromMessages } from './chat-preset-utils';
|
|
2
|
+
export { formatChatTranscript, usedPresetIdsFromMessages, } from './chat-preset-utils';
|
|
3
3
|
export { ChatChrome } from './ChatChrome';
|
|
4
4
|
export type { ChatChromeProps, ChatChromeResizeHandleConfig, } from './ChatChrome';
|
|
5
5
|
export { TEXT_ATTACHMENT_ACCEPT_PARTS, filterToTextAttachments, } from './chatAttachmentAccept';
|
|
@@ -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 {
|
|
18
|
-
export type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
17
|
+
export { MessageRole } from './Chat.types';
|
|
18
|
+
export type { SlashCommandItem, SlashItemCommandContext, SlashOnItemCommand, } from '#uilib/tiptap/slash-mention/types';
|
|
19
19
|
export { CsvIcon } from '../../icons/CsvIcon/CsvIcon';
|
|
@@ -5,9 +5,10 @@ export type SlashCommandItem = {
|
|
|
5
5
|
description?: string;
|
|
6
6
|
};
|
|
7
7
|
export type SlashItemCommandContext = {
|
|
8
|
-
editor: Editor;
|
|
9
|
-
range: Range;
|
|
10
8
|
item: SlashCommandItem;
|
|
9
|
+
/** Present when picked from the slash palette; omitted on Enter submit of `/id`. */
|
|
10
|
+
editor?: Editor;
|
|
11
|
+
range?: Range;
|
|
11
12
|
};
|
|
12
13
|
/**
|
|
13
14
|
* If provided, run when a slash item is picked. Return true to skip mention insert
|
package/package.json
CHANGED
|
@@ -13,16 +13,6 @@ export enum MessageRole {
|
|
|
13
13
|
SYSTEM = 'system',
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/** System placeholder while dashboard generation runs (must match ChatSheet `addMessage` text). */
|
|
17
|
-
export const GENERATING_DASHBOARD_SYSTEM_TEXT = 'Generating dashboard…';
|
|
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
|
-
|
|
26
16
|
/** USER-only: text file attached to a message; shown as downloadable file row(s). */
|
|
27
17
|
export type UserTextFileAttachment = {
|
|
28
18
|
displayName: string;
|
|
@@ -2,12 +2,7 @@ import cn from 'classnames';
|
|
|
2
2
|
|
|
3
3
|
import { InteractiveContent } from '#uilib/components/ui/InteractiveContent';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
type ChatMessageProps,
|
|
8
|
-
GENERATING_DASHBOARD_SYSTEM_TEXT,
|
|
9
|
-
MessageRole,
|
|
10
|
-
} from '../Chat.types';
|
|
5
|
+
import { type ChatMessageProps, MessageRole } from '../Chat.types';
|
|
11
6
|
import { userTextFileAttachmentsFromMessage } from '../userTextFileAttachments';
|
|
12
7
|
import { AgentMessageContent } from './AgentMessageContent';
|
|
13
8
|
import S from './ChatMessage.styl';
|
|
@@ -34,13 +29,7 @@ export function ChatMessage({
|
|
|
34
29
|
return (
|
|
35
30
|
<div className={cn(S.root, S[`role-${role}`])}>
|
|
36
31
|
{isSystem ? (
|
|
37
|
-
<div className={S.text}>
|
|
38
|
-
{text === GENERATING_DASHBOARD_SYSTEM_TEXT ? (
|
|
39
|
-
<TextShimmer as="span">{text}</TextShimmer>
|
|
40
|
-
) : (
|
|
41
|
-
text
|
|
42
|
-
)}
|
|
43
|
-
</div>
|
|
32
|
+
<div className={S.text}>{text}</div>
|
|
44
33
|
) : isAssistant ? (
|
|
45
34
|
<AgentMessageContent
|
|
46
35
|
text={text}
|
|
@@ -41,13 +41,13 @@ export function ChatSheet({
|
|
|
41
41
|
scopeId,
|
|
42
42
|
onMessage,
|
|
43
43
|
onScriptComplete,
|
|
44
|
-
onGenerateDashboard,
|
|
45
44
|
renderMessageChart,
|
|
46
45
|
emptyState,
|
|
47
46
|
allowedAttachments,
|
|
48
47
|
allowPdfAttachments,
|
|
49
48
|
onAttachmentsDropped,
|
|
50
49
|
slashCommandItems,
|
|
50
|
+
onSlashItemCommand,
|
|
51
51
|
inline = false,
|
|
52
52
|
}: ChatSheetProps) {
|
|
53
53
|
const model = useChatPanelChromeModel({
|
|
@@ -56,13 +56,13 @@ export function ChatSheet({
|
|
|
56
56
|
scopeId,
|
|
57
57
|
onMessage,
|
|
58
58
|
onScriptComplete,
|
|
59
|
-
onGenerateDashboard,
|
|
60
59
|
renderMessageChart,
|
|
61
60
|
emptyState,
|
|
62
61
|
allowedAttachments,
|
|
63
62
|
allowPdfAttachments,
|
|
64
63
|
onAttachmentsDropped,
|
|
65
64
|
slashCommandItems,
|
|
65
|
+
onSlashItemCommand,
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
if (actionsRef) {
|
|
@@ -2,11 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
ChatPreset,
|
|
5
|
-
GENERATE_DASHBOARD_SLASH_COMMAND_ID,
|
|
6
|
-
GENERATING_DASHBOARD_SYSTEM_TEXT,
|
|
7
5
|
MessageRole,
|
|
8
6
|
type ScriptCompletePayload,
|
|
9
|
-
isGenerateDashboardSlashMessage,
|
|
10
7
|
} from '#uilib/components/ui/Chat/Chat.types';
|
|
11
8
|
import {
|
|
12
9
|
branchKeysUsedByUserMessages,
|
|
@@ -25,10 +22,7 @@ import {
|
|
|
25
22
|
buildChatSendMessagePayload,
|
|
26
23
|
displayTextFromSendPayload,
|
|
27
24
|
} from '#uilib/components/ui/Chat/buildChatSendMessagePayload';
|
|
28
|
-
import {
|
|
29
|
-
formatChatTranscript,
|
|
30
|
-
usedPresetIdsFromMessages,
|
|
31
|
-
} from '#uilib/components/ui/Chat/chat-preset-utils';
|
|
25
|
+
import { usedPresetIdsFromMessages } from '#uilib/components/ui/Chat/chat-preset-utils';
|
|
32
26
|
import {
|
|
33
27
|
isChatEmpty,
|
|
34
28
|
useChat,
|
|
@@ -42,7 +36,7 @@ import { useQueryParams } from '#uilib/hooks/useQueryParams';
|
|
|
42
36
|
import logger from '#uilib/lib/logger';
|
|
43
37
|
import type {
|
|
44
38
|
SlashCommandItem,
|
|
45
|
-
|
|
39
|
+
SlashOnItemCommand,
|
|
46
40
|
} from '#uilib/tiptap/slash-mention/types';
|
|
47
41
|
import { mergePresetFreeformDefaults } from '#uilib/utils/chatPresetMerge';
|
|
48
42
|
import { ScrollRef } from '@homecode/ui';
|
|
@@ -63,8 +57,6 @@ export type UseChatPanelChromeModelInput = {
|
|
|
63
57
|
onMessage?: (message: string) => void;
|
|
64
58
|
/** Fires when a preset script has no further `[Label|branchKey]` steps (graph leaf or linear script end). */
|
|
65
59
|
onScriptComplete?: (payload: ScriptCompletePayload) => void;
|
|
66
|
-
/** Generate dashboard from chat transcript (e.g. via `/generate-dashboard` slash command). */
|
|
67
|
-
onGenerateDashboard?: (transcript: string) => void | Promise<void>;
|
|
68
60
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
69
61
|
renderMessageChart?: () => React.ReactNode;
|
|
70
62
|
/** Forwarded to `ChatChrome` when the thread is empty. */
|
|
@@ -78,6 +70,8 @@ export type UseChatPanelChromeModelInput = {
|
|
|
78
70
|
) => void | Promise<void>;
|
|
79
71
|
/** Slash menu (`/`) in the composer; omit or pass empty to disable. */
|
|
80
72
|
slashCommandItems?: SlashCommandItem[];
|
|
73
|
+
/** Custom slash command handler (palette pick or Enter on `/id`). */
|
|
74
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
81
75
|
};
|
|
82
76
|
|
|
83
77
|
export type UseChatPanelChromeModelResult = {
|
|
@@ -120,13 +114,13 @@ export function useChatPanelChromeModel({
|
|
|
120
114
|
scopeId,
|
|
121
115
|
onMessage,
|
|
122
116
|
onScriptComplete,
|
|
123
|
-
onGenerateDashboard,
|
|
124
117
|
renderMessageChart,
|
|
125
118
|
emptyState,
|
|
126
119
|
allowedAttachments,
|
|
127
120
|
allowPdfAttachments,
|
|
128
121
|
onAttachmentsDropped,
|
|
129
122
|
slashCommandItems,
|
|
123
|
+
onSlashItemCommand,
|
|
130
124
|
}: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult {
|
|
131
125
|
const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
|
|
132
126
|
const isMobile = useIsMobile();
|
|
@@ -202,7 +196,6 @@ export function useChatPanelChromeModel({
|
|
|
202
196
|
const [scriptCompleteByChatId, setScriptCompleteByChatId] = useState<
|
|
203
197
|
Record<string, boolean>
|
|
204
198
|
>({});
|
|
205
|
-
const [generatingDashboard, setGeneratingDashboard] = useState(false);
|
|
206
199
|
const scriptAdvanceLockRef = useRef(false);
|
|
207
200
|
const quickReplyLockRef = useRef(false);
|
|
208
201
|
const scrollRef = useRef<ScrollRef>(null);
|
|
@@ -961,75 +954,18 @@ export function useChatPanelChromeModel({
|
|
|
961
954
|
!linearScriptActive &&
|
|
962
955
|
(!graphActive || (!lastHasQuickMarkers && unusedBranchKeys.length === 0));
|
|
963
956
|
|
|
964
|
-
const handleGenerateDashboard = useCallback(async () => {
|
|
965
|
-
if (!currentChatId || !onGenerateDashboard) return;
|
|
966
|
-
const transcript = formatChatTranscript(
|
|
967
|
-
(chat?.messages ?? []).filter(m => m.role !== MessageRole.SYSTEM),
|
|
968
|
-
);
|
|
969
|
-
setGeneratingDashboard(true);
|
|
970
|
-
const systemPlaceholderId = addMessage(
|
|
971
|
-
currentChatId,
|
|
972
|
-
MessageRole.SYSTEM,
|
|
973
|
-
GENERATING_DASHBOARD_SYSTEM_TEXT,
|
|
974
|
-
);
|
|
975
|
-
try {
|
|
976
|
-
await Promise.resolve(onGenerateDashboard(transcript));
|
|
977
|
-
setScriptCompleteByChatId(prev => {
|
|
978
|
-
const next = { ...prev };
|
|
979
|
-
delete next[currentChatId];
|
|
980
|
-
return next;
|
|
981
|
-
});
|
|
982
|
-
} finally {
|
|
983
|
-
if (systemPlaceholderId) {
|
|
984
|
-
removeMessageById(currentChatId, systemPlaceholderId);
|
|
985
|
-
}
|
|
986
|
-
setGeneratingDashboard(false);
|
|
987
|
-
}
|
|
988
|
-
}, [
|
|
989
|
-
currentChatId,
|
|
990
|
-
onGenerateDashboard,
|
|
991
|
-
chat?.messages,
|
|
992
|
-
addMessage,
|
|
993
|
-
removeMessageById,
|
|
994
|
-
]);
|
|
995
|
-
|
|
996
|
-
const onSlashItemCommand = useCallback(
|
|
997
|
-
({ item }: SlashItemCommandContext) => {
|
|
998
|
-
if (
|
|
999
|
-
item.id !== GENERATE_DASHBOARD_SLASH_COMMAND_ID ||
|
|
1000
|
-
!onGenerateDashboard
|
|
1001
|
-
) {
|
|
1002
|
-
return false;
|
|
1003
|
-
}
|
|
1004
|
-
if (generatingDashboard) {
|
|
1005
|
-
return true;
|
|
1006
|
-
}
|
|
1007
|
-
queueMicrotask(() => {
|
|
1008
|
-
void handleGenerateDashboard();
|
|
1009
|
-
});
|
|
1010
|
-
return true;
|
|
1011
|
-
},
|
|
1012
|
-
[onGenerateDashboard, generatingDashboard, handleGenerateDashboard],
|
|
1013
|
-
);
|
|
1014
|
-
|
|
1015
957
|
const onPromptSubmitWithSlashCommands = useCallback(
|
|
1016
958
|
async (message: string, attachments?: ChatAttachmentDropItem[]) => {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
) {
|
|
1022
|
-
void handleGenerateDashboard();
|
|
959
|
+
const trimmed = message.trim();
|
|
960
|
+
const slashItem = slashCommandItems?.find(
|
|
961
|
+
item => trimmed === `/${item.id}`,
|
|
962
|
+
);
|
|
963
|
+
if (slashItem && onSlashItemCommand?.({ item: slashItem }) === true) {
|
|
1023
964
|
return;
|
|
1024
965
|
}
|
|
1025
966
|
return handlePromptSubmit(message, attachments);
|
|
1026
967
|
},
|
|
1027
|
-
[
|
|
1028
|
-
handlePromptSubmit,
|
|
1029
|
-
onGenerateDashboard,
|
|
1030
|
-
generatingDashboard,
|
|
1031
|
-
handleGenerateDashboard,
|
|
1032
|
-
],
|
|
968
|
+
[handlePromptSubmit, slashCommandItems, onSlashItemCommand],
|
|
1033
969
|
);
|
|
1034
970
|
|
|
1035
971
|
const onDragChatWidth = useCallback(
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { Chat } from './Chat';
|
|
2
|
-
export {
|
|
2
|
+
export {
|
|
3
|
+
formatChatTranscript,
|
|
4
|
+
usedPresetIdsFromMessages,
|
|
5
|
+
} from './chat-preset-utils';
|
|
3
6
|
export { ChatChrome } from './ChatChrome';
|
|
4
7
|
export type {
|
|
5
8
|
ChatChromeProps,
|
|
@@ -39,10 +42,10 @@ export type {
|
|
|
39
42
|
Message,
|
|
40
43
|
UserTextFileAttachment,
|
|
41
44
|
} from './Chat.types';
|
|
42
|
-
export {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
export { MessageRole } from './Chat.types';
|
|
46
|
+
export type {
|
|
47
|
+
SlashCommandItem,
|
|
48
|
+
SlashItemCommandContext,
|
|
49
|
+
SlashOnItemCommand,
|
|
50
|
+
} from '#uilib/tiptap/slash-mention/types';
|
|
48
51
|
export { CsvIcon } from '../../icons/CsvIcon/CsvIcon';
|
|
@@ -157,9 +157,6 @@ export default function ChatSlashCommandsPage() {
|
|
|
157
157
|
title: 'Try a slash command',
|
|
158
158
|
description:
|
|
159
159
|
'Pick sample-command from the palette or send /sample-command — onSlashItemCommand clears the composer and runs the demo action.',
|
|
160
|
-
additionalContent: (
|
|
161
|
-
<p>Optional empty-state slot via additionalContent.</p>
|
|
162
|
-
),
|
|
163
160
|
}}
|
|
164
161
|
/>
|
|
165
162
|
</PageContentSection>
|
|
@@ -7,9 +7,10 @@ export type SlashCommandItem = {
|
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
export type SlashItemCommandContext = {
|
|
10
|
-
editor: Editor;
|
|
11
|
-
range: Range;
|
|
12
10
|
item: SlashCommandItem;
|
|
11
|
+
/** Present when picked from the slash palette; omitted on Enter submit of `/id`. */
|
|
12
|
+
editor?: Editor;
|
|
13
|
+
range?: Range;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,32 +0,0 @@
|
|
|
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
|
-
});
|