mirror of
https://github.com/QuivrHQ/quivr.git
synced 2025-01-05 23:03:53 +03:00
feat: add onboarding first step (#1303)
* refactor: split <MessageRow /> into small components * feat: add onboarding page first step
This commit is contained in:
parent
160588cfae
commit
63e90a317c
@ -1,3 +1,5 @@
|
|||||||
|
import { useFeatureIsOn } from "@growthbook/growthbook-react";
|
||||||
|
|
||||||
import { useChatContext } from "@/lib/context";
|
import { useChatContext } from "@/lib/context";
|
||||||
|
|
||||||
import { ChatDialogue } from "./components/ChatDialogue";
|
import { ChatDialogue } from "./components/ChatDialogue";
|
||||||
@ -11,8 +13,10 @@ export const ChatDialogueArea = (): JSX.Element => {
|
|||||||
messages,
|
messages,
|
||||||
notifications
|
notifications
|
||||||
);
|
);
|
||||||
|
const shouldDisplayOnboarding = useFeatureIsOn("onboarding");
|
||||||
|
|
||||||
const shouldDisplayShortcuts = chatItems.length === 0;
|
const shouldDisplayShortcuts =
|
||||||
|
chatItems.length === 0 && !shouldDisplayOnboarding;
|
||||||
|
|
||||||
if (!shouldDisplayShortcuts) {
|
if (!shouldDisplayShortcuts) {
|
||||||
return <ChatDialogue chatItems={chatItems} />;
|
return <ChatDialogue chatItems={chatItems} />;
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { RiDownloadLine } from "react-icons/ri";
|
||||||
|
|
||||||
|
import Button from "@/lib/components/ui/Button";
|
||||||
|
|
||||||
|
import { useStreamText } from "./hooks/useStreamText";
|
||||||
|
import { MessageRow } from "../QADisplay";
|
||||||
|
|
||||||
|
export const Onboarding = (): JSX.Element => {
|
||||||
|
const { t } = useTranslation(["chat"]);
|
||||||
|
const assistantMessage = t("onboarding.step_1_message_1");
|
||||||
|
const step1Text = t("onboarding.step_1_message_2");
|
||||||
|
|
||||||
|
const { streamingText: streamingAssistantMessage, isDone: isAssistantDone } =
|
||||||
|
useStreamText(assistantMessage);
|
||||||
|
const { streamingText: streamingStep1Text, isDone: isStep1Done } =
|
||||||
|
useStreamText(step1Text, isAssistantDone);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MessageRow speaker={"assistant"} brainName={"Quivr"}>
|
||||||
|
<p>{streamingAssistantMessage}</p>
|
||||||
|
<div>
|
||||||
|
{streamingStep1Text}
|
||||||
|
{isStep1Done && isAssistantDone && (
|
||||||
|
<Link
|
||||||
|
href="/documents/doc.pdf"
|
||||||
|
download
|
||||||
|
target="_blank"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
>
|
||||||
|
<Button className="bg-black p-2 ml-2 rounded-full inline-flex">
|
||||||
|
<RiDownloadLine />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</MessageRow>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,32 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
export const useStreamText = (text: string, enabled = true) => {
|
||||||
|
const [streamingText, setStreamingText] = useState<string>("");
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
|
const isDone = currentIndex === text.length;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!enabled) {
|
||||||
|
setStreamingText("");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageInterval = setInterval(() => {
|
||||||
|
if (currentIndex < text.length) {
|
||||||
|
setStreamingText((prevText) => prevText + (text[currentIndex] ?? ""));
|
||||||
|
setCurrentIndex((prevIndex) => prevIndex + 1);
|
||||||
|
} else {
|
||||||
|
clearInterval(messageInterval);
|
||||||
|
}
|
||||||
|
}, 30);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(messageInterval);
|
||||||
|
};
|
||||||
|
}, [text, currentIndex, enabled]);
|
||||||
|
|
||||||
|
return { streamingText, isDone };
|
||||||
|
};
|
@ -1,49 +1,35 @@
|
|||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import { FaCheckCircle, FaCopy } from "react-icons/fa";
|
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
|
import { CopyButton } from "./components/CopyButton";
|
||||||
|
import { MessageContent } from "./components/MessageContent";
|
||||||
import { QuestionBrain } from "./components/QuestionBrain";
|
import { QuestionBrain } from "./components/QuestionBrain";
|
||||||
import { QuestionPrompt } from "./components/QuestionPrompt";
|
import { QuestionPrompt } from "./components/QuestionPrompt";
|
||||||
|
import { useMessageRow } from "./hooks/useMessageRow";
|
||||||
|
|
||||||
type MessageRowProps = {
|
type MessageRowProps = {
|
||||||
speaker: string;
|
speaker: "user" | "assistant";
|
||||||
text: string;
|
text?: string;
|
||||||
brainName?: string | null;
|
brainName?: string | null;
|
||||||
promptName?: string | null;
|
promptName?: string | null;
|
||||||
|
children?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MessageRow = React.forwardRef(
|
export const MessageRow = React.forwardRef(
|
||||||
(
|
(
|
||||||
{ speaker, text, brainName, promptName }: MessageRowProps,
|
{ speaker, text, brainName, promptName, children }: MessageRowProps,
|
||||||
ref: React.Ref<HTMLDivElement>
|
ref: React.Ref<HTMLDivElement>
|
||||||
) => {
|
) => {
|
||||||
const isUserSpeaker = speaker === "user";
|
const {
|
||||||
const [isCopied, setIsCopied] = useState(false);
|
containerClasses,
|
||||||
|
containerWrapperClasses,
|
||||||
const handleCopy = () => {
|
handleCopy,
|
||||||
navigator.clipboard.writeText(text).then(
|
isCopied,
|
||||||
() => setIsCopied(true),
|
isUserSpeaker,
|
||||||
(err) => console.error("Failed to copy!", err)
|
markdownClasses,
|
||||||
);
|
} = useMessageRow({
|
||||||
setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds
|
speaker,
|
||||||
};
|
text,
|
||||||
|
});
|
||||||
const containerClasses = cn(
|
|
||||||
"py-3 px-5 w-fit",
|
|
||||||
isUserSpeaker
|
|
||||||
? "bg-msg-gray bg-opacity-60 items-start"
|
|
||||||
: "bg-msg-purple bg-opacity-60 items-end",
|
|
||||||
"dark:bg-gray-800 rounded-3xl flex flex-col overflow-hidden scroll-pb-32"
|
|
||||||
);
|
|
||||||
|
|
||||||
const containerWrapperClasses = cn(
|
|
||||||
"flex flex-col",
|
|
||||||
isUserSpeaker ? "items-end" : "items-start"
|
|
||||||
);
|
|
||||||
|
|
||||||
const markdownClasses = cn("prose", "dark:prose-invert");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerWrapperClasses}>
|
<div className={containerWrapperClasses}>
|
||||||
@ -53,19 +39,16 @@ export const MessageRow = React.forwardRef(
|
|||||||
<QuestionBrain brainName={brainName} />
|
<QuestionBrain brainName={brainName} />
|
||||||
<QuestionPrompt promptName={promptName} />
|
<QuestionPrompt promptName={promptName} />
|
||||||
</div>
|
</div>
|
||||||
{!isUserSpeaker && (
|
{!isUserSpeaker && text !== undefined && (
|
||||||
<button
|
<CopyButton handleCopy={handleCopy} isCopied={isCopied} />
|
||||||
className="text-gray-500 hover:text-gray-700 transition"
|
|
||||||
onClick={handleCopy}
|
|
||||||
title={isCopied ? "Copied!" : "Copy to clipboard"}
|
|
||||||
>
|
|
||||||
{isCopied ? <FaCheckCircle /> : <FaCopy />}
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div data-testid="chat-message-text">
|
{children ?? (
|
||||||
<ReactMarkdown className={markdownClasses}>{text}</ReactMarkdown>
|
<MessageContent
|
||||||
</div>
|
text={text ?? ""}
|
||||||
|
markdownClasses={markdownClasses}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
import { FaCheckCircle, FaCopy } from "react-icons/fa";
|
||||||
|
|
||||||
|
type CopyButtonProps = {
|
||||||
|
handleCopy: () => void;
|
||||||
|
isCopied: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CopyButton = ({
|
||||||
|
handleCopy,
|
||||||
|
isCopied,
|
||||||
|
}: CopyButtonProps): JSX.Element => (
|
||||||
|
<button
|
||||||
|
className="text-gray-500 hover:text-gray-700 transition"
|
||||||
|
onClick={handleCopy}
|
||||||
|
title={isCopied ? "Copied!" : "Copy to clipboard"}
|
||||||
|
>
|
||||||
|
{isCopied ? <FaCheckCircle /> : <FaCopy />}
|
||||||
|
</button>
|
||||||
|
);
|
@ -0,0 +1,13 @@
|
|||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
|
export const MessageContent = ({
|
||||||
|
text,
|
||||||
|
markdownClasses,
|
||||||
|
}: {
|
||||||
|
text: string;
|
||||||
|
markdownClasses: string;
|
||||||
|
}): JSX.Element => (
|
||||||
|
<div data-testid="chat-message-text">
|
||||||
|
<ReactMarkdown className={markdownClasses}>{text}</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
);
|
@ -0,0 +1,47 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
type UseMessageRowProps = {
|
||||||
|
speaker: "user" | "assistant";
|
||||||
|
text?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
|
export const useMessageRow = ({ speaker, text }: UseMessageRowProps) => {
|
||||||
|
const isUserSpeaker = speaker === "user";
|
||||||
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
if (text === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigator.clipboard.writeText(text).then(
|
||||||
|
() => setIsCopied(true),
|
||||||
|
(err) => console.error("Failed to copy!", err)
|
||||||
|
);
|
||||||
|
setTimeout(() => setIsCopied(false), 2000); // Reset after 2 seconds
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerClasses = cn(
|
||||||
|
"py-3 px-5 w-fit",
|
||||||
|
isUserSpeaker ? "bg-msg-gray bg-opacity-60" : "bg-msg-purple bg-opacity-60",
|
||||||
|
"dark:bg-gray-800 rounded-3xl flex flex-col overflow-hidden scroll-pb-32"
|
||||||
|
);
|
||||||
|
|
||||||
|
const containerWrapperClasses = cn(
|
||||||
|
"flex flex-col",
|
||||||
|
isUserSpeaker ? "items-end" : "items-start"
|
||||||
|
);
|
||||||
|
|
||||||
|
const markdownClasses = cn("prose", "dark:prose-invert");
|
||||||
|
|
||||||
|
return {
|
||||||
|
isUserSpeaker,
|
||||||
|
isCopied,
|
||||||
|
handleCopy,
|
||||||
|
containerClasses,
|
||||||
|
containerWrapperClasses,
|
||||||
|
markdownClasses,
|
||||||
|
};
|
||||||
|
};
|
@ -1,7 +1,13 @@
|
|||||||
|
import { useFeatureIsOn } from "@growthbook/growthbook-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { ChatItem } from "./components";
|
import { ChatItem } from "./components";
|
||||||
|
import { Onboarding } from "./components/Onboarding/Onboarding";
|
||||||
import { useChatDialogue } from "./hooks/useChatDialogue";
|
import { useChatDialogue } from "./hooks/useChatDialogue";
|
||||||
|
import {
|
||||||
|
chatDialogueContainerClassName,
|
||||||
|
chatItemContainerClassName,
|
||||||
|
} from "./styles";
|
||||||
import { getKeyFromChatItem } from "./utils/getKeyFromChatItem";
|
import { getKeyFromChatItem } from "./utils/getKeyFromChatItem";
|
||||||
import { ChatItemWithGroupedNotifications } from "../../types";
|
import { ChatItemWithGroupedNotifications } from "../../types";
|
||||||
|
|
||||||
@ -15,17 +21,23 @@ export const ChatDialogue = ({
|
|||||||
const { t } = useTranslation(["chat"]);
|
const { t } = useTranslation(["chat"]);
|
||||||
const { chatListRef } = useChatDialogue();
|
const { chatListRef } = useChatDialogue();
|
||||||
|
|
||||||
|
const shouldDisplayOnboarding = useFeatureIsOn("onboarding");
|
||||||
|
|
||||||
|
if (shouldDisplayOnboarding) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={chatDialogueContainerClassName} ref={chatListRef}>
|
||||||
style={{
|
<Onboarding />
|
||||||
display: "flex",
|
<div className={chatItemContainerClassName}>
|
||||||
flexDirection: "column",
|
{chatItems.map((chatItem) => (
|
||||||
flex: 1,
|
<ChatItem key={getKeyFromChatItem(chatItem)} content={chatItem} />
|
||||||
overflowY: "auto",
|
))}
|
||||||
marginBottom: 10,
|
</div>
|
||||||
}}
|
</div>
|
||||||
ref={chatListRef}
|
);
|
||||||
>
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={chatDialogueContainerClassName} ref={chatListRef}>
|
||||||
{chatItems.length === 0 ? (
|
{chatItems.length === 0 ? (
|
||||||
<div
|
<div
|
||||||
data-testid="empty-history-message"
|
data-testid="empty-history-message"
|
||||||
@ -34,7 +46,7 @@ export const ChatDialogue = ({
|
|||||||
{t("ask", { ns: "chat" })}
|
{t("ask", { ns: "chat" })}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-3">
|
<div className={chatItemContainerClassName}>
|
||||||
{chatItems.map((chatItem) => (
|
{chatItems.map((chatItem) => (
|
||||||
<ChatItem key={getKeyFromChatItem(chatItem)} content={chatItem} />
|
<ChatItem key={getKeyFromChatItem(chatItem)} content={chatItem} />
|
||||||
))}
|
))}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
export const chatItemContainerClassName = "flex flex-col gap-3";
|
||||||
|
|
||||||
|
export const chatDialogueContainerClassName =
|
||||||
|
"flex flex-col flex-1 overflow-y-auto mb-10";
|
198
frontend/public/documents/doc.pdf
Normal file
198
frontend/public/documents/doc.pdf
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
%PDF-1.3
|
||||||
|
%âãÏÓ
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Catalog
|
||||||
|
/Outlines 2 0 R
|
||||||
|
/Pages 3 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Outlines
|
||||||
|
/Count 0
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Pages
|
||||||
|
/Count 2
|
||||||
|
/Kids [ 4 0 R 6 0 R ]
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Page
|
||||||
|
/Parent 3 0 R
|
||||||
|
/Resources <<
|
||||||
|
/Font <<
|
||||||
|
/F1 9 0 R
|
||||||
|
>>
|
||||||
|
/ProcSet 8 0 R
|
||||||
|
>>
|
||||||
|
/MediaBox [0 0 612.0000 792.0000]
|
||||||
|
/Contents 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Length 1074 >>
|
||||||
|
stream
|
||||||
|
2 J
|
||||||
|
BT
|
||||||
|
0 0 0 rg
|
||||||
|
/F1 0027 Tf
|
||||||
|
57.3750 722.2800 Td
|
||||||
|
( A Simple PDF File ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 688.6080 Td
|
||||||
|
( This is a small demonstration .pdf file - ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 664.7040 Td
|
||||||
|
( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 652.7520 Td
|
||||||
|
( text. And more text. And more text. And more text. ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 628.8480 Td
|
||||||
|
( And more text. And more text. And more text. And more text. And more ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 616.8960 Td
|
||||||
|
( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 604.9440 Td
|
||||||
|
( more text. And more text. And more text. And more text. And more text. ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 592.9920 Td
|
||||||
|
( And more text. And more text. ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 569.0880 Td
|
||||||
|
( And more text. And more text. And more text. And more text. And more ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 557.1360 Td
|
||||||
|
( text. And more text. And more text. Even more. Continued on page 2 ...) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Page
|
||||||
|
/Parent 3 0 R
|
||||||
|
/Resources <<
|
||||||
|
/Font <<
|
||||||
|
/F1 9 0 R
|
||||||
|
>>
|
||||||
|
/ProcSet 8 0 R
|
||||||
|
>>
|
||||||
|
/MediaBox [0 0 612.0000 792.0000]
|
||||||
|
/Contents 7 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
7 0 obj
|
||||||
|
<< /Length 676 >>
|
||||||
|
stream
|
||||||
|
2 J
|
||||||
|
BT
|
||||||
|
0 0 0 rg
|
||||||
|
/F1 0027 Tf
|
||||||
|
57.3750 722.2800 Td
|
||||||
|
( Simple PDF File 2 ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 688.6080 Td
|
||||||
|
( ...continued from page 1. Yet more text. And more text. And more text. ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 676.6560 Td
|
||||||
|
( And more text. And more text. And more text. And more text. And more ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 664.7040 Td
|
||||||
|
( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 652.7520 Td
|
||||||
|
( paint dry. And more text. And more text. And more text. And more text. ) Tj
|
||||||
|
ET
|
||||||
|
BT
|
||||||
|
/F1 0010 Tf
|
||||||
|
69.2500 640.8000 Td
|
||||||
|
( Boring. More, a little more text. The end, and just as well. ) Tj
|
||||||
|
ET
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
8 0 obj
|
||||||
|
[/PDF /Text]
|
||||||
|
endobj
|
||||||
|
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/Type /Font
|
||||||
|
/Subtype /Type1
|
||||||
|
/Name /F1
|
||||||
|
/BaseFont /Helvetica
|
||||||
|
/Encoding /WinAnsiEncoding
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/Creator (Rave \(http://www.nevrona.com/rave\))
|
||||||
|
/Producer (Nevrona Designs)
|
||||||
|
/CreationDate (D:20060301072826)
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 11
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000019 00000 n
|
||||||
|
0000000093 00000 n
|
||||||
|
0000000147 00000 n
|
||||||
|
0000000222 00000 n
|
||||||
|
0000000390 00000 n
|
||||||
|
0000001522 00000 n
|
||||||
|
0000001690 00000 n
|
||||||
|
0000002423 00000 n
|
||||||
|
0000002456 00000 n
|
||||||
|
0000002574 00000 n
|
||||||
|
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/Size 11
|
||||||
|
/Root 1 0 R
|
||||||
|
/Info 10 0 R
|
||||||
|
>>
|
||||||
|
|
||||||
|
startxref
|
||||||
|
2714
|
||||||
|
%%EOF
|
@ -36,5 +36,9 @@
|
|||||||
"new_prompt": "Create new prompt",
|
"new_prompt": "Create new prompt",
|
||||||
"feed_brain_placeholder": "Choose which @brain you want to feed with these files",
|
"feed_brain_placeholder": "Choose which @brain you want to feed with these files",
|
||||||
"feedingBrain": "Your newly added knowledge is being processed, you can keep chatting in the meantime !",
|
"feedingBrain": "Your newly added knowledge is being processed, you can keep chatting in the meantime !",
|
||||||
"add_content_card_button_tooltip": "Add knowledge to a brain"
|
"add_content_card_button_tooltip": "Add knowledge to a brain",
|
||||||
|
"onboarding":{
|
||||||
|
"step_1_message_1": "Hi 👋🏻 Want to discover Quivr ? 😇",
|
||||||
|
"step_1_message_2": "Step 1: Download “Quivr documentation”"
|
||||||
|
}
|
||||||
}
|
}
|
@ -37,5 +37,9 @@
|
|||||||
"new_prompt": "Crear nueva instrucción",
|
"new_prompt": "Crear nueva instrucción",
|
||||||
"feed_brain_placeholder" : "Elige cuál @cerebro quieres alimentar con estos archivos",
|
"feed_brain_placeholder" : "Elige cuál @cerebro quieres alimentar con estos archivos",
|
||||||
"feedingBrain": "Su conocimiento recién agregado se está procesando, ¡puede seguir chateando mientras tanto!",
|
"feedingBrain": "Su conocimiento recién agregado se está procesando, ¡puede seguir chateando mientras tanto!",
|
||||||
"add_content_card_button_tooltip": "Agregar conocimiento a un cerebro"
|
"add_content_card_button_tooltip": "Agregar conocimiento a un cerebro",
|
||||||
|
"onboarding":{
|
||||||
|
"step_1_message_1": "Hola 👋🏻 ¿Quieres descubrir Quivr? 😇",
|
||||||
|
"step_1_message_2": "Paso 1: Descargar la documentación de “Quivr”"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,5 +37,9 @@
|
|||||||
"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",
|
"feed_brain_placeholder" : "Choisissez le @cerveau que vous souhaitez nourrir avec ces fichiers",
|
||||||
"feedingBrain": "Vos nouvelles connaissances sont en cours de traitement. Vous pouvez continuer à discuter en attendant !",
|
"feedingBrain": "Vos nouvelles connaissances sont en cours de traitement. Vous pouvez continuer à discuter en attendant !",
|
||||||
"add_content_card_button_tooltip": "Ajouter des connaissances à un cerveau"
|
"add_content_card_button_tooltip": "Ajouter des connaissances à un cerveau",
|
||||||
|
"onboarding":{
|
||||||
|
"step_1_message_1": "Salut 👋🏻 Envie de découvrir Quivr ? 😇",
|
||||||
|
"step_1_message_2": "Étape 1 : Téléchargez la documentation de “Quivr”"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,5 +37,9 @@
|
|||||||
"new_prompt": "Criar novo prompt",
|
"new_prompt": "Criar novo prompt",
|
||||||
"feed_brain_placeholder" : "Escolha qual @cérebro você deseja alimentar com esses arquivos",
|
"feed_brain_placeholder" : "Escolha qual @cérebro você deseja alimentar com esses arquivos",
|
||||||
"feedingBrain": "Seu conhecimento recém-adicionado está sendo processado, você pode continuar conversando enquanto isso!",
|
"feedingBrain": "Seu conhecimento recém-adicionado está sendo processado, você pode continuar conversando enquanto isso!",
|
||||||
"add_content_card_button_tooltip": "Adicionar conhecimento a um cérebro"
|
"add_content_card_button_tooltip": "Adicionar conhecimento a um cérebro",
|
||||||
|
"onboarding":{
|
||||||
|
"step_1_message_1": "Oi 👋🏻 Quer descobrir o Quivr? 😇",
|
||||||
|
"step_1_message_2": "Passo 1: Baixe a documentação do 'Quivr'"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,5 +37,9 @@
|
|||||||
"new_prompt": "Создать новый запрос",
|
"new_prompt": "Создать новый запрос",
|
||||||
"feed_brain_placeholder" : "Выберите, какой @мозг вы хотите питать этими файлами",
|
"feed_brain_placeholder" : "Выберите, какой @мозг вы хотите питать этими файлами",
|
||||||
"feedingBrain": "Ваш недавно добавленный знаний обрабатывается, вы можете продолжить общение в это время!",
|
"feedingBrain": "Ваш недавно добавленный знаний обрабатывается, вы можете продолжить общение в это время!",
|
||||||
"add_content_card_button_tooltip": "Добавить знаний в мозг"
|
"add_content_card_button_tooltip": "Добавить знаний в мозг",
|
||||||
|
"onboarding":{
|
||||||
|
"step_1_message_1": "Привет 👋🏻 Хочешь узнать о Quivr? 😇",
|
||||||
|
"step_1_message_2": "Шаг 1: Скачайте документацию Quivr"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,5 +37,9 @@
|
|||||||
"new_prompt": "新提示",
|
"new_prompt": "新提示",
|
||||||
"feed_brain_placeholder" : "选择要用这些文件喂养的 @大脑",
|
"feed_brain_placeholder" : "选择要用这些文件喂养的 @大脑",
|
||||||
"feedingBrain": "您新添加的知识正在处理中,同时您可以继续聊天!",
|
"feedingBrain": "您新添加的知识正在处理中,同时您可以继续聊天!",
|
||||||
"add_content_card_button_tooltip": "向大脑添加知识"
|
"add_content_card_button_tooltip": "向大脑添加知识",
|
||||||
|
"onboarding":{
|
||||||
|
"step_1_message_1": "你好 👋🏻 想要发现 Quivr 吗? 😇",
|
||||||
|
"step_1_message_2": "步骤 1:下载“Quivr 文档”"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user