@lobehub/chat 1.64.0 → 1.64.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/src/components/WebFavicon/index.tsx +26 -0
- package/src/config/aiModels/siliconcloud.ts +2 -1
- package/src/config/aiModels/volcengine.ts +2 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +1 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +6 -0
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +1 -1
- package/src/store/chat/slices/builtinTool/{action.test.ts → actions/dalle.test.ts} +2 -2
- package/src/store/chat/slices/builtinTool/actions/dalle.ts +126 -0
- package/src/store/chat/slices/builtinTool/actions/index.ts +18 -0
- package/src/store/chat/slices/builtinTool/{action.ts → actions/searXNG.ts} +15 -116
- package/src/store/chat/slices/message/action.ts +1 -1
- package/src/store/chat/store.ts +1 -1
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/TitleExtra.tsx +5 -1
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/Video.tsx +6 -1
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/index.tsx +8 -3
- package/src/tools/web-browsing/Render/SearchQuery/index.tsx +2 -2
- package/src/tools/web-browsing/Render/SearchResult/SearchResultItem.tsx +2 -9
- package/src/tools/web-browsing/Render/index.tsx +1 -1
- package/src/tools/web-browsing/const.ts +10 -9
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,56 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.64.2](https://github.com/lobehub/lobe-chat/compare/v1.64.1...v1.64.2)
|
6
|
+
|
7
|
+
<sup>Released on **2025-02-25**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix 0 search results with specific search engine.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix 0 search results with specific search engine, closes [#6487](https://github.com/lobehub/lobe-chat/issues/6487) ([74a09e2](https://github.com/lobehub/lobe-chat/commit/74a09e2))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.64.1](https://github.com/lobehub/lobe-chat/compare/v1.64.0...v1.64.1)
|
31
|
+
|
32
|
+
<sup>Released on **2025-02-25**</sup>
|
33
|
+
|
34
|
+
#### 🐛 Bug Fixes
|
35
|
+
|
36
|
+
- **misc**: Disable fc for ds-v3 series.
|
37
|
+
|
38
|
+
<br/>
|
39
|
+
|
40
|
+
<details>
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
42
|
+
|
43
|
+
#### What's fixed
|
44
|
+
|
45
|
+
- **misc**: Disable fc for ds-v3 series, closes [#6486](https://github.com/lobehub/lobe-chat/issues/6486) ([0092213](https://github.com/lobehub/lobe-chat/commit/0092213))
|
46
|
+
|
47
|
+
</details>
|
48
|
+
|
49
|
+
<div align="right">
|
50
|
+
|
51
|
+
[](#readme-top)
|
52
|
+
|
53
|
+
</div>
|
54
|
+
|
5
55
|
## [Version 1.64.0](https://github.com/lobehub/lobe-chat/compare/v1.63.3...v1.64.0)
|
6
56
|
|
7
57
|
<sup>Released on **2025-02-24**</sup>
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Fix 0 search results with specific search engine."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-02-25",
|
9
|
+
"version": "1.64.2"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"fixes": [
|
14
|
+
"Disable fc for ds-v3 series."
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"date": "2025-02-25",
|
18
|
+
"version": "1.64.1"
|
19
|
+
},
|
2
20
|
{
|
3
21
|
"children": {
|
4
22
|
"features": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.64.
|
3
|
+
"version": "1.64.2",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import Image from 'next/image';
|
2
|
+
|
3
|
+
interface WebFaviconProps {
|
4
|
+
alt?: string;
|
5
|
+
size?: number;
|
6
|
+
title?: string;
|
7
|
+
url: string;
|
8
|
+
}
|
9
|
+
|
10
|
+
const WebFavicon = ({ url, title, alt, size = 14 }: WebFaviconProps) => {
|
11
|
+
const urlObj = new URL(url);
|
12
|
+
const host = urlObj.hostname;
|
13
|
+
|
14
|
+
return (
|
15
|
+
<Image
|
16
|
+
alt={alt || title || url}
|
17
|
+
height={size}
|
18
|
+
src={`https://icons.duckduckgo.com/ip3/${host}.ico`}
|
19
|
+
style={{ borderRadius: 4 }}
|
20
|
+
unoptimized
|
21
|
+
width={size}
|
22
|
+
/>
|
23
|
+
);
|
24
|
+
};
|
25
|
+
|
26
|
+
export default WebFavicon;
|
@@ -22,7 +22,8 @@ const siliconcloudChatModels: AIChatModelCard[] = [
|
|
22
22
|
},
|
23
23
|
{
|
24
24
|
abilities: {
|
25
|
-
|
25
|
+
// Not support tool use, ref: https://cloud.siliconflow.cn/models?target=deepseek-ai%2FDeepSeek-V3
|
26
|
+
functionCall: false,
|
26
27
|
},
|
27
28
|
contextWindowTokens: 65_536,
|
28
29
|
description:
|
@@ -67,7 +67,8 @@ const doubaoChatModels: AIChatModelCard[] = [
|
|
67
67
|
},
|
68
68
|
{
|
69
69
|
abilities: {
|
70
|
-
|
70
|
+
// FC not supported yet, ref: https://www.volcengine.com/docs/82379/1262342#8c325d45
|
71
|
+
functionCall: false,
|
71
72
|
},
|
72
73
|
config: {
|
73
74
|
deploymentName: 'deepseek-v3-241226',
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Suspense, memo } from 'react';
|
2
2
|
|
3
|
+
import { LOADING_FLAT } from '@/const/message';
|
3
4
|
import ErrorResponse from '@/features/Conversation/Messages/Assistant/Tool/Render/ErrorResponse';
|
4
5
|
import { useChatStore } from '@/store/chat';
|
5
6
|
import { chatSelectors } from '@/store/chat/selectors';
|
@@ -28,6 +29,11 @@ const Render = memo<RenderProps>(
|
|
28
29
|
return <ErrorResponse {...toolMessage.error} id={messageId} plugin={toolMessage.plugin} />;
|
29
30
|
}
|
30
31
|
|
32
|
+
// 如果是 LOADING_FLAT 则说明还在加载中
|
33
|
+
// 而 standalone 模式的插件 content 应该始终是 LOADING_FLAT
|
34
|
+
if (toolMessage.content === LOADING_FLAT && toolMessage.plugin?.type !== 'standalone')
|
35
|
+
return <Arguments arguments={requestArgs} shine />;
|
36
|
+
|
31
37
|
return (
|
32
38
|
<Suspense fallback={<Arguments arguments={requestArgs} shine />}>
|
33
39
|
<CustomRender
|
@@ -10,9 +10,9 @@ import { chatSelectors } from '@/store/chat/selectors';
|
|
10
10
|
import { ChatMessage } from '@/types/message';
|
11
11
|
import { DallEImageItem } from '@/types/tool/dalle';
|
12
12
|
|
13
|
-
import { useChatStore } from '
|
13
|
+
import { useChatStore } from '../../../store';
|
14
14
|
|
15
|
-
describe('chatToolSlice', () => {
|
15
|
+
describe('chatToolSlice - dalle', () => {
|
16
16
|
describe('generateImageFromPrompts', () => {
|
17
17
|
it('should generate images from prompts, update items, and upload images', async () => {
|
18
18
|
const { result } = renderHook(() => useChatStore());
|
@@ -0,0 +1,126 @@
|
|
1
|
+
import { produce } from 'immer';
|
2
|
+
import pMap from 'p-map';
|
3
|
+
import { SWRResponse } from 'swr';
|
4
|
+
import { StateCreator } from 'zustand/vanilla';
|
5
|
+
|
6
|
+
import { useClientDataSWR } from '@/libs/swr';
|
7
|
+
import { fileService } from '@/services/file';
|
8
|
+
import { imageGenerationService } from '@/services/textToImage';
|
9
|
+
import { uploadService } from '@/services/upload';
|
10
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
11
|
+
import { ChatStore } from '@/store/chat/store';
|
12
|
+
import { useFileStore } from '@/store/file';
|
13
|
+
import { DallEImageItem } from '@/types/tool/dalle';
|
14
|
+
|
15
|
+
|
16
|
+
import { setNamespace } from '@/utils/storeDebug';
|
17
|
+
|
18
|
+
const n = setNamespace('tool');
|
19
|
+
|
20
|
+
const SWR_FETCH_KEY = 'FetchImageItem';
|
21
|
+
|
22
|
+
export interface ChatDallEAction {
|
23
|
+
generateImageFromPrompts: (items: DallEImageItem[], id: string) => Promise<void>;
|
24
|
+
text2image: (id: string, data: DallEImageItem[]) => Promise<void>;
|
25
|
+
toggleDallEImageLoading: (key: string, value: boolean) => void;
|
26
|
+
updateImageItem: (id: string, updater: (data: DallEImageItem[]) => void) => Promise<void>;
|
27
|
+
useFetchDalleImageItem: (id: string) => SWRResponse;
|
28
|
+
}
|
29
|
+
|
30
|
+
export const dalleSlice: StateCreator<
|
31
|
+
ChatStore,
|
32
|
+
[['zustand/devtools', never]],
|
33
|
+
[],
|
34
|
+
ChatDallEAction
|
35
|
+
> = (set, get) => ({
|
36
|
+
generateImageFromPrompts: async (items, messageId) => {
|
37
|
+
const { toggleDallEImageLoading, updateImageItem } = get();
|
38
|
+
// eslint-disable-next-line unicorn/consistent-function-scoping
|
39
|
+
const getMessageById = (id: string) => chatSelectors.getMessageById(id)(get());
|
40
|
+
|
41
|
+
const message = getMessageById(messageId);
|
42
|
+
if (!message) return;
|
43
|
+
|
44
|
+
const parent = getMessageById(message!.parentId!);
|
45
|
+
const originPrompt = parent?.content;
|
46
|
+
let errorArray: any[] = [];
|
47
|
+
|
48
|
+
await pMap(items, async (params, index) => {
|
49
|
+
toggleDallEImageLoading(messageId + params.prompt, true);
|
50
|
+
|
51
|
+
let url = '';
|
52
|
+
try {
|
53
|
+
url = await imageGenerationService.generateImage(params);
|
54
|
+
} catch (e) {
|
55
|
+
toggleDallEImageLoading(messageId + params.prompt, false);
|
56
|
+
errorArray[index] = e;
|
57
|
+
|
58
|
+
await get().updatePluginState(messageId, { error: errorArray });
|
59
|
+
}
|
60
|
+
|
61
|
+
if (!url) return;
|
62
|
+
|
63
|
+
await updateImageItem(messageId, (draft) => {
|
64
|
+
draft[index].previewUrl = url;
|
65
|
+
});
|
66
|
+
|
67
|
+
toggleDallEImageLoading(messageId + params.prompt, false);
|
68
|
+
const imageFile = await uploadService.getImageFileByUrlWithCORS(
|
69
|
+
url,
|
70
|
+
`${originPrompt || params.prompt}_${index}.png`,
|
71
|
+
);
|
72
|
+
|
73
|
+
const data = await useFileStore.getState().uploadWithProgress({
|
74
|
+
file: imageFile,
|
75
|
+
});
|
76
|
+
|
77
|
+
if (!data) return;
|
78
|
+
|
79
|
+
await updateImageItem(messageId, (draft) => {
|
80
|
+
draft[index].imageId = data.id;
|
81
|
+
draft[index].previewUrl = undefined;
|
82
|
+
});
|
83
|
+
});
|
84
|
+
},
|
85
|
+
text2image: async (id, data) => {
|
86
|
+
// const isAutoGen = settingsSelectors.isDalleAutoGenerating(useGlobalStore.getState());
|
87
|
+
// if (!isAutoGen) return;
|
88
|
+
|
89
|
+
await get().generateImageFromPrompts(data, id);
|
90
|
+
},
|
91
|
+
|
92
|
+
toggleDallEImageLoading: (key, value) => {
|
93
|
+
set(
|
94
|
+
{ dalleImageLoading: { ...get().dalleImageLoading, [key]: value } },
|
95
|
+
false,
|
96
|
+
n('toggleDallEImageLoading'),
|
97
|
+
);
|
98
|
+
},
|
99
|
+
|
100
|
+
updateImageItem: async (id, updater) => {
|
101
|
+
const message = chatSelectors.getMessageById(id)(get());
|
102
|
+
if (!message) return;
|
103
|
+
|
104
|
+
const data: DallEImageItem[] = JSON.parse(message.content);
|
105
|
+
|
106
|
+
const nextContent = produce(data, updater);
|
107
|
+
await get().internal_updateMessageContent(id, JSON.stringify(nextContent));
|
108
|
+
},
|
109
|
+
|
110
|
+
useFetchDalleImageItem: (id) =>
|
111
|
+
useClientDataSWR([SWR_FETCH_KEY, id], async () => {
|
112
|
+
const item = await fileService.getFile(id);
|
113
|
+
|
114
|
+
set(
|
115
|
+
produce((draft) => {
|
116
|
+
if (draft.dalleImageMap[id]) return;
|
117
|
+
|
118
|
+
draft.dalleImageMap[id] = item;
|
119
|
+
}),
|
120
|
+
false,
|
121
|
+
n('useFetchFile'),
|
122
|
+
);
|
123
|
+
|
124
|
+
return item;
|
125
|
+
}),
|
126
|
+
});
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { StateCreator } from 'zustand/vanilla';
|
2
|
+
|
3
|
+
import { ChatStore } from '@/store/chat/store';
|
4
|
+
|
5
|
+
import { ChatDallEAction, dalleSlice } from './dalle';
|
6
|
+
import { SearchAction, searchSlice } from './searXNG';
|
7
|
+
|
8
|
+
export interface ChatBuiltinToolAction extends ChatDallEAction, SearchAction {}
|
9
|
+
|
10
|
+
export const chatToolSlice: StateCreator<
|
11
|
+
ChatStore,
|
12
|
+
[['zustand/devtools', never]],
|
13
|
+
[],
|
14
|
+
ChatBuiltinToolAction
|
15
|
+
> = (...params) => ({
|
16
|
+
...dalleSlice(...params),
|
17
|
+
...searchSlice(...params),
|
18
|
+
});
|
@@ -1,35 +1,18 @@
|
|
1
|
-
import { produce } from 'immer';
|
2
|
-
import pMap from 'p-map';
|
3
|
-
import { SWRResponse } from 'swr';
|
4
1
|
import { StateCreator } from 'zustand/vanilla';
|
5
2
|
|
6
|
-
import { useClientDataSWR } from '@/libs/swr';
|
7
|
-
import { fileService } from '@/services/file';
|
8
3
|
import { searchService } from '@/services/search';
|
9
|
-
import { imageGenerationService } from '@/services/textToImage';
|
10
|
-
import { uploadService } from '@/services/upload';
|
11
4
|
import { chatSelectors } from '@/store/chat/selectors';
|
12
5
|
import { ChatStore } from '@/store/chat/store';
|
13
|
-
import { useFileStore } from '@/store/file';
|
14
6
|
import { CreateMessageParams } from '@/types/message';
|
15
|
-
import { DallEImageItem } from '@/types/tool/dalle';
|
16
7
|
import {
|
17
8
|
SEARCH_SEARXNG_NOT_CONFIG,
|
18
9
|
SearchContent,
|
19
10
|
SearchQuery,
|
20
11
|
SearchResponse,
|
21
12
|
} from '@/types/tool/search';
|
22
|
-
import { setNamespace } from '@/utils/storeDebug';
|
23
13
|
import { nanoid } from '@/utils/uuid';
|
24
14
|
|
25
|
-
|
26
|
-
|
27
|
-
const SWR_FETCH_KEY = 'FetchImageItem';
|
28
|
-
/**
|
29
|
-
* builtin tool action
|
30
|
-
*/
|
31
|
-
export interface ChatBuiltinToolAction {
|
32
|
-
generateImageFromPrompts: (items: DallEImageItem[], id: string) => Promise<void>;
|
15
|
+
export interface SearchAction {
|
33
16
|
/**
|
34
17
|
* 重新发起搜索
|
35
18
|
* @description 会更新插件的 arguments 参数,然后再次搜索
|
@@ -45,69 +28,15 @@ export interface ChatBuiltinToolAction {
|
|
45
28
|
data: SearchQuery,
|
46
29
|
aiSummary?: boolean,
|
47
30
|
) => Promise<void | boolean>;
|
48
|
-
text2image: (id: string, data: DallEImageItem[]) => Promise<void>;
|
49
|
-
|
50
|
-
toggleDallEImageLoading: (key: string, value: boolean) => void;
|
51
31
|
toggleSearchLoading: (id: string, loading: boolean) => void;
|
52
|
-
updateImageItem: (id: string, updater: (data: DallEImageItem[]) => void) => Promise<void>;
|
53
|
-
useFetchDalleImageItem: (id: string) => SWRResponse;
|
54
32
|
}
|
55
33
|
|
56
|
-
export const
|
34
|
+
export const searchSlice: StateCreator<
|
57
35
|
ChatStore,
|
58
36
|
[['zustand/devtools', never]],
|
59
37
|
[],
|
60
|
-
|
38
|
+
SearchAction
|
61
39
|
> = (set, get) => ({
|
62
|
-
generateImageFromPrompts: async (items, messageId) => {
|
63
|
-
const { toggleDallEImageLoading, updateImageItem } = get();
|
64
|
-
// eslint-disable-next-line unicorn/consistent-function-scoping
|
65
|
-
const getMessageById = (id: string) => chatSelectors.getMessageById(id)(get());
|
66
|
-
|
67
|
-
const message = getMessageById(messageId);
|
68
|
-
if (!message) return;
|
69
|
-
|
70
|
-
const parent = getMessageById(message!.parentId!);
|
71
|
-
const originPrompt = parent?.content;
|
72
|
-
let errorArray: any[] = [];
|
73
|
-
|
74
|
-
await pMap(items, async (params, index) => {
|
75
|
-
toggleDallEImageLoading(messageId + params.prompt, true);
|
76
|
-
|
77
|
-
let url = '';
|
78
|
-
try {
|
79
|
-
url = await imageGenerationService.generateImage(params);
|
80
|
-
} catch (e) {
|
81
|
-
toggleDallEImageLoading(messageId + params.prompt, false);
|
82
|
-
errorArray[index] = e;
|
83
|
-
|
84
|
-
await get().updatePluginState(messageId, { error: errorArray });
|
85
|
-
}
|
86
|
-
|
87
|
-
if (!url) return;
|
88
|
-
|
89
|
-
await updateImageItem(messageId, (draft) => {
|
90
|
-
draft[index].previewUrl = url;
|
91
|
-
});
|
92
|
-
|
93
|
-
toggleDallEImageLoading(messageId + params.prompt, false);
|
94
|
-
const imageFile = await uploadService.getImageFileByUrlWithCORS(
|
95
|
-
url,
|
96
|
-
`${originPrompt || params.prompt}_${index}.png`,
|
97
|
-
);
|
98
|
-
|
99
|
-
const data = await useFileStore.getState().uploadWithProgress({
|
100
|
-
file: imageFile,
|
101
|
-
});
|
102
|
-
|
103
|
-
if (!data) return;
|
104
|
-
|
105
|
-
await updateImageItem(messageId, (draft) => {
|
106
|
-
draft[index].imageId = data.id;
|
107
|
-
draft[index].previewUrl = undefined;
|
108
|
-
});
|
109
|
-
});
|
110
|
-
},
|
111
40
|
reSearchWithSearXNG: async (id, data, options) => {
|
112
41
|
get().toggleSearchLoading(id, true);
|
113
42
|
await get().updatePluginArguments(id, data);
|
@@ -158,6 +87,13 @@ export const chatToolSlice: StateCreator<
|
|
158
87
|
let data: SearchResponse | undefined;
|
159
88
|
try {
|
160
89
|
data = await searchService.search(params.query, params.searchEngines);
|
90
|
+
|
91
|
+
// 如果没有搜索到结果,那么尝试使用默认的搜索引擎再搜一次
|
92
|
+
if (data?.results.length === 0 && params.searchEngines && params.searchEngines?.length > 0) {
|
93
|
+
data = await searchService.search(params.query);
|
94
|
+
get().updatePluginArguments(id, { ...params, searchEngines: undefined });
|
95
|
+
}
|
96
|
+
|
161
97
|
await get().updatePluginState(id, data);
|
162
98
|
} catch (e) {
|
163
99
|
if ((e as Error).message === SEARCH_SEARXNG_NOT_CONFIG) {
|
@@ -181,8 +117,8 @@ export const chatToolSlice: StateCreator<
|
|
181
117
|
|
182
118
|
if (!data) return;
|
183
119
|
|
184
|
-
//
|
185
|
-
const searchContent: SearchContent[] = data.results.slice(0,
|
120
|
+
// add 15 search results to message content
|
121
|
+
const searchContent: SearchContent[] = data.results.slice(0, 15).map((item) => ({
|
186
122
|
content: item.content,
|
187
123
|
title: item.title,
|
188
124
|
url: item.url,
|
@@ -196,49 +132,12 @@ export const chatToolSlice: StateCreator<
|
|
196
132
|
// 如果 aiSummary 为 true,则会自动触发总结
|
197
133
|
return aiSummary;
|
198
134
|
},
|
199
|
-
text2image: async (id, data) => {
|
200
|
-
// const isAutoGen = settingsSelectors.isDalleAutoGenerating(useGlobalStore.getState());
|
201
|
-
// if (!isAutoGen) return;
|
202
|
-
|
203
|
-
await get().generateImageFromPrompts(data, id);
|
204
|
-
},
|
205
135
|
|
206
|
-
|
136
|
+
toggleSearchLoading: (id, loading) => {
|
207
137
|
set(
|
208
|
-
{
|
138
|
+
{ searchLoading: { ...get().searchLoading, [id]: loading } },
|
209
139
|
false,
|
210
|
-
|
140
|
+
`toggleSearchLoading/${loading ? 'start' : 'end'}`,
|
211
141
|
);
|
212
142
|
},
|
213
|
-
|
214
|
-
toggleSearchLoading: (id, loading) => {
|
215
|
-
set({ searchLoading: { ...get().searchLoading, [id]: loading } }, false, 'toggleSearchLoading');
|
216
|
-
},
|
217
|
-
|
218
|
-
updateImageItem: async (id, updater) => {
|
219
|
-
const message = chatSelectors.getMessageById(id)(get());
|
220
|
-
if (!message) return;
|
221
|
-
|
222
|
-
const data: DallEImageItem[] = JSON.parse(message.content);
|
223
|
-
|
224
|
-
const nextContent = produce(data, updater);
|
225
|
-
await get().internal_updateMessageContent(id, JSON.stringify(nextContent));
|
226
|
-
},
|
227
|
-
|
228
|
-
useFetchDalleImageItem: (id) =>
|
229
|
-
useClientDataSWR([SWR_FETCH_KEY, id], async () => {
|
230
|
-
const item = await fileService.getFile(id);
|
231
|
-
|
232
|
-
set(
|
233
|
-
produce((draft) => {
|
234
|
-
if (draft.dalleImageMap[id]) return;
|
235
|
-
|
236
|
-
draft.dalleImageMap[id] = item;
|
237
|
-
}),
|
238
|
-
false,
|
239
|
-
n('useFetchFile'),
|
240
|
-
);
|
241
|
-
|
242
|
-
return item;
|
243
|
-
}),
|
244
143
|
});
|
@@ -380,7 +380,7 @@ export const chatMessage: StateCreator<
|
|
380
380
|
messageLoadingIds: toggleBooleanList(get().messageLoadingIds, id, loading),
|
381
381
|
},
|
382
382
|
false,
|
383
|
-
|
383
|
+
`internal_toggleMessageLoading/${loading ? 'start' : 'end'}`,
|
384
384
|
);
|
385
385
|
},
|
386
386
|
internal_toggleLoadingArrays: (key, loading, id, action) => {
|
package/src/store/chat/store.ts
CHANGED
@@ -6,7 +6,7 @@ import { StateCreator } from 'zustand/vanilla';
|
|
6
6
|
|
7
7
|
import { createDevtools } from '../middleware/createDevtools';
|
8
8
|
import { ChatStoreState, initialState } from './initialState';
|
9
|
-
import { ChatBuiltinToolAction, chatToolSlice } from './slices/builtinTool/
|
9
|
+
import { ChatBuiltinToolAction, chatToolSlice } from './slices/builtinTool/actions';
|
10
10
|
import { ChatPortalAction, chatPortalSlice } from './slices/portal/action';
|
11
11
|
import { ChatTranslateAction, chatTranslate } from './slices/translate/action';
|
12
12
|
import { ChatMessageAction, chatMessage } from './slices/message/action';
|
@@ -4,19 +4,23 @@ import { memo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
6
6
|
|
7
|
+
import { EngineAvatarGroup } from '@/tools/web-browsing/components/EngineAvatar';
|
8
|
+
|
7
9
|
import CategoryAvatar from './CategoryAvatar';
|
8
10
|
|
9
11
|
interface TitleExtraProps {
|
10
12
|
category: string;
|
13
|
+
engines: string[];
|
11
14
|
highlight?: boolean;
|
12
15
|
score: number;
|
13
16
|
}
|
14
17
|
|
15
|
-
const TitleExtra = memo<TitleExtraProps>(({ category, score, highlight }) => {
|
18
|
+
const TitleExtra = memo<TitleExtraProps>(({ category, score, highlight, engines }) => {
|
16
19
|
const { t } = useTranslation('tool');
|
17
20
|
|
18
21
|
return (
|
19
22
|
<Flexbox align={'center'} gap={4} horizontal>
|
23
|
+
<EngineAvatarGroup engines={engines} />
|
20
24
|
<Tooltip title={t(highlight ? 'search.includedTooltip' : 'search.scoreTooltip')}>
|
21
25
|
{highlight ? (
|
22
26
|
<Tag bordered={false} color={'blue'} style={{ marginInlineEnd: 0 }}>
|
@@ -113,7 +113,12 @@ const VideoItem = memo<SearchResultProps>(
|
|
113
113
|
</AntAvatar.Group>
|
114
114
|
<Flexbox className={styles.title}>{title}</Flexbox>
|
115
115
|
</Flexbox>
|
116
|
-
<TitleExtra
|
116
|
+
<TitleExtra
|
117
|
+
category={category}
|
118
|
+
engines={engines}
|
119
|
+
highlight={highlight}
|
120
|
+
score={score}
|
121
|
+
/>
|
117
122
|
</Flexbox>
|
118
123
|
<Typography.Text className={styles.url} type={'secondary'}>
|
119
124
|
{url}
|
@@ -3,9 +3,9 @@ import { createStyles } from 'antd-style';
|
|
3
3
|
import { memo } from 'react';
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
5
5
|
|
6
|
+
import WebFavicon from '@/components/WebFavicon';
|
6
7
|
import { SearchResult } from '@/types/tool/search';
|
7
8
|
|
8
|
-
import { EngineAvatarGroup } from '../../../components/EngineAvatar';
|
9
9
|
import TitleExtra from './TitleExtra';
|
10
10
|
import Video from './Video';
|
11
11
|
|
@@ -73,10 +73,15 @@ const SearchItem = memo<SearchResultProps>((props) => {
|
|
73
73
|
<Flexbox gap={8}>
|
74
74
|
<Flexbox align={'center'} distribution={'space-between'} horizontal>
|
75
75
|
<Flexbox align={'center'} gap={8} horizontal>
|
76
|
-
<
|
76
|
+
<WebFavicon title={title} url={url} />
|
77
77
|
<Flexbox className={styles.title}>{title}</Flexbox>
|
78
78
|
</Flexbox>
|
79
|
-
<TitleExtra
|
79
|
+
<TitleExtra
|
80
|
+
category={category}
|
81
|
+
engines={engines}
|
82
|
+
highlight={props.highlight}
|
83
|
+
score={score}
|
84
|
+
/>
|
80
85
|
</Flexbox>
|
81
86
|
<Typography.Text className={styles.url} type={'secondary'}>
|
82
87
|
{url}
|
@@ -33,8 +33,8 @@ const SearchQueryView = memo<SearchQueryViewProps>(
|
|
33
33
|
|
34
34
|
return !pluginState ? (
|
35
35
|
<Flexbox align={'center'} distribution={'space-between'} height={32} horizontal>
|
36
|
-
<Skeleton.Button active style={{ borderRadius:
|
37
|
-
<Skeleton.Button active style={{ borderRadius:
|
36
|
+
<Skeleton.Button active style={{ borderRadius: 4, height: 32, width: 180 }} />
|
37
|
+
<Skeleton.Button active style={{ borderRadius: 4, height: 32, width: 220 }} />
|
38
38
|
</Flexbox>
|
39
39
|
) : editing ? (
|
40
40
|
<SearchBar
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import { Typography } from 'antd';
|
2
2
|
import { createStyles } from 'antd-style';
|
3
|
-
import Image from 'next/image';
|
4
3
|
import Link from 'next/link';
|
5
4
|
import { memo } from 'react';
|
6
5
|
import { Flexbox } from 'react-layout-kit';
|
7
6
|
|
7
|
+
import WebFavicon from '@/components/WebFavicon';
|
8
8
|
import { SearchResult } from '@/types/tool/search';
|
9
9
|
|
10
10
|
const useStyles = createStyles(({ css, token }) => ({
|
@@ -52,14 +52,7 @@ const SearchResultItem = memo<SearchResult>(({ url, title }) => {
|
|
52
52
|
<Flexbox className={styles.container} gap={2} justify={'space-between'} key={url}>
|
53
53
|
<div className={styles.title}>{title}</div>
|
54
54
|
<Flexbox align={'center'} gap={4} horizontal>
|
55
|
-
<
|
56
|
-
alt={title || url}
|
57
|
-
height={14}
|
58
|
-
src={`https://icons.duckduckgo.com/ip3/${host}.ico`}
|
59
|
-
style={{ borderRadius: 4 }}
|
60
|
-
unoptimized
|
61
|
-
width={14}
|
62
|
-
/>
|
55
|
+
<WebFavicon size={14} title={title} url={url} />
|
63
56
|
<Typography.Text className={styles.url} type={'secondary'}>
|
64
57
|
{host.replace('www.', '')}
|
65
58
|
</Typography.Text>
|
@@ -1,11 +1,12 @@
|
|
1
1
|
export const ENGINE_ICON_MAP: Record<string, string> = {
|
2
|
-
arxiv: 'https://
|
3
|
-
bilibili: 'https://
|
4
|
-
bing: 'https://www.bing.com
|
5
|
-
brave: 'https://
|
6
|
-
duckduckgo: 'https://
|
7
|
-
google: 'https://
|
8
|
-
|
9
|
-
|
10
|
-
|
2
|
+
'arxiv': 'https://icons.duckduckgo.com/ip3/arxiv.org.ico',
|
3
|
+
'bilibili': 'https://icons.duckduckgo.com/ip3/bilibili.com.ico',
|
4
|
+
'bing': 'https://icons.duckduckgo.com/ip3/www.bing.com.ico',
|
5
|
+
'brave': 'https://icons.duckduckgo.com/ip3/brave.com.ico',
|
6
|
+
'duckduckgo': 'https://icons.duckduckgo.com/ip3/www.duckduckgo.com.ico',
|
7
|
+
'google': 'https://icons.duckduckgo.com/ip3/google.com.ico',
|
8
|
+
'google scholar': 'https://icons.duckduckgo.com/ip3/scholar.google.com.ico',
|
9
|
+
'npm': 'https://icons.duckduckgo.com/ip3/npmjs.com.ico',
|
10
|
+
'qwant': 'https://icons.duckduckgo.com/ip3/www.qwant.com.ico',
|
11
|
+
'youtube': 'https://icons.duckduckgo.com/ip3/youtube.com.ico',
|
11
12
|
};
|