mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-12-15 09:32:22 +03:00
feat: add FeedBrainInput component (#1101)
* feat: add FeedBrainInput component * feat: add upload button * feat: update translations add feed_brain_placeholder * refactor(uploadPage): add config.ts * feat(lib): add MentionInput * feat: add <BrainSelector/> component * feat: update FeedBrainInput
This commit is contained in:
parent
500f27f81c
commit
14e44ac6ec
@ -0,0 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { BrainSelector } from "./components";
|
||||
import { useFeedBrainInput } from "./hooks/useFeedBrainInput";
|
||||
import { MentionItem } from "../ChatBar/components/MentionItem";
|
||||
|
||||
export const FeedBrainInput = (): JSX.Element => {
|
||||
const { currentBrain, setCurrentBrainId } = useBrainContext();
|
||||
|
||||
useFeedBrainInput();
|
||||
|
||||
return (
|
||||
<div className="flex flex-row flex-1 w-full item-start">
|
||||
{currentBrain !== undefined && (
|
||||
<MentionItem
|
||||
text={currentBrain.name}
|
||||
onRemove={() => {
|
||||
setCurrentBrainId(null);
|
||||
}}
|
||||
trigger={"@"}
|
||||
/>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<BrainSelector />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,67 @@
|
||||
import Editor from "@draft-js-plugins/editor";
|
||||
import { ReactElement } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import "@draft-js-plugins/mention/lib/plugin.css";
|
||||
import "draft-js/dist/Draft.css";
|
||||
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { BrainSuggestionsContainer } from "./components";
|
||||
import { SuggestionRow } from "./components/SuggestionRow";
|
||||
import { useBrainSelector } from "./hooks/useBrainSelector";
|
||||
|
||||
export const BrainSelector = (): ReactElement => {
|
||||
const {
|
||||
mentionInputRef,
|
||||
MentionSuggestions,
|
||||
keyBindingFn,
|
||||
editorState,
|
||||
onAddMention,
|
||||
setOpen,
|
||||
onSearchChange,
|
||||
open,
|
||||
plugins,
|
||||
suggestions,
|
||||
handleEditorChange,
|
||||
} = useBrainSelector();
|
||||
const { currentBrainId } = useBrainContext();
|
||||
|
||||
const { t } = useTranslation(["chat"]);
|
||||
|
||||
const hasBrainSelected = currentBrainId !== null;
|
||||
|
||||
return (
|
||||
<div className="w-full" data-testid="chat-input">
|
||||
<Editor
|
||||
editorKey={"editor"}
|
||||
editorState={editorState}
|
||||
onChange={handleEditorChange}
|
||||
plugins={plugins}
|
||||
ref={mentionInputRef}
|
||||
placeholder={hasBrainSelected ? "" : t("feed_brain_placeholder")}
|
||||
keyBindingFn={keyBindingFn}
|
||||
readOnly={hasBrainSelected}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
// `open` should be directly passed to the MentionSuggestions component.
|
||||
// However, it is not working as expected since we are not able to click on button in custom suggestion renderer.
|
||||
// So, we are using this hack to make it work.
|
||||
opacity: open ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<MentionSuggestions
|
||||
open
|
||||
onOpenChange={setOpen}
|
||||
suggestions={suggestions}
|
||||
onSearchChange={onSearchChange}
|
||||
popoverContainer={BrainSuggestionsContainer}
|
||||
entryComponent={SuggestionRow}
|
||||
renderEmptyPopup
|
||||
onAddMention={onAddMention}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
type BrainSuggestionProps = {
|
||||
content: string;
|
||||
};
|
||||
export const BrainSuggestion = ({
|
||||
content,
|
||||
}: BrainSuggestionProps): JSX.Element => {
|
||||
return <span>{content}</span>;
|
||||
};
|
@ -0,0 +1,18 @@
|
||||
import { Popover } from "@draft-js-plugins/mention";
|
||||
import { PopoverProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Popover";
|
||||
|
||||
export const BrainSuggestionsContainer = ({
|
||||
children,
|
||||
...popoverProps
|
||||
}: PopoverProps): JSX.Element => (
|
||||
<Popover {...popoverProps}>
|
||||
<div
|
||||
style={{
|
||||
width: "max-content",
|
||||
}}
|
||||
className="bg-white dark:bg-black border border-black/10 dark:border-white/25 rounded-md shadow-md overflow-y-auto"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
@ -0,0 +1,16 @@
|
||||
import { EntryComponentProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Entry/Entry";
|
||||
|
||||
import { BrainSuggestion } from "./BrainSuggestion";
|
||||
|
||||
export const SuggestionRow = ({
|
||||
mention,
|
||||
...otherProps
|
||||
}: EntryComponentProps): JSX.Element => {
|
||||
return (
|
||||
<div {...otherProps}>
|
||||
<div className="relative flex group px-4">
|
||||
<BrainSuggestion content={mention.name} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
export * from "./BrainSuggestion";
|
||||
export * from "./BrainSuggestionsContainer";
|
||||
export * from "./SuggestionRow";
|
@ -0,0 +1,44 @@
|
||||
import createMentionPlugin from "@draft-js-plugins/mention";
|
||||
import { useMemo } from "react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useMentionPlugin = () => {
|
||||
const { MentionSuggestions, plugins } = useMemo(() => {
|
||||
const mentionPlugin = createMentionPlugin({
|
||||
mentionComponent: () => <span />,
|
||||
mentionTrigger: ["@"],
|
||||
mentionPrefix: "@#",
|
||||
popperOptions: {
|
||||
placement: "top-end",
|
||||
modifiers: [
|
||||
{
|
||||
name: "customStyle", // Custom modifier for applying styles
|
||||
enabled: true,
|
||||
phase: "beforeWrite",
|
||||
fn: ({ state }) => {
|
||||
state.styles.popper = {
|
||||
...state.styles.popper,
|
||||
minWidth: "auto",
|
||||
backgroundColor: "transparent",
|
||||
padding: "0",
|
||||
// We are adding a bottom margin to the suggestions container since it is overlapping with mention remove icon
|
||||
// Please, do not remove!
|
||||
marginBottom: "20px",
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const { MentionSuggestions: LegacyMentionSuggestions } = mentionPlugin;
|
||||
const legacyPlugins = [mentionPlugin];
|
||||
|
||||
return {
|
||||
plugins: legacyPlugins,
|
||||
MentionSuggestions: LegacyMentionSuggestions,
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { MentionSuggestions, plugins };
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
import { EditorState } from "draft-js";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { requiredRolesForUpload } from "@/app/upload/config";
|
||||
import { mapMinimalBrainToMentionData } from "@/lib/components/MentionInput";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { MentionInputMentionsType } from "../../types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useMentionState = () => {
|
||||
const { allBrains } = useBrainContext();
|
||||
|
||||
const brainsWithUploadRights = useMemo(
|
||||
() =>
|
||||
allBrains.filter((brain) => requiredRolesForUpload.includes(brain.role)),
|
||||
[allBrains]
|
||||
);
|
||||
|
||||
const [editorState, setEditorState] = useState(EditorState.createEmpty());
|
||||
|
||||
const [mentionItems, setMentionItems] = useState<MentionInputMentionsType>({
|
||||
"@": brainsWithUploadRights.map(mapMinimalBrainToMentionData),
|
||||
});
|
||||
|
||||
const [suggestions, setSuggestions] = useState<MentionData[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setMentionItems({
|
||||
"@": brainsWithUploadRights.map(mapMinimalBrainToMentionData),
|
||||
});
|
||||
}, [brainsWithUploadRights]);
|
||||
|
||||
return {
|
||||
editorState,
|
||||
setEditorState,
|
||||
mentionItems,
|
||||
setSuggestions,
|
||||
setMentionItems,
|
||||
suggestions,
|
||||
};
|
||||
};
|
@ -0,0 +1,146 @@
|
||||
/* eslint-disable max-lines */
|
||||
import Editor from "@draft-js-plugins/editor";
|
||||
import {
|
||||
defaultSuggestionsFilter,
|
||||
MentionData,
|
||||
} from "@draft-js-plugins/mention";
|
||||
import { UUID } from "crypto";
|
||||
import { EditorState, getDefaultKeyBinding } from "draft-js";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { mentionTriggers } from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
import { getEditorText } from "@/lib/components/MentionInput/utils/getEditorText";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import "@draft-js-plugins/mention/lib/plugin.css";
|
||||
import "draft-js/dist/Draft.css";
|
||||
|
||||
import { useMentionPlugin } from "./helpers/MentionPlugin";
|
||||
import { useMentionState } from "./helpers/MentionState";
|
||||
import { MentionTriggerType } from "../types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useBrainSelector = () => {
|
||||
const {
|
||||
currentBrainId,
|
||||
currentPromptId,
|
||||
setCurrentBrainId,
|
||||
setCurrentPromptId,
|
||||
} = useBrainContext();
|
||||
|
||||
const {
|
||||
editorState,
|
||||
setEditorState,
|
||||
mentionItems,
|
||||
setSuggestions,
|
||||
suggestions,
|
||||
} = useMentionState();
|
||||
|
||||
const { MentionSuggestions, plugins } = useMentionPlugin();
|
||||
|
||||
const [currentTrigger, setCurrentTrigger] = useState<MentionTriggerType>("@");
|
||||
|
||||
const mentionInputRef = useRef<Editor>(null);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onSearchChange = useCallback(
|
||||
({ trigger, value }: { trigger: MentionTriggerType; value: string }) => {
|
||||
setCurrentTrigger(trigger);
|
||||
if (currentBrainId !== null) {
|
||||
setSuggestions([]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === "") {
|
||||
setSuggestions(mentionItems["@"]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setSuggestions(defaultSuggestionsFilter(value, mentionItems, trigger));
|
||||
},
|
||||
[currentBrainId, mentionItems, setSuggestions]
|
||||
);
|
||||
const onAddMention = useCallback(
|
||||
(mention: MentionData) => {
|
||||
if (mention.trigger === "@") {
|
||||
setCurrentBrainId(mention.id as UUID);
|
||||
}
|
||||
},
|
||||
[setCurrentBrainId]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Reset editor state when there is no brain selected in order to show placeholder
|
||||
if (currentBrainId === null) {
|
||||
setEditorState(EditorState.createEmpty());
|
||||
}
|
||||
}, [currentBrainId, setEditorState]);
|
||||
|
||||
const keyBindingFn = useCallback(
|
||||
// eslint-disable-next-line complexity
|
||||
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (mentionTriggers.includes(e.key as MentionTriggerType)) {
|
||||
setOpen(true);
|
||||
|
||||
return getDefaultKeyBinding(e);
|
||||
}
|
||||
|
||||
if (e.key === "Backspace" || e.key === "Delete") {
|
||||
const editorContent = getEditorText(editorState);
|
||||
|
||||
if (editorContent !== "") {
|
||||
return getDefaultKeyBinding(e);
|
||||
}
|
||||
if (currentPromptId !== null) {
|
||||
setCurrentPromptId(null);
|
||||
|
||||
return "backspace";
|
||||
}
|
||||
if (currentBrainId !== null) {
|
||||
setCurrentBrainId(null);
|
||||
|
||||
return "backspace";
|
||||
}
|
||||
|
||||
return "backspace";
|
||||
}
|
||||
|
||||
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getDefaultKeyBinding(e);
|
||||
},
|
||||
[
|
||||
currentBrainId,
|
||||
currentPromptId,
|
||||
editorState,
|
||||
setCurrentBrainId,
|
||||
setCurrentPromptId,
|
||||
]
|
||||
);
|
||||
|
||||
const handleEditorChange = useCallback(
|
||||
(newEditorState: EditorState) => {
|
||||
setEditorState(newEditorState);
|
||||
},
|
||||
[setEditorState]
|
||||
);
|
||||
|
||||
return {
|
||||
mentionInputRef,
|
||||
plugins,
|
||||
MentionSuggestions,
|
||||
onSearchChange,
|
||||
open,
|
||||
suggestions,
|
||||
editorState,
|
||||
handleEditorChange,
|
||||
keyBindingFn,
|
||||
currentTrigger,
|
||||
setOpen,
|
||||
onAddMention,
|
||||
};
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from "./BrainSelector";
|
@ -0,0 +1,8 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
|
||||
export const mentionTriggers = ["@"] as const;
|
||||
export type MentionTriggerType = (typeof mentionTriggers)[number];
|
||||
export type MentionInputMentionsType = Record<
|
||||
MentionTriggerType,
|
||||
MentionData[]
|
||||
>;
|
@ -0,0 +1 @@
|
||||
export * from "./BrainSelector";
|
@ -0,0 +1,17 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { requiredRolesForUpload } from "@/app/upload/config";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useFeedBrainInput = () => {
|
||||
const { currentBrain, setCurrentBrainId } = useBrainContext();
|
||||
useEffect(() => {
|
||||
if (
|
||||
currentBrain !== undefined &&
|
||||
!requiredRolesForUpload.includes(currentBrain.role)
|
||||
) {
|
||||
setCurrentBrainId(null);
|
||||
}
|
||||
}, [currentBrain, setCurrentBrainId]);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from "./FeedBrainInput";
|
@ -0,0 +1,8 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
|
||||
import { MentionTriggerType } from "../../../../types";
|
||||
|
||||
export type MentionInputMentionsType = Record<
|
||||
MentionTriggerType,
|
||||
MentionData[]
|
||||
>;
|
@ -6,6 +6,7 @@ import { useChat } from "@/app/chat/[chatId]/hooks/useChat";
|
||||
export const useChatInput = () => {
|
||||
const [message, setMessage] = useState<string>("");
|
||||
const { addQuestion, generatingAnswer, chatId } = useChat();
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
const submitQuestion = () => {
|
||||
if (!generatingAnswer) {
|
||||
@ -19,5 +20,7 @@ export const useChatInput = () => {
|
||||
submitQuestion,
|
||||
generatingAnswer,
|
||||
chatId,
|
||||
isUploading,
|
||||
setIsUploading,
|
||||
};
|
||||
};
|
||||
|
@ -1,16 +1,29 @@
|
||||
"use client";
|
||||
import { useFeature } from "@growthbook/growthbook-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MdAddCircle, MdSend } from "react-icons/md";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { ChatBar } from "./components/ChatBar/ChatBar";
|
||||
import { ConfigModal } from "./components/ConfigModal";
|
||||
import { FeedBrainInput } from "./components/FeedBrainInput";
|
||||
import { useChatInput } from "./hooks/useChatInput";
|
||||
|
||||
export const ChatInput = (): JSX.Element => {
|
||||
const { setMessage, submitQuestion, chatId, generatingAnswer, message } =
|
||||
useChatInput();
|
||||
const {
|
||||
setMessage,
|
||||
submitQuestion,
|
||||
chatId,
|
||||
generatingAnswer,
|
||||
message,
|
||||
isUploading,
|
||||
setIsUploading,
|
||||
} = useChatInput();
|
||||
const { t } = useTranslation(["chat"]);
|
||||
const shouldDisplayUploadButton = useFeature("ux-upload").on;
|
||||
const { currentBrainId } = useBrainContext();
|
||||
|
||||
return (
|
||||
<form
|
||||
@ -21,28 +34,60 @@ export const ChatInput = (): JSX.Element => {
|
||||
}}
|
||||
className="sticky flex items-star bottom-0 bg-white dark:bg-black w-full flex justify-center gap-2 z-20"
|
||||
>
|
||||
{!isUploading && shouldDisplayUploadButton && (
|
||||
<div className="flex items-start">
|
||||
<Button
|
||||
className="p-0"
|
||||
variant={"tertiary"}
|
||||
data-testid="upload-button"
|
||||
type="button"
|
||||
onClick={() => setIsUploading(true)}
|
||||
>
|
||||
<MdAddCircle className="text-3xl" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-1 flex-col items-center">
|
||||
<ChatBar
|
||||
message={message}
|
||||
setMessage={setMessage}
|
||||
onSubmit={submitQuestion}
|
||||
/>
|
||||
{isUploading ? (
|
||||
<FeedBrainInput />
|
||||
) : (
|
||||
<ChatBar
|
||||
message={message}
|
||||
setMessage={setMessage}
|
||||
onSubmit={submitQuestion}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-end">
|
||||
<Button
|
||||
className="px-3 py-2 sm:px-4 sm:py-2"
|
||||
type="submit"
|
||||
isLoading={generatingAnswer}
|
||||
data-testid="submit-button"
|
||||
>
|
||||
{generatingAnswer
|
||||
? t("thinking", { ns: "chat" })
|
||||
: t("chat", { ns: "chat" })}
|
||||
</Button>
|
||||
<div className="flex items-center">
|
||||
<ConfigModal chatId={chatId} />
|
||||
</div>
|
||||
{isUploading ? (
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
disabled={currentBrainId === null}
|
||||
variant="tertiary"
|
||||
onClick={() => setIsUploading(false)}
|
||||
>
|
||||
<MdSend className="text-3xl transform -rotate-90" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
className="px-3 py-2 sm:px-4 sm:py-2"
|
||||
type="submit"
|
||||
isLoading={generatingAnswer}
|
||||
data-testid="submit-button"
|
||||
>
|
||||
{generatingAnswer
|
||||
? t("thinking", { ns: "chat" })
|
||||
: t("chat", { ns: "chat" })}
|
||||
</Button>
|
||||
<div className="flex items-center">
|
||||
<ConfigModal chatId={chatId} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
3
frontend/app/upload/config.ts
Normal file
3
frontend/app/upload/config.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
|
||||
export const requiredRolesForUpload: BrainRoleType[] = ["Editor", "Owner"];
|
@ -2,7 +2,6 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Divider } from "@/lib/components/ui/Divider";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
@ -12,8 +11,7 @@ import { redirectToLogin } from "@/lib/router/redirectToLogin";
|
||||
|
||||
import { Crawler } from "./components/Crawler";
|
||||
import { FileUploader } from "./components/FileUploader";
|
||||
|
||||
const requiredRolesForUpload: BrainRoleType[] = ["Editor", "Owner"];
|
||||
import { requiredRolesForUpload } from "./config";
|
||||
|
||||
const UploadPage = (): JSX.Element => {
|
||||
const { currentBrain } = useBrainContext();
|
||||
|
1
frontend/lib/components/MentionInput/index.ts
Normal file
1
frontend/lib/components/MentionInput/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./utils";
|
@ -0,0 +1,21 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
import { EditorState } from "draft-js";
|
||||
|
||||
import { isMention } from "./isMention";
|
||||
|
||||
export const getEditorMentions = (editorState: EditorState): MentionData[] => {
|
||||
const contentState = editorState.getCurrentContent();
|
||||
const entities = contentState.getAllEntities();
|
||||
|
||||
const mentions: MentionData[] = [];
|
||||
|
||||
entities.forEach((contentBlock) => {
|
||||
if (isMention(contentBlock?.getType())) {
|
||||
mentions.push(
|
||||
(contentBlock?.getData() as { mention: MentionData }).mention
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return mentions;
|
||||
};
|
22
frontend/lib/components/MentionInput/utils/getEditorText.ts
Normal file
22
frontend/lib/components/MentionInput/utils/getEditorText.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
import { EditorState } from "draft-js";
|
||||
|
||||
export const getEditorText = (editorState: EditorState): string => {
|
||||
const mentions: string[] = [];
|
||||
const editorEntities = editorState.getCurrentContent().getAllEntities();
|
||||
|
||||
editorEntities.forEach((entity) => {
|
||||
const entityData = entity?.getData() as { mention?: MentionData };
|
||||
if (entityData.mention !== undefined) {
|
||||
mentions.push(entityData.mention.name);
|
||||
}
|
||||
});
|
||||
|
||||
let content = editorState.getCurrentContent().getPlainText();
|
||||
|
||||
for (const mention of mentions) {
|
||||
content = content.replace(`@#${mention}`, "");
|
||||
}
|
||||
|
||||
return content.trim();
|
||||
};
|
3
frontend/lib/components/MentionInput/utils/index.ts
Normal file
3
frontend/lib/components/MentionInput/utils/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./isMention";
|
||||
export * from "./mapMinimalBrainToMentionData";
|
||||
export * from "./mapPromptToMentionData";
|
9
frontend/lib/components/MentionInput/utils/isMention.ts
Normal file
9
frontend/lib/components/MentionInput/utils/isMention.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { mentionTriggers } from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
|
||||
const mentionsTags = [
|
||||
"mention",
|
||||
...mentionTriggers.map((trigger) => `${trigger}mention`),
|
||||
];
|
||||
|
||||
export const isMention = (type?: string): boolean =>
|
||||
type !== undefined && mentionsTags.includes(type);
|
@ -0,0 +1,11 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
|
||||
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
|
||||
|
||||
export const mapMinimalBrainToMentionData = (
|
||||
brain: MinimalBrainForUser
|
||||
): MentionData => ({
|
||||
name: brain.name,
|
||||
id: brain.id as string,
|
||||
trigger: "@",
|
||||
});
|
@ -0,0 +1,9 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
|
||||
import { Prompt } from "@/lib/types/Prompt";
|
||||
|
||||
export const mapPromptToMentionData = (prompt: Prompt): MentionData => ({
|
||||
name: prompt.title,
|
||||
id: prompt.id,
|
||||
trigger: "#",
|
||||
});
|
@ -33,5 +33,6 @@
|
||||
"empty_brain_title_suffix": "and chat with them",
|
||||
"actions_bar_placeholder": "Ask a question to a @brain and choose your #prompt",
|
||||
"missing_brain": "Please select a brain to chat with",
|
||||
"new_prompt": "Create new prompt"
|
||||
"new_prompt": "Create new prompt",
|
||||
"feed_brain_placeholder":"Choose which @brain you want to feed with these files"
|
||||
}
|
||||
|
@ -34,5 +34,6 @@
|
||||
"thinking": "Pensando...",
|
||||
"title": "Conversa con {{brain}}",
|
||||
"missing_brain": "No hay cerebro seleccionado",
|
||||
"new_prompt": "Crear nueva instrucción"
|
||||
"new_prompt": "Crear nueva instrucción",
|
||||
"feed_brain_placeholder" : "Elige cuál @cerebro quieres alimentar con estos archivos"
|
||||
}
|
||||
|
@ -34,5 +34,6 @@
|
||||
"thinking": "Réflexion...",
|
||||
"title": "Discuter avec {{brain}}",
|
||||
"missing_brain": "Veuillez selectionner un cerveau pour discuter",
|
||||
"new_prompt": "Créer un nouveau prompt"
|
||||
"new_prompt": "Créer un nouveau prompt",
|
||||
"feed_brain_placeholder" : "Choisissez le @cerveau que vous souhaitez nourrir avec ces fichiers"
|
||||
}
|
||||
|
@ -34,5 +34,6 @@
|
||||
"thinking": "Pensando...",
|
||||
"title": "Converse com {{brain}}",
|
||||
"missing_brain": "Cérebro não encontrado",
|
||||
"new_prompt": "Criar novo prompt"
|
||||
"new_prompt": "Criar novo prompt",
|
||||
"feed_brain_placeholder" : "Escolha qual @cérebro você deseja alimentar com esses arquivos"
|
||||
}
|
||||
|
@ -34,5 +34,6 @@
|
||||
"thinking": "Думаю...",
|
||||
"title": "Чат с {{brain}}",
|
||||
"missing_brain": "Мозг не найден",
|
||||
"new_prompt": "Создать новый запрос"
|
||||
"new_prompt": "Создать новый запрос",
|
||||
"feed_brain_placeholder" : "Выберите, какой @мозг вы хотите питать этими файлами"
|
||||
}
|
||||
|
@ -34,5 +34,6 @@
|
||||
"empty_brain_title_suffix": "和他们聊天",
|
||||
"actions_bar_placeholder": "向@大脑提问,选择您的#提示",
|
||||
"missing_brain": "请选择一个大脑进行聊天",
|
||||
"new_prompt": "新提示"
|
||||
"new_prompt": "新提示",
|
||||
"feed_brain_placeholder" : "选择要用这些文件喂养的 @大脑"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user