Mamadou DICKO 2023-12-18 14:23:45 +01:00 committed by GitHub
parent 7db832d200
commit 7480e89fa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 447 additions and 155 deletions

View File

@ -2,9 +2,10 @@ from enum import Enum
from typing import List, Optional
from uuid import UUID
from modules.brain.entity.api_brain_definition_entity import ApiBrainDefinitionEntity
from pydantic import BaseModel
from modules.brain.entity.api_brain_definition_entity import ApiBrainDefinitionEntity
class BrainType(str, Enum):
DOC = "doc"
@ -67,3 +68,4 @@ class MinimalUserBrainEntity(BaseModel):
rights: RoleEnum
status: str
brain_type: BrainType
description: str

View File

@ -2,6 +2,7 @@ from uuid import UUID
from logger import get_logger
from models.settings import get_supabase_client
from modules.brain.entity.brain_entity import BrainUser, MinimalUserBrainEntity
from modules.brain.repository.interfaces.brains_users_interface import (
BrainsUsersInterface,
@ -18,7 +19,9 @@ class BrainsUsers(BrainsUsersInterface):
def get_user_brains(self, user_id) -> list[MinimalUserBrainEntity]:
response = (
self.db.from_("brains_users")
.select("id:brain_id, rights, brains (brain_id, name, status, brain_type)")
.select(
"id:brain_id, rights, brains (brain_id, name, status, brain_type, description)"
)
.filter("user_id", "eq", user_id)
.execute()
)
@ -31,6 +34,9 @@ class BrainsUsers(BrainsUsersInterface):
rights=item["rights"],
status=item["brains"]["status"],
brain_type=item["brains"]["brain_type"],
description=item["brains"]["description"]
if item["brains"]["description"] is not None
else "",
)
)
user_brains[-1].rights = item["rights"]
@ -40,7 +46,7 @@ class BrainsUsers(BrainsUsersInterface):
response = (
self.db.from_("brains_users")
.select(
"id:brain_id, rights, brains (id: brain_id, status, name, brain_type)"
"id:brain_id, rights, brains (id: brain_id, status, name, brain_type, description)"
)
.filter("user_id", "eq", user_id)
.filter("brain_id", "eq", brain_id)
@ -56,6 +62,9 @@ class BrainsUsers(BrainsUsersInterface):
rights=brain_data["rights"],
status=brain_data["brains"]["status"],
brain_type=brain_data["brains"]["brain_type"],
description=brain_data["brains"]["description"]
if brain_data["brains"]["description"] is not None
else "",
)
def delete_brain_user_by_id(

View File

@ -1,27 +1,8 @@
"use client";
import { UUID } from "crypto";
import { useParams } from "next/navigation";
import { useTranslation } from "react-i18next";
import { BrainManagementTabs } from "./components";
const BrainsManagement = (): JSX.Element => {
const params = useParams();
const { t } = useTranslation(["brain"]);
const brainId = params?.brainId as UUID | undefined;
if (brainId === undefined) {
return (
<div className="flex justify-center mt-5 w-full">
<div className="bg-blue-100 border border-blue-400 text-blue-700 px-4 py-3 rounded relative max-w-md h-fit">
<p>{t("selectBrain")}</p>
</div>
</div>
);
}
return <BrainManagementTabs />;
};

View File

@ -0,0 +1,60 @@
import { useTranslation } from "react-i18next";
import Spinner from "@/lib/components/ui/Spinner";
import { Tabs, TabsContent, TabsList } from "@/lib/components/ui/Tabs";
import { BrainSearchBar } from "./components/BrainSearchBar";
import { BrainsList } from "./components/BrainsList";
import { StyledTabsTrigger } from "./components/StyledTabsTrigger";
import { useBrainsTabs } from "./hooks/useBrainsTabs";
export const BrainsTabs = (): JSX.Element => {
const { t } = useTranslation(["brain", "translation"]);
const {
searchQuery,
isFetchingBrains,
setSearchQuery,
brains,
privateBrains,
publicBrains,
} = useBrainsTabs();
if (isFetchingBrains && brains.length === 0) {
return (
<div className="flex w-full h-full justify-center items-center">
<Spinner />
</div>
);
}
return (
<Tabs defaultValue="all" className="flex flex-col">
<TabsList className="flex flex-row justify-start gap-2 border-b-2 rounded-none pb-3">
<StyledTabsTrigger value="all">
{t("translation:all")}
</StyledTabsTrigger>
<StyledTabsTrigger value="private" className="capitalize">
{t("private_brain_label")}
</StyledTabsTrigger>
<StyledTabsTrigger value="public" className="capitalize">
{t("public_brain_label")}
</StyledTabsTrigger>
<div className="w-full flex justify-end">
<BrainSearchBar
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
/>
</div>
</TabsList>
<TabsContent value="all">
<BrainsList brains={brains} />
</TabsContent>
<TabsContent value="private">
<BrainsList brains={privateBrains} />
</TabsContent>
<TabsContent value="public">
<BrainsList brains={publicBrains} />
</TabsContent>
</Tabs>
);
};

View File

@ -0,0 +1,55 @@
import Link from "next/link";
import { useTranslation } from "react-i18next";
import { CgFileDocument } from "react-icons/cg";
import { LuChevronRightCircle } from "react-icons/lu";
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
import { getBrainIconFromBrainType } from "@/lib/helpers/getBrainIconFromBrainType";
type BrainItemProps = {
brain: MinimalBrainForUser;
};
export const BrainItem = ({ brain }: BrainItemProps): JSX.Element => {
const { t } = useTranslation("brain");
const isBrainDescriptionEmpty = brain.description === "";
const brainDescription = isBrainDescriptionEmpty
? t("empty_brain_description")
: brain.description;
return (
<div className="flex justify-center items-center flex-col flex-1 w-full h-full shadow-md dark:shadow-primary/25 hover:shadow-xl transition-shadow rounded-xl overflow-hidden dark:bg-black border border-black/10 dark:border-white/25 pb-2 bg-secondary">
<div className="w-full">
<div className="w-full py-2 flex gap-2 justify-center items-center bg-primary bg-opacity-40 px-2">
{getBrainIconFromBrainType(brain.brain_type, {
iconSize: 24,
DocBrainIcon: CgFileDocument,
iconClassName: "text-primary",
})}
<span className="line-clamp-1 mr-2 font-semibold text-md">
{brain.name}
</span>
</div>
</div>
<div className="flex-1 py-2">
<p
className={`line-clamp-2 text-center px-5 ${
isBrainDescriptionEmpty && "text-gray-400"
}`}
>
{brainDescription}
</p>
</div>
<div className="w-full px-2">
<Link
href={`/brains-management/${brain.id}`}
className="px-8 py-3 flex items-center justify-center gap-2 bg-white text-primary rounded-lg border-0 w-content mt-3 disabled:bg-secondary hover:bg-primary/50 disabled:hover:bg-primary/50 w-full text-md"
>
<span>Modifier</span>
<LuChevronRightCircle className="text-md" />
</Link>
</div>
</div>
);
};

View File

@ -0,0 +1,29 @@
import { useTranslation } from "react-i18next";
import { LuSearch } from "react-icons/lu";
import Field from "@/lib/components/ui/Field";
type BrainSearchBarProps = {
searchQuery: string;
setSearchQuery: (searchQuery: string) => void;
};
export const BrainSearchBar = ({
searchQuery,
setSearchQuery,
}: BrainSearchBarProps): JSX.Element => {
const { t } = useTranslation(["brain"]);
return (
<Field
name="brainsearch"
placeholder={t("searchBrain")}
autoComplete="off"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-auto"
inputClassName="w-max w-[200px] rounded-3xl border-none"
icon={<LuSearch className="text-primary" size={20} />}
/>
);
};

View File

@ -0,0 +1,21 @@
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
import { BrainItem } from "./BrainItem";
type BrainsListProps = {
brains: MinimalBrainForUser[];
};
export const BrainsList = ({ brains }: BrainsListProps): JSX.Element => {
return (
<div className="flex flex-1 flex-col items-center justify-center">
<div className="w-full lg:grid-cols-4 md:grid-cols-3 grid mt-5 gap-3 items-stretch">
{brains.map((brain) => (
<div key={brain.id} className="h-[180px]">
<BrainItem brain={brain} />
</div>
))}
</div>
</div>
);
};

View File

@ -0,0 +1,21 @@
import { forwardRef } from "react";
import { TabsTrigger } from "@/lib/components/ui/Tabs";
import { cn } from "@/lib/utils";
export const StyledTabsTrigger = forwardRef<
React.ElementRef<typeof TabsTrigger>,
React.ComponentPropsWithoutRef<typeof TabsTrigger>
>(({ className, ...props }, ref) => (
<TabsTrigger
ref={ref}
className={cn(
"capitalize",
"data-[state=active]:shadow-none",
"data-[state=active]:text-primary",
className
)}
{...props}
/>
));
StyledTabsTrigger.displayName = TabsTrigger.displayName;

View File

@ -0,0 +1,29 @@
import { useState } from "react";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useBrainsTabs = () => {
const [searchQuery, setSearchQuery] = useState("");
const { allBrains, isFetchingBrains } = useBrainContext();
const brains = allBrains.filter((brain) => {
const query = searchQuery.toLowerCase();
const name = brain.name.toLowerCase();
return name.includes(query);
});
const privateBrains = brains.filter((brain) => brain.status === "private");
const publicBrains = brains.filter((brain) => brain.status === "public");
return {
searchQuery,
setSearchQuery,
brains,
privateBrains,
publicBrains,
isFetchingBrains,
};
};

View File

@ -0,0 +1,2 @@
const brainTabs = ["all", "private", "public"] as const;
export type BrainTab = (typeof brainTabs)[number];

View File

@ -4,8 +4,6 @@ import { ReactNode } from "react";
import { useSupabase } from "@/lib/context/SupabaseProvider";
import { redirectToLogin } from "@/lib/router/redirectToLogin";
import { BrainsList } from "./[brainId]/components";
interface LayoutProps {
children?: ReactNode;
}
@ -18,7 +16,6 @@ const Layout = ({ children }: LayoutProps): JSX.Element => {
return (
<div className="relative h-full w-full flex justify-stretch items-stretch overflow-scroll">
<BrainsList />
{children}
</div>
);

View File

@ -1,4 +1,33 @@
"use client";
import BrainsManagementPage from "./[brainId]/page";
export default BrainsManagementPage;
import { useTranslation } from "react-i18next";
import { LuBrain } from "react-icons/lu";
import { AddBrainModal } from "@/lib/components/AddBrainModal";
import { BrainsTabs } from "./components/BrainsTabs/BrainsTabs";
const BrainsManagement = (): JSX.Element => {
const { t } = useTranslation("chat");
return (
<div className="flex flex-col flex-1">
<div className="w-full h-full p-6 bg-highlight flex flex-col flex-1 overflow-auto">
<div className="w-full mb-10">
<div className="flex flex-row justify-center items-center gap-2">
<LuBrain size={20} className="text-primary" />
<span className="capitalize text-2xl font-semibold">
{t("brains")}
</span>
</div>
</div>
<BrainsTabs />
</div>
<div className="w-full flex justify-center py-4">
<AddBrainModal triggerClassName="bg-primary text-white font-normal" />
</div>
</div>
);
};
export default BrainsManagement;

View File

@ -11,4 +11,5 @@ export const mapBackendMinimalBrainToMinimalBrain = (
role: backendMinimalBrain.rights,
status: backendMinimalBrain.status,
brain_type: backendMinimalBrain.brain_type,
description: backendMinimalBrain.description,
});

View File

@ -1,5 +1,5 @@
import { useTranslation } from "react-i18next";
import { MdAdd } from "react-icons/md";
import { LuPlusCircle } from "react-icons/lu";
import Button from "@/lib/components/ui/Button";
import { Modal } from "@/lib/components/ui/Modal";
@ -32,8 +32,8 @@ export const AddBrainSteps = ({
className={cn("border-0", triggerClassName)}
data-testid="add-brain-button"
>
<LuPlusCircle className="text-xl" />
{t("newBrain", { ns: "brain" })}
<MdAdd className="text-xl" />
</Button>
}
title={t("newBrainTitle", { ns: "brain" })}

View File

@ -27,7 +27,7 @@ export const Menu = (): JSX.Element => {
return <></>;
}
const displayedOnPages = ["/chat", "/library"];
const displayedOnPages = ["/chat", "/library", "/brains-management"];
const isMenuDisplayed = displayedOnPages.some((page) =>
pathname.includes(page)

View File

@ -0,0 +1,55 @@
"use client";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import * as React from "react";
import { cn } from "@/lib/utils";
const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
)}
{...props}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsContent, TabsList, TabsTrigger };

View File

@ -25,7 +25,7 @@ export const useBrainProvider = () => {
const [allBrains, setAllBrains] = useState<MinimalBrainForUser[]>([]);
const [currentBrainId, setCurrentBrainId] = useState<null | UUID>(null);
const [defaultBrainId, setDefaultBrainId] = useState<UUID>();
const [isFetchingBrains, setIsFetchingBrains] = useState(false);
const [isFetchingBrains, setIsFetchingBrains] = useState(true);
const [publicPrompts, setPublicPrompts] = useState<Prompt[]>([]);
const [currentPromptId, setCurrentPromptId] = useState<null | string>(null);

View File

@ -29,6 +29,7 @@ export type MinimalBrainForUser = {
role: BrainRoleType;
status: BrainAccessStatus;
brain_type: BrainType;
description: string;
};
//TODO: rename rights to role in Backend and use MinimalBrainForUser instead of BackendMinimalBrainForUser

View File

@ -52,7 +52,6 @@
"searchBrain": "Search for a brain",
"secrets_update_error": "Error while updating secrets",
"secrets_updated": "Secrets updated",
"selectBrain": "Select a brain please",
"set_brain_status_to_private_modal_description": "Every Quivr users won't be able to use this brain anymore and they won't see it in the brain library.",
"set_brain_status_to_private_modal_title": "Are you sure you want to set this as <span class='text-primary'>Private</span>?<br/><br/>",
"set_brain_status_to_public_modal_description": "Every Quivr user will be able to:<br/>- Subscribe to your brain in the 'brains library'.<br/>- Use this brain and check the prompt and model configurations.<br/><br/>They won't have access to your uploaded files and people section.",

View File

@ -1,4 +1,5 @@
{
"all": "All",
"and": "and",
"cancel": "Cancel",
"Chat": "Chat",

View File

@ -52,7 +52,6 @@
"searchBrain": "Buscar un cerebro",
"secrets_update_error": "Error al actualizar secretos",
"secrets_updated": "Secretos actualizados",
"selectBrain": "Por favor, selecciona un cerebro",
"set_brain_status_to_private_modal_description": "Los usuarios de Quivr ya no podrán utilizar este cerebro y no lo verán en la biblioteca de cerebros.",
"set_brain_status_to_private_modal_title": "¿Estás seguro de que quieres establecer esto como <span class='text-primary'>Privado</span>?<br/><br/>",
"set_brain_status_to_public_modal_description": "Cada usuario de Quivr podrá:<br/>- Suscribirse a tu cerebro en la 'biblioteca de cerebros'.<br/>- Usar este cerebro y comprobar las configuraciones de las indicaciones y el modelo.<br/><br/>No tendrán acceso a tus archivos cargados ni a la sección de personas.",

View File

@ -1,7 +1,11 @@
{
"all": "Todos",
"and": "y",
"cancel": "Cancelar",
"Chat": "Conversar",
"chatButton": "Conversar",
"comingSoon": "Próximamente",
"crawlButton": "Rastrear",
"createButton": "Crear",
"deleteButton": "Borrar",
"deleteForeverButton": "Borrar para siempre",
@ -10,28 +14,25 @@
"Editor": "Editor",
"email": "Correo electrónico",
"Explore": "Explorar",
"invalidUrl": "URL inválida",
"lang": "es-ES",
"languageSelect": "Idioma preferido",
"loading": "Cargando...",
"logoutButton": "Cerrar sesión",
"newChatButton": "Nueva conversación",
"next": "Siguiente",
"or": "o",
"and": "y",
"Owner": "Propietario",
"previous": "Anterior",
"resetButton": "Restaurar",
"saveButton": "Guardar",
"shareButton": "Compartir",
"themeSelect": "Tema de interfaz",
"title": "Quivr - Tu segundo cerebro con IA generativa",
"toastDismiss": "cerrar",
"updateButton": "Actualizar",
"Upload": "Subir",
"uploadButton": "Subir",
"uploadingButton": "Subiendo...",
"Viewer": "Espectador",
"saveButton": "Guardar",
"invalidUrl": "URL inválida",
"crawlButton": "Rastrear",
"themeSelect": "Tema de interfaz",
"languageSelect": "Idioma preferido",
"cancel": "Cancelar",
"next": "Siguiente",
"previous": "Anterior"
}
"Viewer": "Espectador"
}

View File

@ -52,7 +52,6 @@
"searchBrain": "Rechercher un cerveau",
"secrets_update_error": "Erreur lors de la mise à jour des secrets",
"secrets_updated": "Secrets mis à jour",
"selectBrain": "Sélectionnez un cerveau, s'il vous plaît",
"set_brain_status_to_private_modal_description": "Les utilisateurs de Quivr ne pourront plus utiliser ce cerveau et ne le verront plus dans la bibliothèque des cerveaux.",
"set_brain_status_to_private_modal_title": "Êtes-vous sûr de vouloir définir ceci comme <span class='text-primary'>Privé</span>?<br/><br/>",
"set_brain_status_to_public_modal_description": "Chaque utilisateur de Quivr pourra :<br/>- S'abonner à votre cerveau dans la 'bibliothèque des cerveaux'.<br/>- Utiliser ce cerveau et vérifier les configurations de prompts et de modèles.<br/><br/>Ils n'auront pas accès à vos fichiers téléchargés et à la section des personnes.",

View File

@ -1,37 +1,38 @@
{
"lang": "fr-FR",
"title": "Quivr - Obtenez un deuxième cerveau avec l'IA générative",
"description": "Quivr est votre deuxième cerveau dans le nuage, conçu pour stocker et récupérer facilement des informations non structurées.",
"toastDismiss": "ignorer",
"email": "Email",
"or": "ou",
"all": "Tout",
"and": "et",
"logoutButton": "Déconnexion",
"updateButton": "Mettre à jour",
"uploadButton": "Télécharger",
"uploadingButton": "Téléchargement...",
"cancel": "Annuler",
"Chat": "Chat",
"chatButton": "Chat",
"comingSoon": "Bientôt disponible",
"crawlButton": "Crawler",
"createButton": "Créer",
"deleteButton": "Supprimer",
"deleteForeverButton": "Supprimer définitivement",
"description": "Quivr est votre deuxième cerveau dans le nuage, conçu pour stocker et récupérer facilement des informations non structurées.",
"doneButton": "Fait",
"resetButton": "Réinitialiser",
"newChatButton": "Nouveau chat",
"createButton": "Créer",
"shareButton": "Partager",
"Upload": "Télécharger",
"Chat": "Chat",
"Explore": "Explorer",
"loading": "Chargement...",
"comingSoon": "Bientôt disponible",
"Viewer": "Visualiseur",
"Editor": "Éditeur",
"Owner": "Propriétaire",
"saveButton": "Sauvegarder",
"email": "Email",
"Explore": "Explorer",
"invalidUrl": "URL invalide",
"crawlButton": "Crawler",
"themeSelect": "Thème de l'interface",
"lang": "fr-FR",
"languageSelect": "Langue préférée",
"cancel": "Annuler",
"loading": "Chargement...",
"logoutButton": "Déconnexion",
"newChatButton": "Nouveau chat",
"next": "Suivant",
"previous": "Précédent"
}
"or": "ou",
"Owner": "Propriétaire",
"previous": "Précédent",
"resetButton": "Réinitialiser",
"saveButton": "Sauvegarder",
"shareButton": "Partager",
"themeSelect": "Thème de l'interface",
"title": "Quivr - Obtenez un deuxième cerveau avec l'IA générative",
"toastDismiss": "ignorer",
"updateButton": "Mettre à jour",
"Upload": "Télécharger",
"uploadButton": "Télécharger",
"uploadingButton": "Téléchargement...",
"Viewer": "Visualiseur"
}

View File

@ -52,7 +52,6 @@
"searchBrain": "Pesquisar cérebros",
"secrets_update_error": "Erro ao atualizar segredos",
"secrets_updated": "Segredos atualizados",
"selectBrain": "Selecione um cérebro, por favor",
"set_brain_status_to_private_modal_description": "Os utilizadores do Quivr não poderão mais utilizar este cérebro e não o verão na biblioteca de cérebros.",
"set_brain_status_to_private_modal_title": "Tem a certeza de que deseja definir isto como <span class='text-primary'>Privado</span>?<br/><br/>",
"set_brain_status_to_public_modal_description": "Cada usuário do Quivr poderá:<br/>- Se inscrever em seu cérebro na 'biblioteca de cérebros'.<br/>- Usar este cérebro e verificar as configurações de prompts e modelos.<br/><br/>Eles não terão acesso aos seus arquivos enviados e à seção de pessoas.",

View File

@ -1,37 +1,38 @@
{
"lang": "pt-BR",
"title": "Quivr - Tenha um Segundo Cérebro com IA Generativa",
"description": "Quivr é o seu segundo cérebro na nuvem, projetado para armazenar e recuperar facilmente informações não estruturadas.",
"toastDismiss": "fechar",
"email": "Email",
"or": "ou",
"all": "Todos",
"and": "e",
"logoutButton": "Sair",
"updateButton": "Atualizar",
"uploadButton": "Enviar",
"uploadingButton": "Enviando...",
"cancel": "Cancelar",
"Chat": "Chat",
"chatButton": "Chat",
"comingSoon": "Em breve",
"crawlButton": "Rastrear",
"createButton": "Criar",
"deleteButton": "Excluir",
"deleteForeverButton": "Excluir permanentemente",
"description": "Quivr é o seu segundo cérebro na nuvem, projetado para armazenar e recuperar facilmente informações não estruturadas.",
"doneButton": "Concluído",
"resetButton": "Redefinir",
"newChatButton": "Novo Chat",
"createButton": "Criar",
"shareButton": "Compartilhar",
"Upload": "Enviar",
"Chat": "Chat",
"Explore": "Explorar",
"loading": "Carregando...",
"comingSoon": "Em breve",
"Viewer": "Visualizador",
"Editor": "Editor",
"Owner": "Proprietário",
"saveButton": "Salvar",
"email": "Email",
"Explore": "Explorar",
"invalidUrl": "URL inválida",
"crawlButton": "Rastrear",
"themeSelect": "Tema de interface",
"lang": "pt-BR",
"languageSelect": "Língua preferida",
"cancel": "Cancelar",
"loading": "Carregando...",
"logoutButton": "Sair",
"newChatButton": "Novo Chat",
"next": "Próximo",
"previous": "Anterior"
}
"or": "ou",
"Owner": "Proprietário",
"previous": "Anterior",
"resetButton": "Redefinir",
"saveButton": "Salvar",
"shareButton": "Compartilhar",
"themeSelect": "Tema de interface",
"title": "Quivr - Tenha um Segundo Cérebro com IA Generativa",
"toastDismiss": "fechar",
"updateButton": "Atualizar",
"Upload": "Enviar",
"uploadButton": "Enviar",
"uploadingButton": "Enviando...",
"Viewer": "Visualizador"
}

View File

@ -52,7 +52,6 @@
"searchBrain": "Поиск мозга",
"secrets_update_error": "Ошибка при обновлении секретов",
"secrets_updated": "Секреты обновлены",
"selectBrain": "Выберите мозг, пожалуйста",
"set_brain_status_to_private_modal_description": "Пользователи Quivr больше не смогут использовать этот мозг, и они не увидят его в библиотеке мозгов.",
"set_brain_status_to_private_modal_title": "Вы уверены, что хотите установить это как <span class='text-primary'>Частное</span>?<br/><br/>",
"set_brain_status_to_public_modal_description": "Каждый пользователь Quivr сможет:<br/>- Подписаться на ваш мозг в 'библиотеке мозгов'.<br/>- Использовать этот мозг и проверить настройки подсказок и модели.<br/><br/>У них не будет доступа к вашим загруженным файлам и разделу 'люди'.",

View File

@ -1,37 +1,38 @@
{
"lang": "ru-RU",
"title": "Quivr - Второй мозг с генеративным ИИ",
"description": "Quivr - это ваш второй мозг в облаке, предназначенный для легкого хранения и извлечения неструктурированной информации.",
"toastDismiss": "закрыть",
"email": "Email",
"or": "или",
"all": "Все",
"and": "и",
"logoutButton": "Выйти",
"updateButton": "Обновить",
"uploadButton": "Загрузить",
"uploadingButton": "Загрузка...",
"cancel": "Отмена",
"Chat": "Чат",
"chatButton": "Чат",
"comingSoon": "Скоро",
"crawlButton": "Поиск",
"createButton": "Создать",
"deleteButton": "Удалить",
"deleteForeverButton": "Удалить навсегда",
"description": "Quivr - это ваш второй мозг в облаке, предназначенный для легкого хранения и извлечения неструктурированной информации.",
"doneButton": "Готово",
"resetButton": "Сбросить",
"newChatButton": "Новый чат",
"createButton": "Создать",
"shareButton": "Поделиться",
"Upload": "Загрузка",
"Chat": "Чат",
"Explore": "Исследовать",
"loading": "Загрузка...",
"comingSoon": "Скоро",
"Viewer": "Просмотр",
"Editor": "Редактор",
"Owner": "Владелец",
"saveButton": "Сохранить",
"email": "Email",
"Explore": "Исследовать",
"invalidUrl": "Неверный URL",
"crawlButton": "Поиск",
"themeSelect": "Тема интерфейса",
"lang": "ru-RU",
"languageSelect": "предпочтительный язык",
"cancel": "Отмена",
"loading": "Загрузка...",
"logoutButton": "Выйти",
"newChatButton": "Новый чат",
"next": "Далее",
"previous": "Назад"
}
"or": "или",
"Owner": "Владелец",
"previous": "Назад",
"resetButton": "Сбросить",
"saveButton": "Сохранить",
"shareButton": "Поделиться",
"themeSelect": "Тема интерфейса",
"title": "Quivr - Второй мозг с генеративным ИИ",
"toastDismiss": "закрыть",
"updateButton": "Обновить",
"Upload": "Загрузка",
"uploadButton": "Загрузить",
"uploadingButton": "Загрузка...",
"Viewer": "Просмотр"
}

View File

@ -52,7 +52,6 @@
"searchBrain": "搜索大脑",
"secrets_update_error": "更新秘密时出错",
"secrets_updated": "秘密已更新",
"selectBrain": "请选择一个大脑",
"set_brain_status_to_private_modal_description": "Quivr 用户将无法再使用此大脑,并且不会在大脑库中看到它。",
"set_brain_status_to_private_modal_title": "您确定要将此大脑设置为<span class='text-primary'>私有</span>模式吗?<br/><br/>",
"set_brain_status_to_public_modal_description": "每个 Quivr 用户都可以:<br/>- 在 “大脑库” 中订阅此大脑。<br/>- 使用此大脑并检查提示和模型配置。<br/><br/>他们将无法访问您上传的文件和人员部分。",

View File

@ -1,37 +1,38 @@
{
"lang": "zh-CN",
"title": "Quivr - 通过生成式人工智能获得第二个大脑",
"description": "Quivr 是您在云中的第二个大脑,让您轻松存储和检索非结构化信息。",
"toastDismiss": "好的",
"email": "电子邮件",
"or": "或",
"all": "全部",
"and": "和",
"logoutButton": "登出",
"updateButton": "更新",
"uploadButton": "上传",
"uploadingButton": "上传中…",
"cancel": "取消",
"Chat": "聊天",
"chatButton": "聊天",
"comingSoon": "即将推出",
"crawlButton": "爬取",
"createButton": "创建",
"deleteButton": "删除",
"deleteForeverButton": "永久删除",
"description": "Quivr 是您在云中的第二个大脑,让您轻松存储和检索非结构化信息。",
"doneButton": "完成",
"resetButton": "重置",
"newChatButton": "新聊天",
"createButton": "创建",
"shareButton": "分享",
"Upload": "上传",
"Chat": "聊天",
"Explore": "探索",
"loading": "加载中…",
"comingSoon": "即将推出",
"Viewer": "查看",
"Editor": "编辑",
"Owner": "作者",
"saveButton": "保存",
"email": "电子邮件",
"Explore": "探索",
"invalidUrl": "无效的 URL",
"crawlButton": "爬取",
"themeSelect": "主题",
"lang": "zh-CN",
"languageSelect": "首选语言",
"cancel": "取消",
"loading": "加载中…",
"logoutButton": "登出",
"newChatButton": "新聊天",
"next": "下一个",
"previous": "上一个"
"or": "或",
"Owner": "作者",
"previous": "上一个",
"resetButton": "重置",
"saveButton": "保存",
"shareButton": "分享",
"themeSelect": "主题",
"title": "Quivr - 通过生成式人工智能获得第二个大脑",
"toastDismiss": "好的",
"updateButton": "更新",
"Upload": "上传",
"uploadButton": "上传",
"uploadingButton": "上传中…",
"Viewer": "查看"
}