mirror of https://github.com/buster-so/buster.git
Added suggestion trigger
This commit is contained in:
parent
1676402541
commit
b900700686
|
@ -1,10 +1,11 @@
|
|||
import type { ListShortcutsResponse } from '@buster/server-shared/shortcuts';
|
||||
import type { GetSuggestedPromptsResponse } from '@buster/server-shared/user';
|
||||
import type { Editor } from '@tiptap/react';
|
||||
import sampleSize from 'lodash/sampleSize';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCreateShortcutsMentionsSuggestions } from '@/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions';
|
||||
import { Plus } from '@/components/ui/icons';
|
||||
import React, { useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
useCreateShortcutsMentionsSuggestions,
|
||||
useShortcutsSuggestions,
|
||||
} from '@/components/features/input/Mentions/ShortcutsSuggestions/ShortcutsSuggestions';
|
||||
import CircleQuestion from '@/components/ui/icons/NucleoIconOutlined/circle-question';
|
||||
import FileSparkle from '@/components/ui/icons/NucleoIconOutlined/file-sparkle';
|
||||
import type { MentionSuggestionExtension } from '@/components/ui/inputs/MentionInput';
|
||||
|
@ -36,7 +37,7 @@ export const BusterChatInputBase: React.FC<BusterChatInput> = React.memo(
|
|||
const [openCreateShortcutModal, setOpenCreateShortcutModal] = useState(false);
|
||||
const [mode, setMode] = useState<BusterChatInputMode>('auto');
|
||||
|
||||
const shortcutsSuggestions = useShortcuts(
|
||||
const shortcutsSuggestions = useShortcutsSuggestions(
|
||||
shortcuts,
|
||||
setOpenCreateShortcutModal,
|
||||
mentionInputSuggestionsRef
|
||||
|
@ -166,46 +167,3 @@ const useUniqueSuggestions = (
|
|||
] satisfies MentionInputSuggestionsProps['suggestionItems'];
|
||||
}, [suggestedPrompts]);
|
||||
};
|
||||
|
||||
const useShortcuts = (
|
||||
shortcuts: ListShortcutsResponse['shortcuts'],
|
||||
setOpenCreateShortcutModal: (open: boolean) => void,
|
||||
mentionInputSuggestionsRef: React.RefObject<MentionInputSuggestionsRef | null>
|
||||
): MentionInputSuggestionsProps['suggestionItems'] => {
|
||||
return useMemo(() => {
|
||||
const shortcutsItems = shortcuts.map<MentionInputSuggestionsDropdownItem>((shortcut) => {
|
||||
return {
|
||||
type: 'item',
|
||||
value: shortcut.name,
|
||||
label: shortcut.name,
|
||||
icon: '/',
|
||||
inputValue: `/ ${shortcut.name}`,
|
||||
onClick: () => {
|
||||
const onChangeValue = mentionInputSuggestionsRef.current?.onChangeValue;
|
||||
onChangeValue?.(shortcut.name);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
shortcutsItems.push({
|
||||
type: 'item',
|
||||
value: 'createShortcut',
|
||||
label: 'Create shortcut',
|
||||
icon: <Plus />,
|
||||
inputValue: '/ Create shortcut',
|
||||
onClick: () => {
|
||||
setOpenCreateShortcutModal(true);
|
||||
},
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Shortcuts',
|
||||
suggestionItems: shortcutsItems,
|
||||
addValueToInput: false,
|
||||
closeOnSelect: true,
|
||||
},
|
||||
];
|
||||
}, [shortcuts, setOpenCreateShortcutModal, mentionInputSuggestionsRef]);
|
||||
};
|
||||
|
|
|
@ -6,11 +6,18 @@ import PenWriting from '@/components/ui/icons/NucleoIconOutlined/pen-writing';
|
|||
import Plus from '@/components/ui/icons/NucleoIconOutlined/plus';
|
||||
import {
|
||||
createMentionSuggestionExtension,
|
||||
type MentionInputTriggerItem,
|
||||
MentionSecondaryContentDropdown,
|
||||
type MentionTriggerItem,
|
||||
} from '@/components/ui/inputs/MentionInput';
|
||||
import type {
|
||||
MentionInputSuggestionsDropdownItem,
|
||||
MentionInputSuggestionsProps,
|
||||
MentionInputSuggestionsRef,
|
||||
} from '@/components/ui/inputs/MentionInputSuggestions';
|
||||
import { ShortcutPopoverContent } from './ShortcutPopoverContent';
|
||||
|
||||
export const SHORTCUT_MENTION_TRIGGER = '/';
|
||||
|
||||
export const useCreateShortcutsMentionsSuggestions = (
|
||||
shortcuts: ListShortcutsResponse['shortcuts'],
|
||||
setOpenCreateShortcutModal: (open: boolean) => void
|
||||
|
@ -20,10 +27,10 @@ export const useCreateShortcutsMentionsSuggestions = (
|
|||
return useMemo(
|
||||
() =>
|
||||
createMentionSuggestionExtension({
|
||||
trigger: '/',
|
||||
trigger: SHORTCUT_MENTION_TRIGGER,
|
||||
items: [
|
||||
...shortcuts.map(createShortcut),
|
||||
{ type: 'separator' },
|
||||
...shortcuts.map(createShortcutForMention),
|
||||
{ type: 'separator' as const },
|
||||
{
|
||||
value: 'manageShortcuts',
|
||||
label: 'Manage shortcuts',
|
||||
|
@ -58,14 +65,11 @@ export const useCreateShortcutsMentionsSuggestions = (
|
|||
);
|
||||
};
|
||||
|
||||
const createShortcut = (shortcut: Shortcut): MentionInputTriggerItem<string> => {
|
||||
const createShortcutForMention = (shortcut: Shortcut): MentionTriggerItem<string> => {
|
||||
return {
|
||||
value: shortcut.id,
|
||||
label: shortcut.name,
|
||||
icon: <PenWriting />,
|
||||
onSelect: (props) => {
|
||||
console.log('onSelect shortcut?', props);
|
||||
},
|
||||
secondaryContent: (
|
||||
<MentionSecondaryContentDropdown
|
||||
items={[
|
||||
|
@ -90,3 +94,53 @@ const createShortcut = (shortcut: Shortcut): MentionInputTriggerItem<string> =>
|
|||
),
|
||||
};
|
||||
};
|
||||
export const useShortcutsSuggestions = (
|
||||
shortcuts: ListShortcutsResponse['shortcuts'],
|
||||
setOpenCreateShortcutModal: (open: boolean) => void,
|
||||
mentionInputSuggestionsRef: React.RefObject<MentionInputSuggestionsRef | null>
|
||||
): MentionInputSuggestionsProps['suggestionItems'] => {
|
||||
return useMemo(() => {
|
||||
const shortcutsItems = shortcuts.map<MentionInputSuggestionsDropdownItem>((shortcut) => {
|
||||
return {
|
||||
type: 'item',
|
||||
value: shortcut.name,
|
||||
label: shortcut.name,
|
||||
icon: SHORTCUT_MENTION_TRIGGER,
|
||||
inputValue: `/ ${shortcut.name}`,
|
||||
onClick: () => {
|
||||
const addMentionToInput = mentionInputSuggestionsRef.current?.addMentionToInput;
|
||||
if (!addMentionToInput) {
|
||||
console.warn('addMentionToInput is not defined', mentionInputSuggestionsRef.current);
|
||||
return;
|
||||
}
|
||||
const shortcutForMention = createShortcutForMention(shortcut);
|
||||
addMentionToInput?.({
|
||||
...shortcutForMention,
|
||||
trigger: SHORTCUT_MENTION_TRIGGER,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
shortcutsItems.push({
|
||||
type: 'item',
|
||||
value: 'createShortcut',
|
||||
label: 'Create shortcut',
|
||||
icon: <Plus />,
|
||||
inputValue: `${SHORTCUT_MENTION_TRIGGER} Create shortcut`,
|
||||
onClick: () => {
|
||||
setOpenCreateShortcutModal(true);
|
||||
},
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Shortcuts',
|
||||
suggestionItems: shortcutsItems,
|
||||
addValueToInput: false,
|
||||
closeOnSelect: true,
|
||||
},
|
||||
];
|
||||
}, [shortcuts, setOpenCreateShortcutModal, mentionInputSuggestionsRef]);
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@ import type {
|
|||
MentionInputRef,
|
||||
MentionSuggestionExtension,
|
||||
} from './MentionInput.types';
|
||||
import type { MentionPillAttributes } from './MentionPill';
|
||||
import { SubmitOnEnter } from './SubmitEnterExtension';
|
||||
import { onUpdateTransformer } from './update-transformers';
|
||||
|
||||
|
@ -97,11 +98,24 @@ export const MentionInput = forwardRef<MentionInputRef, MentionInputProps>(
|
|||
onBlur: onBlur,
|
||||
});
|
||||
|
||||
//exported for use in the mention input suggestions
|
||||
const addMentionToInput = (mention: MentionPillAttributes) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContent([
|
||||
{ type: 'mention', attrs: mention },
|
||||
{ type: 'text', text: ' ' }, // add a trailing space so the caret leaves the atom
|
||||
])
|
||||
.run();
|
||||
};
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
editor,
|
||||
getValue,
|
||||
addMentionToInput,
|
||||
}),
|
||||
[editor]
|
||||
);
|
||||
|
|
|
@ -107,6 +107,7 @@ export type MentionInputProps = {
|
|||
|
||||
export type MentionInputRef = {
|
||||
editor: Editor | null;
|
||||
addMentionToInput: (mention: MentionPillAttributes) => void;
|
||||
getValue: () => ReturnType<typeof onUpdateTransformer>;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ import { Popover } from '@/components/ui/popover';
|
|||
import { cn } from '@/lib/utils';
|
||||
import type { MentionTriggerItem } from './MentionInput.types';
|
||||
|
||||
export type MentionPillAttributes<T = string> = Required<
|
||||
Pick<MentionTriggerItem<T>, 'label' | 'value' | 'doNotAddPipeOnSelect' | 'pillLabel'>
|
||||
export type MentionPillAttributes<T = string> = Pick<
|
||||
MentionTriggerItem<T>,
|
||||
'label' | 'value' | 'doNotAddPipeOnSelect' | 'pillLabel'
|
||||
> & { trigger: string };
|
||||
|
||||
export const MentionPill = <T extends string>({
|
||||
|
|
|
@ -110,9 +110,11 @@ export const MentionInputSuggestions = forwardRef<
|
|||
}
|
||||
);
|
||||
|
||||
const getValue = useMemoizedFn(() => {
|
||||
return mentionsInputRef.current?.getValue();
|
||||
});
|
||||
// biome-ignore lint/style/noNonNullAssertion: we know the ref is not null
|
||||
const getValue = mentionsInputRef.current?.getValue!;
|
||||
// biome-ignore lint/style/noNonNullAssertion: we know the ref is not null
|
||||
const addMentionToInput = mentionsInputRef.current?.addMentionToInput!;
|
||||
const mounted = useMounted();
|
||||
|
||||
// Track arrow key navigation in the command list
|
||||
useEffect(() => {
|
||||
|
@ -157,8 +159,9 @@ export const MentionInputSuggestions = forwardRef<
|
|||
value,
|
||||
onChangeValue,
|
||||
getValue,
|
||||
addMentionToInput,
|
||||
}),
|
||||
[value, onChangeValue, getValue]
|
||||
[value, mounted, onChangeValue, getValue, addMentionToInput]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -222,11 +225,7 @@ const MentionInputSuggestionsContext = createContext<{
|
|||
value: string;
|
||||
onChangeValue: MentionInputSuggestionsRef['onChangeValue'];
|
||||
getValue: MentionInputSuggestionsRef['getValue'];
|
||||
}>({
|
||||
value: '',
|
||||
onChangeValue: () => {},
|
||||
getValue: () => undefined,
|
||||
});
|
||||
}>({} as MentionInputSuggestionsRef);
|
||||
|
||||
const MentionInputSuggestionsProvider = ({
|
||||
children,
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import type { Command } from 'cmdk';
|
||||
import type React from 'react';
|
||||
import type { MentionSuggestionExtension } from '../MentionInput';
|
||||
import type { MentionInputProps, MentionTriggerItem } from '../MentionInput/MentionInput.types';
|
||||
import type {
|
||||
MentionInputProps,
|
||||
MentionInputRef,
|
||||
MentionTriggerItem,
|
||||
} from '../MentionInput/MentionInput.types';
|
||||
import type { onUpdateTransformer } from '../MentionInput/update-transformers';
|
||||
|
||||
/**
|
||||
|
@ -86,8 +90,7 @@ export type MentionInputSuggestionsProps<T = string> = {
|
|||
export type MentionInputSuggestionsRef = {
|
||||
value: string;
|
||||
onChangeValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
getValue: () => ReturnType<typeof onUpdateTransformer> | undefined;
|
||||
};
|
||||
} & Pick<MentionInputRef, 'addMentionToInput' | 'getValue'>;
|
||||
|
||||
export type MentionInputSuggestionsContainerProps = {
|
||||
children: React.ReactNode;
|
||||
|
|
Loading…
Reference in New Issue