Skip to content

Commit 7152096

Browse files
authored
Merge pull request #650 from n4ze3m/next
v1.5.21
2 parents 321350c + 940c59c commit 7152096

File tree

6 files changed

+101
-122
lines changed

6 files changed

+101
-122
lines changed

src/assets/locale/en/common.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
},
187187
"mermaid": "Mermaid",
188188
"search": "Search",
189+
"searchResults": "Search Results",
189190
"embeddingGen": "Creating embeddings, this may take a while",
190191
"semanticSearch": "Performing semantic search",
191192
"newBranch": "New Branch"

src/components/Option/Sidebar.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
useMutation,
3-
useQuery,
43
useQueryClient,
54
useInfiniteQuery
65
} from "@tanstack/react-query"
@@ -99,7 +98,17 @@ export const Sidebar = ({
9998
debouncedSearchQuery || undefined
10099
)
101100

102-
// Group the histories by date as before
101+
// If searching, don't group by date - just return all results in a single group
102+
if (debouncedSearchQuery) {
103+
console.log("Search results:", result.histories)
104+
return {
105+
groups: result.histories.length > 0 ? [{ label: "searchResults", items: result.histories }] : [],
106+
hasMore: result.hasMore,
107+
totalCount: result.totalCount
108+
}
109+
}
110+
111+
// Group the histories by date only when not searching
103112
const now = new Date()
104113
const today = new Date(now.setHours(0, 0, 0, 0))
105114
const yesterday = new Date(today)
@@ -146,7 +155,7 @@ export const Sidebar = ({
146155
getNextPageParam: (lastPage, allPages) => {
147156
return lastPage.hasMore ? allPages.length + 1 : undefined
148157
},
149-
placeholderData: (prev) => prev,
158+
placeholderData: undefined,
150159
enabled: isOpen,
151160
initialPageParam: 1
152161
})
@@ -253,6 +262,8 @@ export const Sidebar = ({
253262
}
254263
}
255264

265+
console.log("Chat histories data:", chatHistoriesData)
266+
256267
return (
257268
<div
258269
className={`overflow-y-auto z-99 ${temporaryChat ? "pointer-events-none opacity-50" : ""}`}>
@@ -301,26 +312,34 @@ export const Sidebar = ({
301312
<div key={groupIndex}>
302313
<div className="flex items-center justify-between mt-2">
303314
<h3 className="px-2 text-sm font-medium text-gray-500">
304-
{t(`common:date:${group.label}`)}
315+
{group.label === "searchResults"
316+
? t("common:searchResults")
317+
: t(`common:date:${group.label}`)}
305318
</h3>
306-
<Tooltip
307-
title={t(`common:range:tooltip:${group.label}`)}
308-
placement="top">
309-
<button
310-
onClick={() => handleDeleteHistoriesByRange(group.label)}>
311-
{deleteRangeLoading && deleteGroup === group.label ? (
312-
<Loader2 className="w-4 h-4 text-gray-500 hover:text-gray-700 dark:hover:text-gray-200 animate-spin" />
313-
) : (
314-
<Trash2Icon className="w-4 h-4 text-gray-500 hover:text-gray-700 dark:hover:text-gray-200" />
315-
)}
316-
</button>
317-
</Tooltip>
319+
{group.label !== "searchResults" && (
320+
<Tooltip
321+
title={t(`common:range:tooltip:${group.label}`)}
322+
placement="top">
323+
<button
324+
onClick={() => handleDeleteHistoriesByRange(group.label)}>
325+
{deleteRangeLoading && deleteGroup === group.label ? (
326+
<Loader2 className="w-4 h-4 text-gray-500 hover:text-gray-700 dark:hover:text-gray-200 animate-spin" />
327+
) : (
328+
<Trash2Icon className="w-4 h-4 text-gray-500 hover:text-gray-700 dark:hover:text-gray-200" />
329+
)}
330+
</button>
331+
</Tooltip>
332+
)}
318333
</div>
319334
<div className="flex flex-col gap-2 mt-2">
320335
{group.items.map((chat, index) => (
321-
<div
322-
key={index}
323-
className="flex py-2 px-2 items-center gap-3 relative rounded-md truncate hover:pr-4 group transition-opacity duration-300 ease-in-out bg-gray-100 dark:bg-[#232222] dark:text-gray-100 text-gray-800 border hover:bg-gray-200 dark:hover:bg-[#2d2d2d] dark:border-gray-800">
336+
<div
337+
key={chat.id}
338+
className={`flex py-2 px-2 items-center gap-3 relative rounded-md truncate hover:pr-4 group transition-opacity duration-300 ease-in-out border ${
339+
historyId === chat.id
340+
? "bg-gray-200 dark:bg-[#454242] border-gray-400 dark:border-gray-600 text-gray-900 dark:text-gray-100"
341+
: "bg-gray-50 dark:bg-[#232222] dark:text-gray-100 text-gray-800 border-gray-300 dark:border-gray-800 hover:bg-gray-200 dark:hover:bg-[#2d2d2d]"
342+
}`}>
324343
{chat?.message_source === "copilot" && (
325344
<BotIcon className="size-3 text-gray-500 dark:text-gray-400" />
326345
)}

src/db/dexie/chat.ts

Lines changed: 59 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { getAllModelNicknames } from '../nickname';
21
import {
32
ChatHistory,
43
HistoryInfo,
@@ -13,7 +12,21 @@ import {
1312

1413
} from "./types"
1514
import { db } from './schema';
16-
const PAGE_SIZE = 30;
15+
import { getAllModelNicknames } from "./nickname";
16+
const PAGE_SIZE = 30;
17+
18+
function searchQueryInContent(content: string, query: string): boolean {
19+
if (!content || !query) {
20+
return false;
21+
}
22+
23+
const normalizedContent = content.toLowerCase();
24+
const normalizedQuery = query.toLowerCase().trim();
25+
26+
const wordBoundaryPattern = new RegExp(`\\b${normalizedQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i');
27+
28+
return wordBoundaryPattern.test(normalizedContent);
29+
}
1730

1831
function fastForward(lastRow: any, idProp: string, otherCriterion?: (item: any) => boolean) {
1932
let fastForwardComplete = false;
@@ -26,50 +39,6 @@ function fastForward(lastRow: any, idProp: string, otherCriterion?: (item: any)
2639
};
2740
}
2841

29-
function simpleFuzzyMatch(text: string, query: string): boolean {
30-
if (!text || !query) {
31-
return false;
32-
}
33-
34-
const lowerText = text.toLowerCase();
35-
const lowerQuery = query.toLowerCase().trim();
36-
37-
if (lowerQuery === '') {
38-
return true;
39-
}
40-
41-
if (lowerText.includes(lowerQuery)) {
42-
return true;
43-
}
44-
45-
const queryWords = lowerQuery.split(/\s+/).filter((word) => word.length > 2);
46-
47-
if (queryWords.length > 1) {
48-
const matchedWords = queryWords.filter((word) => lowerText.includes(word));
49-
return matchedWords.length >= Math.ceil(queryWords.length * 0.7);
50-
}
51-
52-
if (lowerQuery.length > 3) {
53-
const maxDistance = Math.floor(lowerQuery.length * 0.3);
54-
55-
const textWords = lowerText.split(/\s+/);
56-
return textWords.some((word) => {
57-
if (Math.abs(word.length - lowerQuery.length) > maxDistance) {
58-
return false;
59-
}
60-
let matches = 0;
61-
for (let i = 0; i < lowerQuery.length; i++) {
62-
if (word.includes(lowerQuery[i])) {
63-
matches++;
64-
}
65-
}
66-
67-
return matches >= lowerQuery.length - maxDistance;
68-
});
69-
}
70-
71-
return false;
72-
}
7342

7443

7544
export class PageAssistDatabase {
@@ -154,6 +123,38 @@ export class PageAssistDatabase {
154123
return await db.chatHistories.orderBy('createdAt').reverse().toArray();
155124
}
156125

126+
async fullTextSearchChatHistories(query: string): Promise<ChatHistory> {
127+
const normalizedQuery = query.toLowerCase().trim();
128+
if (!normalizedQuery) {
129+
return this.getChatHistories();
130+
}
131+
132+
const titleMatches = await db.chatHistories
133+
.where('title')
134+
.startsWithIgnoreCase(normalizedQuery)
135+
.or('title')
136+
.anyOfIgnoreCase(normalizedQuery.split(' '))
137+
.toArray();
138+
139+
const messageMatches = await db.messages
140+
.filter(message => searchQueryInContent(message.content, normalizedQuery))
141+
.toArray();
142+
143+
const historyIdsFromMessages = [...new Set(messageMatches.map(msg => msg.history_id))];
144+
145+
const historiesFromMessages = await db.chatHistories
146+
.where('id')
147+
.anyOf(historyIdsFromMessages)
148+
.toArray();
149+
150+
const allMatches = [...titleMatches, ...historiesFromMessages];
151+
const uniqueHistories = allMatches.filter((history, index, self) =>
152+
index === self.findIndex(h => h.id === history.id)
153+
);
154+
155+
return uniqueHistories.sort((a, b) => b.createdAt - a.createdAt);
156+
}
157+
157158
async getChatHistoryTitleById(id: string): Promise<string> {
158159
const chatHistory = await db.chatHistories.get(id);
159160
return chatHistory?.title || '';
@@ -228,11 +229,12 @@ export class PageAssistDatabase {
228229
totalCount: number;
229230
}> {
230231
const offset = (page - 1) * PAGE_SIZE;
231-
232+
232233
if (searchQuery) {
233-
const allResults = await this.searchChatHistories(searchQuery);
234-
const paginatedResults = allResults.slice(offset, offset + PAGE_SIZE);
235-
234+
console.log("Searching chat histories with query:", searchQuery);
235+
const allResults = await this.fullTextSearchChatHistories(searchQuery);
236+
const paginatedResults = allResults.slice(offset, offset + PAGE_SIZE)
237+
console.log("Paginated search results:", paginatedResults);
236238
return {
237239
histories: paginatedResults,
238240
hasMore: offset + PAGE_SIZE < allResults.length,
@@ -246,9 +248,9 @@ export class PageAssistDatabase {
246248
.reverse()
247249
.limit(PAGE_SIZE)
248250
.toArray();
249-
251+
250252
const totalCount = await db.chatHistories.count();
251-
253+
252254
return {
253255
histories,
254256
hasMore: histories.length === PAGE_SIZE,
@@ -262,9 +264,9 @@ export class PageAssistDatabase {
262264
.offset(skipCount)
263265
.limit(PAGE_SIZE)
264266
.toArray();
265-
267+
266268
const totalCount = await db.chatHistories.count();
267-
269+
268270
return {
269271
histories,
270272
hasMore: offset + PAGE_SIZE < totalCount,
@@ -277,7 +279,7 @@ export class PageAssistDatabase {
277279
hasMore: boolean;
278280
}> {
279281
if (searchQuery) {
280-
const allResults = await this.searchChatHistories(searchQuery);
282+
const allResults = await this.fullTextSearchChatHistories(searchQuery);
281283
return {
282284
histories: allResults.slice(0, PAGE_SIZE),
283285
hasMore: allResults.length > PAGE_SIZE
@@ -290,7 +292,7 @@ export class PageAssistDatabase {
290292
.reverse()
291293
.limit(PAGE_SIZE)
292294
.toArray();
293-
295+
294296
return {
295297
histories,
296298
hasMore: histories.length === PAGE_SIZE
@@ -303,7 +305,7 @@ export class PageAssistDatabase {
303305
.limit(PAGE_SIZE)
304306
.reverse()
305307
.toArray();
306-
308+
307309
return {
308310
histories,
309311
hasMore: histories.length === PAGE_SIZE
@@ -359,50 +361,7 @@ export class PageAssistDatabase {
359361
await db.userSettings.put({ id: 'main', user_id: id });
360362
}
361363

362-
// Search Methods
363-
async searchChatHistories(query: string): Promise<ChatHistory> {
364-
const normalizedQuery = query.toLowerCase().trim();
365-
if (!normalizedQuery) {
366-
return this.getChatHistories();
367-
}
368-
369-
const allHistories = await this.getChatHistories();
370-
const matchedHistories: ChatHistory = [];
371-
const matchedHistoryIds = new Set<string>();
372-
373-
for (const history of allHistories) {
374-
if (simpleFuzzyMatch(history.title, normalizedQuery)) {
375-
if (!matchedHistoryIds.has(history.id)) {
376-
matchedHistories.push(history);
377-
matchedHistoryIds.add(history.id);
378-
}
379-
continue;
380-
}
381364

382-
try {
383-
const messages = await this.getChatHistory(history.id);
384-
for (const message of messages) {
385-
if (
386-
message.content &&
387-
simpleFuzzyMatch(message.content, normalizedQuery)
388-
) {
389-
if (!matchedHistoryIds.has(history.id)) {
390-
matchedHistories.push(history);
391-
matchedHistoryIds.add(history.id);
392-
}
393-
break;
394-
}
395-
}
396-
} catch (error) {
397-
console.error(
398-
`Error fetching messages for history ${history.id}:`,
399-
error
400-
);
401-
}
402-
}
403-
404-
return matchedHistories;
405-
}
406365
async importChatHistoryV2(data: any[], options: {
407366
replaceExisting?: boolean;
408367
mergeData?: boolean;

src/models/CustomAIMessageChunk.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class CustomAIMessageChunk {
5151
value as NonNullable<BaseMessageFields["additional_kwargs"]>
5252
);
5353
} else {
54-
throw new Error(
54+
console.warn(
5555
`additional_kwargs[${key}] already exists in this message chunk and cannot be merged.`
5656
);
5757
}

src/models/CustomChatOpenAI.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ function _convertDeltaToMessageChunk(
101101
const role = delta.role ?? defaultRole
102102
const content = delta.content ?? ""
103103
const reasoning_content: string | undefined | null =
104-
delta?.reasoning_content ?? undefined
104+
delta?.reasoning_content ?? delta?.reasoning ?? undefined
105105
let additional_kwargs
106106
if (delta?.function_call) {
107107
additional_kwargs = {

wxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export default defineConfig({
5151
outDir: "build",
5252

5353
manifest: {
54-
version: "1.5.20",
54+
version: "1.5.21",
5555
name:
5656
process.env.TARGET === "firefox"
5757
? "Page Assist - A Web UI for Local AI Models"

0 commit comments

Comments
 (0)