This commit is contained in:
hailin 2025-04-19 20:37:09 +08:00
parent 475b67ee02
commit 05a8685504
14 changed files with 174 additions and 57 deletions

View File

@ -13,11 +13,15 @@ import { SidebarItem } from "../all/sidebar-display-item"
import { AssistantRetrievalSelect } from "./assistant-retrieval-select" import { AssistantRetrievalSelect } from "./assistant-retrieval-select"
import { AssistantToolSelect } from "./assistant-tool-select" import { AssistantToolSelect } from "./assistant-tool-select"
import { useTranslation } from 'react-i18next'
interface AssistantItemProps { interface AssistantItemProps {
assistant: Tables<"assistants"> assistant: Tables<"assistants">
} }
export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => { export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => {
const { t } = useTranslation()
const { selectedWorkspace, assistantImages } = useContext(ChatbotUIContext) const { selectedWorkspace, assistantImages } = useContext(ChatbotUIContext)
const [name, setName] = useState(assistant.name) const [name, setName] = useState(assistant.name)
@ -167,10 +171,10 @@ export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => {
}) => ( }) => (
<> <>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input <Input
placeholder="Assistant name..." placeholder={t("side.assistantNamePlaceholder")}
value={name} value={name}
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
maxLength={ASSISTANT_NAME_MAX} maxLength={ASSISTANT_NAME_MAX}
@ -178,10 +182,10 @@ export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => {
</div> </div>
<div className="space-y-1 pt-2"> <div className="space-y-1 pt-2">
<Label>Description</Label> <Label>{t("side.description")}</Label>
<Input <Input
placeholder="Assistant description..." placeholder={t("side.assistantDescriptionPlaceholder")}
value={description} value={description}
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
maxLength={ASSISTANT_DESCRIPTION_MAX} maxLength={ASSISTANT_DESCRIPTION_MAX}
@ -189,7 +193,7 @@ export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Image</Label> <Label>{t("side.image")}</Label>
<ImagePicker <ImagePicker
src={imageLink} src={imageLink}
@ -208,7 +212,7 @@ export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => {
/> />
<div className="space-y-1 pt-2"> <div className="space-y-1 pt-2">
<Label>Files & Collections</Label> <Label>{t("side.filesAndCollections")}</Label>
<AssistantRetrievalSelect <AssistantRetrievalSelect
selectedAssistantRetrievalItems={ selectedAssistantRetrievalItems={
@ -270,7 +274,7 @@ export const AssistantItem: FC<AssistantItemProps> = ({ assistant }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Tools</Label> <Label>{t("side.tools")}</Label>
<AssistantToolSelect <AssistantToolSelect
selectedAssistantTools={ selectedAssistantTools={

View File

@ -15,6 +15,8 @@ import {
import { FileIcon } from "lucide-react" import { FileIcon } from "lucide-react"
import { FC, useContext, useEffect, useRef, useState } from "react" import { FC, useContext, useEffect, useRef, useState } from "react"
import { useTranslation } from 'react-i18next'
interface AssistantRetrievalSelectProps { interface AssistantRetrievalSelectProps {
selectedAssistantRetrievalItems: Tables<"files">[] | Tables<"collections">[] selectedAssistantRetrievalItems: Tables<"files">[] | Tables<"collections">[]
onAssistantRetrievalItemsSelect: ( onAssistantRetrievalItemsSelect: (
@ -26,6 +28,8 @@ export const AssistantRetrievalSelect: FC<AssistantRetrievalSelectProps> = ({
selectedAssistantRetrievalItems, selectedAssistantRetrievalItems,
onAssistantRetrievalItemsSelect onAssistantRetrievalItemsSelect
}) => { }) => {
const { t } = useTranslation()
const { files, collections } = useContext(ChatbotUIContext) const { files, collections } = useContext(ChatbotUIContext)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
@ -67,7 +71,7 @@ export const AssistantRetrievalSelect: FC<AssistantRetrievalSelectProps> = ({
> >
<div className="flex items-center"> <div className="flex items-center">
<div className="ml-2 flex items-center"> <div className="ml-2 flex items-center">
{selectedAssistantRetrievalItems.length} files selected {selectedAssistantRetrievalItems.length} {t("side.filesSelected")}
</div> </div>
</div> </div>
@ -82,7 +86,7 @@ export const AssistantRetrievalSelect: FC<AssistantRetrievalSelectProps> = ({
> >
<Input <Input
ref={inputRef} ref={inputRef}
placeholder="Search files..." placeholder={t("side.searchFilesPlaceholder")}
value={search} value={search}
onChange={e => setSearch(e.target.value)} onChange={e => setSearch(e.target.value)}
onKeyDown={e => e.stopPropagation()} onKeyDown={e => e.stopPropagation()}

View File

@ -14,6 +14,8 @@ import {
} from "@tabler/icons-react" } from "@tabler/icons-react"
import { FC, useContext, useEffect, useRef, useState } from "react" import { FC, useContext, useEffect, useRef, useState } from "react"
import { useTranslation } from 'react-i18next'
interface AssistantToolSelectProps { interface AssistantToolSelectProps {
selectedAssistantTools: Tables<"tools">[] selectedAssistantTools: Tables<"tools">[]
onAssistantToolsSelect: (tool: Tables<"tools">) => void onAssistantToolsSelect: (tool: Tables<"tools">) => void
@ -23,6 +25,8 @@ export const AssistantToolSelect: FC<AssistantToolSelectProps> = ({
selectedAssistantTools, selectedAssistantTools,
onAssistantToolsSelect onAssistantToolsSelect
}) => { }) => {
const { t } = useTranslation()
const { tools } = useContext(ChatbotUIContext) const { tools } = useContext(ChatbotUIContext)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
@ -64,7 +68,7 @@ export const AssistantToolSelect: FC<AssistantToolSelectProps> = ({
> >
<div className="flex items-center"> <div className="flex items-center">
<div className="ml-2 flex items-center"> <div className="ml-2 flex items-center">
{selectedAssistantTools.length} tools selected {selectedAssistantTools.length} {t("side.toolsSelected")}
</div> </div>
</div> </div>
@ -79,7 +83,7 @@ export const AssistantToolSelect: FC<AssistantToolSelectProps> = ({
> >
<Input <Input
ref={inputRef} ref={inputRef}
placeholder="Search tools..." placeholder={t("side.searchToolsPlaceholder")}
value={search} value={search}
onChange={e => setSearch(e.target.value)} onChange={e => setSearch(e.target.value)}
onKeyDown={e => e.stopPropagation()} onKeyDown={e => e.stopPropagation()}

View File

@ -11,6 +11,8 @@ import { CollectionFile } from "@/types"
import { IconChevronDown, IconCircleCheckFilled } from "@tabler/icons-react" import { IconChevronDown, IconCircleCheckFilled } from "@tabler/icons-react"
import { FC, useContext, useEffect, useRef, useState } from "react" import { FC, useContext, useEffect, useRef, useState } from "react"
import { useTranslation } from 'react-i18next'
interface CollectionFileSelectProps { interface CollectionFileSelectProps {
selectedCollectionFiles: CollectionFile[] selectedCollectionFiles: CollectionFile[]
onCollectionFileSelect: (file: CollectionFile) => void onCollectionFileSelect: (file: CollectionFile) => void
@ -20,6 +22,8 @@ export const CollectionFileSelect: FC<CollectionFileSelectProps> = ({
selectedCollectionFiles, selectedCollectionFiles,
onCollectionFileSelect onCollectionFileSelect
}) => { }) => {
const { t } = useTranslation()
const { files } = useContext(ChatbotUIContext) const { files } = useContext(ChatbotUIContext)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
@ -61,7 +65,7 @@ export const CollectionFileSelect: FC<CollectionFileSelectProps> = ({
> >
<div className="flex items-center"> <div className="flex items-center">
<div className="ml-2 flex items-center"> <div className="ml-2 flex items-center">
{selectedCollectionFiles.length} files selected {selectedCollectionFiles.length} {t("side.filesSelected")}
</div> </div>
</div> </div>
@ -76,7 +80,7 @@ export const CollectionFileSelect: FC<CollectionFileSelectProps> = ({
> >
<Input <Input
ref={inputRef} ref={inputRef}
placeholder="Search files..." placeholder={t("side.searchFilesPlaceholder")}
value={search} value={search}
onChange={e => setSearch(e.target.value)} onChange={e => setSearch(e.target.value)}
onKeyDown={e => e.stopPropagation()} onKeyDown={e => e.stopPropagation()}

View File

@ -8,11 +8,16 @@ import { FC, useState } from "react"
import { SidebarItem } from "../all/sidebar-display-item" import { SidebarItem } from "../all/sidebar-display-item"
import { CollectionFileSelect } from "./collection-file-select" import { CollectionFileSelect } from "./collection-file-select"
import { useTranslation } from 'react-i18next'
interface CollectionItemProps { interface CollectionItemProps {
collection: Tables<"collections"> collection: Tables<"collections">
} }
export const CollectionItem: FC<CollectionItemProps> = ({ collection }) => { export const CollectionItem: FC<CollectionItemProps> = ({ collection }) => {
const { t } = useTranslation()
const [name, setName] = useState(collection.name) const [name, setName] = useState(collection.name)
const [isTyping, setIsTyping] = useState(false) const [isTyping, setIsTyping] = useState(false)
const [description, setDescription] = useState(collection.description) const [description, setDescription] = useState(collection.description)
@ -59,7 +64,7 @@ export const CollectionItem: FC<CollectionItemProps> = ({ collection }) => {
return ( return (
<> <>
<div className="space-y-1"> <div className="space-y-1">
<Label>Files</Label> <Label>{t("side.files")}</Label>
<CollectionFileSelect <CollectionFileSelect
selectedCollectionFiles={ selectedCollectionFiles={
@ -89,10 +94,10 @@ export const CollectionItem: FC<CollectionItemProps> = ({ collection }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input <Input
placeholder="Collection name..." placeholder={t("side.collectionNamePlaceholder")}
value={name} value={name}
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
maxLength={COLLECTION_NAME_MAX} maxLength={COLLECTION_NAME_MAX}
@ -100,10 +105,10 @@ export const CollectionItem: FC<CollectionItemProps> = ({ collection }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Description</Label> <Label>{t("side.description")}</Label>
<Input <Input
placeholder="Collection description..." placeholder={t("side.collectionDescriptionPlaceholder")}
value={description} value={description}
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
maxLength={COLLECTION_DESCRIPTION_MAX} maxLength={COLLECTION_DESCRIPTION_MAX}

View File

@ -8,6 +8,8 @@ import { CollectionFile } from "@/types"
import { FC, useContext, useState } from "react" import { FC, useContext, useState } from "react"
import { CollectionFileSelect } from "./collection-file-select" import { CollectionFileSelect } from "./collection-file-select"
import { useTranslation } from 'react-i18next'
interface CreateCollectionProps { interface CreateCollectionProps {
isOpen: boolean isOpen: boolean
onOpenChange: (isOpen: boolean) => void onOpenChange: (isOpen: boolean) => void
@ -17,6 +19,8 @@ export const CreateCollection: FC<CreateCollectionProps> = ({
isOpen, isOpen,
onOpenChange onOpenChange
}) => { }) => {
const { t } = useTranslation()
const { profile, selectedWorkspace } = useContext(ChatbotUIContext) const { profile, selectedWorkspace } = useContext(ChatbotUIContext)
const [name, setName] = useState("") const [name, setName] = useState("")
@ -64,7 +68,7 @@ export const CreateCollection: FC<CreateCollectionProps> = ({
renderInputs={() => ( renderInputs={() => (
<> <>
<div className="space-y-1"> <div className="space-y-1">
<Label>Files</Label> <Label>{t("side.files")}</Label>
<CollectionFileSelect <CollectionFileSelect
selectedCollectionFiles={selectedCollectionFiles} selectedCollectionFiles={selectedCollectionFiles}
@ -73,7 +77,7 @@ export const CreateCollection: FC<CreateCollectionProps> = ({
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input <Input
placeholder="Collection name..." placeholder="Collection name..."
@ -84,10 +88,10 @@ export const CreateCollection: FC<CreateCollectionProps> = ({
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Description</Label> <Label>{t("side.description")}</Label>
<Input <Input
placeholder="Collection description..." placeholder={t("side.collectionDescriptionPlaceholder")}
value={description} value={description}
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
maxLength={COLLECTION_DESCRIPTION_MAX} maxLength={COLLECTION_DESCRIPTION_MAX}

View File

@ -7,12 +7,16 @@ import { FILE_DESCRIPTION_MAX, FILE_NAME_MAX } from "@/db/limits"
import { TablesInsert } from "@/supabase/types" import { TablesInsert } from "@/supabase/types"
import { FC, useContext, useState } from "react" import { FC, useContext, useState } from "react"
import { useTranslation } from 'react-i18next'
interface CreateFileProps { interface CreateFileProps {
isOpen: boolean isOpen: boolean
onOpenChange: (isOpen: boolean) => void onOpenChange: (isOpen: boolean) => void
} }
export const CreateFile: FC<CreateFileProps> = ({ isOpen, onOpenChange }) => { export const CreateFile: FC<CreateFileProps> = ({ isOpen, onOpenChange }) => {
const { t } = useTranslation()
const { profile, selectedWorkspace } = useContext(ChatbotUIContext) const { profile, selectedWorkspace } = useContext(ChatbotUIContext)
const [name, setName] = useState("") const [name, setName] = useState("")
@ -56,7 +60,7 @@ export const CreateFile: FC<CreateFileProps> = ({ isOpen, onOpenChange }) => {
renderInputs={() => ( renderInputs={() => (
<> <>
<div className="space-y-1"> <div className="space-y-1">
<Label>File</Label> <Label>{t("side.file")}</Label>
<Input <Input
type="file" type="file"
@ -66,10 +70,10 @@ export const CreateFile: FC<CreateFileProps> = ({ isOpen, onOpenChange }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input <Input
placeholder="File name..." placeholder={t("side.fileNamePlaceholder")}
value={name} value={name}
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
maxLength={FILE_NAME_MAX} maxLength={FILE_NAME_MAX}
@ -77,10 +81,10 @@ export const CreateFile: FC<CreateFileProps> = ({ isOpen, onOpenChange }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Description</Label> <Label>{t("side.description")}</Label>
<Input <Input
placeholder="File description..." placeholder={t("side.fileDescriptionPlaceholder")}
value={name} value={name}
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
maxLength={FILE_DESCRIPTION_MAX} maxLength={FILE_DESCRIPTION_MAX}

View File

@ -6,12 +6,14 @@ import { getFileFromStorage } from "@/db/storage/files"
import { Tables } from "@/supabase/types" import { Tables } from "@/supabase/types"
import { FC, useState } from "react" import { FC, useState } from "react"
import { SidebarItem } from "../all/sidebar-display-item" import { SidebarItem } from "../all/sidebar-display-item"
import { useTranslation } from 'react-i18next'
interface FileItemProps { interface FileItemProps {
file: Tables<"files"> file: Tables<"files">
} }
export const FileItem: FC<FileItemProps> = ({ file }) => { export const FileItem: FC<FileItemProps> = ({ file }) => {
const { t } = useTranslation()
const [name, setName] = useState(file.name) const [name, setName] = useState(file.name)
const [isTyping, setIsTyping] = useState(false) const [isTyping, setIsTyping] = useState(false)
const [description, setDescription] = useState(file.description) const [description, setDescription] = useState(file.description)
@ -34,7 +36,7 @@ export const FileItem: FC<FileItemProps> = ({ file }) => {
className="cursor-pointer underline hover:opacity-50" className="cursor-pointer underline hover:opacity-50"
onClick={getLinkAndView} onClick={getLinkAndView}
> >
View {file.name} {t("side.view")} {file.name}
</div> </div>
<div className="flex flex-col justify-between"> <div className="flex flex-col justify-between">
@ -46,10 +48,10 @@ export const FileItem: FC<FileItemProps> = ({ file }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input <Input
placeholder="File name..." placeholder={t("side.fileNamePlaceholder")}
value={name} value={name}
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
maxLength={FILE_NAME_MAX} maxLength={FILE_NAME_MAX}
@ -57,10 +59,10 @@ export const FileItem: FC<FileItemProps> = ({ file }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Description</Label> <Label>{t("side.description")}</Label>
<Input <Input
placeholder="File description..." placeholder={t("side.fileDescriptionPlaceholder")}
value={description} value={description}
onChange={e => setDescription(e.target.value)} onChange={e => setDescription(e.target.value)}
maxLength={FILE_DESCRIPTION_MAX} maxLength={FILE_DESCRIPTION_MAX}

View File

@ -16,6 +16,7 @@ import { ContentType } from "@/types"
import { IconTrash } from "@tabler/icons-react" import { IconTrash } from "@tabler/icons-react"
import { FC, useContext, useRef, useState } from "react" import { FC, useContext, useRef, useState } from "react"
import { toast } from "sonner" import { toast } from "sonner"
import { useTranslation } from 'react-i18next'
interface DeleteFolderProps { interface DeleteFolderProps {
folder: Tables<"folders"> folder: Tables<"folders">
@ -26,6 +27,7 @@ export const DeleteFolder: FC<DeleteFolderProps> = ({
folder, folder,
contentType contentType
}) => { }) => {
const { t } = useTranslation()
const { const {
setChats, setChats,
setFolders, setFolders,
@ -107,16 +109,16 @@ export const DeleteFolder: FC<DeleteFolderProps> = ({
<DialogContent className="min-w-[550px]"> <DialogContent className="min-w-[550px]">
<DialogHeader> <DialogHeader>
<DialogTitle>Delete {folder.name}</DialogTitle> <DialogTitle>{t("side.delete")} {folder.name}</DialogTitle>
<DialogDescription> <DialogDescription>
Are you sure you want to delete this folder? {t("side.confirmDeleteFolder")}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogFooter> <DialogFooter>
<Button variant="ghost" onClick={() => setShowFolderDialog(false)}> <Button variant="ghost" onClick={() => setShowFolderDialog(false)}>
Cancel {t("side.cancel")}
</Button> </Button>
<Button <Button
@ -124,7 +126,7 @@ export const DeleteFolder: FC<DeleteFolderProps> = ({
variant="destructive" variant="destructive"
onClick={handleDeleteFolderAndItems} onClick={handleDeleteFolderAndItems}
> >
Delete Folder & Included Items {t("side.deleteFolderWithContents")}
</Button> </Button>
<Button <Button
@ -132,7 +134,7 @@ export const DeleteFolder: FC<DeleteFolderProps> = ({
variant="destructive" variant="destructive"
onClick={handleDeleteFolderOnly} onClick={handleDeleteFolderOnly}
> >
Delete Folder Only {t("side.deleteFolderOnly")}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

View File

@ -14,12 +14,14 @@ import { updateFolder } from "@/db/folders"
import { Tables } from "@/supabase/types" import { Tables } from "@/supabase/types"
import { IconEdit } from "@tabler/icons-react" import { IconEdit } from "@tabler/icons-react"
import { FC, useContext, useRef, useState } from "react" import { FC, useContext, useRef, useState } from "react"
import { useTranslation } from 'react-i18next'
interface UpdateFolderProps { interface UpdateFolderProps {
folder: Tables<"folders"> folder: Tables<"folders">
} }
export const UpdateFolder: FC<UpdateFolderProps> = ({ folder }) => { export const UpdateFolder: FC<UpdateFolderProps> = ({ folder }) => {
const { t } = useTranslation()
const { setFolders } = useContext(ChatbotUIContext) const { setFolders } = useContext(ChatbotUIContext)
const buttonRef = useRef<HTMLButtonElement>(null) const buttonRef = useRef<HTMLButtonElement>(null)
@ -52,22 +54,22 @@ export const UpdateFolder: FC<UpdateFolderProps> = ({ folder }) => {
<DialogContent onKeyDown={handleKeyDown}> <DialogContent onKeyDown={handleKeyDown}>
<DialogHeader> <DialogHeader>
<DialogTitle>Edit Folder</DialogTitle> <DialogTitle>{t("side.editFolder")}</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input value={name} onChange={e => setName(e.target.value)} /> <Input value={name} onChange={e => setName(e.target.value)} />
</div> </div>
<DialogFooter> <DialogFooter>
<Button variant="ghost" onClick={() => setShowFolderDialog(false)}> <Button variant="ghost" onClick={() => setShowFolderDialog(false)}>
Cancel {t("side.cancel")}
</Button> </Button>
<Button ref={buttonRef} onClick={handleUpdateFolder}> <Button ref={buttonRef} onClick={handleUpdateFolder}>
Save {t("side.save")}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

View File

@ -5,6 +5,7 @@ import { ChatbotUIContext } from "@/context/context"
import { MODEL_NAME_MAX } from "@/db/limits" import { MODEL_NAME_MAX } from "@/db/limits"
import { TablesInsert } from "@/supabase/types" import { TablesInsert } from "@/supabase/types"
import { FC, useContext, useState } from "react" import { FC, useContext, useState } from "react"
import { useTranslation, Trans } from 'react-i18next'
interface CreateModelProps { interface CreateModelProps {
isOpen: boolean isOpen: boolean
@ -12,6 +13,7 @@ interface CreateModelProps {
} }
export const CreateModel: FC<CreateModelProps> = ({ isOpen, onOpenChange }) => { export const CreateModel: FC<CreateModelProps> = ({ isOpen, onOpenChange }) => {
const { t } = useTranslation()
const { profile, selectedWorkspace } = useContext(ChatbotUIContext) const { profile, selectedWorkspace } = useContext(ChatbotUIContext)
const [isTyping, setIsTyping] = useState(false) const [isTyping, setIsTyping] = useState(false)
@ -45,19 +47,20 @@ export const CreateModel: FC<CreateModelProps> = ({ isOpen, onOpenChange }) => {
renderInputs={() => ( renderInputs={() => (
<> <>
<div className="space-y-1.5 text-sm"> <div className="space-y-1.5 text-sm">
<div>Create a custom model.</div> <div>{t("side.createCustomModel")}</div>
<div> <div>
Your API <span className="font-bold">*must*</span> be compatible {/* Your API <span className="font-bold">*must*</span> be compatible
with the OpenAI SDK. with the OpenAI SDK. */}
<Trans i18nKey="side.apiCompatibilityWarning" components={{ strong: <span className="font-bold" /> }} />
</div> </div>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Name</Label> <Label>{t("side.name")}</Label>
<Input <Input
placeholder="Model name..." placeholder={t("side.modelNamePlaceholder")}
value={name} value={name}
onChange={e => setName(e.target.value)} onChange={e => setName(e.target.value)}
maxLength={MODEL_NAME_MAX} maxLength={MODEL_NAME_MAX}
@ -65,42 +68,43 @@ export const CreateModel: FC<CreateModelProps> = ({ isOpen, onOpenChange }) => {
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Model ID</Label> <Label>{t("side.modelId")}</Label>
<Input <Input
placeholder="Model ID..." placeholder={t("side.modelIdPlaceholder")}
value={modelId} value={modelId}
onChange={e => setModelId(e.target.value)} onChange={e => setModelId(e.target.value)}
/> />
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Base URL</Label> <Label>{t("side.baseUrl")}</Label>
<Input <Input
placeholder="Base URL..." placeholder={t("side.baseUrlPlaceholder")}
value={baseUrl} value={baseUrl}
onChange={e => setBaseUrl(e.target.value)} onChange={e => setBaseUrl(e.target.value)}
/> />
<div className="pt-1 text-xs italic"> <div className="pt-1 text-xs italic">
Your API must be compatible with the OpenAI SDK. {t("side.apiCompatibilityNotice")}
</div> </div>
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>API Key</Label> <Label>{t("side.apiKey")}</Label>
<Input <Input
type="password" type="password"
placeholder="API Key..." placeholder={t("side.apiKeyPlaceholder")}
value={apiKey} value={apiKey}
onChange={e => setApiKey(e.target.value)} onChange={e => setApiKey(e.target.value)}
/> />
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label>Max Context Length</Label> <Label>{t("side.maxContextLength")}</Label>
<Input <Input
type="number" type="number"

View File

@ -166,7 +166,33 @@
"editChat": "Edit Chat", "editChat": "Edit Chat",
"confirmDelete": "Are you sure you want to delete?", "confirmDelete": "Are you sure you want to delete?",
"edit": "Edit", "edit": "Edit",
"assignedWorkspaces": "Assigned Workspaces" "assignedWorkspaces": "Assigned Workspaces",
"searchFilesPlaceholder": "Search files...",
"filesSelected": "files selected",
"toolsSelected": "tools selected",
"searchToolsPlaceholder": "Search tools...",
"files": "Files",
"file": "File",
"collectionDescriptionPlaceholder": "Collection description...",
"collectionNamePlaceholder": "Collection name...",
"fileNamePlaceholder": "File name...",
"fileDescriptionPlaceholder": "File description...",
"view": "View",
"confirmDeleteFolder": "Are you sure you want to delete this folder?",
"deleteFolderWithContents": "Delete Folder & Included Items",
"deleteFolderOnly": "Delete Folder Only",
"editFolder": "Edit Folder",
"createCustomModel": "Create a custom model.",
"apiCompatibilityWarning": "Your API <strong>*must*</strong> be compatible with the OpenAI SDK.",
"apiCompatibilityNotice": "Your API must be compatible with the OpenAI SDK.",
"modelNamePlaceholder": "Model name...",
"modelId": "Model ID",
"modelIdPlaceholder": "Model ID...",
"baseUrl": "Base URL",
"baseUrlPlaceholder": "Base URL...",
"apiKey": "API Key",
"apiKeyPlaceholder": "API Key...",
"maxContextLength": "Max Context Length"
}, },
"contentType": { "contentType": {

View File

@ -165,7 +165,33 @@
"editChat": "チャットを編集", "editChat": "チャットを編集",
"confirmDelete": "本当に削除しますか?", "confirmDelete": "本当に削除しますか?",
"edit": "編集", "edit": "編集",
"assignedWorkspaces": "割り当てられたワークスペース" "assignedWorkspaces": "割り当てられたワークスペース",
"searchFilesPlaceholder": "ファイルを検索...",
"filesSelected": "件のファイルが選択されました",
"toolsSelected": "件のツールが選択されました",
"searchToolsPlaceholder": "ツールを検索...",
"files": "ファイル",
"file": "ファイル",
"collectionDescriptionPlaceholder": "コレクションの説明...",
"collectionNamePlaceholder": "コレクション名...",
"fileNamePlaceholder": "ファイル名...",
"fileDescriptionPlaceholder": "ファイルの説明...",
"view": "表示",
"confirmDeleteFolder": "このフォルダーを削除してもよろしいですか?",
"deleteFolderWithContents": "フォルダーと含まれる項目を削除",
"deleteFolderOnly": "フォルダーのみ削除",
"editFolder": "フォルダーを編集",
"createCustomModel": "カスタムモデルを作成します。",
"apiCompatibilityWarning": "あなたの API は <strong>*完全に*</strong> OpenAI SDK と互換である必要があります。",
"apiCompatibilityNotice": "あなたの API は OpenAI SDK と互換である必要があります。",
"modelNamePlaceholder": "モデル名...",
"modelId": "モデル ID",
"modelIdPlaceholder": "モデル ID...",
"baseUrl": "ベース URL",
"baseUrlPlaceholder": "ベース URL...",
"apiKey": "API キー",
"apiKeyPlaceholder": "API キー...",
"maxContextLength": "最大コンテキスト長"
}, },
"contentType": { "contentType": {

View File

@ -165,7 +165,33 @@
"editChat": "编辑对话", "editChat": "编辑对话",
"confirmDelete": "你确定要删除吗?", "confirmDelete": "你确定要删除吗?",
"edit": "编辑", "edit": "编辑",
"assignedWorkspaces": "已分配的工作区" "assignedWorkspaces": "已分配的工作区",
"searchFilesPlaceholder": "搜索文件...",
"filesSelected": "个文件已选择",
"toolsSelected": "个工具已选择",
"searchToolsPlaceholder": "搜索工具...",
"files": "文件",
"file": "文件",
"collectionDescriptionPlaceholder": "集合描述...",
"collectionNamePlaceholder": "集合名称...",
"fileNamePlaceholder": "文件名称...",
"fileDescriptionPlaceholder": "文件描述...",
"view": "查看",
"confirmDeleteFolder": "你确定要删除这个文件夹吗?",
"deleteFolderWithContents": "删除文件夹及其内容",
"deleteFolderOnly": "仅删除文件夹",
"editFolder": "编辑文件夹",
"createCustomModel": "创建自定义模型。",
"apiCompatibilityWarning": "您的 API <strong>*必须*</strong> 与 OpenAI SDK 兼容。",
"apiCompatibilityNotice": "您的 API 必须与 OpenAI SDK 兼容。",
"modelNamePlaceholder": "模型名称...",
"modelId": "模型 ID",
"modelIdPlaceholder": "模型 ID...",
"baseUrl": "基础地址",
"baseUrlPlaceholder": "基础地址...",
"apiKey": "API 密钥",
"apiKeyPlaceholder": "API 密钥...",
"maxContextLength": "最大上下文长度"
}, },
"contentType": { "contentType": {