Update language files and fix UI issues
This commit is contained in:
@@ -26,7 +26,7 @@ const LinkComponent = (item: {
|
||||
item.current === item.href
|
||||
? "bg-gray-100 text-gray-600 dark:bg-[#262626] dark:text-white"
|
||||
: "text-gray-700 hover:text-gray-600 hover:bg-gray-100 dark:text-gray-200 dark:hover:text-white dark:hover:bg-[#262626]",
|
||||
"group flex gap-x-3 rounded-md py-2 pl-2 pr-3 text-sm leading-6 font-semibold"
|
||||
"group flex gap-x-3 rounded-md py-2 pl-2 pr-3 text-sm font-semibold"
|
||||
)}>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
|
||||
@@ -14,7 +14,7 @@ dayjs.extend(relativeTime)
|
||||
export const ModelsBody = () => {
|
||||
const queryClient = useQueryClient()
|
||||
const [open, setOpen] = useState(false)
|
||||
const { t } = useTranslation("option")
|
||||
const { t } = useTranslation(["option", "common"])
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
@@ -188,9 +188,13 @@ export const ModelsBody = () => {
|
||||
}
|
||||
]}
|
||||
dataSource={[record.details]}
|
||||
locale={{
|
||||
emptyText: t("common:noData")
|
||||
}}
|
||||
/>
|
||||
),
|
||||
defaultExpandAllRows: false
|
||||
defaultExpandAllRows: false,
|
||||
|
||||
}}
|
||||
bordered
|
||||
dataSource={data}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { RotateCcw } from "lucide-react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import {
|
||||
getOllamaURL,
|
||||
isOllamaRunning,
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
|
||||
export const PlaygroundEmpty = () => {
|
||||
const [ollamaURL, setOllamaURL] = useState<string>("")
|
||||
const { t } = useTranslation(["playground", "common"])
|
||||
const {
|
||||
data: ollamaInfo,
|
||||
status: ollamaStatus,
|
||||
@@ -40,7 +42,7 @@ export const PlaygroundEmpty = () => {
|
||||
<div className="inline-flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-blue-500 rounded-full animate-bounce"></div>
|
||||
<p className="dark:text-gray-400 text-gray-900">
|
||||
Searching for Your Ollama 🦙
|
||||
{t("ollamaState.searching")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -49,7 +51,7 @@ export const PlaygroundEmpty = () => {
|
||||
<div className="inline-flex items-center space-x-2">
|
||||
<div className="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
<p className="dark:text-gray-400 text-gray-900">
|
||||
Ollama is running 🦙
|
||||
{t("ollamaState.running")}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -57,7 +59,7 @@ export const PlaygroundEmpty = () => {
|
||||
<div className="inline-flex space-x-2">
|
||||
<div className="w-3 h-3 bg-red-500 rounded-full"></div>
|
||||
<p className="dark:text-gray-400 text-gray-900">
|
||||
Unable to connect to Ollama 🦙
|
||||
{t("ollamaState.notRunning")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -75,7 +77,7 @@ export const PlaygroundEmpty = () => {
|
||||
}}
|
||||
className="inline-flex mt-4 items-center rounded-md border border-transparent bg-black px-2 py-2 text-sm font-medium leading-4 text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:bg-white dark:text-gray-800 dark:hover:bg-gray-100 dark:focus:ring-gray-500 dark:focus:ring-offset-gray-100 disabled:opacity-50 ">
|
||||
<RotateCcw className="h-4 w-4 mr-3" />
|
||||
Retry
|
||||
{t("common:retry")}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,12 +11,14 @@ import { useWebUI } from "~/store/webui"
|
||||
import { defaultEmbeddingModelForRag } from "~/services/ollama"
|
||||
import { ImageIcon, MicIcon, StopCircleIcon, X } from "lucide-react"
|
||||
import { getVariable } from "~/utils/select-varaible"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
type Props = {
|
||||
dropedFile: File | undefined
|
||||
}
|
||||
|
||||
export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
const { t } = useTranslation(["playground", "common"])
|
||||
const inputRef = React.useRef<HTMLInputElement>(null)
|
||||
const [typing, setTyping] = React.useState<boolean>(false)
|
||||
const {
|
||||
@@ -117,13 +119,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
})
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === "Process" || e.key === "229" ) return
|
||||
if (e.key === "Process" || e.key === "229") return
|
||||
if (
|
||||
!typing &&
|
||||
e.key === "Enter" &&
|
||||
!e.shiftKey &&
|
||||
!isSending &&
|
||||
sendWhenEnter
|
||||
sendWhenEnter
|
||||
) {
|
||||
e.preventDefault()
|
||||
form.onSubmit(async (value) => {
|
||||
@@ -131,16 +133,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
return
|
||||
}
|
||||
if (!selectedModel || selectedModel.length === 0) {
|
||||
form.setFieldError("message", "Please select a model")
|
||||
form.setFieldError("message", t("formError.noModel"))
|
||||
return
|
||||
}
|
||||
if (webSearch) {
|
||||
const defaultEM = await defaultEmbeddingModelForRag()
|
||||
if (!defaultEM) {
|
||||
form.setFieldError(
|
||||
"message",
|
||||
"Please set an embedding model on the Settings > Ollama page"
|
||||
)
|
||||
form.setFieldError("message", t("formError.noEmbeddingModel"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -181,16 +180,13 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
<form
|
||||
onSubmit={form.onSubmit(async (value) => {
|
||||
if (!selectedModel || selectedModel.length === 0) {
|
||||
form.setFieldError("message", "Please select a model")
|
||||
form.setFieldError("message", t("formError.noModel"))
|
||||
return
|
||||
}
|
||||
if (webSearch) {
|
||||
const defaultEM = await defaultEmbeddingModelForRag()
|
||||
if (!defaultEM) {
|
||||
form.setFieldError(
|
||||
"message",
|
||||
"Please set an embedding model on the Settings > Ollama page"
|
||||
)
|
||||
form.setFieldError("message", t("formError.noEmbeddingModel"))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -223,12 +219,12 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
rows={1}
|
||||
style={{ minHeight: "60px" }}
|
||||
tabIndex={0}
|
||||
placeholder="Type a message..."
|
||||
placeholder={t("form.textarea.placeholder")}
|
||||
{...form.getInputProps("message")}
|
||||
/>
|
||||
<div className="mt-4 flex justify-between items-center">
|
||||
<div className="flex">
|
||||
<Tooltip title="Search Internet">
|
||||
<Tooltip title={t("tooltip.searchInternet")}>
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -246,14 +242,14 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
<Switch
|
||||
value={webSearch}
|
||||
onChange={(e) => setWebSearch(e)}
|
||||
checkedChildren="On"
|
||||
unCheckedChildren="Off"
|
||||
checkedChildren={t("form.webSearch.on")}
|
||||
unCheckedChildren={t("form.webSearch.off")}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex !justify-end gap-3">
|
||||
<Tooltip title="Voice Message">
|
||||
<Tooltip title={t("tooltip.speechToText")}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -277,7 +273,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
)}
|
||||
</button>
|
||||
</Tooltip>
|
||||
<Tooltip title="Upload Image">
|
||||
<Tooltip title={t("tooltip.uploadImage")}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -319,7 +315,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
onChange={(e) =>
|
||||
setSendWhenEnter(e.target.checked)
|
||||
}>
|
||||
Send when Enter pressed
|
||||
{t("sendWhenEnter")}
|
||||
</Checkbox>
|
||||
)
|
||||
}
|
||||
@@ -340,11 +336,11 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
|
||||
<path d="M20 4v7a4 4 0 01-4 4H4"></path>
|
||||
</svg>
|
||||
) : null}
|
||||
Submit
|
||||
{t("common:submit")}
|
||||
</div>
|
||||
</Dropdown.Button>
|
||||
) : (
|
||||
<Tooltip title="Stop Streaming">
|
||||
<Tooltip title={t("tooltip.stopStreaming")}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={stopStreamingRequest}
|
||||
|
||||
@@ -7,7 +7,8 @@ import {
|
||||
Modal,
|
||||
Input,
|
||||
Form,
|
||||
Switch
|
||||
Switch,
|
||||
Empty
|
||||
} from "antd"
|
||||
import { Trash2, Pen, Computer, Zap } from "lucide-react"
|
||||
import { useState } from "react"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { SUPPORTED_LANGUAGES } from "~/utils/supporetd-languages"
|
||||
import { MoonIcon, SunIcon } from "lucide-react"
|
||||
import { SearchModeSettings } from "./search-mode"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useI18n } from "@/hooks/useI18n"
|
||||
|
||||
export const SettingOther = () => {
|
||||
const { clearChat, speechToTextLanguage, setSpeechToTextLanguage } =
|
||||
@@ -16,6 +17,11 @@ export const SettingOther = () => {
|
||||
|
||||
const { mode, toggleDarkMode } = useDarkMode()
|
||||
const { t } = useTranslation("option")
|
||||
const {
|
||||
changeLocale,
|
||||
locale,
|
||||
supportLanguage
|
||||
}= useI18n()
|
||||
|
||||
|
||||
return (
|
||||
@@ -46,6 +52,26 @@ export const SettingOther = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span className="text-gray-500 dark:text-neutral-50">
|
||||
{t("generalSettings.settings.language.label")}
|
||||
</span>
|
||||
|
||||
<Select
|
||||
placeholder={t("generalSettings.settings.language.placeholder")}
|
||||
allowClear
|
||||
showSearch
|
||||
options={supportLanguage}
|
||||
value={locale}
|
||||
filterOption={(input, option) =>
|
||||
option!.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 ||
|
||||
option!.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
}
|
||||
onChange={(value) => {
|
||||
changeLocale(value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<span className="text-gray-500 dark:text-neutral-50 ">
|
||||
{t("generalSettings.settings.darkMode.label")}
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
} from "~/libs/db"
|
||||
import { Empty, Skeleton } from "antd"
|
||||
import { useMessageOption } from "~/hooks/useMessageOption"
|
||||
import { useState } from "react"
|
||||
import { PencilIcon, Trash2 } from "lucide-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
@@ -19,6 +19,7 @@ type Props = {
|
||||
export const Sidebar = ({ onClose }: Props) => {
|
||||
const { setMessages, setHistory, setHistoryId, historyId, clearChat } =
|
||||
useMessageOption()
|
||||
const { t } = useTranslation(["option", "common"])
|
||||
const client = useQueryClient()
|
||||
const navigate = useNavigate()
|
||||
|
||||
@@ -60,7 +61,7 @@ export const Sidebar = ({ onClose }: Props) => {
|
||||
<div className="overflow-y-auto z-99">
|
||||
{status === "success" && chatHistories.length === 0 && (
|
||||
<div className="flex justify-center items-center mt-20 overflow-hidden">
|
||||
<Empty description="No history yet" />
|
||||
<Empty description={t("common:noHistory")} />
|
||||
</div>
|
||||
)}
|
||||
{status === "pending" && (
|
||||
@@ -95,7 +96,7 @@ export const Sidebar = ({ onClose }: Props) => {
|
||||
<div className="flex flex-row gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
const newTitle = prompt("Enter new title", chat.title)
|
||||
const newTitle = prompt(t("editHistoryTitle"), chat.title)
|
||||
|
||||
if (newTitle) {
|
||||
editHistory({ id: chat.id, title: newTitle })
|
||||
@@ -107,10 +108,7 @@ export const Sidebar = ({ onClose }: Props) => {
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
if (
|
||||
!confirm("Are you sure you want to delete this history?")
|
||||
)
|
||||
return
|
||||
if (!confirm(t("deleteHistoryConfirmation"))) return
|
||||
deleteHistory(chat.id)
|
||||
}}
|
||||
className="text-red-500 dark:text-red-400 opacity-80">
|
||||
|
||||
Reference in New Issue
Block a user