mirror of
https://github.com/StanGirard/quivr.git
synced 2024-12-25 04:12:44 +03:00
feat: add prompt trigger through # (#1023)
* feat: add prompt trigger to mention input * feat: update chat shortcuts * test: update BrainProviderMock * feat: improve ux * feat: update message header position * feat: improve mention input dx * fix(MentionInput): fix minor bugs * feat: refactor <ShareBrain/> * feat: add brain sharing button * fix: make popover buttons click working * feat: update backspace handle logic * feat: update add new brain button ui
This commit is contained in:
parent
619f5bc007
commit
072d97adb1
@ -7,17 +7,19 @@ import { NavBar } from "@/lib/components/NavBar";
|
||||
import { TrackingWrapper } from "@/lib/components/TrackingWrapper";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { useSupabase } from "@/lib/context/SupabaseProvider";
|
||||
import '../lib/config/LocaleConfig/i18n'
|
||||
import { UpdateMetadata } from "@/lib/helpers/updateMetadata";
|
||||
import "../lib/config/LocaleConfig/i18n";
|
||||
|
||||
// This wrapper is used to make effect calls at a high level in app rendering.
|
||||
export const App = ({ children }: PropsWithChildren): JSX.Element => {
|
||||
const { fetchAllBrains, fetchAndSetActiveBrain } = useBrainContext();
|
||||
const { fetchAllBrains, fetchAndSetActiveBrain, fetchPublicPrompts } =
|
||||
useBrainContext();
|
||||
const { session } = useSupabase();
|
||||
|
||||
useEffect(() => {
|
||||
void fetchAllBrains();
|
||||
void fetchAndSetActiveBrain();
|
||||
void fetchPublicPrompts();
|
||||
}, [session?.user]);
|
||||
|
||||
return (
|
||||
|
@ -1,6 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { MentionInput } from "./components";
|
||||
import { MentionItem } from "./components/MentionItem";
|
||||
|
||||
type ChatBarProps = {
|
||||
onSubmit: () => void;
|
||||
@ -13,11 +16,36 @@ export const ChatBar = ({
|
||||
setMessage,
|
||||
message,
|
||||
}: ChatBarProps): JSX.Element => {
|
||||
const { currentBrain, setCurrentBrainId, currentPrompt, setCurrentPromptId } =
|
||||
useBrainContext();
|
||||
|
||||
return (
|
||||
<MentionInput
|
||||
message={message}
|
||||
setMessage={setMessage}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
<div className="flex flex-row flex-1 w-full item-start">
|
||||
{currentBrain !== undefined && (
|
||||
<MentionItem
|
||||
text={currentBrain.name}
|
||||
onRemove={() => {
|
||||
setCurrentBrainId(null);
|
||||
}}
|
||||
trigger={"@"}
|
||||
/>
|
||||
)}
|
||||
{currentPrompt !== undefined && (
|
||||
<MentionItem
|
||||
text={currentPrompt.title}
|
||||
onRemove={() => {
|
||||
setCurrentPromptId(null);
|
||||
}}
|
||||
trigger={"#"}
|
||||
/>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<MentionInput
|
||||
message={message}
|
||||
setMessage={setMessage}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,12 +1,16 @@
|
||||
import Editor from "@draft-js-plugins/editor";
|
||||
import { ReactElement } from "react";
|
||||
import { PopoverProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Popover";
|
||||
import { ComponentType, ReactElement } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import "@draft-js-plugins/mention/lib/plugin.css";
|
||||
import "draft-js/dist/Draft.css";
|
||||
|
||||
import { MentionTriggerType } from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
|
||||
import { BrainSuggestionsContainer } from "./components/BrainSuggestionsContainer";
|
||||
import { PromptSuggestionsContainer } from "./components/PromptSuggestionsContainer";
|
||||
import { SuggestionRow } from "./components/SuggestionRow";
|
||||
import { SuggestionsContainer } from "./components/SuggestionsContainer";
|
||||
import { useMentionInput } from "./hooks/useMentionInput";
|
||||
|
||||
type MentionInputProps = {
|
||||
@ -14,6 +18,15 @@ type MentionInputProps = {
|
||||
setMessage: (text: string) => void;
|
||||
message: string;
|
||||
};
|
||||
|
||||
const triggerToSuggestionsContainer: Record<
|
||||
MentionTriggerType,
|
||||
ComponentType<PopoverProps>
|
||||
> = {
|
||||
"@": BrainSuggestionsContainer,
|
||||
"#": PromptSuggestionsContainer,
|
||||
};
|
||||
|
||||
export const MentionInput = ({
|
||||
onSubmit,
|
||||
setMessage,
|
||||
@ -24,13 +37,14 @@ export const MentionInput = ({
|
||||
MentionSuggestions,
|
||||
keyBindingFn,
|
||||
editorState,
|
||||
onOpenChange,
|
||||
setOpen,
|
||||
onSearchChange,
|
||||
open,
|
||||
plugins,
|
||||
suggestions,
|
||||
onAddMention,
|
||||
handleEditorChange,
|
||||
currentTrigger,
|
||||
} = useMentionInput({
|
||||
message,
|
||||
onSubmit,
|
||||
@ -50,15 +64,24 @@ export const MentionInput = ({
|
||||
placeholder={t("actions_bar_placeholder")}
|
||||
keyBindingFn={keyBindingFn}
|
||||
/>
|
||||
<MentionSuggestions
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
suggestions={suggestions}
|
||||
onSearchChange={onSearchChange}
|
||||
popoverContainer={SuggestionsContainer}
|
||||
onAddMention={onAddMention}
|
||||
entryComponent={SuggestionRow}
|
||||
/>
|
||||
<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.
|
||||
visibility: open ? "visible" : "hidden",
|
||||
}}
|
||||
>
|
||||
<MentionSuggestions
|
||||
open
|
||||
onOpenChange={setOpen}
|
||||
suggestions={suggestions}
|
||||
onSearchChange={onSearchChange}
|
||||
popoverContainer={triggerToSuggestionsContainer[currentTrigger]}
|
||||
onAddMention={onAddMention}
|
||||
entryComponent={SuggestionRow}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,22 +0,0 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
|
||||
export const AddNewBrainButton = (): JSX.Element => {
|
||||
const { t } = useTranslation(["chat"]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={"/brains-management"}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<Button className="px-5 py-2 text-sm" variant={"tertiary"}>
|
||||
{t("new_brain")}
|
||||
</Button>
|
||||
</Link>
|
||||
);
|
||||
};
|
@ -0,0 +1,17 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { MdAdd } from "react-icons/md";
|
||||
|
||||
export const AddNewPromptButton = (): JSX.Element => {
|
||||
const { t } = useTranslation(["chat"]);
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={"/brains-management"}
|
||||
className="flex px-5 py-3 text-sm decoration-none text-center w-full justify-between items-center"
|
||||
>
|
||||
{t("new_prompt")}
|
||||
<MdAdd />
|
||||
</Link>
|
||||
);
|
||||
};
|
@ -1,23 +1,8 @@
|
||||
type BrainSuggestionProps = {
|
||||
content: string;
|
||||
id: string;
|
||||
};
|
||||
export const BrainSuggestion = ({
|
||||
content,
|
||||
id,
|
||||
}: BrainSuggestionProps): JSX.Element => {
|
||||
//TODO: use this id for ShareBrain component
|
||||
console.log({ id });
|
||||
|
||||
return (
|
||||
<div className="relative flex group items-center">
|
||||
<div
|
||||
className={
|
||||
"flex flex-1 items-center gap-2 w-full text-left px-5 py-2 text-sm leading-5 text-gray-900 dark:text-gray-300 group-hover:bg-gray-100 dark:group-hover:bg-gray-700 group-focus:bg-gray-100 dark:group-focus:bg-gray-700 group-focus:outline-none transition-colors"
|
||||
}
|
||||
>
|
||||
<span className="flex-1">{content}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <span>{content}</span>;
|
||||
};
|
||||
|
@ -1,18 +1,21 @@
|
||||
import { Popover } from "@draft-js-plugins/mention";
|
||||
import { PopoverProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Popover";
|
||||
|
||||
export const SuggestionsContainer = ({
|
||||
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
||||
|
||||
export const BrainSuggestionsContainer = ({
|
||||
children,
|
||||
...popoverProps
|
||||
}: PopoverProps): JSX.Element => (
|
||||
<Popover {...popoverProps}>
|
||||
<div
|
||||
style={{
|
||||
maxWidth: "max-content",
|
||||
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}
|
||||
<AddBrainModal />
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
@ -0,0 +1,14 @@
|
||||
type PromptSuggestionProps = {
|
||||
content: string;
|
||||
};
|
||||
export const PromptSuggestion = ({
|
||||
content,
|
||||
}: PromptSuggestionProps): JSX.Element => {
|
||||
return (
|
||||
<div className="flex flex-1 flex-row gap-2 w-full text-left px-5 py-2 text-sm text-gray-900 dark:text-gray-300">
|
||||
<div className="flex flex-1">
|
||||
<span>{content}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
import { Popover } from "@draft-js-plugins/mention";
|
||||
import { PopoverProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Popover";
|
||||
|
||||
import { AddNewPromptButton } from "./AddNewPromptButton";
|
||||
|
||||
export const PromptSuggestionsContainer = ({
|
||||
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}
|
||||
<AddNewPromptButton />
|
||||
</div>
|
||||
</Popover>
|
||||
);
|
@ -1,14 +1,32 @@
|
||||
import { EntryComponentProps } from "@draft-js-plugins/mention/lib/MentionSuggestions/Entry/Entry";
|
||||
import { UUID } from "crypto";
|
||||
|
||||
import { MentionTriggerType } from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
import { ShareBrain } from "@/lib/components/ShareBrain";
|
||||
|
||||
import { BrainSuggestion } from "./BrainSuggestion";
|
||||
import { PromptSuggestion } from "./PromptSuggestion";
|
||||
|
||||
export const SuggestionRow = ({
|
||||
mention,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
className,
|
||||
...otherProps
|
||||
}: EntryComponentProps): JSX.Element => (
|
||||
<div {...otherProps}>
|
||||
<BrainSuggestion id={mention.id as string} content={mention.name} />
|
||||
</div>
|
||||
);
|
||||
}: EntryComponentProps): JSX.Element => {
|
||||
if ((mention.trigger as MentionTriggerType) === "@") {
|
||||
return (
|
||||
<div {...otherProps}>
|
||||
<div className="relative flex group px-4">
|
||||
<BrainSuggestion content={mention.name} />
|
||||
<div className="absolute right-0 flex flex-row">
|
||||
<ShareBrain brainId={mention.id as UUID} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...otherProps}>
|
||||
<PromptSuggestion content={mention.name} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1 +1,5 @@
|
||||
export * from "./AddNewBrainButton";
|
||||
export * from "./AddNewPromptButton";
|
||||
export * from "./BrainSuggestion";
|
||||
export * from "./BrainSuggestionsContainer";
|
||||
export * from "./PromptSuggestion";
|
||||
export * from "./SuggestionRow";
|
||||
|
@ -1,31 +1,13 @@
|
||||
import createMentionPlugin from "@draft-js-plugins/mention";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { BrainMentionItem } from "../../../BrainMentionItem";
|
||||
|
||||
interface MentionPluginProps {
|
||||
removeMention: (entityKeyToRemove: string) => void;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useMentionPlugin = (props: MentionPluginProps) => {
|
||||
const { removeMention } = props;
|
||||
const { setCurrentBrainId } = useBrainContext();
|
||||
|
||||
export const useMentionPlugin = () => {
|
||||
const { MentionSuggestions, plugins } = useMemo(() => {
|
||||
const mentionPlugin = createMentionPlugin({
|
||||
mentionComponent: ({ entityKey, mention: { name } }) => (
|
||||
<BrainMentionItem
|
||||
text={name}
|
||||
onRemove={() => {
|
||||
setCurrentBrainId(null);
|
||||
removeMention(entityKey);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
mentionComponent: () => <span />,
|
||||
|
||||
mentionTrigger: ["@", "#"],
|
||||
popperOptions: {
|
||||
placement: "top-end",
|
||||
modifiers: [
|
||||
|
@ -1,90 +1,41 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
import { EditorState } from "draft-js";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { MentionTriggerType } from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { MentionInputMentionsType, TriggerMap } from "../../../../types";
|
||||
import { MentionInputMentionsType } from "../../../../types";
|
||||
import { mapMinimalBrainToMentionData } from "../../utils/mapMinimalBrainToMentionData";
|
||||
import { mapPromptToMentionData } from "../../utils/mapPromptToMentionData";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useMentionState = () => {
|
||||
const { allBrains } = useBrainContext();
|
||||
const [editorState, legacySetEditorState] = useState(() =>
|
||||
EditorState.createEmpty()
|
||||
);
|
||||
const { allBrains, publicPrompts } = useBrainContext();
|
||||
|
||||
const [editorState, setEditorState] = useState(EditorState.createEmpty());
|
||||
|
||||
const [mentionItems, setMentionItems] = useState<MentionInputMentionsType>({
|
||||
"@": allBrains.map((brain) => ({ ...brain, value: brain.name })),
|
||||
"@": allBrains.map(mapMinimalBrainToMentionData),
|
||||
"#": publicPrompts.map(mapPromptToMentionData),
|
||||
});
|
||||
|
||||
const [suggestions, setSuggestions] = useState(
|
||||
mapMinimalBrainToMentionData(mentionItems["@"])
|
||||
);
|
||||
|
||||
const setEditorState = (newState: EditorState) => {
|
||||
const currentSelection = newState.getSelection();
|
||||
const stateWithContentAndSelection = EditorState.forceSelection(
|
||||
newState,
|
||||
currentSelection
|
||||
);
|
||||
|
||||
legacySetEditorState(stateWithContentAndSelection);
|
||||
};
|
||||
|
||||
const getEditorCurrentMentions = (): TriggerMap[] => {
|
||||
const contentState = editorState.getCurrentContent();
|
||||
const plainText = contentState.getPlainText();
|
||||
const mentionTriggers = Object.keys(mentionItems);
|
||||
|
||||
const mentionTexts: TriggerMap[] = [];
|
||||
|
||||
mentionTriggers.forEach((trigger) => {
|
||||
if (trigger === "@") {
|
||||
mentionItems["@"].forEach((item) => {
|
||||
const mentionText = `${trigger}${item.name}`;
|
||||
if (plainText.includes(mentionText)) {
|
||||
mentionTexts.push({
|
||||
trigger: trigger as MentionTriggerType,
|
||||
content: item.name,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return mentionTexts;
|
||||
};
|
||||
|
||||
const getEditorTextWithoutMentions = (
|
||||
editorCurrentState: EditorState
|
||||
): string => {
|
||||
const contentState = editorCurrentState.getCurrentContent();
|
||||
let plainText = contentState.getPlainText();
|
||||
Object.keys(mentionItems).forEach((trigger) => {
|
||||
if (trigger === "@") {
|
||||
mentionItems[trigger].forEach((item) => {
|
||||
const regex = new RegExp(`${trigger}${item.name}`, "g");
|
||||
plainText = plainText.replace(regex, "");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return plainText;
|
||||
};
|
||||
const [suggestions, setSuggestions] = useState<MentionData[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setMentionItems({
|
||||
...mentionItems,
|
||||
"@": [
|
||||
...allBrains.map((brain) => ({
|
||||
...brain,
|
||||
value: brain.name,
|
||||
})),
|
||||
],
|
||||
"@": allBrains.map(mapMinimalBrainToMentionData),
|
||||
});
|
||||
}, [allBrains]);
|
||||
|
||||
useEffect(() => {
|
||||
setMentionItems({
|
||||
...mentionItems,
|
||||
"#": publicPrompts.map(mapPromptToMentionData),
|
||||
});
|
||||
}, [publicPrompts]);
|
||||
|
||||
return {
|
||||
editorState,
|
||||
setEditorState,
|
||||
@ -92,7 +43,6 @@ export const useMentionState = () => {
|
||||
setSuggestions,
|
||||
setMentionItems,
|
||||
suggestions,
|
||||
getEditorCurrentMentions,
|
||||
getEditorTextWithoutMentions,
|
||||
publicPrompts,
|
||||
};
|
||||
};
|
||||
|
@ -1,8 +1,4 @@
|
||||
import { addMention, MentionData } from "@draft-js-plugins/mention";
|
||||
import { EditorState } from "draft-js";
|
||||
|
||||
import { MentionTriggerType } from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { EditorState, Modifier } from "draft-js";
|
||||
|
||||
type MentionUtilsProps = {
|
||||
editorState: EditorState;
|
||||
@ -12,49 +8,24 @@ type MentionUtilsProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useMentionUtils = (props: MentionUtilsProps) => {
|
||||
const { editorState, setEditorState } = props;
|
||||
const { setCurrentBrainId } = useBrainContext();
|
||||
|
||||
const removeMention = (entityKeyToRemove: string): void => {
|
||||
const contentState = editorState.getCurrentContent();
|
||||
const entity = contentState.getEntity(entityKeyToRemove);
|
||||
|
||||
if (entity.getType() === "mention") {
|
||||
const newContentState = contentState.replaceEntityData(
|
||||
entityKeyToRemove,
|
||||
{}
|
||||
);
|
||||
|
||||
const newEditorState = EditorState.push(
|
||||
editorState,
|
||||
newContentState,
|
||||
"apply-entity"
|
||||
);
|
||||
|
||||
setEditorState(newEditorState);
|
||||
setCurrentBrainId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const insertMention = (
|
||||
mention: MentionData,
|
||||
trigger: MentionTriggerType,
|
||||
customEditorState?: EditorState
|
||||
): EditorState => {
|
||||
const editorStateWithMention = addMention(
|
||||
customEditorState ?? editorState,
|
||||
mention,
|
||||
trigger,
|
||||
trigger,
|
||||
"MUTABLE"
|
||||
const removeEntity = (entityKeyToRemove: string): void => {
|
||||
const contentState = Modifier.replaceText(
|
||||
editorState.getCurrentContent(),
|
||||
editorState.getSelection(),
|
||||
"",
|
||||
editorState.getCurrentInlineStyle(),
|
||||
entityKeyToRemove
|
||||
);
|
||||
|
||||
setEditorState(editorStateWithMention);
|
||||
const newEditorState = EditorState.set(editorState, {
|
||||
currentContent: contentState,
|
||||
});
|
||||
|
||||
return editorStateWithMention;
|
||||
setEditorState(newEditorState);
|
||||
};
|
||||
|
||||
return {
|
||||
removeMention,
|
||||
insertMention,
|
||||
removeEntity,
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,21 @@
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
import { EditorState } from "draft-js";
|
||||
|
||||
import { isMention } from "../../utils/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;
|
||||
};
|
@ -0,0 +1,4 @@
|
||||
import { EditorState } from "draft-js";
|
||||
|
||||
export const getEditorText = (editorState: EditorState): string =>
|
||||
editorState.getCurrentContent().getPlainText();
|
@ -8,15 +8,19 @@ import { UUID } from "crypto";
|
||||
import { EditorState, getDefaultKeyBinding } from "draft-js";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import {
|
||||
mentionTriggers,
|
||||
MentionTriggerType,
|
||||
} from "@/app/chat/[chatId]/components/ActionsBar/types";
|
||||
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 { useMentionUtils } from "./helpers/MentionUtils";
|
||||
import { mapMinimalBrainToMentionData } from "../utils/mapMinimalBrainToMentionData";
|
||||
|
||||
import "@draft-js-plugins/mention/lib/plugin.css";
|
||||
import "draft-js/dist/Draft.css";
|
||||
import { getEditorText } from "./helpers/getEditorText";
|
||||
|
||||
type UseMentionInputProps = {
|
||||
message: string;
|
||||
@ -30,95 +34,106 @@ export const useMentionInput = ({
|
||||
onSubmit,
|
||||
setMessage,
|
||||
}: UseMentionInputProps) => {
|
||||
const { allBrains, currentBrainId, setCurrentBrainId } = useBrainContext();
|
||||
const {
|
||||
currentBrainId,
|
||||
currentPromptId,
|
||||
setCurrentBrainId,
|
||||
setCurrentPromptId,
|
||||
} = useBrainContext();
|
||||
|
||||
const {
|
||||
editorState,
|
||||
setEditorState,
|
||||
setMentionItems,
|
||||
mentionItems,
|
||||
setSuggestions,
|
||||
suggestions,
|
||||
getEditorCurrentMentions,
|
||||
getEditorTextWithoutMentions,
|
||||
publicPrompts,
|
||||
} = useMentionState();
|
||||
|
||||
const { removeMention, insertMention } = useMentionUtils({
|
||||
const { removeEntity } = useMentionUtils({
|
||||
editorState,
|
||||
setEditorState,
|
||||
});
|
||||
|
||||
const { MentionSuggestions, plugins } = useMentionPlugin({
|
||||
removeMention,
|
||||
});
|
||||
const [currentTrigger, setCurrentTrigger] = useState<MentionTriggerType>("@");
|
||||
|
||||
const { MentionSuggestions, plugins } = useMentionPlugin();
|
||||
|
||||
const mentionInputRef = useRef<Editor>(null);
|
||||
|
||||
const [selectedBrainAddedOnload, setSelectedBrainAddedOnload] =
|
||||
useState(false);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const onOpenChange = useCallback((_open: boolean) => {
|
||||
setOpen(_open);
|
||||
}, []);
|
||||
|
||||
const onAddMention = (mention: MentionData) => {
|
||||
setCurrentBrainId(mention.id as UUID);
|
||||
if (mention.trigger === "#") {
|
||||
setCurrentPromptId(mention.id as UUID);
|
||||
}
|
||||
|
||||
if (mention.trigger === "@") {
|
||||
setCurrentBrainId(mention.id as UUID);
|
||||
}
|
||||
|
||||
const lastEntityKey = editorState
|
||||
.getCurrentContent()
|
||||
.getLastCreatedEntityKey();
|
||||
removeEntity(lastEntityKey);
|
||||
};
|
||||
|
||||
const onSearchChange = ({
|
||||
trigger,
|
||||
value,
|
||||
}: {
|
||||
trigger: string;
|
||||
trigger: MentionTriggerType;
|
||||
value: string;
|
||||
}) => {
|
||||
if (currentBrainId !== null) {
|
||||
setSuggestions([]);
|
||||
setCurrentTrigger(trigger);
|
||||
if (trigger === "@") {
|
||||
if (currentBrainId !== null) {
|
||||
setSuggestions([]);
|
||||
|
||||
return;
|
||||
}
|
||||
setSuggestions(
|
||||
defaultSuggestionsFilter(
|
||||
value,
|
||||
mapMinimalBrainToMentionData(mentionItems["@"]),
|
||||
trigger
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const insertCurrentBrainAsMention = (): void => {
|
||||
const mention = mentionItems["@"].find(
|
||||
(item) => item.id === currentBrainId
|
||||
);
|
||||
|
||||
if (mention !== undefined) {
|
||||
insertMention(mention, "@");
|
||||
}
|
||||
};
|
||||
|
||||
const resetEditorContent = () => {
|
||||
const currentMentions = getEditorCurrentMentions();
|
||||
let newEditorState = EditorState.createEmpty();
|
||||
currentMentions.forEach((mention) => {
|
||||
if (mention.trigger === "@") {
|
||||
const correspondingMention = mentionItems["@"].find(
|
||||
(item) => item.name === mention.content
|
||||
);
|
||||
if (correspondingMention !== undefined) {
|
||||
newEditorState = insertMention(
|
||||
correspondingMention,
|
||||
mention.trigger,
|
||||
newEditorState
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
setEditorState(newEditorState);
|
||||
|
||||
if (value === "") {
|
||||
setSuggestions(mentionItems["@"]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (trigger === "#") {
|
||||
if (currentPromptId !== null) {
|
||||
setSuggestions([]);
|
||||
|
||||
return;
|
||||
}
|
||||
if (value === "") {
|
||||
setSuggestions(mentionItems["#"]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setSuggestions(defaultSuggestionsFilter(value, mentionItems, trigger));
|
||||
};
|
||||
|
||||
const resetEditorContent = useCallback(() => {
|
||||
setEditorState(EditorState.createEmpty());
|
||||
}, [setEditorState]);
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
const keyBindingFn = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (mentionTriggers.includes(e.key as MentionTriggerType)) {
|
||||
setOpen(true);
|
||||
|
||||
return getDefaultKeyBinding(e);
|
||||
}
|
||||
|
||||
if (
|
||||
(e.key === "Backspace" || e.key === "Delete") &&
|
||||
getEditorText(editorState) === ""
|
||||
) {
|
||||
return "backspace";
|
||||
}
|
||||
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
onSubmit();
|
||||
|
||||
@ -134,67 +149,35 @@ export const useMentionInput = ({
|
||||
|
||||
const handleEditorChange = (newEditorState: EditorState) => {
|
||||
setEditorState(newEditorState);
|
||||
const currentMessage = getEditorTextWithoutMentions(newEditorState);
|
||||
setMessage(currentMessage);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const currentMessage = getEditorText(editorState);
|
||||
|
||||
if (currentMessage !== "") {
|
||||
setMessage(currentMessage);
|
||||
}
|
||||
}, [editorState, setMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (message === "") {
|
||||
resetEditorContent();
|
||||
}
|
||||
}, [message]);
|
||||
|
||||
useEffect(() => {
|
||||
setSuggestions(mapMinimalBrainToMentionData(mentionItems["@"]));
|
||||
}, [mentionItems]);
|
||||
|
||||
useEffect(() => {
|
||||
setMentionItems({
|
||||
...mentionItems,
|
||||
"@": [
|
||||
...allBrains.map((brain) => ({
|
||||
...brain,
|
||||
value: brain.name,
|
||||
})),
|
||||
],
|
||||
});
|
||||
}, [allBrains]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
selectedBrainAddedOnload ||
|
||||
mentionItems["@"].length === 0 ||
|
||||
currentBrainId === null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
insertCurrentBrainAsMention();
|
||||
|
||||
setSelectedBrainAddedOnload(true);
|
||||
}, [currentBrainId, mentionItems]);
|
||||
|
||||
useEffect(() => {
|
||||
const contentState = editorState.getCurrentContent();
|
||||
const plainText = contentState.getPlainText();
|
||||
|
||||
if (plainText === "" && currentBrainId !== null) {
|
||||
insertCurrentBrainAsMention();
|
||||
}
|
||||
}, [editorState]);
|
||||
}, [message, resetEditorContent]);
|
||||
|
||||
return {
|
||||
mentionInputRef,
|
||||
plugins,
|
||||
MentionSuggestions,
|
||||
onOpenChange,
|
||||
onSearchChange,
|
||||
open,
|
||||
suggestions,
|
||||
onAddMention,
|
||||
editorState,
|
||||
insertCurrentBrainAsMention,
|
||||
handleEditorChange,
|
||||
keyBindingFn,
|
||||
publicPrompts,
|
||||
currentTrigger,
|
||||
setOpen,
|
||||
};
|
||||
};
|
||||
|
@ -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);
|
@ -3,9 +3,9 @@ import { MentionData } from "@draft-js-plugins/mention";
|
||||
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
|
||||
|
||||
export const mapMinimalBrainToMentionData = (
|
||||
brains: MinimalBrainForUser[]
|
||||
): MentionData[] =>
|
||||
brains.map((brain) => ({
|
||||
name: brain.name,
|
||||
id: brain.id as string,
|
||||
}));
|
||||
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: "#",
|
||||
});
|
@ -1,18 +1,22 @@
|
||||
import { MdRemoveCircleOutline } from "react-icons/md";
|
||||
|
||||
import { MentionTriggerType } from "../../../../../types";
|
||||
|
||||
type MentionItemProps = {
|
||||
text: string;
|
||||
onRemove: () => void;
|
||||
trigger?: MentionTriggerType;
|
||||
};
|
||||
|
||||
export const BrainMentionItem = ({
|
||||
export const MentionItem = ({
|
||||
text,
|
||||
onRemove,
|
||||
trigger,
|
||||
}: MentionItemProps): JSX.Element => {
|
||||
return (
|
||||
<div className="relative inline-block w-fit-content">
|
||||
<div className="flex items-center bg-gray-300 mr-2 text-gray-600 rounded-2xl py-1 px-2">
|
||||
<span className="flex-grow">@{text}</span>
|
||||
<span className="flex-grow">{`${trigger ?? ""}${text}`}</span>
|
||||
<MdRemoveCircleOutline
|
||||
className="cursor-pointer absolute top-[-10px] right-[5px]"
|
||||
onClick={onRemove}
|
@ -1,10 +0,0 @@
|
||||
import { useFeature } from "@growthbook/growthbook-react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export const useChatBar = () => {
|
||||
const shouldUseNewUX = useFeature("new-ux").on;
|
||||
|
||||
return {
|
||||
shouldUseNewUX,
|
||||
};
|
||||
};
|
@ -1,11 +1,8 @@
|
||||
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
|
||||
import { MentionData } from "@draft-js-plugins/mention";
|
||||
|
||||
import { MentionTriggerType } from "../../../../types";
|
||||
|
||||
export type MentionInputMentionsType = {
|
||||
"@": MinimalBrainForUser[];
|
||||
};
|
||||
export type TriggerMap = {
|
||||
trigger: MentionTriggerType;
|
||||
content: string;
|
||||
};
|
||||
export type MentionInputMentionsType = Record<
|
||||
MentionTriggerType,
|
||||
MentionData[]
|
||||
>;
|
||||
|
@ -1,3 +1,3 @@
|
||||
export type MentionType = { id: string; display: string };
|
||||
export const mentionTriggers = ["@", "#"] as const;
|
||||
|
||||
export type MentionTriggerType = "@" | "#";
|
||||
export type MentionTriggerType = (typeof mentionTriggers)[number];
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { useFeature } from "@growthbook/growthbook-react";
|
||||
import React from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
|
||||
@ -16,8 +15,6 @@ export const ChatMessage = React.forwardRef(
|
||||
{ speaker, text, brainName, promptName }: ChatMessageProps,
|
||||
ref: React.Ref<HTMLDivElement>
|
||||
) => {
|
||||
const isNewUxOn = useFeature("new-ux").on;
|
||||
|
||||
const isUserSpeaker = speaker === "user";
|
||||
const containerClasses = cn(
|
||||
"py-3 px-5 w-fit ",
|
||||
@ -39,14 +36,14 @@ export const ChatMessage = React.forwardRef(
|
||||
<div className={containerWrapperClasses}>
|
||||
{" "}
|
||||
<div ref={ref} className={containerClasses}>
|
||||
{isNewUxOn && (
|
||||
<div className="w-full">
|
||||
<span
|
||||
data-testid="brain-prompt-tags"
|
||||
className="text-gray-400 mb-1"
|
||||
className="text-gray-400 mb-1 text-xs"
|
||||
>
|
||||
@{brainName ?? "-"} #{promptName ?? "-"}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div data-testid="chat-message-text">
|
||||
<ReactMarkdown className={markdownClasses}>{text}</ReactMarkdown>
|
||||
</div>
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { useFeature } from "@growthbook/growthbook-react";
|
||||
|
||||
import { useChatContext } from "@/lib/context";
|
||||
|
||||
import { ChatMessages } from "./ChatMessages";
|
||||
@ -8,8 +6,7 @@ import { ShortCuts } from "./ShortCuts";
|
||||
export const ChatDialog = (): JSX.Element => {
|
||||
const { history } = useChatContext();
|
||||
|
||||
const shouldDisplayShortcuts =
|
||||
useFeature("new-ux").on && history.length === 0;
|
||||
const shouldDisplayShortcuts = history.length === 0;
|
||||
|
||||
if (!shouldDisplayShortcuts) {
|
||||
return <ChatMessages />;
|
||||
|
@ -8,26 +8,23 @@ export const ShortCuts = (): JSX.Element => {
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
content: [
|
||||
t("shortcut_select_brain"),
|
||||
t("shortcut_select_file"),
|
||||
t("shortcut_choose_prompt"),
|
||||
],
|
||||
},
|
||||
{
|
||||
content: [
|
||||
t("shortcut_create_brain"),
|
||||
t("shortcut_feed_brain"),
|
||||
t("shortcut_create_prompt"),
|
||||
],
|
||||
},
|
||||
{
|
||||
content: [
|
||||
t("shortcut_manage_brains"),
|
||||
t("shortcut_go_to_user_page"),
|
||||
t("shortcut_go_to_shortcuts"),
|
||||
],
|
||||
content: [t("shortcut_select_brain"), t("shortcut_choose_prompt")],
|
||||
},
|
||||
// {
|
||||
// content: [
|
||||
// t("shortcut_select_file"),
|
||||
// t("shortcut_create_brain"),
|
||||
// t("shortcut_feed_brain"),
|
||||
// t("shortcut_create_prompt"),
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// content: [
|
||||
// t("shortcut_manage_brains"),
|
||||
// t("shortcut_go_to_user_page"),
|
||||
// t("shortcut_go_to_shortcuts"),
|
||||
// ],
|
||||
// },
|
||||
];
|
||||
|
||||
return (
|
||||
|
@ -24,7 +24,7 @@ export const useChat = () => {
|
||||
const [generatingAnswer, setGeneratingAnswer] = useState(false);
|
||||
|
||||
const { history } = useChatContext();
|
||||
const { currentBrain } = useBrainContext();
|
||||
const { currentBrain, currentPromptId } = useBrainContext();
|
||||
const { publish } = useToast();
|
||||
const { createChat } = useChatApi();
|
||||
|
||||
@ -64,6 +64,7 @@ export const useChat = () => {
|
||||
temperature: chatConfig?.temperature,
|
||||
max_tokens: chatConfig?.maxTokens,
|
||||
brain_id: currentBrain?.id,
|
||||
prompt_id: currentPromptId ?? undefined,
|
||||
};
|
||||
|
||||
await addStreamQuestion(currentChatId, chatQuestion);
|
||||
|
@ -35,7 +35,11 @@ export const AddBrainModal = (): JSX.Element => {
|
||||
return (
|
||||
<Modal
|
||||
Trigger={
|
||||
<Button variant={"secondary"}>
|
||||
<Button
|
||||
onClick={() => void 0}
|
||||
variant={"tertiary"}
|
||||
className="border-0"
|
||||
>
|
||||
{t("newBrain", { ns: "brain" })}
|
||||
<MdAdd className="text-xl" />
|
||||
</Button>
|
||||
|
@ -2,20 +2,15 @@ import { ShareBrain } from "@/lib/components/ShareBrain";
|
||||
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
|
||||
|
||||
import { DeleteBrain } from "./components";
|
||||
import { BrainRoleType } from "./types";
|
||||
|
||||
type BrainActionsProps = {
|
||||
brain: MinimalBrainForUser;
|
||||
};
|
||||
|
||||
const requiredAccessToShareBrain: BrainRoleType[] = ["Owner", "Editor"];
|
||||
|
||||
export const BrainActions = ({ brain }: BrainActionsProps): JSX.Element => {
|
||||
return (
|
||||
<div className="absolute right-0 flex flex-row">
|
||||
{requiredAccessToShareBrain.includes(brain.role) && (
|
||||
<ShareBrain brainId={brain.id} name={brain.name} />
|
||||
)}
|
||||
<ShareBrain brainId={brain.id} />
|
||||
<DeleteBrain brainId={brain.id} />
|
||||
</div>
|
||||
);
|
||||
|
@ -2,6 +2,7 @@
|
||||
"use client";
|
||||
|
||||
import { UUID } from "crypto";
|
||||
import { Fragment } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ImUserPlus } from "react-icons/im";
|
||||
import { MdContentPaste, MdShare } from "react-icons/md";
|
||||
@ -10,17 +11,17 @@ import { BrainUsers } from "@/lib/components/BrainUsers/BrainUsers";
|
||||
import { UserToInvite } from "@/lib/components/UserToInvite";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Modal } from "@/lib/components/ui/Modal";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { useShareBrain } from "@/lib/hooks/useShareBrain";
|
||||
|
||||
import { BrainRoleType } from "../NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
|
||||
type ShareBrainModalProps = {
|
||||
brainId: UUID;
|
||||
name: string;
|
||||
};
|
||||
const requiredAccessToShareBrain: BrainRoleType[] = ["Owner", "Editor"];
|
||||
|
||||
export const ShareBrain = ({
|
||||
brainId,
|
||||
name,
|
||||
}: ShareBrainModalProps): JSX.Element => {
|
||||
export const ShareBrain = ({ brainId }: ShareBrainModalProps): JSX.Element => {
|
||||
const {
|
||||
roleAssignations,
|
||||
brainShareLink,
|
||||
@ -34,8 +35,19 @@ export const ShareBrain = ({
|
||||
isShareModalOpen,
|
||||
canAddNewRow,
|
||||
} = useShareBrain(brainId);
|
||||
|
||||
const { t } = useTranslation(["translation", "brain"]);
|
||||
|
||||
const { allBrains } = useBrainContext();
|
||||
const correspondingBrain = allBrains.find((brain) => brain.id === brainId);
|
||||
|
||||
if (
|
||||
correspondingBrain === undefined ||
|
||||
!requiredAccessToShareBrain.includes(correspondingBrain.role)
|
||||
) {
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
Trigger={
|
||||
@ -49,7 +61,7 @@ export const ShareBrain = ({
|
||||
</Button>
|
||||
}
|
||||
CloseTrigger={<div />}
|
||||
title={t("shareBrain", { name, ns: "brain" })}
|
||||
title={t("shareBrain", { name: correspondingBrain.name, ns: "brain" })}
|
||||
isOpen={isShareModalOpen}
|
||||
setOpen={setIsShareModalOpen}
|
||||
>
|
||||
|
@ -38,6 +38,13 @@ vi.mock("@/lib/context/BrainProvider/hooks/useBrainContext", async () => {
|
||||
...actual,
|
||||
useBrainContext: () => ({
|
||||
...actual.useBrainContext(),
|
||||
allBrains: [
|
||||
{
|
||||
id: "cf9bb422-b1b6-4fd7-abc1-01bd395d2318",
|
||||
name: "test",
|
||||
role: "Owner",
|
||||
},
|
||||
],
|
||||
currentBrain: {
|
||||
role: "Editor",
|
||||
},
|
||||
@ -69,10 +76,7 @@ describe("ShareBrain", () => {
|
||||
<SupabaseProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<BrainProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
<ShareBrain brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318" />
|
||||
</BrainProviderMock>
|
||||
</BrainConfigProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
@ -87,10 +91,7 @@ describe("ShareBrain", () => {
|
||||
<SupabaseProviderMock>
|
||||
<BrainProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
<ShareBrain brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318" />
|
||||
</BrainConfigProviderMock>
|
||||
</BrainProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
@ -105,10 +106,7 @@ describe("ShareBrain", () => {
|
||||
<SupabaseProviderMock>
|
||||
<BrainConfigProviderMock>
|
||||
<BrainProviderMock>
|
||||
<ShareBrain
|
||||
name="test"
|
||||
brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318"
|
||||
/>
|
||||
<ShareBrain brainId="cf9bb422-b1b6-4fd7-abc1-01bd395d2318" />
|
||||
</BrainProviderMock>
|
||||
</BrainConfigProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
|
@ -4,7 +4,9 @@ import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { CreateBrainInput } from "@/lib/api/brain/types";
|
||||
import { useBrainApi } from "@/lib/api/brain/useBrainApi";
|
||||
import { usePromptApi } from "@/lib/api/prompt/usePromptApi";
|
||||
import { useToast } from "@/lib/hooks";
|
||||
import { Prompt } from "@/lib/types/Prompt";
|
||||
import { useEventTracking } from "@/services/analytics/useEventTracking";
|
||||
|
||||
import {
|
||||
@ -21,12 +23,18 @@ export const useBrainProvider = () => {
|
||||
const { track } = useEventTracking();
|
||||
const { createBrain, deleteBrain, getBrains, getDefaultBrain } =
|
||||
useBrainApi();
|
||||
const { getPublicPrompts } = usePromptApi();
|
||||
|
||||
const [allBrains, setAllBrains] = useState<MinimalBrainForUser[]>([]);
|
||||
const [currentBrainId, setCurrentBrainId] = useState<null | UUID>(null);
|
||||
const [defaultBrainId, setDefaultBrainId] = useState<UUID>();
|
||||
const [isFetchingBrains, setIsFetchingBrains] = useState(false);
|
||||
const [publicPrompts, setPublicPrompts] = useState<Prompt[]>([]);
|
||||
const [currentPromptId, setCurrentPromptId] = useState<null | string>(null);
|
||||
|
||||
const currentPrompt = publicPrompts.find(
|
||||
(prompt) => prompt.id === currentPromptId
|
||||
);
|
||||
const currentBrain = allBrains.find((brain) => brain.id === currentBrainId);
|
||||
const createBrainHandler = async (
|
||||
brain: CreateBrainInput
|
||||
@ -100,6 +108,11 @@ export const useBrainProvider = () => {
|
||||
const fetchDefaultBrain = async () => {
|
||||
setDefaultBrainId((await getDefaultBrain())?.id);
|
||||
};
|
||||
|
||||
const fetchPublicPrompts = async () => {
|
||||
setPublicPrompts(await getPublicPrompts());
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
void fetchDefaultBrain();
|
||||
}, []);
|
||||
@ -118,5 +131,10 @@ export const useBrainProvider = () => {
|
||||
isFetchingBrains,
|
||||
defaultBrainId,
|
||||
fetchDefaultBrain,
|
||||
fetchPublicPrompts,
|
||||
publicPrompts,
|
||||
currentPrompt,
|
||||
setCurrentPromptId,
|
||||
currentPromptId,
|
||||
};
|
||||
};
|
||||
|
@ -13,6 +13,7 @@ export const BrainProviderMock = ({
|
||||
<BrainContextMock.Provider
|
||||
value={{
|
||||
allBrains: [],
|
||||
publicPrompts:[],
|
||||
currentBrain: undefined,
|
||||
//@ts-ignore we are not using the functions in tests
|
||||
createBrain: () => void 0,
|
||||
|
@ -33,5 +33,5 @@
|
||||
"empty_brain_title_suffix": "and chat with them",
|
||||
"actions_bar_placeholder": "Ask a question to @brains or /files and choose your #prompt",
|
||||
"missing_brain": "Please select a brain to chat with",
|
||||
"new_brain": "Create new brain"
|
||||
"new_prompt": "Create new prompt"
|
||||
}
|
||||
|
@ -34,5 +34,5 @@
|
||||
"thinking": "Pensando...",
|
||||
"title": "Conversa con {{brain}}",
|
||||
"missing_brain": "No hay cerebro seleccionado",
|
||||
"new_brain": "Crear nuevo cerebro"
|
||||
"new_prompt": "Crear nueva instrucción"
|
||||
}
|
||||
|
@ -34,5 +34,5 @@
|
||||
"thinking": "Réflexion...",
|
||||
"title": "Discuter avec {{brain}}",
|
||||
"missing_brain": "Veuillez selectionner un cerveau pour discuter",
|
||||
"new_brain": "Créer un nouveau cerveau"
|
||||
"new_prompt": "Créer un nouveau prompt"
|
||||
}
|
||||
|
@ -34,5 +34,5 @@
|
||||
"thinking": "Pensando...",
|
||||
"title": "Converse com {{brain}}",
|
||||
"missing_brain": "Cérebro não encontrado",
|
||||
"new_brain": "Criar novo cérebro"
|
||||
"new_prompt": "Criar novo prompt"
|
||||
}
|
||||
|
@ -34,5 +34,5 @@
|
||||
"thinking": "Думаю...",
|
||||
"title": "Чат с {{brain}}",
|
||||
"missing_brain": "Мозг не найден",
|
||||
"new_brain": "Создать новый мозг"
|
||||
"new_prompt": "Создать новый запрос"
|
||||
}
|
||||
|
@ -34,5 +34,5 @@
|
||||
"empty_brain_title_suffix": "和他们聊天",
|
||||
"actions_bar_placeholder": "向 @brains 或 /files 提问,并选择您的 #prompt",
|
||||
"missing_brain": "请选择一个大脑进行聊天",
|
||||
"new_brain": "新大脑"
|
||||
"new_prompt": "新提示"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user