@lobehub/chat 1.18.2 → 1.19.1
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.
Potentially problematic release.
This version of @lobehub/chat might be problematic. Click here for more details.
- package/CHANGELOG.md +52 -0
- package/Dockerfile +2 -0
- package/Dockerfile.database +2 -0
- package/locales/ar/chat.json +6 -0
- package/locales/ar/error.json +1 -0
- package/locales/ar/modelProvider.json +7 -0
- package/locales/ar/portal.json +16 -0
- package/locales/bg-BG/chat.json +6 -0
- package/locales/bg-BG/error.json +1 -0
- package/locales/bg-BG/modelProvider.json +7 -0
- package/locales/bg-BG/portal.json +16 -0
- package/locales/de-DE/chat.json +6 -0
- package/locales/de-DE/error.json +1 -0
- package/locales/de-DE/modelProvider.json +7 -0
- package/locales/de-DE/portal.json +16 -0
- package/locales/en-US/chat.json +6 -0
- package/locales/en-US/error.json +1 -0
- package/locales/en-US/modelProvider.json +7 -0
- package/locales/en-US/portal.json +16 -0
- package/locales/es-ES/chat.json +6 -0
- package/locales/es-ES/error.json +1 -0
- package/locales/es-ES/modelProvider.json +7 -0
- package/locales/es-ES/portal.json +16 -0
- package/locales/fr-FR/chat.json +6 -0
- package/locales/fr-FR/error.json +1 -0
- package/locales/fr-FR/modelProvider.json +7 -0
- package/locales/fr-FR/portal.json +16 -0
- package/locales/it-IT/chat.json +6 -0
- package/locales/it-IT/error.json +1 -0
- package/locales/it-IT/modelProvider.json +7 -0
- package/locales/it-IT/portal.json +16 -0
- package/locales/ja-JP/chat.json +6 -0
- package/locales/ja-JP/error.json +1 -0
- package/locales/ja-JP/modelProvider.json +7 -0
- package/locales/ja-JP/portal.json +16 -0
- package/locales/ko-KR/chat.json +6 -0
- package/locales/ko-KR/error.json +1 -0
- package/locales/ko-KR/modelProvider.json +7 -0
- package/locales/ko-KR/portal.json +16 -0
- package/locales/nl-NL/chat.json +6 -0
- package/locales/nl-NL/error.json +1 -0
- package/locales/nl-NL/modelProvider.json +7 -0
- package/locales/nl-NL/portal.json +16 -0
- package/locales/pl-PL/chat.json +6 -0
- package/locales/pl-PL/error.json +1 -0
- package/locales/pl-PL/modelProvider.json +7 -0
- package/locales/pl-PL/portal.json +16 -0
- package/locales/pt-BR/chat.json +6 -0
- package/locales/pt-BR/error.json +1 -0
- package/locales/pt-BR/modelProvider.json +7 -0
- package/locales/pt-BR/portal.json +16 -0
- package/locales/ru-RU/chat.json +6 -0
- package/locales/ru-RU/error.json +1 -0
- package/locales/ru-RU/modelProvider.json +7 -0
- package/locales/ru-RU/portal.json +16 -0
- package/locales/tr-TR/chat.json +6 -0
- package/locales/tr-TR/error.json +1 -0
- package/locales/tr-TR/modelProvider.json +7 -0
- package/locales/tr-TR/portal.json +16 -0
- package/locales/vi-VN/chat.json +6 -0
- package/locales/vi-VN/error.json +1 -0
- package/locales/vi-VN/modelProvider.json +7 -0
- package/locales/vi-VN/portal.json +16 -0
- package/locales/zh-CN/chat.json +6 -0
- package/locales/zh-CN/error.json +2 -1
- package/locales/zh-CN/modelProvider.json +7 -0
- package/locales/zh-CN/portal.json +17 -1
- package/locales/zh-TW/chat.json +6 -0
- package/locales/zh-TW/error.json +1 -0
- package/locales/zh-TW/modelProvider.json +7 -0
- package/locales/zh-TW/portal.json +16 -0
- package/package.json +4 -2
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/Body/Renderer/HTML.tsx +25 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/Body/Renderer/React.tsx +30 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/Body/Renderer/SVG.tsx +114 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/Body/Renderer/index.tsx +25 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/Body/index.tsx +79 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/Header.tsx +69 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/index.ts +10 -0
- package/src/app/(main)/chat/(workspace)/@portal/Artifacts/useEnable.ts +4 -0
- package/src/app/(main)/chat/(workspace)/@portal/FilePreview/index.ts +2 -1
- package/src/app/(main)/chat/(workspace)/@portal/Home/Body/{Artifacts → Plugins}/index.tsx +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/Home/Body/index.tsx +2 -2
- package/src/app/(main)/chat/(workspace)/@portal/MessageDetail/index.ts +2 -1
- package/src/app/(main)/chat/(workspace)/@portal/Plugins/Body/ToolRender.tsx +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/Plugins/Body/index.tsx +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/Plugins/Footer.tsx +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/Plugins/index.ts +2 -1
- package/src/app/(main)/chat/(workspace)/@portal/Plugins/useEnable.ts +1 -1
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +2 -4
- package/src/app/(main)/chat/(workspace)/@portal/features/Body.tsx +27 -0
- package/src/app/(main)/chat/(workspace)/@portal/router.tsx +3 -1
- package/src/app/(main)/chat/(workspace)/@portal/type.ts +7 -0
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +3 -2
- package/src/app/(main)/settings/llm/ProviderList/Github/index.tsx +53 -0
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +6 -1
- package/src/app/api/chat/agentRuntime.ts +14 -0
- package/src/components/SidebarHeader/index.tsx +1 -1
- package/src/config/llm.ts +14 -0
- package/src/config/modelProviders/ai21.ts +37 -0
- package/src/config/modelProviders/anthropic.ts +4 -0
- package/src/config/modelProviders/github.ts +209 -0
- package/src/config/modelProviders/index.ts +8 -0
- package/src/config/modelProviders/mistral.ts +38 -21
- package/src/config/modelProviders/upstage.ts +9 -9
- package/src/const/layoutTokens.ts +1 -1
- package/src/const/plugin.test.ts +80 -0
- package/src/const/plugin.ts +12 -0
- package/src/const/settings/llm.ts +10 -0
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +4 -0
- package/src/features/Conversation/Messages/Tool/Inspector/index.tsx +1 -1
- package/src/features/Conversation/Messages/Tool/index.tsx +1 -1
- package/src/features/Conversation/components/ChatItem/index.tsx +24 -2
- package/src/features/Conversation/components/ChatItem/utils.test.ts +150 -0
- package/src/features/Conversation/components/ChatItem/utils.ts +28 -0
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/Render/Icon.tsx +96 -0
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/Render/index.tsx +129 -0
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/index.ts +10 -0
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/rehypePlugin.ts +74 -0
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/Render.tsx +86 -0
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/index.ts +12 -0
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/rehypePlugin.test.ts +124 -0
- package/src/features/Conversation/components/MarkdownElements/LobeThinking/rehypePlugin.ts +51 -0
- package/src/features/Conversation/components/MarkdownElements/index.ts +4 -0
- package/src/features/Conversation/components/MarkdownElements/type.ts +7 -0
- package/src/libs/agent-runtime/AgentRuntime.ts +14 -0
- package/src/libs/agent-runtime/ai21/index.test.ts +255 -0
- package/src/libs/agent-runtime/ai21/index.ts +18 -0
- package/src/libs/agent-runtime/error.ts +2 -0
- package/src/libs/agent-runtime/github/index.test.ts +246 -0
- package/src/libs/agent-runtime/github/index.ts +15 -0
- package/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap +2215 -28
- package/src/libs/agent-runtime/openrouter/fixtures/models.json +3345 -37
- package/src/libs/agent-runtime/openrouter/index.ts +2 -1
- package/src/libs/agent-runtime/types/type.ts +2 -0
- package/src/locales/default/chat.ts +7 -0
- package/src/locales/default/error.ts +3 -0
- package/src/locales/default/modelProvider.ts +7 -0
- package/src/locales/default/portal.ts +17 -1
- package/src/server/globalConfig/index.ts +14 -0
- package/src/store/chat/slices/message/selectors.ts +30 -28
- package/src/store/chat/slices/portal/action.ts +15 -2
- package/src/store/chat/slices/portal/initialState.ts +11 -0
- package/src/store/chat/slices/portal/selectors.test.ts +29 -7
- package/src/store/chat/slices/portal/selectors.ts +56 -12
- package/src/styles/loading.ts +28 -0
- package/src/tools/artifacts/index.ts +13 -0
- package/src/tools/artifacts/systemRole.ts +338 -0
- package/src/tools/index.ts +6 -0
- package/src/types/user/settings/keyVaults.ts +2 -0
- package/src/utils/clipboard.ts +53 -0
- /package/src/app/(main)/chat/(workspace)/@portal/Home/Body/{Artifacts → Plugins}/ArtifactList/Item/index.tsx +0 -0
- /package/src/app/(main)/chat/(workspace)/@portal/Home/Body/{Artifacts → Plugins}/ArtifactList/Item/style.ts +0 -0
- /package/src/app/(main)/chat/(workspace)/@portal/Home/Body/{Artifacts → Plugins}/ArtifactList/index.tsx +0 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.19.1",
|
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",
|
@@ -109,9 +109,10 @@
|
|
109
109
|
"@azure/core-rest-pipeline": "1.16.0",
|
110
110
|
"@azure/openai": "1.0.0-beta.12",
|
111
111
|
"@cfworker/json-schema": "^2.0.0",
|
112
|
-
"@clerk/localizations": "
|
112
|
+
"@clerk/localizations": "^3.0.4",
|
113
113
|
"@clerk/nextjs": "^5.3.3",
|
114
114
|
"@clerk/themes": "^2.1.27",
|
115
|
+
"@codesandbox/sandpack-react": "^2.19.8",
|
115
116
|
"@cyntler/react-doc-viewer": "^1.16.6",
|
116
117
|
"@google/generative-ai": "^0.16.0",
|
117
118
|
"@icons-pack/react-simple-icons": "9.6.0",
|
@@ -256,6 +257,7 @@
|
|
256
257
|
"@types/semver": "^7.5.8",
|
257
258
|
"@types/systemjs": "^6.13.5",
|
258
259
|
"@types/ua-parser-js": "^0.7.39",
|
260
|
+
"@types/unist": "^3.0.3",
|
259
261
|
"@types/uuid": "^10.0.0",
|
260
262
|
"@types/ws": "^8.5.12",
|
261
263
|
"@vitest/coverage-v8": "~1.2.2",
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { memo, useEffect, useRef } from 'react';
|
2
|
+
|
3
|
+
interface HTMLRendererProps {
|
4
|
+
height?: string;
|
5
|
+
htmlContent: string;
|
6
|
+
width?: string;
|
7
|
+
}
|
8
|
+
const HTMLRenderer = memo<HTMLRendererProps>(({ htmlContent, width = '100%', height = '100%' }) => {
|
9
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
10
|
+
|
11
|
+
useEffect(() => {
|
12
|
+
if (!iframeRef.current) return;
|
13
|
+
|
14
|
+
const doc = iframeRef.current.contentDocument;
|
15
|
+
if (!doc) return;
|
16
|
+
|
17
|
+
doc.open();
|
18
|
+
doc.write(htmlContent);
|
19
|
+
doc.close();
|
20
|
+
}, [htmlContent]);
|
21
|
+
|
22
|
+
return <iframe ref={iframeRef} style={{ border: 'none', height, width }} title="html-renderer" />;
|
23
|
+
});
|
24
|
+
|
25
|
+
export default HTMLRenderer;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { SandpackLayout, SandpackPreview, SandpackProvider } from '@codesandbox/sandpack-react';
|
2
|
+
import { memo } from 'react';
|
3
|
+
|
4
|
+
interface ReactRendererProps {
|
5
|
+
code: string;
|
6
|
+
}
|
7
|
+
const ReactRenderer = memo<ReactRendererProps>(({ code }) => {
|
8
|
+
return (
|
9
|
+
<SandpackProvider
|
10
|
+
customSetup={{
|
11
|
+
dependencies: {
|
12
|
+
antd: 'latest',
|
13
|
+
},
|
14
|
+
}}
|
15
|
+
files={{
|
16
|
+
'App.js': code,
|
17
|
+
}}
|
18
|
+
options={{ externalResources: ['https://cdn.tailwindcss.com'] }}
|
19
|
+
style={{ height: '100%' }}
|
20
|
+
template="react"
|
21
|
+
theme="auto"
|
22
|
+
>
|
23
|
+
<SandpackLayout style={{ height: '100%' }}>
|
24
|
+
<SandpackPreview style={{ height: '100%' }} />
|
25
|
+
</SandpackLayout>
|
26
|
+
</SandpackProvider>
|
27
|
+
);
|
28
|
+
});
|
29
|
+
|
30
|
+
export default ReactRenderer;
|
@@ -0,0 +1,114 @@
|
|
1
|
+
import { Icon, Tooltip } from '@lobehub/ui';
|
2
|
+
import { App, Button, Dropdown, Space } from 'antd';
|
3
|
+
import { css, cx } from 'antd-style';
|
4
|
+
import { CopyIcon, DownloadIcon } from 'lucide-react';
|
5
|
+
import { domToPng } from 'modern-screenshot';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
8
|
+
|
9
|
+
import { BRANDING_NAME } from '@/const/branding';
|
10
|
+
import { useChatStore } from '@/store/chat';
|
11
|
+
import { chatPortalSelectors } from '@/store/chat/selectors';
|
12
|
+
import { copyImageToClipboard } from '@/utils/clipboard';
|
13
|
+
|
14
|
+
const svgContainer = css`
|
15
|
+
width: 100%;
|
16
|
+
height: 100%;
|
17
|
+
|
18
|
+
> svg {
|
19
|
+
width: 100%;
|
20
|
+
height: 100%;
|
21
|
+
}
|
22
|
+
`;
|
23
|
+
|
24
|
+
const actions = css`
|
25
|
+
position: absolute;
|
26
|
+
inset-block-end: 8px;
|
27
|
+
inset-inline-end: 8px;
|
28
|
+
`;
|
29
|
+
|
30
|
+
const DOM_ID = 'artfact-svg';
|
31
|
+
interface SVGRendererProps {
|
32
|
+
content: string;
|
33
|
+
}
|
34
|
+
|
35
|
+
const SVGRenderer = ({ content }: SVGRendererProps) => {
|
36
|
+
const { t } = useTranslation('portal');
|
37
|
+
const { message } = App.useApp();
|
38
|
+
|
39
|
+
const generatePng = async () => {
|
40
|
+
return domToPng(document.querySelector(`#${DOM_ID}`) as HTMLDivElement, {
|
41
|
+
features: {
|
42
|
+
// 不启用移除控制符,否则会导致 safari emoji 报错
|
43
|
+
removeControlCharacter: false,
|
44
|
+
},
|
45
|
+
scale: 2,
|
46
|
+
});
|
47
|
+
};
|
48
|
+
|
49
|
+
const downloadImage = async (type: string) => {
|
50
|
+
let dataUrl = '';
|
51
|
+
if (type === 'png') dataUrl = await generatePng();
|
52
|
+
else if (type === 'svg') {
|
53
|
+
const blob = new Blob([content], { type: 'image/svg+xml' });
|
54
|
+
|
55
|
+
dataUrl = URL.createObjectURL(blob);
|
56
|
+
}
|
57
|
+
|
58
|
+
const title = chatPortalSelectors.artifactTitle(useChatStore.getState());
|
59
|
+
|
60
|
+
const link = document.createElement('a');
|
61
|
+
link.download = `${BRANDING_NAME}_${title}.${type}`;
|
62
|
+
link.href = dataUrl;
|
63
|
+
link.click();
|
64
|
+
link.remove();
|
65
|
+
};
|
66
|
+
|
67
|
+
return (
|
68
|
+
<Flexbox
|
69
|
+
align={'center'}
|
70
|
+
className="svg-renderer"
|
71
|
+
height={'100%'}
|
72
|
+
style={{ position: 'relative' }}
|
73
|
+
>
|
74
|
+
<Center
|
75
|
+
className={cx(svgContainer)}
|
76
|
+
dangerouslySetInnerHTML={{ __html: content }}
|
77
|
+
id={DOM_ID}
|
78
|
+
/>
|
79
|
+
<Flexbox className={cx(actions)}>
|
80
|
+
<Space.Compact>
|
81
|
+
<Dropdown
|
82
|
+
menu={{
|
83
|
+
items: [
|
84
|
+
{ key: 'png', label: t('artifacts.svg.download.png') },
|
85
|
+
{ key: 'svg', label: t('artifacts.svg.download.svg') },
|
86
|
+
],
|
87
|
+
onClick: ({ key }) => {
|
88
|
+
downloadImage(key);
|
89
|
+
},
|
90
|
+
}}
|
91
|
+
>
|
92
|
+
<Button icon={<Icon icon={DownloadIcon} />} />
|
93
|
+
</Dropdown>
|
94
|
+
<Tooltip title={t('artifacts.svg.copyAsImage')}>
|
95
|
+
<Button
|
96
|
+
icon={<Icon icon={CopyIcon} />}
|
97
|
+
onClick={async () => {
|
98
|
+
const dataUrl = await generatePng();
|
99
|
+
try {
|
100
|
+
await copyImageToClipboard(dataUrl);
|
101
|
+
message.success(t('artifacts.svg.copySuccess'));
|
102
|
+
} catch (e) {
|
103
|
+
message.error(t('artifacts.svg.copyFail', { error: e }));
|
104
|
+
}
|
105
|
+
}}
|
106
|
+
/>
|
107
|
+
</Tooltip>
|
108
|
+
</Space.Compact>
|
109
|
+
</Flexbox>
|
110
|
+
</Flexbox>
|
111
|
+
);
|
112
|
+
};
|
113
|
+
|
114
|
+
export default SVGRenderer;
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import dynamic from 'next/dynamic';
|
2
|
+
import { memo } from 'react';
|
3
|
+
|
4
|
+
import HTMLRenderer from './HTML';
|
5
|
+
import SVGRender from './SVG';
|
6
|
+
|
7
|
+
const ReactRenderer = dynamic(() => import('./React'), { ssr: false });
|
8
|
+
|
9
|
+
const Renderer = memo<{ content: string; type?: string }>(({ content, type }) => {
|
10
|
+
switch (type) {
|
11
|
+
case 'application/lobe.artifacts.react': {
|
12
|
+
return <ReactRenderer code={content} />;
|
13
|
+
}
|
14
|
+
|
15
|
+
case 'image/svg+xml': {
|
16
|
+
return <SVGRender content={content} />;
|
17
|
+
}
|
18
|
+
|
19
|
+
default: {
|
20
|
+
return <HTMLRenderer htmlContent={content} />;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
});
|
24
|
+
|
25
|
+
export default Renderer;
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import { Highlighter } from '@lobehub/ui';
|
2
|
+
import { memo, useEffect, useMemo } from 'react';
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
4
|
+
|
5
|
+
import { useChatStore } from '@/store/chat';
|
6
|
+
import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
|
7
|
+
|
8
|
+
import Renderer from './Renderer';
|
9
|
+
|
10
|
+
const ArtifactsUI = memo(() => {
|
11
|
+
const [
|
12
|
+
messageId,
|
13
|
+
displayMode,
|
14
|
+
isMessageGenerating,
|
15
|
+
artifactType,
|
16
|
+
artifactContent,
|
17
|
+
|
18
|
+
isArtifactTagClosed,
|
19
|
+
] = useChatStore((s) => {
|
20
|
+
const messageId = chatPortalSelectors.artifactMessageId(s) || '';
|
21
|
+
|
22
|
+
return [
|
23
|
+
messageId,
|
24
|
+
s.portalArtifactDisplayMode,
|
25
|
+
chatSelectors.isMessageGenerating(messageId)(s),
|
26
|
+
chatPortalSelectors.artifactType(s),
|
27
|
+
chatPortalSelectors.artifactCode(messageId)(s),
|
28
|
+
chatPortalSelectors.isArtifactTagClosed(messageId)(s),
|
29
|
+
];
|
30
|
+
});
|
31
|
+
|
32
|
+
useEffect(() => {
|
33
|
+
// when message generating , check whether the artifact is closed
|
34
|
+
// if close, move the display mode to preview
|
35
|
+
if (isMessageGenerating && isArtifactTagClosed && displayMode === 'code') {
|
36
|
+
useChatStore.setState({ portalArtifactDisplayMode: 'preview' });
|
37
|
+
}
|
38
|
+
}, [isMessageGenerating, displayMode, isArtifactTagClosed]);
|
39
|
+
|
40
|
+
const language = useMemo(() => {
|
41
|
+
switch (artifactType) {
|
42
|
+
case 'application/lobe.artifacts.react': {
|
43
|
+
return 'tsx';
|
44
|
+
}
|
45
|
+
|
46
|
+
case 'python': {
|
47
|
+
return 'python';
|
48
|
+
}
|
49
|
+
|
50
|
+
default: {
|
51
|
+
return 'html';
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}, [artifactType]);
|
55
|
+
|
56
|
+
// make sure the message and id is valid
|
57
|
+
if (!messageId) return;
|
58
|
+
|
59
|
+
return (
|
60
|
+
<Flexbox
|
61
|
+
className={'portal-artifact'}
|
62
|
+
flex={1}
|
63
|
+
gap={8}
|
64
|
+
height={'100%'}
|
65
|
+
paddingInline={12}
|
66
|
+
style={{ overflow: 'hidden' }}
|
67
|
+
>
|
68
|
+
{!isArtifactTagClosed || displayMode === 'code' ? (
|
69
|
+
<Highlighter language={language} style={{ maxHeight: '100%', overflow: 'hidden' }}>
|
70
|
+
{artifactContent}
|
71
|
+
</Highlighter>
|
72
|
+
) : (
|
73
|
+
<Renderer content={artifactContent} type={artifactType} />
|
74
|
+
)}
|
75
|
+
</Flexbox>
|
76
|
+
);
|
77
|
+
});
|
78
|
+
|
79
|
+
export default ArtifactsUI;
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import { ActionIcon, Icon } from '@lobehub/ui';
|
2
|
+
import { ConfigProvider, Segmented, Typography } from 'antd';
|
3
|
+
import { cx } from 'antd-style';
|
4
|
+
import { ArrowLeft, CodeIcon, EyeIcon } from 'lucide-react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
7
|
+
|
8
|
+
import { useChatStore } from '@/store/chat';
|
9
|
+
import { chatPortalSelectors } from '@/store/chat/selectors';
|
10
|
+
import { oneLineEllipsis } from '@/styles';
|
11
|
+
|
12
|
+
const Header = () => {
|
13
|
+
const { t } = useTranslation('portal');
|
14
|
+
|
15
|
+
const [displayMode, artifactTitle, isArtifactTagClosed, closeArtifact] = useChatStore((s) => {
|
16
|
+
const messageId = chatPortalSelectors.artifactMessageId(s) || '';
|
17
|
+
|
18
|
+
return [
|
19
|
+
s.portalArtifactDisplayMode,
|
20
|
+
chatPortalSelectors.artifactTitle(s),
|
21
|
+
chatPortalSelectors.isArtifactTagClosed(messageId)(s),
|
22
|
+
s.closeArtifact,
|
23
|
+
];
|
24
|
+
});
|
25
|
+
|
26
|
+
return (
|
27
|
+
<Flexbox align={'center'} flex={1} gap={12} horizontal justify={'space-between'} width={'100%'}>
|
28
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
29
|
+
<ActionIcon icon={ArrowLeft} onClick={() => closeArtifact()} />
|
30
|
+
<Typography.Text
|
31
|
+
className={cx(oneLineEllipsis)}
|
32
|
+
style={{ fontSize: 16 }}
|
33
|
+
type={'secondary'}
|
34
|
+
>
|
35
|
+
{artifactTitle}
|
36
|
+
</Typography.Text>
|
37
|
+
</Flexbox>
|
38
|
+
<ConfigProvider
|
39
|
+
theme={{
|
40
|
+
token: {
|
41
|
+
borderRadiusSM: 16,
|
42
|
+
borderRadiusXS: 16,
|
43
|
+
fontSize: 12,
|
44
|
+
},
|
45
|
+
}}
|
46
|
+
>
|
47
|
+
{isArtifactTagClosed && (
|
48
|
+
<Segmented
|
49
|
+
onChange={(value: 'code' | 'preview') => {
|
50
|
+
useChatStore.setState({ portalArtifactDisplayMode: value });
|
51
|
+
}}
|
52
|
+
options={[
|
53
|
+
{
|
54
|
+
icon: <Icon icon={EyeIcon} />,
|
55
|
+
label: t('artifacts.display.preview'),
|
56
|
+
value: 'preview',
|
57
|
+
},
|
58
|
+
{ icon: <Icon icon={CodeIcon} />, label: t('artifacts.display.code'), value: 'code' },
|
59
|
+
]}
|
60
|
+
size={'small'}
|
61
|
+
value={displayMode}
|
62
|
+
/>
|
63
|
+
)}
|
64
|
+
</ConfigProvider>
|
65
|
+
</Flexbox>
|
66
|
+
);
|
67
|
+
};
|
68
|
+
|
69
|
+
export default Header;
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import { Flexbox } from 'react-layout-kit';
|
2
2
|
|
3
|
-
import Artifacts from './Artifacts';
|
4
3
|
import Files from './Files';
|
4
|
+
import Plugins from './Plugins';
|
5
5
|
|
6
6
|
const Home = () => {
|
7
7
|
return (
|
8
8
|
<Flexbox gap={12} height={'100%'}>
|
9
9
|
<Files />
|
10
|
-
<
|
10
|
+
<Plugins />
|
11
11
|
</Flexbox>
|
12
12
|
);
|
13
13
|
};
|
@@ -8,7 +8,7 @@ import { BuiltinToolsPortals } from '@/tools/portals';
|
|
8
8
|
import { safeParseJSON } from '@/utils/safeParseJSON';
|
9
9
|
|
10
10
|
const ToolRender = memo(() => {
|
11
|
-
const messageId = useChatStore(chatPortalSelectors.
|
11
|
+
const messageId = useChatStore(chatPortalSelectors.toolMessageId);
|
12
12
|
const message = useChatStore(chatSelectors.getMessageById(messageId || ''), isEqual);
|
13
13
|
|
14
14
|
// make sure the message and id is valid
|
@@ -9,7 +9,7 @@ import Footer from '../Footer';
|
|
9
9
|
import ToolRender from './ToolRender';
|
10
10
|
|
11
11
|
const ToolUI = () => {
|
12
|
-
const messageId = useChatStore(chatPortalSelectors.
|
12
|
+
const messageId = useChatStore(chatPortalSelectors.toolMessageId);
|
13
13
|
const message = useChatStore(chatSelectors.getMessageById(messageId || ''), isEqual);
|
14
14
|
|
15
15
|
// make sure the message and id is valid
|
@@ -9,7 +9,7 @@ import { chatPortalSelectors, chatSelectors } from '@/store/chat/selectors';
|
|
9
9
|
|
10
10
|
const Footer = () => {
|
11
11
|
const [messageId, isAIGenerating, triggerAIMessage, summaryPluginContent] = useChatStore((s) => [
|
12
|
-
chatPortalSelectors.
|
12
|
+
chatPortalSelectors.toolMessageId(s),
|
13
13
|
chatSelectors.isAIGenerating(s),
|
14
14
|
s.triggerAIMessage,
|
15
15
|
s.summaryPluginContent,
|
@@ -1,15 +1,13 @@
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
2
|
-
import { Flexbox } from 'react-layout-kit';
|
3
2
|
|
3
|
+
import Body from '../features/Body';
|
4
4
|
import Header from '../features/Header';
|
5
5
|
|
6
6
|
const Layout = ({ children }: PropsWithChildren) => {
|
7
7
|
return (
|
8
8
|
<>
|
9
9
|
<Header />
|
10
|
-
<
|
11
|
-
{children}
|
12
|
-
</Flexbox>
|
10
|
+
<Body>{children}</Body>
|
13
11
|
</>
|
14
12
|
);
|
15
13
|
};
|
@@ -0,0 +1,27 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { css, cx } from 'antd-style';
|
4
|
+
import { PropsWithChildren } from 'react';
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
6
|
+
|
7
|
+
const body = css`
|
8
|
+
:has(.portal-artifact) {
|
9
|
+
overflow: hidden;
|
10
|
+
padding-block-end: 12px;
|
11
|
+
}
|
12
|
+
`;
|
13
|
+
|
14
|
+
const Body = ({ children }: PropsWithChildren) => {
|
15
|
+
return (
|
16
|
+
<Flexbox
|
17
|
+
className={cx(body, 'portal-body')}
|
18
|
+
height={'100%'}
|
19
|
+
style={{ position: 'relative' }}
|
20
|
+
width={'100%'}
|
21
|
+
>
|
22
|
+
{children}
|
23
|
+
</Flexbox>
|
24
|
+
);
|
25
|
+
};
|
26
|
+
|
27
|
+
export default Body;
|
@@ -2,12 +2,14 @@
|
|
2
2
|
|
3
3
|
import { memo } from 'react';
|
4
4
|
|
5
|
+
import { Artifacts } from './Artifacts';
|
5
6
|
import { FilePreview } from './FilePreview';
|
6
7
|
import { HomeBody, HomeHeader } from './Home';
|
7
8
|
import { MessageDetail } from './MessageDetail';
|
8
9
|
import { Plugins } from './Plugins';
|
10
|
+
import { PortalImpl } from './type';
|
9
11
|
|
10
|
-
const items = [MessageDetail, Plugins, FilePreview];
|
12
|
+
const items: PortalImpl[] = [MessageDetail, Artifacts, Plugins, FilePreview];
|
11
13
|
|
12
14
|
export const PortalHeader = memo(() => {
|
13
15
|
const enabledList: boolean[] = [];
|
@@ -39,8 +39,9 @@ const PortalPanel = memo(({ children }: PropsWithChildren) => {
|
|
39
39
|
const { styles } = useStyles();
|
40
40
|
const { md = true } = useResponsive();
|
41
41
|
|
42
|
-
const [showInspector, showToolUI] = useChatStore((s) => [
|
42
|
+
const [showInspector, showToolUI, showArtifactUI] = useChatStore((s) => [
|
43
43
|
chatPortalSelectors.showPortal(s),
|
44
|
+
chatPortalSelectors.showPluginUI(s),
|
44
45
|
chatPortalSelectors.showArtifactUI(s),
|
45
46
|
]);
|
46
47
|
|
@@ -54,7 +55,7 @@ const PortalPanel = memo(({ children }: PropsWithChildren) => {
|
|
54
55
|
expand
|
55
56
|
hanlderStyle={{ display: 'none' }}
|
56
57
|
maxWidth={MAX_WIDTH}
|
57
|
-
minWidth={showToolUI ? CHAT_DOCK_TOOL_UI_WIDTH : CHAT_DOCK_WIDTH}
|
58
|
+
minWidth={showArtifactUI || showToolUI ? CHAT_DOCK_TOOL_UI_WIDTH : CHAT_DOCK_WIDTH}
|
58
59
|
mode={md ? 'fixed' : 'float'}
|
59
60
|
placement={'right'}
|
60
61
|
showHandlerWhenUnexpand={false}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { Markdown } from '@lobehub/ui';
|
4
|
+
import { Input } from 'antd';
|
5
|
+
import { createStyles } from 'antd-style';
|
6
|
+
import { useTranslation } from 'react-i18next';
|
7
|
+
|
8
|
+
import { GithubProviderCard } from '@/config/modelProviders';
|
9
|
+
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
10
|
+
|
11
|
+
import { KeyVaultsConfigKey, LLMProviderApiTokenKey } from '../../const';
|
12
|
+
import { ProviderItem } from '../../type';
|
13
|
+
|
14
|
+
const useStyles = createStyles(({ css, token }) => ({
|
15
|
+
markdown: css`
|
16
|
+
p {
|
17
|
+
color: ${token.colorTextDescription} !important;
|
18
|
+
}
|
19
|
+
`,
|
20
|
+
tip: css`
|
21
|
+
font-size: 12px;
|
22
|
+
color: ${token.colorTextDescription};
|
23
|
+
`,
|
24
|
+
}));
|
25
|
+
|
26
|
+
const providerKey: GlobalLLMProviderKey = 'github';
|
27
|
+
|
28
|
+
// Same as OpenAIProvider, but replace API Key with Github Personal Access Token
|
29
|
+
export const useGithubProvider = (): ProviderItem => {
|
30
|
+
const { t } = useTranslation('modelProvider');
|
31
|
+
const { styles } = useStyles();
|
32
|
+
|
33
|
+
return {
|
34
|
+
...GithubProviderCard,
|
35
|
+
apiKeyItems: [
|
36
|
+
{
|
37
|
+
children: (
|
38
|
+
<Input.Password
|
39
|
+
autoComplete={'new-password'}
|
40
|
+
placeholder={t(`${providerKey}.personalAccessToken.placeholder`)}
|
41
|
+
/>
|
42
|
+
),
|
43
|
+
desc: (
|
44
|
+
<Markdown className={styles.markdown} fontSize={12} variant={'chat'}>
|
45
|
+
{t(`${providerKey}.personalAccessToken.desc`)}
|
46
|
+
</Markdown>
|
47
|
+
),
|
48
|
+
label: t(`${providerKey}.personalAccessToken.title`),
|
49
|
+
name: [KeyVaultsConfigKey, providerKey, LLMProviderApiTokenKey],
|
50
|
+
},
|
51
|
+
],
|
52
|
+
};
|
53
|
+
};
|