buster/apps/web/src/components/features/input/BusterChatInput/BusterChatInputBase.tsx

157 lines
4.8 KiB
TypeScript
Raw Normal View History

2025-10-02 00:03:41 +08:00
import type { MessageAnalysisMode } from '@buster/server-shared/chats';
2025-09-30 03:52:00 +08:00
import type { ListShortcutsResponse } from '@buster/server-shared/shortcuts';
import type { GetSuggestedPromptsResponse } from '@buster/server-shared/user';
2025-09-30 10:24:58 +08:00
import React, { useMemo, useRef, useState } from 'react';
import {
useCreateShortcutsMentionsSuggestions,
useShortcutsSuggestions,
} from '@/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions';
2025-09-30 10:39:22 +08:00
import type {
MentionArrayItem,
MentionSuggestionExtension,
} from '@/components/ui/inputs/MentionInput';
2025-09-30 05:37:50 +08:00
import type {
MentionInputSuggestionsProps,
MentionInputSuggestionsRef,
} from '@/components/ui/inputs/MentionInputSuggestions';
2025-09-30 03:52:00 +08:00
import { MentionInputSuggestions } from '@/components/ui/inputs/MentionInputSuggestions';
2025-10-01 00:25:03 +08:00
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
2025-09-30 11:41:32 +08:00
import { useMount } from '@/hooks/useMount';
2025-09-30 08:59:49 +08:00
import { NewShortcutModal } from '../../modals/NewShortcutModal';
2025-10-02 00:03:41 +08:00
import { BusterChatInputButtons } from './BusterChatInputButtons';
2025-10-01 12:13:06 +08:00
import { useUniqueSuggestions } from './useUniqueSuggestions';
2025-09-30 03:52:00 +08:00
2025-09-30 11:41:32 +08:00
export type BusterChatInputProps = {
2025-09-30 03:52:00 +08:00
defaultValue: string;
2025-09-30 10:39:22 +08:00
onSubmit: (d: {
transformedValue: string;
arrayValue: MentionArrayItem[];
editorText: string;
2025-10-02 00:03:41 +08:00
mode: MessageAnalysisMode;
2025-09-30 10:39:22 +08:00
}) => void;
2025-09-30 03:52:00 +08:00
onStop: () => void;
submitting: boolean;
disabled: boolean;
shortcuts: ListShortcutsResponse['shortcuts'];
suggestedPrompts: GetSuggestedPromptsResponse['suggestedPrompts'];
2025-09-30 11:41:32 +08:00
autoSubmit?: boolean;
2025-09-30 03:52:00 +08:00
};
2025-09-30 11:41:32 +08:00
export const BusterChatInputBase: React.FC<BusterChatInputProps> = React.memo(
({
defaultValue,
onSubmit,
onStop,
autoSubmit,
submitting,
disabled,
shortcuts,
suggestedPrompts,
}) => {
2025-09-30 05:37:50 +08:00
const mentionInputSuggestionsRef = useRef<MentionInputSuggestionsRef>(null);
2025-09-30 03:52:00 +08:00
const uniqueSuggestions = useUniqueSuggestions(suggestedPrompts);
2025-09-30 08:38:20 +08:00
const [openCreateShortcutModal, setOpenCreateShortcutModal] = useState(false);
2025-10-02 00:03:41 +08:00
const [mode, setMode] = useState<MessageAnalysisMode>('auto');
2025-09-30 09:58:03 +08:00
2025-09-30 10:24:58 +08:00
const shortcutsSuggestions = useShortcutsSuggestions(
2025-09-30 09:15:32 +08:00
shortcuts,
setOpenCreateShortcutModal,
2025-09-30 09:58:03 +08:00
mentionInputSuggestionsRef
2025-09-30 09:15:32 +08:00
);
2025-09-30 05:37:50 +08:00
2025-09-30 03:52:00 +08:00
const suggestionItems: MentionInputSuggestionsProps['suggestionItems'] = useMemo(() => {
const items: MentionInputSuggestionsProps['suggestionItems'] = [...uniqueSuggestions];
if (items.length > 0 && shortcutsSuggestions.length > 0) {
items.push({
type: 'separator',
});
}
if (shortcutsSuggestions.length > 0) {
items.push(...shortcutsSuggestions);
}
return items;
}, [uniqueSuggestions, shortcutsSuggestions]);
2025-09-30 08:38:20 +08:00
const shortcutsMentionsSuggestions = useCreateShortcutsMentionsSuggestions(
shortcuts,
setOpenCreateShortcutModal
);
2025-09-30 03:52:00 +08:00
const mentions: MentionSuggestionExtension[] = useMemo(() => {
2025-09-30 08:38:20 +08:00
return [shortcutsMentionsSuggestions];
}, [shortcutsMentionsSuggestions]);
2025-09-30 03:52:00 +08:00
2025-09-30 10:39:22 +08:00
const onSubmitPreflight = (valueProp?: ReturnType<MentionInputSuggestionsRef['getValue']>) => {
2025-09-30 03:52:00 +08:00
if (submitting) {
console.warn('Input is submitting');
return;
}
2025-09-30 10:39:22 +08:00
const value = valueProp || mentionInputSuggestionsRef.current?.getValue?.();
if (!value) {
console.warn('Value is not defined');
2025-09-30 03:52:00 +08:00
return;
}
2025-09-30 10:39:22 +08:00
if (disabled || !value) {
console.warn('Input is disabled or value is not defined');
return;
2025-09-30 03:52:00 +08:00
}
2025-10-02 00:58:34 +08:00
2025-09-30 10:39:22 +08:00
onSubmit({ ...value, mode });
};
2025-09-30 03:52:00 +08:00
2025-10-02 03:52:19 +08:00
const onSuggestionItemClick = useMemoizedFn(() => {
onSubmitPreflight();
});
2025-10-01 00:25:03 +08:00
const onCloseCreateShortcutModal = useMemoizedFn(() => {
setOpenCreateShortcutModal(false);
});
2025-09-30 11:41:32 +08:00
useMount(() => {
if (autoSubmit && defaultValue) {
onSubmitPreflight({
transformedValue: defaultValue,
arrayValue: [],
editorText: defaultValue,
});
}
});
2025-09-30 03:52:00 +08:00
return (
2025-09-30 08:59:49 +08:00
<React.Fragment>
<MentionInputSuggestions
defaultValue={defaultValue}
2025-09-30 10:39:22 +08:00
onPressEnter={onSubmitPreflight}
2025-10-02 03:52:19 +08:00
onSuggestionItemClick={onSuggestionItemClick}
2025-09-30 08:59:49 +08:00
mentions={mentions}
suggestionItems={suggestionItems}
2025-10-02 02:01:27 +08:00
disabled={disabled}
2025-09-30 08:59:49 +08:00
placeholder="Ask a question or type / for shortcuts..."
ref={mentionInputSuggestionsRef}
2025-09-30 11:58:59 +08:00
inputContainerClassName="px-5 pt-4"
2025-10-01 06:17:16 +08:00
inputClassName="text-md"
2025-10-01 06:21:56 +08:00
behavior="open-on-focus"
2025-09-30 08:59:49 +08:00
>
<BusterChatInputButtons
2025-09-30 09:58:03 +08:00
onSubmit={onSubmitPreflight}
2025-09-30 08:59:49 +08:00
onStop={onStop}
submitting={submitting}
disabled={disabled}
mode={mode}
onModeChange={setMode}
/>
</MentionInputSuggestions>
2025-10-01 00:25:03 +08:00
<NewShortcutModal open={openCreateShortcutModal} onClose={onCloseCreateShortcutModal} />
2025-09-30 08:59:49 +08:00
</React.Fragment>
2025-09-30 03:52:00 +08:00
);
}
);
BusterChatInputBase.displayName = 'BusterChatInputBase';