Skip to content

Commit

Permalink
feat: add prompt trigger through # (#1023)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mamadoudicko authored Aug 29, 2023
1 parent 619f5bc commit 072d97a
Show file tree
Hide file tree
Showing 40 changed files with 419 additions and 386 deletions.
6 changes: 4 additions & 2 deletions frontend/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
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 = {
onSubmit: () => void;
setMessage: (text: string) => void;
message: string;
};

const triggerToSuggestionsContainer: Record<
MentionTriggerType,
ComponentType<PopoverProps>
> = {
"@": BrainSuggestionsContainer,
"#": PromptSuggestionsContainer,
};

export const MentionInput = ({
onSubmit,
setMessage,
Expand All @@ -24,13 +37,14 @@ export const MentionInput = ({
MentionSuggestions,
keyBindingFn,
editorState,
onOpenChange,
setOpen,
onSearchChange,
open,
plugins,
suggestions,
onAddMention,
handleEditorChange,
currentTrigger,
} = useMentionInput({
message,
onSubmit,
Expand All @@ -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>
);
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -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>;
};
Original file line number Diff line number Diff line change
@@ -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>
);
Original file line number Diff line number Diff line change
@@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -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>
);
Original file line number Diff line number Diff line change
@@ -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>
);
};
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from "./AddNewBrainButton";
export * from "./AddNewPromptButton";
export * from "./BrainSuggestion";
export * from "./BrainSuggestionsContainer";
export * from "./PromptSuggestion";
export * from "./SuggestionRow";
Original file line number Diff line number Diff line change
@@ -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: [
Expand Down
Loading

0 comments on commit 072d97a

Please sign in to comment.