@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
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '#uilib/components/ui/Chat/ChatMessage/presetScript';
|
|
8
8
|
import { TextShimmer } from '#uilib/components/ui/TextShimmer';
|
|
9
9
|
import { Scroll } from '@homecode/ui';
|
|
10
|
-
import {
|
|
10
|
+
import { PaperPlaneRightIcon, X } from '@phosphor-icons/react';
|
|
11
11
|
|
|
12
12
|
import { Button } from '../../Button';
|
|
13
13
|
import { DropZone } from '../../DropZone/DropZone';
|
|
@@ -37,13 +37,8 @@ export function ChatChrome({
|
|
|
37
37
|
scriptContinueLabel,
|
|
38
38
|
onScriptContinue,
|
|
39
39
|
renderMessageChart,
|
|
40
|
-
showBranchActionsRow,
|
|
41
40
|
showSyntheticBranchButtons,
|
|
42
41
|
unusedBranchKeys,
|
|
43
|
-
isScriptComplete,
|
|
44
|
-
onGenerateDashboard,
|
|
45
|
-
generatingDashboard,
|
|
46
|
-
onGenerateDashboardClick,
|
|
47
42
|
showInlinePresets,
|
|
48
43
|
isLastMessageFromUser,
|
|
49
44
|
scrollRef,
|
|
@@ -57,6 +52,7 @@ export function ChatChrome({
|
|
|
57
52
|
allowPdfAttachments = false,
|
|
58
53
|
onAttachmentsDropped,
|
|
59
54
|
slashCommandItems,
|
|
55
|
+
onSlashItemCommand,
|
|
60
56
|
promptPlaceholder,
|
|
61
57
|
}: ChatChromeProps) {
|
|
62
58
|
const filteredAllowedAttachments = useMemo(
|
|
@@ -223,48 +219,29 @@ export function ChatChrome({
|
|
|
223
219
|
);
|
|
224
220
|
})}
|
|
225
221
|
|
|
226
|
-
{
|
|
222
|
+
{showSyntheticBranchButtons ? (
|
|
227
223
|
<div className={S.branchRow}>
|
|
228
|
-
{
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
</span>
|
|
248
|
-
);
|
|
249
|
-
})
|
|
250
|
-
: null}
|
|
251
|
-
|
|
252
|
-
{isScriptComplete &&
|
|
253
|
-
onGenerateDashboard &&
|
|
254
|
-
!generatingDashboard ? (
|
|
255
|
-
<Button
|
|
256
|
-
type="button"
|
|
257
|
-
variant="default"
|
|
258
|
-
size="lg"
|
|
259
|
-
disabled={isLoading}
|
|
260
|
-
onClick={onGenerateDashboardClick}
|
|
261
|
-
>
|
|
262
|
-
<ChartLineIcon />
|
|
263
|
-
Generate Dashboard
|
|
264
|
-
</Button>
|
|
265
|
-
) : null}
|
|
224
|
+
{unusedBranchKeys.map(key => {
|
|
225
|
+
const label =
|
|
226
|
+
displayLabelForBranchKeyFromMessages(key, messages) ??
|
|
227
|
+
humanizeBranchKey(key);
|
|
228
|
+
return (
|
|
229
|
+
<span key={key} className={S.branchBtnWrap}>
|
|
230
|
+
<Button
|
|
231
|
+
type="button"
|
|
232
|
+
variant="outline"
|
|
233
|
+
size="sm"
|
|
234
|
+
disabled={isLoading}
|
|
235
|
+
onClick={() => onQuickReply(key, label)}
|
|
236
|
+
>
|
|
237
|
+
<PaperPlaneRightIcon />
|
|
238
|
+
{label}
|
|
239
|
+
</Button>
|
|
240
|
+
</span>
|
|
241
|
+
);
|
|
242
|
+
})}
|
|
266
243
|
</div>
|
|
267
|
-
)}
|
|
244
|
+
) : null}
|
|
268
245
|
|
|
269
246
|
{showInlinePresets && renderPresets('inline')}
|
|
270
247
|
|
|
@@ -291,6 +268,7 @@ export function ChatChrome({
|
|
|
291
268
|
prefillMessage={promptPrefill ?? undefined}
|
|
292
269
|
placeholder={promptPlaceholder}
|
|
293
270
|
slashCommandItems={slashCommandItems}
|
|
271
|
+
onSlashItemCommand={onSlashItemCommand}
|
|
294
272
|
attachmentAccept={
|
|
295
273
|
attachmentsDropzoneEnabled ? attachmentAccept : undefined
|
|
296
274
|
}
|
|
@@ -6,7 +6,10 @@ import type {
|
|
|
6
6
|
} from '#uilib/components/ui/Chat/Chat.types';
|
|
7
7
|
import type { ChatEmptyStateProps } from '#uilib/components/ui/Chat/ChatEmptyState/ChatEmptyState.types';
|
|
8
8
|
import type { ChatPresetsLayout } from '#uilib/components/ui/Chat/ChatPresets';
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
SlashCommandItem,
|
|
11
|
+
SlashOnItemCommand,
|
|
12
|
+
} from '#uilib/tiptap/slash-mention/types';
|
|
10
13
|
import type { ScrollRef } from '@homecode/ui';
|
|
11
14
|
|
|
12
15
|
export type ChatChromeResizeHandleConfig = {
|
|
@@ -33,15 +36,8 @@ export interface ChatChromeProps {
|
|
|
33
36
|
scriptContinueLabel: string | undefined;
|
|
34
37
|
onScriptContinue: (() => void) | undefined;
|
|
35
38
|
renderMessageChart?: () => React.ReactNode;
|
|
36
|
-
showBranchActionsRow: boolean;
|
|
37
39
|
showSyntheticBranchButtons: boolean;
|
|
38
40
|
unusedBranchKeys: string[];
|
|
39
|
-
isScriptComplete: boolean;
|
|
40
|
-
onGenerateDashboard:
|
|
41
|
-
| ((transcript: string) => void | Promise<void>)
|
|
42
|
-
| undefined;
|
|
43
|
-
generatingDashboard: boolean;
|
|
44
|
-
onGenerateDashboardClick: () => void;
|
|
45
41
|
showInlinePresets: boolean;
|
|
46
42
|
isLastMessageFromUser: boolean;
|
|
47
43
|
scrollRef: RefObject<ScrollRef | null>;
|
|
@@ -65,6 +61,8 @@ export interface ChatChromeProps {
|
|
|
65
61
|
) => void | Promise<void>;
|
|
66
62
|
/** Slash menu (`/`), forwarded to `Chat.Prompt`; omit or pass empty list to disable slash palette. */
|
|
67
63
|
slashCommandItems?: SlashCommandItem[];
|
|
64
|
+
/** Custom slash pick handler; forwarded to `Chat.Prompt`. */
|
|
65
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
68
66
|
/** Composer placeholder forwarded to `Chat.Prompt`. */
|
|
69
67
|
promptPlaceholder?: string;
|
|
70
68
|
}
|
|
@@ -14,6 +14,7 @@ export function ChatPrompt({
|
|
|
14
14
|
footer,
|
|
15
15
|
prefillMessage,
|
|
16
16
|
slashCommandItems,
|
|
17
|
+
onSlashItemCommand,
|
|
17
18
|
attachments = [],
|
|
18
19
|
onRemoveAttachment,
|
|
19
20
|
disabled = false,
|
|
@@ -28,6 +29,7 @@ export function ChatPrompt({
|
|
|
28
29
|
disabled,
|
|
29
30
|
placeholder,
|
|
30
31
|
slashCommandItems,
|
|
32
|
+
onSlashItemCommand,
|
|
31
33
|
prefillMessage,
|
|
32
34
|
attachmentsCount,
|
|
33
35
|
onEnterSubmit: () => emitSubmitRef.current(),
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
DEFAULT_CHAT_SLASH_ITEMS,
|
|
13
13
|
createSlashMentionExtension,
|
|
14
14
|
} from '#uilib/tiptap/slash-mention';
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
SlashCommandItem,
|
|
17
|
+
SlashOnItemCommand,
|
|
18
|
+
} from '#uilib/tiptap/slash-mention/types';
|
|
16
19
|
import type { AnyExtension, Editor } from '@tiptap/core';
|
|
17
20
|
import { Placeholder } from '@tiptap/extensions';
|
|
18
21
|
import { useEditor } from '@tiptap/react';
|
|
@@ -28,6 +31,7 @@ export type UseChatPromptEditorOptions = {
|
|
|
28
31
|
disabled: boolean;
|
|
29
32
|
placeholder?: string;
|
|
30
33
|
slashCommandItems?: SlashCommandItem[];
|
|
34
|
+
onSlashItemCommand?: SlashOnItemCommand;
|
|
31
35
|
prefillMessage?: string | null;
|
|
32
36
|
/** Staged attachment count — Enter-to-send when text empty but files present. */
|
|
33
37
|
attachmentsCount?: number;
|
|
@@ -46,11 +50,14 @@ export function useChatPromptEditor({
|
|
|
46
50
|
disabled,
|
|
47
51
|
placeholder,
|
|
48
52
|
slashCommandItems,
|
|
53
|
+
onSlashItemCommand,
|
|
49
54
|
prefillMessage,
|
|
50
55
|
attachmentsCount = 0,
|
|
51
56
|
onEnterSubmit,
|
|
52
57
|
}: UseChatPromptEditorOptions): UseChatPromptEditorResult {
|
|
53
58
|
const slashOpenRef = useRef(false);
|
|
59
|
+
const onSlashItemCommandRef = useRef(onSlashItemCommand);
|
|
60
|
+
onSlashItemCommandRef.current = onSlashItemCommand;
|
|
54
61
|
const suggestionActiveUpdater = useCallback((active: boolean) => {
|
|
55
62
|
slashOpenRef.current = active;
|
|
56
63
|
}, []);
|
|
@@ -94,7 +101,9 @@ export function useChatPromptEditor({
|
|
|
94
101
|
exts.push(
|
|
95
102
|
createSlashMentionExtension({
|
|
96
103
|
items: slashItemsStable,
|
|
104
|
+
suggestionPlacement: 'above',
|
|
97
105
|
onSuggestionUiActiveChange: suggestionActiveUpdater,
|
|
106
|
+
onItemCommand: ctx => onSlashItemCommandRef.current?.(ctx) === true,
|
|
98
107
|
}),
|
|
99
108
|
);
|
|
100
109
|
}
|
|
@@ -138,7 +147,7 @@ export function useChatPromptEditor({
|
|
|
138
147
|
},
|
|
139
148
|
onCreate: bindEditorDom,
|
|
140
149
|
},
|
|
141
|
-
[extensions,
|
|
150
|
+
[extensions, bindEditorDom, ariaLabelComposer],
|
|
142
151
|
);
|
|
143
152
|
|
|
144
153
|
useEffect(() => {
|
|
@@ -47,6 +47,7 @@ export function ChatSheet({
|
|
|
47
47
|
allowedAttachments,
|
|
48
48
|
allowPdfAttachments,
|
|
49
49
|
onAttachmentsDropped,
|
|
50
|
+
slashCommandItems,
|
|
50
51
|
inline = false,
|
|
51
52
|
}: ChatSheetProps) {
|
|
52
53
|
const model = useChatPanelChromeModel({
|
|
@@ -61,6 +62,7 @@ export function ChatSheet({
|
|
|
61
62
|
allowedAttachments,
|
|
62
63
|
allowPdfAttachments,
|
|
63
64
|
onAttachmentsDropped,
|
|
65
|
+
slashCommandItems,
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
if (actionsRef) {
|
|
@@ -2,9 +2,11 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
ChatPreset,
|
|
5
|
+
GENERATE_DASHBOARD_SLASH_COMMAND_ID,
|
|
5
6
|
GENERATING_DASHBOARD_SYSTEM_TEXT,
|
|
6
7
|
MessageRole,
|
|
7
8
|
type ScriptCompletePayload,
|
|
9
|
+
isGenerateDashboardSlashMessage,
|
|
8
10
|
} from '#uilib/components/ui/Chat/Chat.types';
|
|
9
11
|
import {
|
|
10
12
|
branchKeysUsedByUserMessages,
|
|
@@ -38,6 +40,10 @@ import useEvent from '#uilib/hooks/useEvent';
|
|
|
38
40
|
import { useIsMobile } from '#uilib/hooks/useIsMobile';
|
|
39
41
|
import { useQueryParams } from '#uilib/hooks/useQueryParams';
|
|
40
42
|
import logger from '#uilib/lib/logger';
|
|
43
|
+
import type {
|
|
44
|
+
SlashCommandItem,
|
|
45
|
+
SlashItemCommandContext,
|
|
46
|
+
} from '#uilib/tiptap/slash-mention/types';
|
|
41
47
|
import { mergePresetFreeformDefaults } from '#uilib/utils/chatPresetMerge';
|
|
42
48
|
import { ScrollRef } from '@homecode/ui';
|
|
43
49
|
|
|
@@ -57,7 +63,7 @@ export type UseChatPanelChromeModelInput = {
|
|
|
57
63
|
onMessage?: (message: string) => void;
|
|
58
64
|
/** Fires when a preset script has no further `[Label|branchKey]` steps (graph leaf or linear script end). */
|
|
59
65
|
onScriptComplete?: (payload: ScriptCompletePayload) => void;
|
|
60
|
-
/**
|
|
66
|
+
/** Generate dashboard from chat transcript (e.g. via `/generate-dashboard` slash command). */
|
|
61
67
|
onGenerateDashboard?: (transcript: string) => void | Promise<void>;
|
|
62
68
|
/** Renders `[CHART]` tokens in assistant messages. */
|
|
63
69
|
renderMessageChart?: () => React.ReactNode;
|
|
@@ -70,6 +76,8 @@ export type UseChatPanelChromeModelInput = {
|
|
|
70
76
|
onAttachmentsDropped?: (
|
|
71
77
|
items: ChatAttachmentDropItem[],
|
|
72
78
|
) => void | Promise<void>;
|
|
79
|
+
/** Slash menu (`/`) in the composer; omit or pass empty to disable. */
|
|
80
|
+
slashCommandItems?: SlashCommandItem[];
|
|
73
81
|
};
|
|
74
82
|
|
|
75
83
|
export type UseChatPanelChromeModelResult = {
|
|
@@ -118,6 +126,7 @@ export function useChatPanelChromeModel({
|
|
|
118
126
|
allowedAttachments,
|
|
119
127
|
allowPdfAttachments,
|
|
120
128
|
onAttachmentsDropped,
|
|
129
|
+
slashCommandItems,
|
|
121
130
|
}: UseChatPanelChromeModelInput): UseChatPanelChromeModelResult {
|
|
122
131
|
const effectiveScopeId = scopeId ?? NO_SCOPE_FALLBACK;
|
|
123
132
|
const isMobile = useIsMobile();
|
|
@@ -189,7 +198,7 @@ export function useChatPanelChromeModel({
|
|
|
189
198
|
const [intakeByChatId, setIntakeByChatId] = useState<
|
|
190
199
|
Record<string, IntakeScriptState | undefined>
|
|
191
200
|
>({});
|
|
192
|
-
/** Preset intake finished for this session
|
|
201
|
+
/** Preset intake finished for this session (e.g. `onScriptComplete` callback). */
|
|
193
202
|
const [scriptCompleteByChatId, setScriptCompleteByChatId] = useState<
|
|
194
203
|
Record<string, boolean>
|
|
195
204
|
>({});
|
|
@@ -616,103 +625,107 @@ export function useChatPanelChromeModel({
|
|
|
616
625
|
],
|
|
617
626
|
);
|
|
618
627
|
|
|
619
|
-
const submitPreset = useCallback(
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
628
|
+
const submitPreset = useCallback(
|
|
629
|
+
async (preset: ChatPreset) => {
|
|
630
|
+
const script = preset.script;
|
|
631
|
+
const scriptGraph = isPresetScriptGraph(script);
|
|
632
|
+
const hasLinearScript = Array.isArray(script) && script.length > 0;
|
|
633
|
+
const hasReplies =
|
|
634
|
+
preset.replies && Object.keys(preset.replies).length > 0;
|
|
635
|
+
const isLocalDemo =
|
|
636
|
+
hasLinearScript ||
|
|
637
|
+
scriptGraph ||
|
|
638
|
+
Boolean(preset.answer?.trim()) ||
|
|
639
|
+
Boolean(hasReplies);
|
|
640
|
+
|
|
641
|
+
if (!isLocalDemo) {
|
|
642
|
+
if (!currentChatId) return;
|
|
643
|
+
endLocalDemoFlow(currentChatId);
|
|
644
|
+
await handlePromptSubmit(preset.text);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
636
647
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
delete next[currentChatId];
|
|
643
|
-
return next;
|
|
644
|
-
});
|
|
645
|
-
addMessage(currentChatId, MessageRole.USER, preset.text);
|
|
646
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
647
|
-
const firstAssistant = scriptGraph
|
|
648
|
-
? script.initialMessage
|
|
649
|
-
: (preset.answer ?? '');
|
|
650
|
-
addMessage(currentChatId, MessageRole.ASSISTANT, firstAssistant);
|
|
651
|
-
|
|
652
|
-
setScriptByChatId(prev => {
|
|
653
|
-
const next = { ...prev };
|
|
654
|
-
if (hasLinearScript) {
|
|
655
|
-
next[currentChatId] = {
|
|
656
|
-
lines: script,
|
|
657
|
-
nextIndex: 0,
|
|
658
|
-
presetId: preset.id,
|
|
659
|
-
};
|
|
660
|
-
} else {
|
|
648
|
+
setLocalUiBusy(true);
|
|
649
|
+
try {
|
|
650
|
+
if (!currentChatId) return;
|
|
651
|
+
setScriptCompleteByChatId(prev => {
|
|
652
|
+
const next = { ...prev };
|
|
661
653
|
delete next[currentChatId];
|
|
654
|
+
return next;
|
|
655
|
+
});
|
|
656
|
+
addMessage(currentChatId, MessageRole.USER, preset.text);
|
|
657
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
658
|
+
const firstAssistant = scriptGraph
|
|
659
|
+
? script.initialMessage
|
|
660
|
+
: (preset.answer ?? '');
|
|
661
|
+
addMessage(currentChatId, MessageRole.ASSISTANT, firstAssistant);
|
|
662
|
+
|
|
663
|
+
setScriptByChatId(prev => {
|
|
664
|
+
const next = { ...prev };
|
|
665
|
+
if (hasLinearScript) {
|
|
666
|
+
next[currentChatId] = {
|
|
667
|
+
lines: script,
|
|
668
|
+
nextIndex: 0,
|
|
669
|
+
presetId: preset.id,
|
|
670
|
+
};
|
|
671
|
+
} else {
|
|
672
|
+
delete next[currentChatId];
|
|
673
|
+
}
|
|
674
|
+
return next;
|
|
675
|
+
});
|
|
676
|
+
setQuickReplyBranchesByChat(prev => {
|
|
677
|
+
const next = { ...prev };
|
|
678
|
+
if (scriptGraph) {
|
|
679
|
+
next[currentChatId] = branchesFromPresetScriptGraph(script);
|
|
680
|
+
} else if (hasReplies && !hasLinearScript) {
|
|
681
|
+
next[currentChatId] = preset.replies!;
|
|
682
|
+
} else {
|
|
683
|
+
delete next[currentChatId];
|
|
684
|
+
}
|
|
685
|
+
return next;
|
|
686
|
+
});
|
|
687
|
+
if (scriptGraph || (hasReplies && !hasLinearScript)) {
|
|
688
|
+
setUsedScriptBranchKeysByChat(prev => ({
|
|
689
|
+
...prev,
|
|
690
|
+
[currentChatId]: [],
|
|
691
|
+
}));
|
|
662
692
|
}
|
|
663
|
-
return next;
|
|
664
|
-
});
|
|
665
|
-
setQuickReplyBranchesByChat(prev => {
|
|
666
|
-
const next = { ...prev };
|
|
667
693
|
if (scriptGraph) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
...prev,
|
|
679
|
-
[currentChatId]: [],
|
|
680
|
-
}));
|
|
681
|
-
}
|
|
682
|
-
if (scriptGraph) {
|
|
683
|
-
const fromMerged = presetsWithFreeform.find(p => p.id === preset.id);
|
|
684
|
-
let freeformNext = {
|
|
685
|
-
...(fromMerged?.freeformNext ?? {}),
|
|
686
|
-
...(preset.freeformNext ?? {}),
|
|
687
|
-
};
|
|
688
|
-
if (Object.keys(freeformNext).length === 0) {
|
|
689
|
-
const ks = Object.keys(branchesFromPresetScriptGraph(script));
|
|
690
|
-
if (ks.length === 1) {
|
|
691
|
-
freeformNext = { initial: ks[0] };
|
|
694
|
+
const fromMerged = presetsWithFreeform.find(p => p.id === preset.id);
|
|
695
|
+
let freeformNext = {
|
|
696
|
+
...(fromMerged?.freeformNext ?? {}),
|
|
697
|
+
...(preset.freeformNext ?? {}),
|
|
698
|
+
};
|
|
699
|
+
if (Object.keys(freeformNext).length === 0) {
|
|
700
|
+
const ks = Object.keys(branchesFromPresetScriptGraph(script));
|
|
701
|
+
if (ks.length === 1) {
|
|
702
|
+
freeformNext = { initial: ks[0] };
|
|
703
|
+
}
|
|
692
704
|
}
|
|
705
|
+
setIntakeByChatId(prev => ({
|
|
706
|
+
...prev,
|
|
707
|
+
[currentChatId!]: {
|
|
708
|
+
presetId: preset.id,
|
|
709
|
+
freeformNext,
|
|
710
|
+
scriptStepId: 'initial',
|
|
711
|
+
answers: {},
|
|
712
|
+
},
|
|
713
|
+
}));
|
|
693
714
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
freeformNext,
|
|
699
|
-
scriptStepId: 'initial',
|
|
700
|
-
answers: {},
|
|
701
|
-
},
|
|
702
|
-
}));
|
|
715
|
+
} catch (error) {
|
|
716
|
+
logger.error('Error sending chat message:', error);
|
|
717
|
+
} finally {
|
|
718
|
+
setLocalUiBusy(false);
|
|
703
719
|
}
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
addMessage,
|
|
714
|
-
presetsWithFreeform,
|
|
715
|
-
]);
|
|
720
|
+
},
|
|
721
|
+
[
|
|
722
|
+
currentChatId,
|
|
723
|
+
endLocalDemoFlow,
|
|
724
|
+
handlePromptSubmit,
|
|
725
|
+
addMessage,
|
|
726
|
+
presetsWithFreeform,
|
|
727
|
+
],
|
|
728
|
+
);
|
|
716
729
|
|
|
717
730
|
const resolvedEmptyState = useMemo((): ChatEmptyStateProps | undefined => {
|
|
718
731
|
if (!emptyState) return undefined;
|
|
@@ -948,15 +961,6 @@ export function useChatPanelChromeModel({
|
|
|
948
961
|
!linearScriptActive &&
|
|
949
962
|
(!graphActive || (!lastHasQuickMarkers && unusedBranchKeys.length === 0));
|
|
950
963
|
|
|
951
|
-
const isScriptComplete = Boolean(
|
|
952
|
-
currentChatId && scriptCompleteByChatId[currentChatId],
|
|
953
|
-
);
|
|
954
|
-
|
|
955
|
-
/** Branch row also when intake is done but all branches used (Generate Dashboard only). */
|
|
956
|
-
const showBranchActionsRow =
|
|
957
|
-
showSyntheticBranchButtons ||
|
|
958
|
-
(isScriptComplete && Boolean(onGenerateDashboard) && !generatingDashboard);
|
|
959
|
-
|
|
960
964
|
const handleGenerateDashboard = useCallback(async () => {
|
|
961
965
|
if (!currentChatId || !onGenerateDashboard) return;
|
|
962
966
|
const transcript = formatChatTranscript(
|
|
@@ -989,6 +993,45 @@ export function useChatPanelChromeModel({
|
|
|
989
993
|
removeMessageById,
|
|
990
994
|
]);
|
|
991
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
|
+
const onPromptSubmitWithSlashCommands = useCallback(
|
|
1016
|
+
async (message: string, attachments?: ChatAttachmentDropItem[]) => {
|
|
1017
|
+
if (
|
|
1018
|
+
isGenerateDashboardSlashMessage(message) &&
|
|
1019
|
+
onGenerateDashboard &&
|
|
1020
|
+
!generatingDashboard
|
|
1021
|
+
) {
|
|
1022
|
+
void handleGenerateDashboard();
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
return handlePromptSubmit(message, attachments);
|
|
1026
|
+
},
|
|
1027
|
+
[
|
|
1028
|
+
handlePromptSubmit,
|
|
1029
|
+
onGenerateDashboard,
|
|
1030
|
+
generatingDashboard,
|
|
1031
|
+
handleGenerateDashboard,
|
|
1032
|
+
],
|
|
1033
|
+
);
|
|
1034
|
+
|
|
992
1035
|
const onDragChatWidth = useCallback(
|
|
993
1036
|
(rawPx: number) => setChatWidthPx(rawPx, { persist: false }),
|
|
994
1037
|
[setChatWidthPx],
|
|
@@ -1020,26 +1063,21 @@ export function useChatPanelChromeModel({
|
|
|
1020
1063
|
scriptContinueLabel,
|
|
1021
1064
|
onScriptContinue,
|
|
1022
1065
|
renderMessageChart,
|
|
1023
|
-
showBranchActionsRow,
|
|
1024
1066
|
showSyntheticBranchButtons,
|
|
1025
1067
|
unusedBranchKeys,
|
|
1026
|
-
isScriptComplete,
|
|
1027
|
-
onGenerateDashboard,
|
|
1028
|
-
generatingDashboard,
|
|
1029
|
-
onGenerateDashboardClick: () => {
|
|
1030
|
-
void handleGenerateDashboard();
|
|
1031
|
-
},
|
|
1032
1068
|
showInlinePresets,
|
|
1033
1069
|
isLastMessageFromUser,
|
|
1034
1070
|
scrollRef,
|
|
1035
1071
|
effectiveScopeId,
|
|
1036
|
-
onPromptSubmit:
|
|
1072
|
+
onPromptSubmit: onPromptSubmitWithSlashCommands,
|
|
1037
1073
|
onChatDeleted: endLocalDemoFlow,
|
|
1038
1074
|
promptPrefill: promptLinkPrefill,
|
|
1039
1075
|
emptyState: resolvedEmptyState,
|
|
1040
1076
|
allowedAttachments,
|
|
1041
1077
|
allowPdfAttachments,
|
|
1042
1078
|
onAttachmentsDropped,
|
|
1079
|
+
slashCommandItems,
|
|
1080
|
+
onSlashItemCommand,
|
|
1043
1081
|
};
|
|
1044
1082
|
|
|
1045
1083
|
const toggleOpen = () => onOpenChange(!isOpen);
|
|
@@ -39,6 +39,10 @@ export type {
|
|
|
39
39
|
Message,
|
|
40
40
|
UserTextFileAttachment,
|
|
41
41
|
} from './Chat.types';
|
|
42
|
-
export {
|
|
42
|
+
export {
|
|
43
|
+
GENERATE_DASHBOARD_SLASH_COMMAND_ID,
|
|
44
|
+
isGenerateDashboardSlashMessage,
|
|
45
|
+
MessageRole,
|
|
46
|
+
} from './Chat.types';
|
|
43
47
|
export type { SlashCommandItem } from '#uilib/tiptap/slash-mention/types';
|
|
44
48
|
export { CsvIcon } from '../../icons/CsvIcon/CsvIcon';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ChatSheet } from '#uilib/components/ui/Chat';
|
|
1
|
+
import { ChatSheet, ChatSheetProps } from '#uilib/components/ui/Chat';
|
|
2
2
|
import { MessageSquare } from 'lucide-react';
|
|
3
3
|
|
|
4
4
|
import { DOCS_CHAT_SCOPE_ID } from './docsConstants';
|
|
5
5
|
|
|
6
|
-
export function DocsHeaderActions() {
|
|
6
|
+
export function DocsHeaderActions(props: ChatSheetProps) {
|
|
7
7
|
return (
|
|
8
8
|
<ChatSheet
|
|
9
9
|
triggerLabel={
|
|
@@ -21,6 +21,7 @@ export function DocsHeaderActions() {
|
|
|
21
21
|
<p>Optional empty-state slot via additionalContent.</p>
|
|
22
22
|
),
|
|
23
23
|
}}
|
|
24
|
+
{...props}
|
|
24
25
|
/>
|
|
25
26
|
);
|
|
26
27
|
}
|
|
@@ -120,13 +120,8 @@ export default function ChatAttachmentsDropzonePage() {
|
|
|
120
120
|
isLoading={isLoading}
|
|
121
121
|
scriptContinueLabel={undefined}
|
|
122
122
|
onScriptContinue={undefined}
|
|
123
|
-
showBranchActionsRow={false}
|
|
124
123
|
showSyntheticBranchButtons={false}
|
|
125
124
|
unusedBranchKeys={[]}
|
|
126
|
-
isScriptComplete={false}
|
|
127
|
-
onGenerateDashboard={undefined}
|
|
128
|
-
generatingDashboard={false}
|
|
129
|
-
onGenerateDashboardClick={() => {}}
|
|
130
125
|
showInlinePresets={false}
|
|
131
126
|
isLastMessageFromUser={isLastMessageFromUser}
|
|
132
127
|
scrollRef={scrollRef}
|
|
@@ -84,13 +84,8 @@ export default function ChatPage() {
|
|
|
84
84
|
isLoading={isLoading}
|
|
85
85
|
scriptContinueLabel={undefined}
|
|
86
86
|
onScriptContinue={undefined}
|
|
87
|
-
showBranchActionsRow={false}
|
|
88
87
|
showSyntheticBranchButtons={false}
|
|
89
88
|
unusedBranchKeys={[]}
|
|
90
|
-
isScriptComplete={false}
|
|
91
|
-
onGenerateDashboard={undefined}
|
|
92
|
-
generatingDashboard={false}
|
|
93
|
-
onGenerateDashboardClick={() => {}}
|
|
94
89
|
showInlinePresets={false}
|
|
95
90
|
isLastMessageFromUser={isLastMessageFromUser}
|
|
96
91
|
scrollRef={scrollRef}
|