Skip to content

Commit cd92036

Browse files
author
heweikang
committed
Add page to search chat history
1 parent edb92f7 commit cd92036

10 files changed

Lines changed: 12522 additions & 1278 deletions

File tree

app/components/home.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ const MaskPage = dynamic(async () => (await import("./mask")).MaskPage, {
5959
loading: () => <Loading noLogo />,
6060
});
6161

62+
const SearchChat = dynamic(
63+
async () => (await import("./search-chat")).SearchChatPage,
64+
{
65+
loading: () => <Loading noLogo />,
66+
},
67+
);
68+
6269
const Sd = dynamic(async () => (await import("./sd")).Sd, {
6370
loading: () => <Loading noLogo />,
6471
});
@@ -174,6 +181,7 @@ function Screen() {
174181
<Route path={Path.Home} element={<Chat />} />
175182
<Route path={Path.NewChat} element={<NewChat />} />
176183
<Route path={Path.Masks} element={<MaskPage />} />
184+
<Route path={Path.SearchChat} element={<SearchChat />} />
177185
<Route path={Path.Chat} element={<Chat />} />
178186
<Route path={Path.Settings} element={<Settings />} />
179187
</Routes>

app/components/search-chat.tsx

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { useState, useEffect } from "react";
2+
import { ErrorBoundary } from "./error";
3+
import styles from "./mask.module.scss";
4+
import { useNavigate } from "react-router-dom";
5+
import { IconButton } from "./button";
6+
import CloseIcon from "../icons/close.svg";
7+
import EyeIcon from "../icons/eye.svg";
8+
import Locale from "../locales";
9+
import { Path } from "../constant";
10+
11+
import { useChatStore } from "../store";
12+
13+
type Item = {
14+
id: number;
15+
name: string;
16+
content: string;
17+
};
18+
export function SearchChatPage() {
19+
const navigate = useNavigate();
20+
21+
const chatStore = useChatStore();
22+
23+
const sessions = chatStore.sessions;
24+
const selectSession = chatStore.selectSession;
25+
26+
const [searchResults, setSearchResults] = useState<Item[]>([]);
27+
28+
const setDefaultItems = () => {
29+
setSearchResults(
30+
sessions.slice(1, 7).map((session, index) => {
31+
console.log(session.messages[0]);
32+
return {
33+
id: index,
34+
name: session.topic,
35+
content: session.messages[0].content as string, //.map((m) => m.content).join("\n")
36+
};
37+
}),
38+
);
39+
};
40+
useEffect(() => {
41+
setDefaultItems();
42+
}, []);
43+
44+
const doSearch = (text: string) => {
45+
// 分割关键词
46+
const keywords = text.split(" ");
47+
48+
// 存储每个会话的匹配结果
49+
const searchResults: Item[] = [];
50+
51+
sessions.forEach((session, index) => {
52+
let matchCount = 0;
53+
const contents: string[] = [];
54+
55+
session.messages.forEach((message) => {
56+
const content = message.content as string;
57+
const lowerCaseContent = content.toLowerCase();
58+
keywords.forEach((keyword) => {
59+
const pos = lowerCaseContent.indexOf(keyword.toLowerCase());
60+
if (pos !== -1) {
61+
matchCount++;
62+
// 提取关键词前后70个字符的内容
63+
const start = Math.max(0, pos - 35);
64+
const end = Math.min(content.length, pos + keyword.length + 35);
65+
contents.push(content.substring(start, end));
66+
}
67+
});
68+
});
69+
70+
if (matchCount > 0) {
71+
searchResults.push({
72+
id: index,
73+
name: session.topic,
74+
content: contents.join("... "), // 使用...连接不同消息中的内容
75+
});
76+
}
77+
});
78+
79+
// 按匹配数量排序,取前10个结果
80+
return searchResults
81+
.sort((a, b) => b.content.length - a.content.length)
82+
.slice(0, 10);
83+
};
84+
85+
return (
86+
<ErrorBoundary>
87+
<div className={styles["mask-page"]}>
88+
{/* header */}
89+
<div className="window-header">
90+
<div className="window-header-title">
91+
<div className="window-header-main-title">
92+
{Locale.SearchChat.Page.Title}
93+
</div>
94+
<div className="window-header-submai-title">
95+
{Locale.SearchChat.Page.SubTitle(searchResults.length)}
96+
</div>
97+
</div>
98+
99+
<div className="window-actions">
100+
<div className="window-action-button">
101+
<IconButton
102+
icon={<CloseIcon />}
103+
bordered
104+
onClick={() => navigate(-1)}
105+
/>
106+
</div>
107+
</div>
108+
</div>
109+
110+
<div className={styles["mask-page-body"]}>
111+
<div className={styles["mask-filter"]}>
112+
{/**搜索输入框 */}
113+
<input
114+
type="text"
115+
className={styles["search-bar"]}
116+
placeholder={Locale.SearchChat.Page.Search}
117+
autoFocus
118+
onKeyDown={(e) => {
119+
if (e.key === "Enter") {
120+
e.preventDefault();
121+
const searchText = e.currentTarget.value;
122+
if (searchText.length > 0) {
123+
const result = doSearch(searchText);
124+
setSearchResults(result);
125+
}
126+
}
127+
}}
128+
/>
129+
</div>
130+
131+
<div>
132+
{searchResults.map((item) => (
133+
<div className={styles["mask-item"]} key={item.id}>
134+
{/** 搜索匹配的文本 */}
135+
<div className={styles["mask-header"]}>
136+
<div className={styles["mask-title"]}>
137+
<div className={styles["mask-name"]}>{item.name}</div>
138+
{item.content.slice(0, 70)}
139+
</div>
140+
</div>
141+
{/** 操作按钮 */}
142+
<div className={styles["mask-actions"]}>
143+
<IconButton
144+
icon={<EyeIcon />}
145+
text={Locale.SearchChat.Item.View}
146+
onClick={() => {
147+
navigate(Path.Chat);
148+
selectSession(item.id);
149+
}}
150+
/>
151+
</div>
152+
</div>
153+
))}
154+
</div>
155+
</div>
156+
</div>
157+
</ErrorBoundary>
158+
);
159+
}

app/components/sidebar.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,15 @@ export function SideBar(props: { className?: string }) {
250250
onClick={() => setShowPluginSelector(true)}
251251
shadow
252252
/>
253+
<IconButton
254+
icon={<DiscoveryIcon />}
255+
text={shouldNarrow ? undefined : Locale.SearchChat.Name}
256+
className={styles["sidebar-bar-button"]}
257+
onClick={() =>
258+
navigate(Path.SearchChat, { state: { fromHome: true } })
259+
}
260+
shadow
261+
/>
253262
</div>
254263
{showPluginSelector && (
255264
<Selector

app/constant.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export enum Path {
4141
Sd = "/sd",
4242
SdNew = "/sd-new",
4343
Artifacts = "/artifacts",
44+
SearchChat = "/search-chat",
4445
}
4546

4647
export enum ApiPath {

app/locales/cn.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,21 @@ const cn = {
519519
FineTuned: {
520520
Sysmessage: "你是一个助手",
521521
},
522+
SearchChat: {
523+
Name: "搜索",
524+
Page: {
525+
Title: "搜索聊天记录",
526+
Search: "输入多个关键词(空格分隔), 回车搜索",
527+
NoResult: "没有找到结果",
528+
NoData: "没有数据",
529+
Loading: "加载中",
530+
531+
SubTitle: (count: number) => `搜索到 ${count} 条结果`,
532+
},
533+
Item: {
534+
View: "查看",
535+
},
536+
},
522537
Mask: {
523538
Name: "面具",
524539
Page: {

app/locales/en.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,22 @@ const en: LocaleType = {
527527
FineTuned: {
528528
Sysmessage: "You are an assistant that",
529529
},
530+
SearchChat: {
531+
Name: "Search",
532+
Page: {
533+
Title: "Search Chat History",
534+
Search:
535+
"Enter multiple keywords (separated by spaces), press Enter to search",
536+
NoResult: "No results found",
537+
NoData: "No data",
538+
Loading: "Loading...",
539+
540+
SubTitle: (count: number) => `Found ${count} results`,
541+
},
542+
Item: {
543+
View: "View",
544+
},
545+
},
530546
Mask: {
531547
Name: "Mask",
532548
Page: {

app/locales/jp.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,22 @@ const jp: PartialLocaleType = {
244244
},
245245
Plugin: { Name: "プラグイン" },
246246
FineTuned: { Sysmessage: "あなたはアシスタントです" },
247+
SearchChat: {
248+
Name: "検索",
249+
Page: {
250+
Title: "チャット履歴を検索",
251+
Search:
252+
"複数のキーワードを入力してください(スペースで区切る)、エンターキーを押して検索",
253+
NoResult: "結果が見つかりませんでした",
254+
NoData: "データがありません",
255+
Loading: "読み込み中...",
256+
257+
SubTitle: (count: number) => `${count} 件の結果を見つけました`,
258+
},
259+
Item: {
260+
View: "表示",
261+
},
262+
},
247263
Mask: {
248264
Name: "キャラクタープリセット",
249265
Page: {

app/locales/ru.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ const ru: PartialLocaleType = {
192192
FineTuned: {
193193
Sysmessage: "Вы - ассистент, который",
194194
},
195+
SearchChat: {
196+
Name: "Поиск",
197+
Page: {
198+
Title: "Поиск в истории чата",
199+
Search:
200+
"Введите несколько ключевых слов (разделенных пробелами), нажмите Enter для поиска",
201+
NoResult: "Результаты не найдены",
202+
NoData: "Данные отсутствуют",
203+
Loading: "Загрузка...",
204+
205+
SubTitle: (count: number) => `Найдено результатов: ${count}`,
206+
},
207+
Item: {
208+
View: "Просмотр",
209+
},
210+
},
195211
Mask: {
196212
Name: "Маска",
197213
Page: {

0 commit comments

Comments
 (0)