{item.error}
@@ -134,6 +133,7 @@ export function Sd() {
{showMaxIcon && (
:
}
bordered
onClick={() => {
@@ -189,13 +189,13 @@ export function Sd() {
className={styles["sd-img-item-info"]}
>
- {locales.SdPanel.Prompt}:{" "}
+ {Locale.SdPanel.Prompt}:{" "}
{
showModal({
- title: locales.Sd.Detail,
+ title: Locale.Sd.Detail,
children: (
{item.params.prompt}
@@ -208,7 +208,7 @@ export function Sd() {
- {locales.SdPanel.AIModel}: {item.model_name}
+ {Locale.SdPanel.AIModel}: {item.model_name}
{getSdTaskStatus(item)}
{item.created_at}
@@ -219,7 +219,7 @@ export function Sd() {
icon={
}
onClick={() => {
showModal({
- title: locales.Sd.GenerateParams,
+ title: Locale.Sd.GenerateParams,
children: (
{Object.keys(item.params).map((key) => {
@@ -325,7 +325,7 @@ export function Sd() {
);
})
) : (
-
{locales.Sd.EmptyRecord}
+
{Locale.Sd.EmptyRecord}
)}
diff --git a/app/components/search-chat.tsx b/app/components/search-chat.tsx
new file mode 100644
index 000000000..7178865f5
--- /dev/null
+++ b/app/components/search-chat.tsx
@@ -0,0 +1,167 @@
+import { useState, useEffect, useRef, useCallback } from "react";
+import { ErrorBoundary } from "./error";
+import styles from "./mask.module.scss";
+import { useNavigate } from "react-router-dom";
+import { IconButton } from "./button";
+import CloseIcon from "../icons/close.svg";
+import EyeIcon from "../icons/eye.svg";
+import Locale from "../locales";
+import { Path } from "../constant";
+
+import { useChatStore } from "../store";
+
+type Item = {
+ id: number;
+ name: string;
+ content: string;
+};
+export function SearchChatPage() {
+ const navigate = useNavigate();
+
+ const chatStore = useChatStore();
+
+ const sessions = chatStore.sessions;
+ const selectSession = chatStore.selectSession;
+
+ const [searchResults, setSearchResults] = useState- ([]);
+
+ const previousValueRef = useRef("");
+ const searchInputRef = useRef(null);
+ const doSearch = useCallback((text: string) => {
+ const lowerCaseText = text.toLowerCase();
+ const results: Item[] = [];
+
+ sessions.forEach((session, index) => {
+ const fullTextContents: string[] = [];
+
+ session.messages.forEach((message) => {
+ const content = message.content as string;
+ if (!content.toLowerCase || content === "") return;
+ const lowerCaseContent = content.toLowerCase();
+
+ // full text search
+ let pos = lowerCaseContent.indexOf(lowerCaseText);
+ while (pos !== -1) {
+ const start = Math.max(0, pos - 35);
+ const end = Math.min(content.length, pos + lowerCaseText.length + 35);
+ fullTextContents.push(content.substring(start, end));
+ pos = lowerCaseContent.indexOf(
+ lowerCaseText,
+ pos + lowerCaseText.length,
+ );
+ }
+ });
+
+ if (fullTextContents.length > 0) {
+ results.push({
+ id: index,
+ name: session.topic,
+ content: fullTextContents.join("... "), // concat content with...
+ });
+ }
+ });
+
+ // sort by length of matching content
+ results.sort((a, b) => b.content.length - a.content.length);
+
+ return results;
+ }, []);
+
+ useEffect(() => {
+ const intervalId = setInterval(() => {
+ if (searchInputRef.current) {
+ const currentValue = searchInputRef.current.value;
+ if (currentValue !== previousValueRef.current) {
+ if (currentValue.length > 0) {
+ const result = doSearch(currentValue);
+ setSearchResults(result);
+ }
+ previousValueRef.current = currentValue;
+ }
+ }
+ }, 1000);
+
+ // Cleanup the interval on component unmount
+ return () => clearInterval(intervalId);
+ }, [doSearch]);
+
+ return (
+
+
+ {/* header */}
+
+
+
+ {Locale.SearchChat.Page.Title}
+
+
+ {Locale.SearchChat.Page.SubTitle(searchResults.length)}
+
+
+
+
+
+ }
+ bordered
+ onClick={() => navigate(-1)}
+ />
+
+
+
+
+
+
+ {/**搜索输入框 */}
+ {
+ if (e.key === "Enter") {
+ e.preventDefault();
+ const searchText = e.currentTarget.value;
+ if (searchText.length > 0) {
+ const result = doSearch(searchText);
+ setSearchResults(result);
+ }
+ }
+ }}
+ />
+
+
+
+ {searchResults.map((item) => (
+
{
+ navigate(Path.Chat);
+ selectSession(item.id);
+ }}
+ style={{ cursor: "pointer" }}
+ >
+ {/** 搜索匹配的文本 */}
+
+
+
{item.name}
+ {item.content.slice(0, 70)}
+
+
+ {/** 操作按钮 */}
+
+ }
+ text={Locale.SearchChat.Item.View}
+ />
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/app/components/settings.tsx b/app/components/settings.tsx
index 319781225..ca0a5a187 100644
--- a/app/components/settings.tsx
+++ b/app/components/settings.tsx
@@ -68,6 +68,7 @@ import {
SlotID,
UPDATE_URL,
Stability,
+ Iflytek,
} from "../constant";
import { Prompt, SearchService, usePromptStore } from "../store/prompt";
import { ErrorBoundary } from "./error";
@@ -245,6 +246,7 @@ function DangerItems() {
subTitle={Locale.Settings.Danger.Reset.SubTitle}
>
{
if (await showConfirm(Locale.Settings.Danger.Reset.Confirm)) {
@@ -259,6 +261,7 @@ function DangerItems() {
subTitle={Locale.Settings.Danger.Clear.SubTitle}
>
{
if (await showConfirm(Locale.Settings.Danger.Clear.Confirm)) {
@@ -512,6 +515,7 @@ function SyncItems() {
>
}
text={Locale.UI.Config}
onClick={() => {
@@ -542,6 +546,7 @@ function SyncItems() {
>
}
text={Locale.UI.Export}
onClick={() => {
@@ -549,6 +554,7 @@ function SyncItems() {
}}
/>
}
text={Locale.UI.Import}
onClick={() => {
@@ -686,6 +692,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.CustomEndpoint.SubTitle}
>
@@ -705,6 +712,7 @@ export function Settings() {
subTitle={Locale.Settings.Access.OpenAI.Endpoint.SubTitle}
>