@lobehub/chat 1.63.2 → 1.64.0
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/locales/ar/models.json +25 -16
- package/locales/ar/plugin.json +16 -0
- package/locales/ar/portal.json +0 -5
- package/locales/ar/tool.json +18 -0
- package/locales/bg-BG/models.json +25 -16
- package/locales/bg-BG/plugin.json +16 -0
- package/locales/bg-BG/portal.json +0 -5
- package/locales/bg-BG/tool.json +18 -0
- package/locales/de-DE/models.json +25 -16
- package/locales/de-DE/plugin.json +16 -0
- package/locales/de-DE/portal.json +0 -5
- package/locales/de-DE/tool.json +18 -0
- package/locales/en-US/models.json +24 -15
- package/locales/en-US/plugin.json +16 -0
- package/locales/en-US/portal.json +0 -5
- package/locales/en-US/tool.json +18 -0
- package/locales/es-ES/models.json +25 -16
- package/locales/es-ES/plugin.json +16 -0
- package/locales/es-ES/portal.json +0 -5
- package/locales/es-ES/tool.json +18 -0
- package/locales/fa-IR/models.json +25 -16
- package/locales/fa-IR/plugin.json +16 -0
- package/locales/fa-IR/portal.json +0 -5
- package/locales/fa-IR/tool.json +18 -0
- package/locales/fr-FR/models.json +25 -16
- package/locales/fr-FR/plugin.json +16 -0
- package/locales/fr-FR/portal.json +0 -5
- package/locales/fr-FR/tool.json +18 -0
- package/locales/it-IT/models.json +25 -16
- package/locales/it-IT/plugin.json +16 -0
- package/locales/it-IT/portal.json +0 -5
- package/locales/it-IT/tool.json +18 -0
- package/locales/ja-JP/models.json +24 -15
- package/locales/ja-JP/plugin.json +16 -0
- package/locales/ja-JP/portal.json +0 -5
- package/locales/ja-JP/tool.json +18 -0
- package/locales/ko-KR/models.json +25 -16
- package/locales/ko-KR/plugin.json +16 -0
- package/locales/ko-KR/portal.json +0 -5
- package/locales/ko-KR/tool.json +18 -0
- package/locales/nl-NL/models.json +25 -16
- package/locales/nl-NL/plugin.json +16 -0
- package/locales/nl-NL/portal.json +0 -5
- package/locales/nl-NL/tool.json +18 -0
- package/locales/pl-PL/models.json +25 -16
- package/locales/pl-PL/plugin.json +16 -0
- package/locales/pl-PL/portal.json +0 -5
- package/locales/pl-PL/tool.json +18 -0
- package/locales/pt-BR/models.json +24 -15
- package/locales/pt-BR/plugin.json +16 -0
- package/locales/pt-BR/portal.json +0 -5
- package/locales/pt-BR/tool.json +18 -0
- package/locales/ru-RU/models.json +25 -16
- package/locales/ru-RU/plugin.json +16 -0
- package/locales/ru-RU/portal.json +0 -5
- package/locales/ru-RU/tool.json +18 -0
- package/locales/tr-TR/models.json +25 -16
- package/locales/tr-TR/plugin.json +16 -0
- package/locales/tr-TR/portal.json +0 -5
- package/locales/tr-TR/tool.json +18 -0
- package/locales/vi-VN/models.json +24 -15
- package/locales/vi-VN/plugin.json +16 -0
- package/locales/vi-VN/portal.json +0 -5
- package/locales/vi-VN/tool.json +18 -0
- package/locales/zh-CN/models.json +30 -21
- package/locales/zh-CN/plugin.json +16 -0
- package/locales/zh-CN/portal.json +1 -6
- package/locales/zh-CN/tool.json +19 -1
- package/locales/zh-TW/models.json +23 -14
- package/locales/zh-TW/plugin.json +16 -0
- package/locales/zh-TW/portal.json +0 -5
- package/locales/zh-TW/tool.json +18 -0
- package/package.json +1 -1
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +1 -0
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/SearchTags.tsx +17 -0
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx +8 -2
- package/src/config/tools.ts +16 -0
- package/src/database/repositories/aiInfra/index.test.ts +29 -0
- package/src/features/ChatInput/ActionBar/Search/index.tsx +6 -15
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +76 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/index.tsx +8 -21
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +62 -50
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +11 -1
- package/src/features/PluginsUI/Render/index.tsx +3 -0
- package/src/features/Portal/Plugins/Body/index.tsx +3 -7
- package/src/features/Portal/Plugins/Header.tsx +14 -2
- package/src/hooks/useAgentEnableSearch.ts +27 -0
- package/src/libs/agent-runtime/perplexity/index.test.ts +26 -0
- package/src/libs/agent-runtime/utils/streams/openai.ts +1 -1
- package/src/libs/trpc/client/index.ts +1 -0
- package/src/libs/trpc/client/tools.ts +20 -0
- package/src/locales/default/plugin.ts +16 -0
- package/src/locales/default/portal.ts +0 -5
- package/src/locales/default/tool.ts +18 -0
- package/src/server/modules/SearXNG.ts +33 -0
- package/src/server/routers/lambda/message.ts +11 -0
- package/src/server/routers/tools/__tests__/fixtures/searXNG.ts +668 -0
- package/src/server/routers/tools/__tests__/search.test.ts +47 -0
- package/src/server/routers/tools/index.ts +3 -0
- package/src/server/routers/tools/search.ts +38 -0
- package/src/services/__tests__/__snapshots__/chat.test.ts.snap +1 -0
- package/src/services/_auth.ts +4 -4
- package/src/services/chat.ts +31 -10
- package/src/services/message/_deprecated.ts +4 -0
- package/src/services/message/client.ts +4 -0
- package/src/services/message/server.ts +5 -5
- package/src/services/message/type.ts +2 -0
- package/src/services/search.ts +9 -0
- package/src/store/aiInfra/slices/aiModel/selectors.ts +12 -5
- package/src/store/chat/slices/builtinTool/action.ts +121 -0
- package/src/store/chat/slices/builtinTool/initialState.ts +2 -0
- package/src/store/chat/slices/builtinTool/selectors.ts +3 -0
- package/src/store/chat/slices/message/action.ts +11 -0
- package/src/store/chat/slices/plugin/action.test.ts +2 -2
- package/src/store/chat/slices/plugin/action.ts +2 -2
- package/src/store/tool/selectors/tool.ts +5 -12
- package/src/store/tool/slices/builtin/selectors.ts +1 -1
- package/src/store/user/slices/modelList/action.ts +6 -0
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +1 -0
- package/src/tools/index.ts +7 -0
- package/src/tools/portals.ts +6 -1
- package/src/tools/renders.ts +3 -0
- package/src/{features/Portal/Plugins → tools/web-browsing/Portal}/Footer.tsx +13 -10
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/CategoryAvatar.tsx +70 -0
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/TitleExtra.tsx +38 -0
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/Video.tsx +135 -0
- package/src/tools/web-browsing/Portal/ResultList/SearchItem/index.tsx +91 -0
- package/src/tools/web-browsing/Portal/ResultList/index.tsx +21 -0
- package/src/tools/web-browsing/Portal/index.tsx +65 -0
- package/src/tools/web-browsing/Render/ConfigForm/Form.tsx +110 -0
- package/src/tools/web-browsing/Render/ConfigForm/SearchXNGIcon.tsx +20 -0
- package/src/tools/web-browsing/Render/ConfigForm/index.tsx +67 -0
- package/src/tools/web-browsing/Render/ConfigForm/style.tsx +63 -0
- package/src/tools/web-browsing/Render/SearchQuery/SearchView.tsx +88 -0
- package/src/tools/web-browsing/Render/SearchQuery/index.tsx +61 -0
- package/src/tools/web-browsing/Render/SearchResult/SearchResultItem.tsx +72 -0
- package/src/tools/web-browsing/Render/SearchResult/ShowMore.tsx +68 -0
- package/src/tools/web-browsing/Render/SearchResult/index.tsx +105 -0
- package/src/tools/web-browsing/Render/index.tsx +57 -0
- package/src/tools/web-browsing/components/EngineAvatar.tsx +32 -0
- package/src/tools/web-browsing/components/SearchBar.tsx +134 -0
- package/src/tools/web-browsing/const.ts +11 -0
- package/src/tools/web-browsing/index.ts +102 -0
- package/src/types/message/chat.ts +1 -0
- package/src/types/message/tools.ts +10 -0
- package/src/types/tool/builtin.ts +2 -0
- package/src/types/tool/search.ts +38 -0
- package/src/types/user/settings/keyVaults.ts +8 -1
- package/src/utils/toolManifest.ts +20 -0
package/src/tools/index.ts
CHANGED
@@ -2,6 +2,7 @@ import { LobeBuiltinTool } from '@/types/tool';
|
|
2
2
|
|
3
3
|
import { ArtifactsManifest } from './artifacts';
|
4
4
|
import { DalleManifest } from './dalle';
|
5
|
+
import { WebBrowsingManifest } from './web-browsing';
|
5
6
|
|
6
7
|
export const builtinTools: LobeBuiltinTool[] = [
|
7
8
|
{
|
@@ -14,4 +15,10 @@ export const builtinTools: LobeBuiltinTool[] = [
|
|
14
15
|
manifest: DalleManifest,
|
15
16
|
type: 'builtin',
|
16
17
|
},
|
18
|
+
{
|
19
|
+
hidden: true,
|
20
|
+
identifier: WebBrowsingManifest.identifier,
|
21
|
+
manifest: WebBrowsingManifest,
|
22
|
+
type: 'builtin',
|
23
|
+
},
|
17
24
|
];
|
package/src/tools/portals.ts
CHANGED
@@ -1,3 +1,8 @@
|
|
1
1
|
import { BuiltinPortal } from '@/types/tool';
|
2
2
|
|
3
|
-
|
3
|
+
import { WebBrowsingManifest } from './web-browsing';
|
4
|
+
import WebBrowsing from './web-browsing/Portal';
|
5
|
+
|
6
|
+
export const BuiltinToolsPortals: Record<string, BuiltinPortal> = {
|
7
|
+
[WebBrowsingManifest.identifier]: WebBrowsing as BuiltinPortal,
|
8
|
+
};
|
package/src/tools/renders.ts
CHANGED
@@ -2,7 +2,10 @@ import { BuiltinRender } from '@/types/tool';
|
|
2
2
|
|
3
3
|
import { DalleManifest } from './dalle';
|
4
4
|
import DalleRender from './dalle/Render';
|
5
|
+
import { WebBrowsingManifest } from './web-browsing';
|
6
|
+
import WebBrowsing from './web-browsing/Render';
|
5
7
|
|
6
8
|
export const BuiltinToolsRenders: Record<string, BuiltinRender> = {
|
7
9
|
[DalleManifest.identifier]: DalleRender as BuiltinRender,
|
10
|
+
[WebBrowsingManifest.identifier]: WebBrowsing as BuiltinRender,
|
8
11
|
};
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { ActionIcon, Icon } from '@lobehub/ui';
|
2
2
|
import { Button } from 'antd';
|
3
|
-
import {
|
3
|
+
import { LucideNotepadText, PlusSquareIcon } from 'lucide-react';
|
4
4
|
import { useTranslation } from 'react-i18next';
|
5
5
|
import { Flexbox } from 'react-layout-kit';
|
6
6
|
|
@@ -8,34 +8,37 @@ import { useChatStore } from '@/store/chat';
|
|
8
8
|
import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
|
9
9
|
|
10
10
|
const Footer = () => {
|
11
|
-
const [messageId, isAIGenerating, triggerAIMessage,
|
11
|
+
const [messageId, isAIGenerating, triggerAIMessage, saveSearchResult] = useChatStore((s) => [
|
12
12
|
chatPortalSelectors.toolMessageId(s),
|
13
13
|
chatSelectors.isAIGenerating(s),
|
14
14
|
s.triggerAIMessage,
|
15
|
-
s.
|
15
|
+
s.saveSearXNGSearchResult,
|
16
16
|
]);
|
17
|
-
|
17
|
+
|
18
|
+
const { t } = useTranslation('tool');
|
18
19
|
|
19
20
|
return (
|
20
21
|
<Flexbox gap={8} horizontal paddingBlock={12} paddingInline={12}>
|
21
22
|
<Button
|
22
|
-
icon={<Icon icon={
|
23
|
+
icon={<Icon icon={LucideNotepadText} />}
|
23
24
|
loading={isAIGenerating}
|
24
25
|
onClick={() => {
|
25
|
-
|
26
|
+
if (!messageId) return;
|
27
|
+
|
28
|
+
triggerAIMessage({});
|
26
29
|
}}
|
27
30
|
>
|
28
|
-
{t('
|
31
|
+
{t('search.summaryTooltip')}
|
29
32
|
</Button>
|
30
33
|
<ActionIcon
|
31
|
-
icon={
|
34
|
+
icon={PlusSquareIcon}
|
32
35
|
loading={isAIGenerating}
|
33
36
|
onClick={() => {
|
34
37
|
if (!messageId) return;
|
35
38
|
|
36
|
-
|
39
|
+
saveSearchResult(messageId);
|
37
40
|
}}
|
38
|
-
title={t('
|
41
|
+
title={t('search.createNewSearch')}
|
39
42
|
/>
|
40
43
|
</Flexbox>
|
41
44
|
);
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import { Avatar, Icon } from '@lobehub/ui';
|
2
|
+
import { useTheme } from 'antd-style';
|
3
|
+
import {
|
4
|
+
LucideAtom,
|
5
|
+
LucideClapperboard,
|
6
|
+
LucideFiles,
|
7
|
+
LucideImages,
|
8
|
+
LucideLaptop,
|
9
|
+
LucideMusic,
|
10
|
+
LucideNewspaper,
|
11
|
+
LucideShoppingBag,
|
12
|
+
LucideTextSearch,
|
13
|
+
LucideUserRound,
|
14
|
+
} from 'lucide-react';
|
15
|
+
import { memo, useMemo } from 'react';
|
16
|
+
|
17
|
+
interface CategoryAvatarProps {
|
18
|
+
category: string;
|
19
|
+
size?: number;
|
20
|
+
}
|
21
|
+
|
22
|
+
const CategoryAvatar = memo<CategoryAvatarProps>(({ category, size = 24 }) => {
|
23
|
+
const theme = useTheme();
|
24
|
+
|
25
|
+
const categoryIcon = useMemo(() => {
|
26
|
+
switch (category) {
|
27
|
+
default:
|
28
|
+
case 'general': {
|
29
|
+
return LucideTextSearch;
|
30
|
+
}
|
31
|
+
case 'videos': {
|
32
|
+
return LucideClapperboard;
|
33
|
+
}
|
34
|
+
case 'images': {
|
35
|
+
return LucideImages;
|
36
|
+
}
|
37
|
+
case 'files': {
|
38
|
+
return LucideFiles;
|
39
|
+
}
|
40
|
+
case 'music': {
|
41
|
+
return LucideMusic;
|
42
|
+
}
|
43
|
+
case 'shopping': {
|
44
|
+
return LucideShoppingBag;
|
45
|
+
}
|
46
|
+
case 'social': {
|
47
|
+
return LucideUserRound;
|
48
|
+
}
|
49
|
+
case 'it': {
|
50
|
+
return LucideLaptop;
|
51
|
+
}
|
52
|
+
case 'news': {
|
53
|
+
return LucideNewspaper;
|
54
|
+
}
|
55
|
+
case 'science': {
|
56
|
+
return LucideAtom;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}, [category]);
|
60
|
+
|
61
|
+
return (
|
62
|
+
<Avatar
|
63
|
+
avatar={<Icon icon={categoryIcon} style={{ color: theme.colorTextSecondary }} />}
|
64
|
+
background={theme.colorFillTertiary}
|
65
|
+
size={size}
|
66
|
+
/>
|
67
|
+
);
|
68
|
+
});
|
69
|
+
|
70
|
+
export default CategoryAvatar;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { Tooltip } from '@lobehub/ui';
|
2
|
+
import { Tag, Typography } from 'antd';
|
3
|
+
import { memo } from 'react';
|
4
|
+
import { useTranslation } from 'react-i18next';
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
6
|
+
|
7
|
+
import CategoryAvatar from './CategoryAvatar';
|
8
|
+
|
9
|
+
interface TitleExtraProps {
|
10
|
+
category: string;
|
11
|
+
highlight?: boolean;
|
12
|
+
score: number;
|
13
|
+
}
|
14
|
+
|
15
|
+
const TitleExtra = memo<TitleExtraProps>(({ category, score, highlight }) => {
|
16
|
+
const { t } = useTranslation('tool');
|
17
|
+
|
18
|
+
return (
|
19
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
20
|
+
<Tooltip title={t(highlight ? 'search.includedTooltip' : 'search.scoreTooltip')}>
|
21
|
+
{highlight ? (
|
22
|
+
<Tag bordered={false} color={'blue'} style={{ marginInlineEnd: 0 }}>
|
23
|
+
{score.toFixed(1)}
|
24
|
+
</Tag>
|
25
|
+
) : (
|
26
|
+
<Typography.Text
|
27
|
+
style={{ textAlign: 'center', width: 32, wordBreak: 'keep-all' }}
|
28
|
+
type={'secondary'}
|
29
|
+
>
|
30
|
+
{score.toFixed(1)}
|
31
|
+
</Typography.Text>
|
32
|
+
)}
|
33
|
+
</Tooltip>
|
34
|
+
<CategoryAvatar category={category} />
|
35
|
+
</Flexbox>
|
36
|
+
);
|
37
|
+
});
|
38
|
+
export default TitleExtra;
|
@@ -0,0 +1,135 @@
|
|
1
|
+
import { Avatar as AntAvatar, Typography } from 'antd';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import { memo, useState } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import { SearchResult } from '@/types/tool/search';
|
7
|
+
|
8
|
+
import { ENGINE_ICON_MAP } from '../../../const';
|
9
|
+
import TitleExtra from './TitleExtra';
|
10
|
+
|
11
|
+
const useStyles = createStyles(({ css, token }) => {
|
12
|
+
return {
|
13
|
+
container: css`
|
14
|
+
display: flex;
|
15
|
+
flex: 1;
|
16
|
+
|
17
|
+
padding: 8px;
|
18
|
+
border-radius: 8px;
|
19
|
+
|
20
|
+
color: initial;
|
21
|
+
|
22
|
+
&:hover {
|
23
|
+
background: ${token.colorFillTertiary};
|
24
|
+
}
|
25
|
+
`,
|
26
|
+
desc: css`
|
27
|
+
overflow: hidden;
|
28
|
+
display: -webkit-box;
|
29
|
+
-webkit-box-orient: vertical;
|
30
|
+
-webkit-line-clamp: 2;
|
31
|
+
|
32
|
+
color: ${token.colorTextTertiary};
|
33
|
+
text-overflow: ellipsis;
|
34
|
+
`,
|
35
|
+
displayLink: css`
|
36
|
+
color: ${token.colorTextQuaternary};
|
37
|
+
`,
|
38
|
+
iframe: css`
|
39
|
+
border: 1px solid ${token.colorBorder};
|
40
|
+
border-radius: 8px;
|
41
|
+
`,
|
42
|
+
title: css`
|
43
|
+
overflow: hidden;
|
44
|
+
display: -webkit-box;
|
45
|
+
-webkit-box-orient: vertical;
|
46
|
+
-webkit-line-clamp: 1;
|
47
|
+
|
48
|
+
font-size: 16px;
|
49
|
+
color: ${token.colorLink};
|
50
|
+
text-overflow: ellipsis;
|
51
|
+
`,
|
52
|
+
url: css`
|
53
|
+
overflow: hidden;
|
54
|
+
display: -webkit-box;
|
55
|
+
-webkit-box-orient: vertical;
|
56
|
+
-webkit-line-clamp: 1;
|
57
|
+
|
58
|
+
color: ${token.colorTextDescription};
|
59
|
+
text-overflow: ellipsis;
|
60
|
+
`,
|
61
|
+
};
|
62
|
+
});
|
63
|
+
|
64
|
+
interface SearchResultProps extends SearchResult {
|
65
|
+
highlight?: boolean;
|
66
|
+
}
|
67
|
+
const VideoItem = memo<SearchResultProps>(
|
68
|
+
({ content, url, iframe_src, highlight, score, engines, title, category }) => {
|
69
|
+
const { styles, theme } = useStyles();
|
70
|
+
|
71
|
+
const [expand, setExpand] = useState(false);
|
72
|
+
return (
|
73
|
+
<Flexbox gap={12}>
|
74
|
+
<Flexbox className={styles.container} onClick={() => setExpand(!expand)}>
|
75
|
+
<Flexbox flex={1} gap={8} horizontal padding={12}>
|
76
|
+
{iframe_src && (
|
77
|
+
<Flexbox>
|
78
|
+
<iframe
|
79
|
+
// alt={title}
|
80
|
+
className={styles.iframe}
|
81
|
+
height={100}
|
82
|
+
onClick={(e) => {
|
83
|
+
e.preventDefault();
|
84
|
+
e.stopPropagation();
|
85
|
+
}}
|
86
|
+
onPlay={(e) => {
|
87
|
+
e.preventDefault();
|
88
|
+
}}
|
89
|
+
src={iframe_src}
|
90
|
+
style={{
|
91
|
+
pointerEvents: 'none',
|
92
|
+
}}
|
93
|
+
width={200}
|
94
|
+
/>
|
95
|
+
</Flexbox>
|
96
|
+
)}
|
97
|
+
<Flexbox flex={1} gap={8}>
|
98
|
+
<Flexbox align={'center'} distribution={'space-between'} gap={12} horizontal>
|
99
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
100
|
+
<AntAvatar.Group>
|
101
|
+
{engines.map((engine) => (
|
102
|
+
<AntAvatar
|
103
|
+
key={engine}
|
104
|
+
src={ENGINE_ICON_MAP[engine]}
|
105
|
+
style={{
|
106
|
+
background: theme.colorBgLayout,
|
107
|
+
height: 20,
|
108
|
+
padding: 3,
|
109
|
+
width: 20,
|
110
|
+
}}
|
111
|
+
/>
|
112
|
+
))}
|
113
|
+
</AntAvatar.Group>
|
114
|
+
<Flexbox className={styles.title}>{title}</Flexbox>
|
115
|
+
</Flexbox>
|
116
|
+
<TitleExtra category={category} highlight={highlight} score={score} />
|
117
|
+
</Flexbox>
|
118
|
+
<Typography.Text className={styles.url} type={'secondary'}>
|
119
|
+
{url}
|
120
|
+
</Typography.Text>
|
121
|
+
<Flexbox className={styles.desc}>{content}</Flexbox>
|
122
|
+
</Flexbox>
|
123
|
+
</Flexbox>
|
124
|
+
</Flexbox>
|
125
|
+
{expand && iframe_src && (
|
126
|
+
<Flexbox>
|
127
|
+
<iframe className={styles.iframe} height={440} src={iframe_src} width={'100%'} />
|
128
|
+
</Flexbox>
|
129
|
+
)}
|
130
|
+
</Flexbox>
|
131
|
+
);
|
132
|
+
},
|
133
|
+
);
|
134
|
+
|
135
|
+
export default VideoItem;
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import { Typography } from 'antd';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import { memo } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import { SearchResult } from '@/types/tool/search';
|
7
|
+
|
8
|
+
import { EngineAvatarGroup } from '../../../components/EngineAvatar';
|
9
|
+
import TitleExtra from './TitleExtra';
|
10
|
+
import Video from './Video';
|
11
|
+
|
12
|
+
const useStyles = createStyles(({ css, token }) => {
|
13
|
+
return {
|
14
|
+
container: css`
|
15
|
+
display: flex;
|
16
|
+
flex: 1;
|
17
|
+
|
18
|
+
padding: 8px;
|
19
|
+
|
20
|
+
color: initial;
|
21
|
+
|
22
|
+
border-radius: 8px;
|
23
|
+
|
24
|
+
&:hover {
|
25
|
+
background: ${token.colorFillTertiary};
|
26
|
+
}
|
27
|
+
`,
|
28
|
+
desc: css`
|
29
|
+
overflow: hidden;
|
30
|
+
|
31
|
+
display: -webkit-box;
|
32
|
+
-webkit-box-orient: vertical;
|
33
|
+
|
34
|
+
color: ${token.colorTextTertiary};
|
35
|
+
text-overflow: ellipsis;
|
36
|
+
|
37
|
+
-webkit-line-clamp: 2;
|
38
|
+
`,
|
39
|
+
displayLink: css`
|
40
|
+
color: ${token.colorTextQuaternary};
|
41
|
+
`,
|
42
|
+
title: css`
|
43
|
+
font-size: 16px;
|
44
|
+
color: ${token.colorLink};
|
45
|
+
`,
|
46
|
+
url: css`
|
47
|
+
overflow: hidden;
|
48
|
+
{ /* stylelint-disable-line */ }
|
49
|
+
display: -webkit-box;
|
50
|
+
-webkit-box-orient: vertical;
|
51
|
+
|
52
|
+
color: ${token.colorTextDescription};
|
53
|
+
text-overflow: ellipsis;
|
54
|
+
|
55
|
+
-webkit-line-clamp: 1;
|
56
|
+
`,
|
57
|
+
};
|
58
|
+
});
|
59
|
+
|
60
|
+
interface SearchResultProps extends SearchResult {
|
61
|
+
highlight?: boolean;
|
62
|
+
}
|
63
|
+
|
64
|
+
const SearchItem = memo<SearchResultProps>((props) => {
|
65
|
+
const { content, url, score, engines, title, category } = props;
|
66
|
+
const { styles } = useStyles();
|
67
|
+
|
68
|
+
if (category === 'videos') return <Video {...props} />;
|
69
|
+
|
70
|
+
return (
|
71
|
+
<a className={styles.container} href={url!} rel="noreferrer" target={'_blank'}>
|
72
|
+
<Flexbox distribution={'space-between'} flex={1} gap={8} padding={12}>
|
73
|
+
<Flexbox gap={8}>
|
74
|
+
<Flexbox align={'center'} distribution={'space-between'} horizontal>
|
75
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
76
|
+
<EngineAvatarGroup engines={engines} />
|
77
|
+
<Flexbox className={styles.title}>{title}</Flexbox>
|
78
|
+
</Flexbox>
|
79
|
+
<TitleExtra category={category} highlight={props.highlight} score={score} />
|
80
|
+
</Flexbox>
|
81
|
+
<Typography.Text className={styles.url} type={'secondary'}>
|
82
|
+
{url}
|
83
|
+
</Typography.Text>
|
84
|
+
<Flexbox className={styles.desc}>{content}</Flexbox>
|
85
|
+
</Flexbox>
|
86
|
+
</Flexbox>
|
87
|
+
</a>
|
88
|
+
);
|
89
|
+
});
|
90
|
+
|
91
|
+
export default SearchItem;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import React, { memo, useCallback } from 'react';
|
2
|
+
import { Virtuoso } from 'react-virtuoso';
|
3
|
+
|
4
|
+
import { SearchResult } from '@/types/tool/search';
|
5
|
+
|
6
|
+
import Item from './SearchItem';
|
7
|
+
|
8
|
+
interface ResultListProps {
|
9
|
+
dataSources: SearchResult[];
|
10
|
+
}
|
11
|
+
|
12
|
+
const ResultList = memo<ResultListProps>(({ dataSources }) => {
|
13
|
+
const itemContent = useCallback(
|
14
|
+
(index: number, result: SearchResult) => <Item {...result} highlight={index < 5} />,
|
15
|
+
[],
|
16
|
+
);
|
17
|
+
|
18
|
+
return <Virtuoso data={dataSources} height={'100%'} itemContent={itemContent} />;
|
19
|
+
});
|
20
|
+
|
21
|
+
export default ResultList;
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { Skeleton } from 'antd';
|
2
|
+
import { uniq } from 'lodash-es';
|
3
|
+
import { memo } from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import { useChatStore } from '@/store/chat';
|
7
|
+
import { chatToolSelectors } from '@/store/chat/selectors';
|
8
|
+
import { SearchResponse } from '@/types/tool/search';
|
9
|
+
|
10
|
+
import SearchBar from '../components/SearchBar';
|
11
|
+
import Footer from './Footer';
|
12
|
+
import ResultList from './ResultList';
|
13
|
+
|
14
|
+
interface SearchArguments {
|
15
|
+
query: string;
|
16
|
+
searchEngine?: string[];
|
17
|
+
}
|
18
|
+
|
19
|
+
interface InspectorUIProps<T = Record<string, any>, S = any> {
|
20
|
+
arguments: T;
|
21
|
+
identifier: string;
|
22
|
+
messageId: string;
|
23
|
+
state: S;
|
24
|
+
}
|
25
|
+
|
26
|
+
const Inspector = memo<InspectorUIProps<SearchArguments, SearchResponse>>(
|
27
|
+
({ arguments: args, messageId, state }) => {
|
28
|
+
const engines = uniq((state.results || []).map((result) => result.engine));
|
29
|
+
const defaultEngines = engines.length > 0 ? engines : args.searchEngine || [];
|
30
|
+
const loading = useChatStore(chatToolSelectors.isSearXNGSearching(messageId));
|
31
|
+
|
32
|
+
return (
|
33
|
+
<Flexbox gap={12} height={'100%'}>
|
34
|
+
<SearchBar
|
35
|
+
aiSummary={false}
|
36
|
+
defaultEngines={defaultEngines}
|
37
|
+
defaultQuery={args.query}
|
38
|
+
messageId={messageId}
|
39
|
+
tooltip={false}
|
40
|
+
/>
|
41
|
+
{loading ? (
|
42
|
+
<Flexbox gap={16} paddingBlock={16} paddingInline={12}>
|
43
|
+
{[1, 2, 3, 4, 6].map((id) => (
|
44
|
+
<Skeleton
|
45
|
+
active
|
46
|
+
key={id}
|
47
|
+
paragraph={{ rows: 3, width: `${(id % 4) + 5}0%` }}
|
48
|
+
title={false}
|
49
|
+
/>
|
50
|
+
))}
|
51
|
+
</Flexbox>
|
52
|
+
) : (
|
53
|
+
<>
|
54
|
+
<Flexbox height={'100%'} width={'100%'}>
|
55
|
+
<ResultList dataSources={state.results} />
|
56
|
+
</Flexbox>
|
57
|
+
<Footer />
|
58
|
+
</>
|
59
|
+
)}
|
60
|
+
</Flexbox>
|
61
|
+
);
|
62
|
+
},
|
63
|
+
);
|
64
|
+
|
65
|
+
export default Inspector;
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
2
|
+
import { Button } from 'antd';
|
3
|
+
import { KeyRoundIcon, Loader2Icon } from 'lucide-react';
|
4
|
+
import { memo, useMemo, useState } from 'react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
7
|
+
|
8
|
+
import { FormInput, FormPassword } from '@/components/FormInput';
|
9
|
+
import { useChatStore } from '@/store/chat';
|
10
|
+
import { useUserStore } from '@/store/user';
|
11
|
+
import { keyVaultsConfigSelectors } from '@/store/user/selectors';
|
12
|
+
|
13
|
+
import SearchXNGIcon from './SearchXNGIcon';
|
14
|
+
import { FormAction } from './style';
|
15
|
+
|
16
|
+
interface ProviderApiKeyFormProps {
|
17
|
+
id: string;
|
18
|
+
provider: string;
|
19
|
+
}
|
20
|
+
|
21
|
+
const Form = memo<ProviderApiKeyFormProps>(({ provider, id }) => {
|
22
|
+
const { t } = useTranslation('plugin');
|
23
|
+
|
24
|
+
const [apiKey, baseURL, setConfig] = useUserStore((s) => [
|
25
|
+
keyVaultsConfigSelectors.getVaultByProvider(provider as any)(s)?.apiKey,
|
26
|
+
keyVaultsConfigSelectors.getVaultByProvider(provider as any)(s)?.baseURL,
|
27
|
+
s.updateKeyVaultSettings,
|
28
|
+
]);
|
29
|
+
|
30
|
+
const [showKey, setShow] = useState(false);
|
31
|
+
|
32
|
+
const [resend, deleteMessage] = useChatStore((s) => [s.reInvokeToolMessage, s.deleteMessage]);
|
33
|
+
|
34
|
+
const [loading, setLoading] = useState(false);
|
35
|
+
|
36
|
+
const avatar = useMemo(() => {
|
37
|
+
switch (provider) {
|
38
|
+
default: {
|
39
|
+
return <SearchXNGIcon />;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}, [provider]);
|
43
|
+
|
44
|
+
return (
|
45
|
+
<Center gap={16} style={{ width: 400 }}>
|
46
|
+
<FormAction
|
47
|
+
avatar={avatar}
|
48
|
+
description={t('search.searchxng.description')}
|
49
|
+
title={t('search.searchxng.title')}
|
50
|
+
>
|
51
|
+
<FormInput
|
52
|
+
onChange={(value) => {
|
53
|
+
setConfig(provider, { baseURL: value });
|
54
|
+
}}
|
55
|
+
placeholder={'https://searxng.xxx'}
|
56
|
+
suffix={<div>{loading && <Icon icon={Loader2Icon} spin />}</div>}
|
57
|
+
value={baseURL}
|
58
|
+
/>
|
59
|
+
{showKey ? (
|
60
|
+
<FormPassword
|
61
|
+
autoComplete={'new-password'}
|
62
|
+
onChange={(value) => {
|
63
|
+
setConfig(provider, { apiKey: value });
|
64
|
+
}}
|
65
|
+
placeholder={t('search.searchxng.keyPlaceholder')}
|
66
|
+
suffix={<div>{loading && <Icon icon={Loader2Icon} spin />}</div>}
|
67
|
+
value={apiKey}
|
68
|
+
/>
|
69
|
+
) : (
|
70
|
+
<Button
|
71
|
+
block
|
72
|
+
icon={<Icon icon={KeyRoundIcon} />}
|
73
|
+
onClick={() => {
|
74
|
+
setShow(true);
|
75
|
+
}}
|
76
|
+
type={'text'}
|
77
|
+
>
|
78
|
+
{t('search.config.addKey')}
|
79
|
+
</Button>
|
80
|
+
)}
|
81
|
+
<Flexbox gap={12} width={'100%'}>
|
82
|
+
<Button
|
83
|
+
block
|
84
|
+
disabled={loading}
|
85
|
+
onClick={async () => {
|
86
|
+
setLoading(true);
|
87
|
+
resend(id).then(() => {
|
88
|
+
setLoading(false);
|
89
|
+
});
|
90
|
+
// deleteMessage(id);
|
91
|
+
}}
|
92
|
+
style={{ marginTop: 8 }}
|
93
|
+
type={'primary'}
|
94
|
+
>
|
95
|
+
{t('search.config.confirm')}
|
96
|
+
</Button>
|
97
|
+
<Button
|
98
|
+
onClick={() => {
|
99
|
+
deleteMessage(id);
|
100
|
+
}}
|
101
|
+
>
|
102
|
+
{t('search.config.close')}
|
103
|
+
</Button>
|
104
|
+
</Flexbox>
|
105
|
+
</FormAction>
|
106
|
+
</Center>
|
107
|
+
);
|
108
|
+
});
|
109
|
+
|
110
|
+
export default Form;
|
@@ -0,0 +1,20 @@
|
|
1
|
+
const SearchXNGIcon = () => {
|
2
|
+
return (
|
3
|
+
<svg height={48} viewBox="0 0 300 300" width={48} xmlns="http://www.w3.org/2000/svg">
|
4
|
+
<path
|
5
|
+
d="M121.929 66.386a50.717 50.717 0 0 0-31.18 5.052l-7.514-14.523a67.076 67.076 0 0 1 78.427 12.336 67.022 67.022 0 0 1 11.688 78.505l-14.464-7.634a50.677 50.677 0 0 0-8.837-59.357 50.71 50.71 0 0 0-28.12-14.38Z"
|
6
|
+
fill="#3050FF"
|
7
|
+
/>
|
8
|
+
<path
|
9
|
+
d="M114.491 34.702c-45.165 0-81.78 36.603-81.78 81.755 0 45.153 36.615 81.756 81.78 81.756 45.166 0 81.78-36.603 81.78-81.756 0-45.152-36.614-81.755-81.78-81.755ZM0 116.457C0 53.244 51.26 2 114.491 2c63.232 0 114.492 51.244 114.492 114.457 0 63.214-51.26 114.458-114.492 114.458C51.26 230.915 0 179.671 0 116.457Z"
|
10
|
+
fill="#3050FF"
|
11
|
+
/>
|
12
|
+
<path
|
13
|
+
d="m205.592 163.093-42.644 44.509L257.357 298 300 253.491l-94.408-90.398Z"
|
14
|
+
fill="#3050FF"
|
15
|
+
/>
|
16
|
+
</svg>
|
17
|
+
);
|
18
|
+
};
|
19
|
+
|
20
|
+
export default SearchXNGIcon;
|