@lobehub/chat 0.96.8 → 0.97.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/.github/workflows/release.yml +3 -0
- package/CHANGELOG.md +58 -0
- package/CODE_OF_CONDUCT.md +21 -21
- package/CONTRIBUTING.md +1 -1
- package/README.md +26 -9
- package/README.zh-CN.md +29 -12
- package/docs/Deploy-with-Azure-OpenAI.md +6 -6
- package/docs/Home.md +14 -2
- package/docs/Lighthouse.md +8 -8
- package/docs/Plugin-Development.md +3 -3
- package/docs/Plugin-Development.zh-CN.md +3 -3
- package/docs/Usage-Agents.md +195 -0
- package/docs/Usage-Agents.zh-CN.md +196 -0
- package/docs/Usage-Topics.md +31 -0
- package/docs/Usage-Topics.zh-CN.md +31 -0
- package/docs/_Sidebar.md +10 -2
- package/locales/en_US/chat.json +9 -2
- package/locales/ja_JP/chat.json +8 -1
- package/locales/ko_KR/chat.json +8 -1
- package/locales/ru_RU/chat.json +8 -1
- package/locales/zh_CN/chat.json +6 -1
- package/locales/zh_TW/chat.json +8 -1
- package/next.config.mjs +9 -1
- package/package.json +6 -7
- package/scripts/readmeWorkflow/const.ts +19 -0
- package/scripts/readmeWorkflow/index.ts +12 -0
- package/scripts/readmeWorkflow/syncAgentIndex.ts +47 -0
- package/scripts/readmeWorkflow/syncPluginIndex.ts +43 -0
- package/scripts/readmeWorkflow/utlis.ts +52 -0
- package/src/app/chat/(desktop)/features/ChatHeader.tsx +11 -1
- package/src/app/chat/{features/ChatInputContent/Footer/index.tsx → (desktop)/features/ChatInput/Footer.tsx} +1 -1
- package/src/app/chat/(desktop)/features/{ChatInput.tsx → ChatInput/index.tsx} +3 -2
- package/src/app/chat/(desktop)/features/SessionList.tsx +1 -1
- package/src/app/chat/(desktop)/features/SideBar.tsx +1 -1
- package/src/app/chat/(mobile)/features/ChatHeader.tsx +2 -6
- package/src/app/chat/(mobile)/features/TopicList.tsx +1 -1
- package/src/app/chat/features/ChatInputContent/ActionBar/ActionRight.tsx +1 -1
- package/src/app/chat/features/ChatInputContent/{Footer/SaveTopic.tsx → Topic/index.tsx} +13 -11
- package/src/app/chat/features/ChatInputContent/index.tsx +4 -4
- package/src/app/chat/features/Conversation/ChatList/Actions/Error.tsx +1 -7
- package/src/app/chat/features/Conversation/ChatList/Actions/User.tsx +0 -1
- package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +48 -0
- package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +27 -0
- package/src/app/chat/features/Conversation/ChatList/index.tsx +7 -29
- package/src/app/chat/features/{TopicListContent/SystemRole/Header.tsx → SidebarHeader/index.tsx} +9 -8
- package/src/app/chat/features/{TopicListContent/SystemRole → SystemRole}/index.tsx +45 -16
- package/src/app/chat/features/TopicListContent/Header.tsx +86 -0
- package/src/app/chat/features/TopicListContent/Topic/SkeletonList.tsx +1 -1
- package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +8 -3
- package/src/app/chat/features/TopicListContent/index.tsx +18 -7
- package/src/app/settings/features/SideBar/index.tsx +1 -1
- package/src/features/AgentInfo/index.tsx +3 -1
- package/src/features/AgentSetting/AgentMeta/index.tsx +5 -1
- package/src/features/PluginDevModal/MetaForm.tsx +4 -3
- package/src/layout/GlobalLayout/Locale.tsx +12 -2
- package/src/locales/default/chat.ts +6 -1
- package/src/store/global/initialState.ts +2 -0
- package/src/store/global/slices/common.ts +7 -0
- package/src/store/session/slices/chat/actions/message.ts +4 -1
- package/src/store/session/slices/chat/actions/topic.ts +46 -5
- package/src/store/session/slices/chat/selectors/index.ts +2 -1
- package/src/store/session/slices/chat/selectors/topic.ts +4 -0
- package/src/components/EmojiPicker/index.tsx +0 -56
- package/src/components/EmojiPicker/style.ts +0 -30
- /package/src/app/chat/features/{TopicListContent/SystemRole → SystemRole}/style.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.97.0",
|
|
4
4
|
"description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"build:analyze": "ANALYZE=true next build",
|
|
28
28
|
"build:docker": "DOCKER=true next build",
|
|
29
29
|
"dev": "next dev -p 3010",
|
|
30
|
-
"i18n": "npm run i18n
|
|
31
|
-
"i18n:workflow": "ts-node --project ./tsconfig.json scripts/i18nWorkflow/index.ts",
|
|
30
|
+
"i18n": "npm run workflow:i18n && lobe-i18n",
|
|
32
31
|
"lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
|
|
33
32
|
"lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true",
|
|
34
33
|
"lint:md": "remark . --quiet --frail --output",
|
|
@@ -43,7 +42,9 @@
|
|
|
43
42
|
"test": "vitest --passWithNoTests",
|
|
44
43
|
"test:coverage": "vitest run --coverage --passWithNoTests",
|
|
45
44
|
"test:update": "vitest -u",
|
|
46
|
-
"type-check": "tsc --noEmit"
|
|
45
|
+
"type-check": "tsc --noEmit",
|
|
46
|
+
"workflow:i18n": "ts-node --project ./tsconfig.json scripts/i18nWorkflow/index.ts",
|
|
47
|
+
"workflow:readme": "bun scripts/readmeWorkflow/index.ts"
|
|
47
48
|
},
|
|
48
49
|
"lint-staged": {
|
|
49
50
|
"*.md": [
|
|
@@ -66,8 +67,6 @@
|
|
|
66
67
|
},
|
|
67
68
|
"dependencies": {
|
|
68
69
|
"@ant-design/icons": "^5",
|
|
69
|
-
"@emoji-mart/data": "^1",
|
|
70
|
-
"@emoji-mart/react": "^1",
|
|
71
70
|
"@icons-pack/react-simple-icons": "^9",
|
|
72
71
|
"@lobehub/chat-plugin-sdk": "latest",
|
|
73
72
|
"@lobehub/chat-plugins-gateway": "latest",
|
|
@@ -81,7 +80,6 @@
|
|
|
81
80
|
"chroma-js": "^2",
|
|
82
81
|
"copy-to-clipboard": "^3",
|
|
83
82
|
"dayjs": "^1",
|
|
84
|
-
"emoji-mart": "^5",
|
|
85
83
|
"fast-deep-equal": "^3",
|
|
86
84
|
"gpt-tokenizer": "^2",
|
|
87
85
|
"i18next": "^23",
|
|
@@ -149,6 +147,7 @@
|
|
|
149
147
|
"jsdom": "^22",
|
|
150
148
|
"lint-staged": "^15",
|
|
151
149
|
"lodash": "^4",
|
|
150
|
+
"markdown-table": "^3",
|
|
152
151
|
"next-pwa": "^5",
|
|
153
152
|
"node-fetch": "^3",
|
|
154
153
|
"prettier": "^3",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DataItem {
|
|
2
|
+
author: string;
|
|
3
|
+
createAt: string;
|
|
4
|
+
homepage: string;
|
|
5
|
+
identifier: string;
|
|
6
|
+
meta: { avatar: string; description: string; tags: string[]; title: string };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const MARKET_URL = 'https://chat-preview.lobehub.com/market';
|
|
10
|
+
export const PLGUIN_URL = 'https://chat-preview.lobehub.com/settings/agent';
|
|
11
|
+
export const AGENT_EN_URL = 'https://chat-agents.lobehub.com/index.json';
|
|
12
|
+
export const AGENT_CN_URL = 'https://chat-agents.lobehub.com/index.zh-CN.json';
|
|
13
|
+
export const AGENT_REPO = 'https://github.com/lobehub/lobe-chat-agents';
|
|
14
|
+
export const PLUGIN_EN_URL = 'https://chat-plugins.lobehub.com/index.json';
|
|
15
|
+
export const PLUGIN_CN_URL = 'https://chat-plugins.lobehub.com/index.zh-CN.json';
|
|
16
|
+
export const PLUGIN_REPO = 'https://github.com/lobehub/lobe-chat-plugins';
|
|
17
|
+
|
|
18
|
+
export const AGENT_SPLIT = '<!-- AGENT LIST -->';
|
|
19
|
+
export const PLUGIN_SPLIT = '<!-- PLUGIN LIST -->';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { consola } from 'consola';
|
|
2
|
+
|
|
3
|
+
import syncAgentIndex from './syncAgentIndex';
|
|
4
|
+
import syncPluginIndex from './syncPluginIndex';
|
|
5
|
+
|
|
6
|
+
const runSync = async () => {
|
|
7
|
+
consola.start('Start sync readme workflow...');
|
|
8
|
+
await syncAgentIndex();
|
|
9
|
+
await syncPluginIndex();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
runSync();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { consola } from 'consola';
|
|
2
|
+
import { markdownTable } from 'markdown-table';
|
|
3
|
+
import qs from 'query-string';
|
|
4
|
+
|
|
5
|
+
import { AGENT_REPO, AGENT_SPLIT, DataItem, MARKET_URL } from './const';
|
|
6
|
+
import { fetchAgentIndex, genLink, genTags, readReadme, updateReadme, writeReadme } from './utlis';
|
|
7
|
+
|
|
8
|
+
const genAgentTable = (data: DataItem[], lang: string) => {
|
|
9
|
+
const isCN = lang === 'zh-CN';
|
|
10
|
+
const content = data.slice(0, 4).map((item) => [
|
|
11
|
+
[
|
|
12
|
+
genLink(
|
|
13
|
+
item.meta.title,
|
|
14
|
+
qs.stringifyUrl({
|
|
15
|
+
query: { agent: item.identifier },
|
|
16
|
+
url: MARKET_URL,
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
`<sup>By **${genLink(item.author, item.homepage)}** on **${item.createAt}**</sup>`,
|
|
20
|
+
].join('<br/>'),
|
|
21
|
+
[item.meta.description, genTags(item.meta.tags)].join('<br/>'),
|
|
22
|
+
]);
|
|
23
|
+
return markdownTable([
|
|
24
|
+
isCN ? ['最近新增', '助手说明'] : ['Recent Submits', 'Description'],
|
|
25
|
+
...content,
|
|
26
|
+
]);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const runAgentTable = async (lang: string) => {
|
|
30
|
+
const data = await fetchAgentIndex(lang);
|
|
31
|
+
const md = readReadme(lang);
|
|
32
|
+
const mdTable = genAgentTable(data, lang);
|
|
33
|
+
const newMd = updateReadme(
|
|
34
|
+
AGENT_SPLIT,
|
|
35
|
+
md,
|
|
36
|
+
[mdTable, `> 📊 Total agents: ${genLink(`<kbd>**${data.length}**</kbd> `, AGENT_REPO)}`].join(
|
|
37
|
+
'\n\n',
|
|
38
|
+
),
|
|
39
|
+
);
|
|
40
|
+
writeReadme(newMd, lang);
|
|
41
|
+
consola.success('Sync agent index success!');
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default async () => {
|
|
45
|
+
await runAgentTable('en-US');
|
|
46
|
+
await runAgentTable('zh-CN');
|
|
47
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { consola } from 'consola';
|
|
2
|
+
import { markdownTable } from 'markdown-table';
|
|
3
|
+
|
|
4
|
+
import { DataItem, PLGUIN_URL, PLUGIN_REPO, PLUGIN_SPLIT } from './const';
|
|
5
|
+
import { fetchPluginIndex, genLink, genTags, readReadme, updateReadme, writeReadme } from './utlis';
|
|
6
|
+
|
|
7
|
+
const genPluginTable = (data: DataItem[], lang: string) => {
|
|
8
|
+
const isCN = lang === 'zh-CN';
|
|
9
|
+
const content = data
|
|
10
|
+
.filter((item) => item.author === 'LobeHub')
|
|
11
|
+
.map((item) => [
|
|
12
|
+
[
|
|
13
|
+
genLink(item.meta.title, PLGUIN_URL),
|
|
14
|
+
`<sup>By **${item.author}** on **${item.createAt}**</sup>`,
|
|
15
|
+
].join('<br/>'),
|
|
16
|
+
genLink(item.homepage.split('github.com/')[1], item.homepage),
|
|
17
|
+
[item.meta.description, genTags(item.meta.tags)].join('<br/>'),
|
|
18
|
+
]);
|
|
19
|
+
return markdownTable([
|
|
20
|
+
isCN ? ['官方插件', '仓库', '插件描述'] : ['Official Plugin', 'Repository', 'Description'],
|
|
21
|
+
...content,
|
|
22
|
+
]);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const runPluginTable = async (lang: string) => {
|
|
26
|
+
const data = await fetchPluginIndex(lang);
|
|
27
|
+
const md = readReadme(lang);
|
|
28
|
+
const mdTable = genPluginTable(data, lang);
|
|
29
|
+
const newMd = updateReadme(
|
|
30
|
+
PLUGIN_SPLIT,
|
|
31
|
+
md,
|
|
32
|
+
[mdTable, `> 📊 Total plugins: ${genLink(`<kbd>**${data.length}**</kbd>`, PLUGIN_REPO)}`].join(
|
|
33
|
+
'\n\n',
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
writeReadme(newMd, lang);
|
|
37
|
+
consola.success('Sync plugin index success!');
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default async () => {
|
|
41
|
+
await runPluginTable('en-US');
|
|
42
|
+
await runPluginTable('zh-CN');
|
|
43
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { kebabCase } from 'lodash';
|
|
2
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { AGENT_CN_URL, AGENT_EN_URL, PLUGIN_CN_URL, PLUGIN_EN_URL } from './const';
|
|
6
|
+
|
|
7
|
+
const fetchIndex = async (url: string) => {
|
|
8
|
+
const res = await fetch(url);
|
|
9
|
+
return await res.json();
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const fetchAgentIndex = async (lang: string) => {
|
|
13
|
+
const isCN = lang === 'zh-CN';
|
|
14
|
+
const url = isCN ? AGENT_CN_URL : AGENT_EN_URL;
|
|
15
|
+
const data = await fetchIndex(url);
|
|
16
|
+
return data.agents;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const fetchPluginIndex = async (lang: string) => {
|
|
20
|
+
const isCN = lang === 'zh-CN';
|
|
21
|
+
const url = isCN ? PLUGIN_CN_URL : PLUGIN_EN_URL;
|
|
22
|
+
const data = await fetchIndex(url);
|
|
23
|
+
return data.plugins;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const genLink = (title: string, url: string) => `[${title}](${url})`;
|
|
27
|
+
|
|
28
|
+
export const genTags = (tags: string[]) =>
|
|
29
|
+
tags
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.map((tag) => `\`${kebabCase(tag)}\``)
|
|
32
|
+
.join(' ');
|
|
33
|
+
|
|
34
|
+
const getReadmePath = (lang: string) => {
|
|
35
|
+
const isCN = lang === 'zh-CN';
|
|
36
|
+
return resolve(__dirname, '../../', isCN ? `./README.zh-CN.md` : `./README.md`);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const readReadme = (lang: string): string => {
|
|
40
|
+
return readFileSync(getReadmePath(lang), 'utf8');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const writeReadme = (content: string, lang: string) => {
|
|
44
|
+
writeFileSync(getReadmePath(lang), content, 'utf8');
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const updateReadme = (split: string, md: string, content: string): string => {
|
|
48
|
+
const mds = md.split(split);
|
|
49
|
+
mds[1] = [' ', content, ' '].join('\n\n');
|
|
50
|
+
|
|
51
|
+
return mds.join(split);
|
|
52
|
+
};
|
|
@@ -55,7 +55,17 @@ const Header = memo(() => {
|
|
|
55
55
|
</Flexbox>
|
|
56
56
|
) : (
|
|
57
57
|
<Flexbox align={'flex-start'} gap={12} horizontal>
|
|
58
|
-
<Avatar
|
|
58
|
+
<Avatar
|
|
59
|
+
avatar={avatar}
|
|
60
|
+
background={backgroundColor}
|
|
61
|
+
onClick={() =>
|
|
62
|
+
isInbox
|
|
63
|
+
? router.push('/settings/agent')
|
|
64
|
+
: router.push(pathString('/chat/settings', { hash: location.hash }))
|
|
65
|
+
}
|
|
66
|
+
size={40}
|
|
67
|
+
title={title}
|
|
68
|
+
/>
|
|
59
69
|
<ChatHeaderTitle
|
|
60
70
|
desc={displayDesc}
|
|
61
71
|
tag={
|
|
@@ -5,7 +5,7 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
|
-
import SaveTopic from '
|
|
8
|
+
import SaveTopic from '../../../features/ChatInputContent/Topic';
|
|
9
9
|
|
|
10
10
|
const Footer = memo(() => {
|
|
11
11
|
const theme = useTheme();
|
|
@@ -4,7 +4,8 @@ import { memo, useState } from 'react';
|
|
|
4
4
|
import { CHAT_TEXTAREA_HEIGHT, HEADER_HEIGHT } from '@/const/layoutTokens';
|
|
5
5
|
import { useGlobalStore } from '@/store/global';
|
|
6
6
|
|
|
7
|
-
import ChatInputContent from '
|
|
7
|
+
import ChatInputContent from '../../../features/ChatInputContent';
|
|
8
|
+
import Footer from './Footer';
|
|
8
9
|
|
|
9
10
|
const ChatInputDesktopLayout = memo(() => {
|
|
10
11
|
const [expand, setExpand] = useState<boolean>(false);
|
|
@@ -29,7 +30,7 @@ const ChatInputDesktopLayout = memo(() => {
|
|
|
29
30
|
size={{ height: inputHeight, width: '100%' }}
|
|
30
31
|
style={{ zIndex: 10 }}
|
|
31
32
|
>
|
|
32
|
-
<ChatInputContent expand={expand} onExpandChange={setExpand} />
|
|
33
|
+
<ChatInputContent expand={expand} footer={<Footer />} onExpandChange={setExpand} />
|
|
33
34
|
</DraggablePanel>
|
|
34
35
|
);
|
|
35
36
|
});
|
|
@@ -11,7 +11,7 @@ import { sessionSelectors } from '@/store/session/selectors';
|
|
|
11
11
|
|
|
12
12
|
import TopicListContent from '../../features/TopicListContent';
|
|
13
13
|
|
|
14
|
-
const SystemRole = dynamic(() => import('../../features/
|
|
14
|
+
const SystemRole = dynamic(() => import('../../features/SystemRole'));
|
|
15
15
|
|
|
16
16
|
const useStyles = createStyles(({ css, token }) => ({
|
|
17
17
|
content: css`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ActionIcon, MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
2
|
-
import {
|
|
2
|
+
import { Clock3, Settings } from 'lucide-react';
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
@@ -32,11 +32,7 @@ const MobileHeader = memo(() => {
|
|
|
32
32
|
right={
|
|
33
33
|
<>
|
|
34
34
|
<ShareButton />
|
|
35
|
-
<ActionIcon
|
|
36
|
-
icon={LayoutList}
|
|
37
|
-
onClick={() => toggleConfig()}
|
|
38
|
-
size={MOBILE_HEADER_ICON_SIZE}
|
|
39
|
-
/>
|
|
35
|
+
<ActionIcon icon={Clock3} onClick={() => toggleConfig()} size={MOBILE_HEADER_ICON_SIZE} />
|
|
40
36
|
{!isInbox && (
|
|
41
37
|
<ActionIcon
|
|
42
38
|
icon={Settings}
|
|
@@ -10,7 +10,7 @@ import HotKeys from '@/components/HotKeys';
|
|
|
10
10
|
import { CLEAN_MESSAGE_KEY, PREFIX_KEY } from '@/const/hotkeys';
|
|
11
11
|
import { useSessionStore } from '@/store/session';
|
|
12
12
|
|
|
13
|
-
import SaveTopic from '../
|
|
13
|
+
import SaveTopic from '../Topic';
|
|
14
14
|
|
|
15
15
|
const ActionsRight = memo(() => {
|
|
16
16
|
const { t } = useTranslation('setting');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionIcon, Icon, Tooltip } from '@lobehub/ui';
|
|
2
2
|
import { Button } from 'antd';
|
|
3
3
|
import { useResponsive } from 'antd-style';
|
|
4
|
-
import { LucideGalleryVerticalEnd } from 'lucide-react';
|
|
4
|
+
import { LucideGalleryVerticalEnd, LucideMessageSquarePlus } from 'lucide-react';
|
|
5
5
|
import { memo } from 'react';
|
|
6
6
|
import { useHotkeys } from 'react-hotkeys-hook';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
@@ -12,23 +12,25 @@ import { useSessionStore } from '@/store/session';
|
|
|
12
12
|
|
|
13
13
|
const SaveTopic = memo(() => {
|
|
14
14
|
const { t } = useTranslation('chat');
|
|
15
|
-
const [hasTopic,
|
|
15
|
+
const [hasTopic, openNewTopicOrSaveTopic] = useSessionStore((s) => [
|
|
16
|
+
!!s.activeTopicId,
|
|
17
|
+
s.openNewTopicOrSaveTopic,
|
|
18
|
+
]);
|
|
16
19
|
const { mobile } = useResponsive();
|
|
17
20
|
|
|
18
|
-
const
|
|
21
|
+
const icon = hasTopic ? LucideMessageSquarePlus : LucideGalleryVerticalEnd;
|
|
22
|
+
const Render = mobile ? ActionIcon : Button;
|
|
23
|
+
const iconRender: any = mobile ? icon : <Icon icon={icon} />;
|
|
24
|
+
const desc = t(hasTopic ? 'topic.openNewTopic' : 'topic.saveCurrentMessages');
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
const hotkeys = [PREFIX_KEY, SAVE_TOPIC_KEY].join('+');
|
|
27
|
+
useHotkeys(hotkeys, openNewTopicOrSaveTopic, {
|
|
21
28
|
preventDefault: true,
|
|
22
29
|
});
|
|
23
30
|
|
|
24
|
-
if (hasTopic) return undefined;
|
|
25
|
-
|
|
26
|
-
const Render = mobile ? ActionIcon : Button;
|
|
27
|
-
const icon: any = mobile ? LucideGalleryVerticalEnd : <Icon icon={LucideGalleryVerticalEnd} />;
|
|
28
|
-
|
|
29
31
|
return (
|
|
30
|
-
<Tooltip title={<HotKeys desc={
|
|
31
|
-
<Render aria-label={
|
|
32
|
+
<Tooltip title={<HotKeys desc={desc} keys={hotkeys} />}>
|
|
33
|
+
<Render aria-label={desc} icon={iconRender} onClick={openNewTopicOrSaveTopic} />
|
|
32
34
|
</Tooltip>
|
|
33
35
|
);
|
|
34
36
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChatInputArea } from '@lobehub/ui';
|
|
2
2
|
import { useResponsive } from 'antd-style';
|
|
3
3
|
import dynamic from 'next/dynamic';
|
|
4
|
-
import { Suspense, memo, useState } from 'react';
|
|
4
|
+
import { ReactNode, Suspense, memo, useState } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { CHAT_TEXTAREA_HEIGHT } from '@/const/layoutTokens';
|
|
@@ -10,18 +10,18 @@ import { agentSelectors } from '@/store/session/selectors';
|
|
|
10
10
|
|
|
11
11
|
import ActionLeft from './ActionBar/ActionLeft';
|
|
12
12
|
import ActionsRight from './ActionBar/ActionRight';
|
|
13
|
-
import Footer from './Footer';
|
|
14
13
|
|
|
15
14
|
const Token = dynamic(() => import('./ActionBar/Token'), { ssr: false });
|
|
16
15
|
|
|
17
16
|
interface ChatContentProps {
|
|
18
17
|
expand?: boolean;
|
|
18
|
+
footer?: ReactNode;
|
|
19
19
|
mobile?: boolean;
|
|
20
20
|
onExpandChange?: (expand: boolean) => void;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const ChatInputContent = memo<ChatContentProps>(
|
|
24
|
-
({ expand, onExpandChange, mobile: defaultMobile }) => {
|
|
24
|
+
({ expand, onExpandChange, mobile: defaultMobile, footer }) => {
|
|
25
25
|
const { t } = useTranslation('common');
|
|
26
26
|
|
|
27
27
|
const [message, setMessage] = useState('');
|
|
@@ -50,7 +50,7 @@ const ChatInputContent = memo<ChatContentProps>(
|
|
|
50
50
|
}
|
|
51
51
|
actionsRight={<ActionsRight />}
|
|
52
52
|
expand={expand}
|
|
53
|
-
footer={
|
|
53
|
+
footer={footer}
|
|
54
54
|
loading={isLoading}
|
|
55
55
|
minHeight={mobile ? 0 : CHAT_TEXTAREA_HEIGHT}
|
|
56
56
|
onExpandChange={onExpandChange}
|
|
@@ -5,11 +5,5 @@ import { memo } from 'react';
|
|
|
5
5
|
export const ErrorActionsBar = memo<ActionsBarProps>(({ text, onActionClick }) => {
|
|
6
6
|
const { regenerate, del } = useChatListActionsBar(text);
|
|
7
7
|
|
|
8
|
-
return
|
|
9
|
-
<ActionIconGroup
|
|
10
|
-
items={[regenerate, del]}
|
|
11
|
-
onActionClick={onActionClick}
|
|
12
|
-
type="ghost"
|
|
13
|
-
/>
|
|
14
|
-
);
|
|
8
|
+
return <ActionIconGroup items={[regenerate, del]} onActionClick={onActionClick} type="ghost" />;
|
|
15
9
|
});
|
|
@@ -7,7 +7,6 @@ export const UserActionsBar: RenderAction = memo(({ text, onActionClick }) => {
|
|
|
7
7
|
const { regenerate, edit, copy, divider, del } = useChatListActionsBar(text);
|
|
8
8
|
const { translate } = useCustomActions();
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
return (
|
|
12
11
|
<ActionIconGroup
|
|
13
12
|
dropdownMenu={[edit, copy, regenerate, divider, translate, divider, del]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ChatListProps } from '@lobehub/ui';
|
|
2
2
|
|
|
3
|
+
import { useSessionStore } from '@/store/session';
|
|
4
|
+
|
|
3
5
|
import { AssistantActionsBar } from './Assistant';
|
|
4
6
|
import { DefaultActionsBar } from './Fallback';
|
|
5
7
|
import { FunctionActionsBar } from './Function';
|
|
@@ -11,3 +13,49 @@ export const renderActions: ChatListProps['renderActions'] = {
|
|
|
11
13
|
system: DefaultActionsBar,
|
|
12
14
|
user: UserActionsBar,
|
|
13
15
|
};
|
|
16
|
+
|
|
17
|
+
interface ActionsClick {
|
|
18
|
+
onClick: () => void;
|
|
19
|
+
trigger: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const useActionsClick = (): ChatListProps['onActionsClick'] => {
|
|
23
|
+
const [deleteMessage, resendMessage, translateMessage] = useSessionStore((s) => [
|
|
24
|
+
s.deleteMessage,
|
|
25
|
+
s.resendMessage,
|
|
26
|
+
s.translateMessage,
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
return (action, { id, error }) => {
|
|
30
|
+
const actionsClick: ActionsClick[] = [
|
|
31
|
+
{
|
|
32
|
+
onClick: () => {
|
|
33
|
+
deleteMessage(id);
|
|
34
|
+
},
|
|
35
|
+
trigger: action.key === 'del',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
onClick: () => {
|
|
39
|
+
resendMessage(id);
|
|
40
|
+
// if this message is an error message, we need to delete it
|
|
41
|
+
if (error) deleteMessage(id);
|
|
42
|
+
},
|
|
43
|
+
trigger: action.key === 'regenerate',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
onClick: () => {
|
|
47
|
+
/**
|
|
48
|
+
* @description Click the menu item with translate item, the result is:
|
|
49
|
+
* @key 'en-US'
|
|
50
|
+
* @keyPath ['en-US','translate']
|
|
51
|
+
*/
|
|
52
|
+
const lang = action.keyPath[0];
|
|
53
|
+
translateMessage(id, lang);
|
|
54
|
+
},
|
|
55
|
+
trigger: action.keyPath.at(-1) === 'translate',
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
actionsClick.find((item) => item.trigger)?.onClick();
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { ChatListProps } from '@lobehub/ui';
|
|
2
|
+
import { useResponsive } from 'antd-style';
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
|
|
5
|
+
import { useGlobalStore } from '@/store/global';
|
|
6
|
+
import { useSessionStore } from '@/store/session';
|
|
7
|
+
import { sessionSelectors } from '@/store/session/slices/session/selectors';
|
|
8
|
+
import { pathString } from '@/utils/url';
|
|
2
9
|
|
|
3
10
|
import { AssistantMessage } from './Assistant';
|
|
4
11
|
import { DefaultMessage } from './Default';
|
|
@@ -9,3 +16,23 @@ export const renderMessages: ChatListProps['renderMessages'] = {
|
|
|
9
16
|
default: DefaultMessage,
|
|
10
17
|
function: FunctionMessage,
|
|
11
18
|
};
|
|
19
|
+
|
|
20
|
+
export const useAvatarsClick = (): ChatListProps['onAvatarsClick'] => {
|
|
21
|
+
const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
|
|
22
|
+
const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
|
|
23
|
+
const { mobile } = useResponsive();
|
|
24
|
+
const router = useRouter();
|
|
25
|
+
|
|
26
|
+
return (role) => {
|
|
27
|
+
switch (role) {
|
|
28
|
+
case 'assistant': {
|
|
29
|
+
return () =>
|
|
30
|
+
isInbox
|
|
31
|
+
? router.push('/settings/agent')
|
|
32
|
+
: mobile
|
|
33
|
+
? router.push(pathString('/chat/settings', { hash: location.hash }))
|
|
34
|
+
: toggleSystemRole(true);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -8,15 +8,16 @@ import { PREFIX_KEY, REGENERATE_KEY } from '@/const/hotkeys';
|
|
|
8
8
|
import { useSessionChatInit, useSessionStore } from '@/store/session';
|
|
9
9
|
import { agentSelectors, chatSelectors } from '@/store/session/selectors';
|
|
10
10
|
|
|
11
|
-
import { renderActions } from './Actions';
|
|
11
|
+
import { renderActions, useActionsClick } from './Actions';
|
|
12
12
|
import { renderErrorMessages } from './Error';
|
|
13
13
|
import { renderMessagesExtra } from './Extras';
|
|
14
|
-
import { renderMessages } from './Messages';
|
|
14
|
+
import { renderMessages, useAvatarsClick } from './Messages';
|
|
15
15
|
import SkeletonList from './SkeletonList';
|
|
16
16
|
|
|
17
17
|
const List = memo(() => {
|
|
18
18
|
const init = useSessionChatInit();
|
|
19
19
|
const { t } = useTranslation('common');
|
|
20
|
+
|
|
20
21
|
const data = useSessionStore(chatSelectors.currentChatsWithGuideMessage, isEqual);
|
|
21
22
|
|
|
22
23
|
const [
|
|
@@ -24,10 +25,8 @@ const List = memo(() => {
|
|
|
24
25
|
enableHistoryCount,
|
|
25
26
|
historyCount,
|
|
26
27
|
chatLoadingId,
|
|
27
|
-
deleteMessage,
|
|
28
28
|
resendMessage,
|
|
29
29
|
dispatchMessage,
|
|
30
|
-
translateMessage,
|
|
31
30
|
] = useSessionStore((s) => {
|
|
32
31
|
const config = agentSelectors.currentAgentConfig(s);
|
|
33
32
|
return [
|
|
@@ -35,12 +34,12 @@ const List = memo(() => {
|
|
|
35
34
|
config.enableHistoryCount,
|
|
36
35
|
config.historyCount,
|
|
37
36
|
s.chatLoadingId,
|
|
38
|
-
s.deleteMessage,
|
|
39
37
|
s.resendMessage,
|
|
40
38
|
s.dispatchMessage,
|
|
41
|
-
s.translateMessage,
|
|
42
39
|
];
|
|
43
40
|
});
|
|
41
|
+
const onActionsClick = useActionsClick();
|
|
42
|
+
const onAvatarsClick = useAvatarsClick();
|
|
44
43
|
|
|
45
44
|
const hotkeys = [PREFIX_KEY, REGENERATE_KEY].join('+');
|
|
46
45
|
|
|
@@ -64,29 +63,8 @@ const List = memo(() => {
|
|
|
64
63
|
enableHistoryCount={enableHistoryCount}
|
|
65
64
|
historyCount={historyCount}
|
|
66
65
|
loadingId={chatLoadingId}
|
|
67
|
-
onActionsClick={
|
|
68
|
-
|
|
69
|
-
case 'del': {
|
|
70
|
-
deleteMessage(id);
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
case 'regenerate': {
|
|
74
|
-
resendMessage(id);
|
|
75
|
-
|
|
76
|
-
// if this message is an error message, we need to delete it
|
|
77
|
-
if (error) deleteMessage(id);
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// click the menu item with translate item, the result is:
|
|
83
|
-
// key: 'en-US'
|
|
84
|
-
// keyPath: ['en-US','translate']
|
|
85
|
-
if (action.keyPath.at(-1) === 'translate') {
|
|
86
|
-
const lang = action.keyPath[0];
|
|
87
|
-
translateMessage(id, lang);
|
|
88
|
-
}
|
|
89
|
-
}}
|
|
66
|
+
onActionsClick={onActionsClick}
|
|
67
|
+
onAvatarsClick={onAvatarsClick}
|
|
90
68
|
onMessageChange={(id, content) =>
|
|
91
69
|
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: content })
|
|
92
70
|
}
|
package/src/app/chat/features/{TopicListContent/SystemRole/Header.tsx → SidebarHeader/index.tsx}
RENAMED
|
@@ -9,13 +9,12 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
9
9
|
`,
|
|
10
10
|
}));
|
|
11
11
|
|
|
12
|
-
interface
|
|
12
|
+
interface SidebarHeaderProps {
|
|
13
13
|
actions?: ReactNode;
|
|
14
|
-
|
|
15
|
-
title: string;
|
|
14
|
+
title: ReactNode;
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
const
|
|
17
|
+
const SidebarHeader = memo<SidebarHeaderProps>(({ title, actions }) => {
|
|
19
18
|
const { styles } = useStyles();
|
|
20
19
|
|
|
21
20
|
return (
|
|
@@ -24,15 +23,17 @@ const Header = memo<HeaderProps>(({ title, actions }) => {
|
|
|
24
23
|
className={styles.header}
|
|
25
24
|
distribution={'space-between'}
|
|
26
25
|
horizontal
|
|
27
|
-
padding={
|
|
26
|
+
padding={14}
|
|
28
27
|
paddingInline={16}
|
|
29
28
|
>
|
|
30
|
-
<Flexbox
|
|
31
|
-
|
|
29
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
30
|
+
{title}
|
|
31
|
+
</Flexbox>
|
|
32
|
+
<Flexbox align={'center'} gap={2} horizontal>
|
|
32
33
|
{actions}
|
|
33
34
|
</Flexbox>
|
|
34
35
|
</Flexbox>
|
|
35
36
|
);
|
|
36
37
|
});
|
|
37
38
|
|
|
38
|
-
export default
|
|
39
|
+
export default SidebarHeader;
|