@lobehub/chat 0.74.0 → 0.75.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +26 -5
  3. package/README.zh-CN.md +25 -4
  4. package/locales/en_US/market.json +12 -0
  5. package/locales/en_US/plugin.json +11 -2
  6. package/locales/en_US/setting.json +3 -1
  7. package/locales/ru_RU/market.json +12 -0
  8. package/locales/ru_RU/plugin.json +44 -49
  9. package/locales/ru_RU/setting.json +3 -1
  10. package/locales/zh_CN/common.json +1 -1
  11. package/locales/zh_CN/market.json +12 -0
  12. package/locales/zh_CN/plugin.json +19 -5
  13. package/locales/zh_CN/setting.json +62 -14
  14. package/package.json +5 -4
  15. package/scripts/i18nWorkflow/const.ts +19 -0
  16. package/scripts/i18nWorkflow/genDefaultLocale.ts +19 -0
  17. package/scripts/i18nWorkflow/genDiff.ts +50 -0
  18. package/scripts/i18nWorkflow/genResources.ts +26 -0
  19. package/scripts/i18nWorkflow/index.ts +15 -0
  20. package/scripts/i18nWorkflow/utils.ts +85 -0
  21. package/src/const/layoutTokens.ts +3 -1
  22. package/src/const/market.ts +15 -0
  23. package/src/const/url.ts +29 -4
  24. package/src/features/AgentSetting/AgentConfig/index.tsx +1 -2
  25. package/src/features/AgentSetting/AgentPlugin/MarketSettingModal.tsx +1 -1
  26. package/src/features/AgentSetting/store/initialState.ts +1 -1
  27. package/src/features/MobileTabBar/index.tsx +2 -2
  28. package/src/features/SideBar/TopActions.tsx +2 -2
  29. package/src/locales/default/market.ts +12 -0
  30. package/src/locales/resources/en_US.ts +2 -0
  31. package/src/locales/resources/ru_RU.ts +2 -0
  32. package/src/locales/resources/zh_CN.ts +2 -0
  33. package/src/pages/chat/features/Conversation/Input/{Action.tsx → ActionBar/ActionLeft.tsx} +2 -2
  34. package/src/pages/chat/features/Conversation/Input/ActionBar/ActionRight.tsx +36 -0
  35. package/src/pages/chat/features/Conversation/Input/Desktop.tsx +40 -0
  36. package/src/pages/chat/features/Conversation/Input/Footer/SaveTopic.tsx +27 -0
  37. package/src/pages/chat/features/Conversation/Input/Footer/index.tsx +35 -0
  38. package/src/pages/chat/features/Conversation/Input/Mobile.tsx +12 -0
  39. package/src/pages/chat/features/Conversation/Input/index.tsx +20 -38
  40. package/src/pages/chat/features/SessionList/Header/Desktop.tsx +6 -13
  41. package/src/pages/chat/features/SessionList/Header/Mobile.tsx +1 -1
  42. package/src/pages/chat/features/SessionList/List/AddButton.tsx +1 -1
  43. package/src/pages/chat/features/SessionList/List/Item/index.tsx +4 -3
  44. package/src/pages/chat/features/SessionList/List/index.tsx +1 -3
  45. package/src/pages/chat/features/SessionList/Mobile.tsx +2 -12
  46. package/src/pages/chat/features/SessionSearchBar/index.tsx +26 -0
  47. package/src/pages/chat/features/Sidebar/Topic/index.tsx +18 -6
  48. package/src/pages/chat/features/Sidebar/TopicSearchBar/index.tsx +24 -0
  49. package/src/pages/chat/features/Sidebar/index.tsx +2 -9
  50. package/src/pages/market/features/AgentCard/AgentCardBanner.tsx +64 -0
  51. package/src/pages/market/features/AgentCard/AgentCardItem.tsx +57 -0
  52. package/src/pages/market/features/AgentCard/Loading.tsx +13 -0
  53. package/src/pages/market/features/AgentCard/index.tsx +37 -0
  54. package/src/pages/market/features/AgentCard/style.ts +22 -0
  55. package/src/pages/market/features/AgentSearchBar/index.tsx +26 -0
  56. package/src/pages/market/features/Header/Desktop.tsx +15 -4
  57. package/src/pages/market/features/Header/Mobile.tsx +7 -24
  58. package/src/pages/market/features/SideBar/AgentInfo/Comment.tsx +16 -0
  59. package/src/pages/market/features/SideBar/AgentInfo/Header.tsx +68 -0
  60. package/src/pages/market/features/SideBar/AgentInfo/Loading.tsx +61 -0
  61. package/src/pages/market/features/SideBar/AgentInfo/TokenTag.tsx +25 -0
  62. package/src/pages/market/features/SideBar/AgentInfo/index.tsx +68 -0
  63. package/src/pages/market/features/SideBar/AgentInfo/style.ts +46 -0
  64. package/src/pages/market/features/SideBar/Desktop.tsx +70 -0
  65. package/src/pages/market/features/SideBar/Mobile.tsx +36 -0
  66. package/src/pages/market/features/SideBar/index.tsx +23 -0
  67. package/src/pages/market/features/TagList/index.tsx +29 -0
  68. package/src/pages/market/index.page.tsx +11 -2
  69. package/src/pages/market/layout.mobile.tsx +8 -7
  70. package/src/pages/market/layout.tsx +39 -1
  71. package/src/pages/settings/features/Header/Desktop.tsx +2 -4
  72. package/src/pages/settings/features/Header/Mobile.tsx +1 -1
  73. package/src/pages/settings/features/Settings/Common.tsx +1 -2
  74. package/src/pages/settings/features/Settings/index.tsx +17 -4
  75. package/src/services/agentMarket.ts +21 -0
  76. package/src/store/global/initialState.ts +1 -2
  77. package/src/store/market/action.ts +57 -0
  78. package/src/store/market/index.ts +3 -0
  79. package/src/store/market/initialState.ts +17 -0
  80. package/src/store/market/selectors.ts +36 -0
  81. package/src/store/market/store.ts +51 -0
  82. package/src/store/session/slices/session/action.ts +5 -3
  83. package/src/store/session/slices/session/initialState.ts +2 -0
  84. package/src/store/session/slices/session/reducers/session.test.ts +4 -4
  85. package/src/types/market.ts +19 -0
  86. package/src/types/meta.ts +1 -1
  87. package/src/utils/filter.ts +1 -1
  88. package/src/utils/findDuplicates.ts +22 -0
  89. package/scripts/genDefaultLocale.ts +0 -18
  90. package/scripts/genResources.ts +0 -51
  91. package/scripts/toc.ts +0 -7
  92. package/src/components/Empty/index.tsx +0 -63
  93. package/src/components/Empty/style.ts +0 -31
  94. package/src/components/SelectWithImg/index.tsx +0 -97
  95. package/src/components/SelectWithImg/styles.ts +0 -43
  96. package/src/pages/chat/features/Conversation/Input/ActionRight.tsx +0 -31
  97. package/src/pages/chat/features/Conversation/Input/Footer.tsx +0 -49
  98. /package/src/components/{ManifestPreviewer.tsx → ManifestPreviewer/index.tsx} +0 -0
  99. /package/src/pages/chat/features/Conversation/Input/{Token.tsx → ActionBar/Token.tsx} +0 -0
@@ -20,6 +20,64 @@
20
20
  "session": "会话设置",
21
21
  "sessionWithName": "会话设置 · {{name}}"
22
22
  },
23
+ "llm": {
24
+ "AzureOpenAI": {
25
+ "endpoint": {
26
+ "desc": "从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值",
27
+ "placeholder": "https://docs-test-001.openai.azure.com",
28
+ "title": "Azure API 地址"
29
+ },
30
+ "models": {
31
+ "desc": "支持的模型",
32
+ "title": "模型列表"
33
+ },
34
+ "title": "Azure OpenAI 设置",
35
+ "token": {
36
+ "desc": "从 Azure 门户检查资源时,可在“密钥和终结点”部分中找到此值。 可以使用 KEY1 或 KEY2",
37
+ "placeholder": "Azure API Key",
38
+ "title": "API Key"
39
+ }
40
+ },
41
+ "OpenAI": {
42
+ "azureApiVersion": {
43
+ "desc": "Azure 的 API 版本,遵循 YYYY-MM-DD 格式,查阅[最新版本](https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference#chat-completions)",
44
+ "fetch": "获取列表",
45
+ "title": "Azure Api Version"
46
+ },
47
+ "check": {
48
+ "button": "检查",
49
+ "desc": "测试 Api Key 与代理地址是否正确填写",
50
+ "pass": "检查通过",
51
+ "title": "连通性检查"
52
+ },
53
+ "endpoint": {
54
+ "desc": "除默认地址外,必须包含 http(s)://",
55
+ "placeholder": "https://api.openai.com/v1",
56
+ "title": "接口代理地址"
57
+ },
58
+ "models": {
59
+ "count": "共支持 {{count}} 个模型",
60
+ "desc": "支持的模型",
61
+ "fetch": "获取模型列表",
62
+ "notSupport": "Azure OpenAI 暂不支持查看模型列表",
63
+ "notSupportTip": "你需要自行确保部署名称与模型名称一致",
64
+ "refetch": "重新获取模型列表",
65
+ "title": "模型列表"
66
+ },
67
+ "title": "OpenAI 设置",
68
+ "token": {
69
+ "desc": "使用自己的 OpenAI Key",
70
+ "placeholder": "OpenAI API Key",
71
+ "title": "API Key"
72
+ },
73
+ "useAzure": {
74
+ "desc": "使用 Azure 提供的 OpenAI 服务",
75
+ "fetch": "获取列表",
76
+ "title": "Azure OpenAI"
77
+ }
78
+ },
79
+ "waitingForMore": "更多模型正在 <1>计划接入</1> 中,敬请期待 ✨"
80
+ },
23
81
  "settingAgent": {
24
82
  "avatar": {
25
83
  "title": "头像"
@@ -114,23 +172,12 @@
114
172
  "title": "核采样"
115
173
  }
116
174
  },
117
- "settingOpenAI": {
118
- "endpoint": {
119
- "desc": "除默认地址外,必须包含 http(s)://",
120
- "placeholder": "https://api.openai.com/v1",
121
- "title": "接口代理地址"
122
- },
123
- "title": "OpenAI 设置",
124
- "token": {
125
- "desc": "使用自己的 OpenAI Key",
126
- "placeholder": "OpenAI API Key",
127
- "title": "API Key"
128
- }
129
- },
130
175
  "settingPlugin": {
131
176
  "add": "添加",
132
177
  "addTooltip": "添加自定义插件",
178
+ "clearDeprecated": "移除无效插件",
133
179
  "config": "{{id}} 插件配置",
180
+ "settings": "配置插件市场",
134
181
  "title": "插件列表"
135
182
  },
136
183
  "settingSystem": {
@@ -170,6 +217,7 @@
170
217
  },
171
218
  "tab": {
172
219
  "agent": "默认助手",
173
- "common": "通用设置"
220
+ "common": "通用设置",
221
+ "llm": "语言模型"
174
222
  }
175
223
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.74.0",
3
+ "version": "0.75.0",
4
4
  "description": "Lobe Chat is an open-source chatbot client using Vercel AI SDK, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -25,8 +25,8 @@
25
25
  "scripts": {
26
26
  "build": "next build",
27
27
  "dev": "next dev -p 3010",
28
- "i18n": "npm run i18n:toc && lobe-i18n",
29
- "i18n:toc": "ts-node --project ./tsconfig.json scripts/toc.ts",
28
+ "i18n": "npm run i18n:workflow && lobe-i18n",
29
+ "i18n:workflow": "ts-node --project ./tsconfig.json scripts/i18nWorkflow/index.ts",
30
30
  "lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
31
31
  "lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true",
32
32
  "lint:md": "remark . --quiet --frail --output",
@@ -89,7 +89,7 @@
89
89
  "lucide-react": "latest",
90
90
  "nanoid": "^4",
91
91
  "next": "13.4.7",
92
- "openai": "^4.5.0",
92
+ "openai": "^4.6.0",
93
93
  "polished": "^4",
94
94
  "react": "^18",
95
95
  "react-dom": "^18",
@@ -100,6 +100,7 @@
100
100
  "swr": "^2",
101
101
  "systemjs": "^6.14.2",
102
102
  "ts-md5": "^1",
103
+ "url-join": "^5",
103
104
  "use-merge-value": "^1",
104
105
  "utility-types": "^3",
105
106
  "uuid": "^9",
@@ -0,0 +1,19 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+
4
+ import i18nConfig from '../../.i18nrc';
5
+
6
+ export const root = resolve(__dirname, '../..');
7
+ export const localesDir = resolve(root, i18nConfig.output);
8
+ export const localeDir = (locale: string) => resolve(localesDir, locale);
9
+ export const localeDirJsonList = (locale: string) =>
10
+ readdirSync(localeDir(locale)).filter((name) => name.includes('.json'));
11
+ export const srcLocalesDir = resolve(root, './src/locales');
12
+ export const entryLocaleJsonFilepath = (file: string) =>
13
+ resolve(localesDir, i18nConfig.entryLocale, file);
14
+ export const outputLocaleJsonFilepath = (locale: string, file: string) =>
15
+ resolve(localesDir, locale, file);
16
+ export const srcLocalesResources = resolve(root, srcLocalesDir, 'resources');
17
+ export const localesResourcesFilepath = (locale: string) => resolve(srcLocalesResources, locale);
18
+
19
+ export { default as i18nConfig } from '../../.i18nrc';
@@ -0,0 +1,19 @@
1
+ import { consola } from 'consola';
2
+ import { colors } from 'consola/utils';
3
+
4
+ import { entryLocaleJsonFilepath, i18nConfig, localesResourcesFilepath } from './const';
5
+ import { tagWhite, writeJSON } from './utils';
6
+
7
+ export const genDefaultLocale = () => {
8
+ consola.info(`Default locale is ${i18nConfig.entryLocale}...`);
9
+
10
+ const resources = require(localesResourcesFilepath(i18nConfig.entryLocale));
11
+ const data = Object.entries(resources.default);
12
+ consola.start(`Generate default locale json, found ${data.length} namespaces...`);
13
+
14
+ for (const [ns, value] of data) {
15
+ const filepath = entryLocaleJsonFilepath(`${ns}.json`);
16
+ writeJSON(filepath, value);
17
+ consola.success(tagWhite(ns), colors.gray(filepath));
18
+ }
19
+ };
@@ -0,0 +1,50 @@
1
+ import { consola } from 'consola';
2
+ import { colors } from 'consola/utils';
3
+ import { existsSync } from 'node:fs';
4
+
5
+ import {
6
+ entryLocaleJsonFilepath,
7
+ i18nConfig,
8
+ localesResourcesFilepath,
9
+ outputLocaleJsonFilepath,
10
+ } from './const';
11
+ import { diff, readJSON, tagWhite, tagYellow, unset, writeJSON } from './utils';
12
+
13
+ export const genDiff = () => {
14
+ consola.start(`Diff between Dev/Prod local...`);
15
+
16
+ const resources = require(localesResourcesFilepath(i18nConfig.entryLocale));
17
+ const data = Object.entries(resources.default);
18
+
19
+ for (const [ns, devJSON] of data) {
20
+ const filepath = entryLocaleJsonFilepath(`${ns}.json`);
21
+ if (!existsSync(filepath)) continue;
22
+ const prodJSON = readJSON(filepath);
23
+
24
+ const diffKeys = diff(devJSON, prodJSON);
25
+
26
+ if (diffKeys.length === 0) {
27
+ consola.success(tagWhite(ns), colors.gray(filepath));
28
+ continue;
29
+ }
30
+
31
+ consola.warn(tagYellow(ns), diffKeys);
32
+
33
+ const clearLocals = [];
34
+
35
+ for (const locale of [i18nConfig.entryLocale, ...i18nConfig.outputLocales]) {
36
+ const localeFilepath = outputLocaleJsonFilepath(locale, `${ns}.json`);
37
+ if (!existsSync(localeFilepath)) continue;
38
+ const localeJSON = readJSON(localeFilepath);
39
+
40
+ for (const key of diffKeys) {
41
+ unset(localeJSON, key);
42
+ }
43
+
44
+ writeJSON(localeFilepath, localeJSON);
45
+ clearLocals.push(locale);
46
+ }
47
+ consola.info('clear', clearLocals);
48
+ consola.success(tagWhite(ns), colors.gray(filepath));
49
+ }
50
+ };
@@ -0,0 +1,26 @@
1
+ import { consola } from 'consola';
2
+ import { colors } from 'consola/utils';
3
+ import { writeFileSync } from 'node:fs';
4
+
5
+ import { i18nConfig, localeDirJsonList, localesResourcesFilepath } from './const';
6
+ import { genResourcesContent, genToc, tagBlue, tagGreen } from './utils';
7
+
8
+ const locales = [i18nConfig.entryLocale, ...i18nConfig.outputLocales];
9
+
10
+ export const genResources = () => {
11
+ consola.start(`Generate locale resources and types, found ${locales.length} locales...`);
12
+
13
+ for (const locale of locales) {
14
+ const files = localeDirJsonList(locale);
15
+ const toc = genToc(files, locale);
16
+
17
+ const filepath = localesResourcesFilepath(`${locale}.ts`);
18
+ writeFileSync(filepath, toc);
19
+ consola.success(tagBlue(locale), colors.gray(filepath));
20
+ }
21
+
22
+ const indexFilepath = localesResourcesFilepath(`index.ts`);
23
+ writeFileSync(indexFilepath, genResourcesContent(locales));
24
+
25
+ consola.success(tagGreen('INDEX'), colors.gray(indexFilepath));
26
+ };
@@ -0,0 +1,15 @@
1
+ import { genDefaultLocale } from './genDefaultLocale';
2
+ import { genDiff } from './genDiff';
3
+ import { genResources } from './genResources';
4
+ import { split } from './utils';
5
+
6
+ split('DIFF ANALYSIS');
7
+ genDiff();
8
+
9
+ split('GENERATE DEFAULT LOCALE');
10
+ genDefaultLocale();
11
+
12
+ split('GENERATE RESOURCE & TOC');
13
+ genResources();
14
+
15
+ split('GENERATE I18N FILES');
@@ -0,0 +1,85 @@
1
+ import { consola } from 'consola';
2
+ import { colors } from 'consola/utils';
3
+ import { tocForResources } from 'i18next-resources-for-ts';
4
+ import { readFileSync, writeFileSync } from 'node:fs';
5
+ import { resolve } from 'node:path';
6
+
7
+ import i18nConfig from '../../.i18nrc';
8
+ import { srcLocalesResources } from './const';
9
+
10
+ export const readJSON = (filePath: string) => {
11
+ const data = readFileSync(filePath, 'utf8');
12
+ return JSON.parse(data);
13
+ };
14
+
15
+ export const writeJSON = (filePath: string, data: any) => {
16
+ const jsonStr = JSON.stringify(data, null, 2);
17
+ writeFileSync(filePath, jsonStr, 'utf8');
18
+ };
19
+
20
+ export const genResourcesContent = (locales: string[]) => {
21
+ let index = '';
22
+ let indexObj = '';
23
+
24
+ for (const locale of locales) {
25
+ index += `import ${locale} from "./${locale}";\n`;
26
+ indexObj += ` "${locale.replace('_', '-')}": ${locale},\n`;
27
+ }
28
+
29
+ return `${index}
30
+ const resources = {
31
+ ${indexObj}} as const;
32
+ export default resources;
33
+ export const defaultResources = ${i18nConfig.entryLocale};
34
+ export type Resources = typeof resources;
35
+ export type DefaultResources = typeof defaultResources;
36
+ export type Namespaces = keyof DefaultResources;
37
+ export type Locales = keyof Resources;
38
+ `;
39
+ };
40
+
41
+ export const genNamespaceList = (files: string[], locale: string) => {
42
+ return files.map((file) => ({
43
+ name: file.replace('.json', ''),
44
+ path: resolve(i18nConfig.output, locale, file),
45
+ }));
46
+ };
47
+
48
+ export const genToc = (files: string[], locale: string) => {
49
+ const ns = genNamespaceList(files, locale);
50
+ let toc = tocForResources(ns, srcLocalesResources).replaceAll('\\', '/');
51
+ if (locale === i18nConfig.entryLocale) {
52
+ toc = toc.replaceAll('.json', '').replaceAll('../../../locales/zh_CN', '../default');
53
+ }
54
+ return toc;
55
+ };
56
+
57
+ export const diff = (obj1: any, obj2: any, prefix: string = ''): string[] => {
58
+ let result: string[] = [];
59
+ for (const key in obj1) {
60
+ if (!obj2[key]) continue;
61
+ if (typeof obj1[key] === 'object' && obj1[key] !== null && obj2[key]) {
62
+ result = result.concat(diff(obj1[key], obj2[key], `${key}.`));
63
+ } else if (obj1[key] !== obj2[key]) {
64
+ result.push(`${prefix}${key}`);
65
+ }
66
+ }
67
+ return result;
68
+ };
69
+
70
+ export const unset = (obj: any, key: string) => {
71
+ const keys = key.split('.');
72
+ const lastKey = keys.pop();
73
+ const lastObj = keys.reduce((obj, key) => (obj[key] = obj[key] || {}), obj);
74
+ if (lastKey) delete lastObj[lastKey];
75
+ };
76
+
77
+ export const tagBlue = (text: string) => colors.bgBlueBright(colors.black(` ${text} `));
78
+ export const tagYellow = (text: string) => colors.bgYellowBright(colors.black(` ${text} `));
79
+ export const tagGreen = (text: string) => colors.bgGreenBright(colors.black(` ${text} `));
80
+ export const tagWhite = (text: string) => colors.bgWhiteBright(colors.black(` ${text} `));
81
+
82
+ export const split = (name: string) => {
83
+ consola.log('');
84
+ consola.log(colors.gray(`========================== ${name} ==============================`));
85
+ };
@@ -5,8 +5,10 @@ export const MOBILE_NABBAR_HEIGHT = 44;
5
5
  export const MOBILE_TABBAR_HEIGHT = 48;
6
6
  export const CHAT_TEXTAREA_HEIGHT = 200;
7
7
  export const CHAT_SIDEBAR_WIDTH = 280;
8
+ export const MARKET_SIDEBAR_WIDTH = 400;
8
9
  export const FOLDER_WIDTH = 256;
10
+ export const MAX_WIDTH = 1024;
9
11
  export const FORM_STYLE: FormProps = {
10
12
  itemMinWidth: 'max(30%,240px)',
11
- style: { maxWidth: 1024, width: '100%' },
13
+ style: { maxWidth: MAX_WIDTH, width: '100%' },
12
14
  };
@@ -0,0 +1,15 @@
1
+ import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
2
+ import { AgentsMarketItem } from '@/types/market';
3
+
4
+ import { DEFAULT_AGENT_META } from './meta';
5
+
6
+ export const DEFAULT_AGENTS_MARKET_ITEM: AgentsMarketItem = {
7
+ author: '',
8
+ config: DEFAULT_AGENT_CONFIG,
9
+ createAt: Date.now().toString(),
10
+ homepage: '',
11
+ identifier: '',
12
+ manifest: '',
13
+ meta: DEFAULT_AGENT_META,
14
+ schemaVersion: 1,
15
+ };
package/src/const/url.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import urlJoin from 'url-join';
2
+
1
3
  import { localeOptions } from '@/locales/options';
2
4
  import { Locales } from '@/locales/resources';
3
5
 
@@ -11,15 +13,38 @@ export const FEEDBACK = pkg.bugs.url;
11
13
  export const DISCORD = 'https://discord.gg/AYFPHvv2jT';
12
14
 
13
15
  export const PLUGINS_INDEX_URL =
14
- process.env.PLUGINS_INDEX_URL ?? 'https://chat-plugins.lobehub.com/index';
16
+ process.env.PLUGINS_INDEX_URL ?? 'https://chat-plugins.lobehub.com';
17
+
18
+ export const DEFAULT_LANG = 'en-US';
15
19
 
16
- export const getPluginIndexJSON = (lang: Locales = 'en-US', baseUrl = PLUGINS_INDEX_URL) => {
17
- if (lang === 'en-US' || !localeOptions.map((o) => o.value).includes(lang)) return baseUrl;
20
+ export const checkLang = (lang: Locales) => {
21
+ return lang === DEFAULT_LANG || !localeOptions.map((o) => o.value).includes(lang);
22
+ };
23
+ export const getPluginIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = PLUGINS_INDEX_URL) => {
24
+ if (checkLang(lang)) return baseUrl;
18
25
 
19
- return `${baseUrl}.${lang}.json`;
26
+ return urlJoin(baseUrl, `index.${lang}.json`);
20
27
  };
21
28
 
22
29
  export const AGENTS_INDEX_URL = process.env.AGENTS_INDEX_URL ?? 'https://chat-agents.lobehub.com';
23
30
 
31
+ export const getAgentIndexJSON = (lang: Locales = DEFAULT_LANG, baseUrl = AGENTS_INDEX_URL) => {
32
+ if (checkLang(lang)) return baseUrl;
33
+
34
+ return urlJoin(baseUrl, `index.${lang}.json`);
35
+ };
36
+
37
+ export const getAgentJSON = (
38
+ identifier: string,
39
+ lang: Locales = DEFAULT_LANG,
40
+ baseUrl = AGENTS_INDEX_URL,
41
+ ) => {
42
+ if (checkLang(lang)) return urlJoin(baseUrl, `${identifier}.json`);
43
+
44
+ return urlJoin(baseUrl, `${identifier}.${lang}.json`);
45
+ };
46
+
47
+ export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents';
48
+
24
49
  export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean) =>
25
50
  mobile ? `/chat/mobile#session=${id}` : `/chat#session=${id}`;
@@ -1,4 +1,4 @@
1
- import { Form, ItemGroup, SliderWithInput } from '@lobehub/ui';
1
+ import { Form, ItemGroup, SelectWithImg, SliderWithInput } from '@lobehub/ui';
2
2
  import { ConfigProvider, Input, Select, Switch } from 'antd';
3
3
  import { useTheme, useThemeMode } from 'antd-style';
4
4
  import isEqual from 'fast-deep-equal';
@@ -7,7 +7,6 @@ import { BrainCog, LayoutList, MessageSquare, MessagesSquare } from 'lucide-reac
7
7
  import { memo, useMemo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
- import SelectWithImg from '@/components/SelectWithImg';
11
10
  import { FORM_STYLE } from '@/const/layoutTokens';
12
11
  import { LanguageModel } from '@/types/llm';
13
12
  import { LobeAgentConfigKeys } from '@/types/session';
@@ -62,7 +62,7 @@ const MarketSettingModal = memo<MarketSettingModalProps>(({ open, onOpenChange }
62
62
  <Input
63
63
  defaultValue={PLUGINS_INDEX_URL}
64
64
  disabled
65
- placeholder={'https://xxxxx.com/index.json'}
65
+ placeholder={'https://xxxxx.com'}
66
66
  />
67
67
  </Tooltip>
68
68
  </Form.Item>
@@ -19,7 +19,7 @@ export const initialState: State = {
19
19
  avatar: false,
20
20
  backgroundColor: false,
21
21
  description: false,
22
- tag: false,
22
+ tags: false,
23
23
  title: false,
24
24
  },
25
25
 
@@ -1,6 +1,6 @@
1
1
  import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
2
2
  import { createStyles } from 'antd-style';
3
- import { MessageSquare, Sticker } from 'lucide-react';
3
+ import { Bot, MessageSquare } from 'lucide-react';
4
4
  import Router from 'next/router';
5
5
  import { rgba } from 'polished';
6
6
  import { memo, useMemo } from 'react';
@@ -33,7 +33,7 @@ export default memo<{ className?: string }>(({ className }) => {
33
33
  title: t('tab.chat'),
34
34
  },
35
35
  {
36
- icon: (active) => <Icon className={active ? styles.active : undefined} icon={Sticker} />,
36
+ icon: (active) => <Icon className={active ? styles.active : undefined} icon={Bot} />,
37
37
  key: 'market',
38
38
  onClick: () => {
39
39
  Router.push({ hash: '', pathname: `/market` });
@@ -1,5 +1,5 @@
1
1
  import { ActionIcon } from '@lobehub/ui';
2
- import { MessageSquare, Sticker } from 'lucide-react';
2
+ import { Bot, MessageSquare } from 'lucide-react';
3
3
  import Router from 'next/router';
4
4
  import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
@@ -32,7 +32,7 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
32
32
  />
33
33
  <ActionIcon
34
34
  active={tab === 'market'}
35
- icon={Sticker}
35
+ icon={Bot}
36
36
  onClick={() => {
37
37
  if (Router.asPath.startsWith('/market')) return;
38
38
  Router.push('/market');
@@ -0,0 +1,12 @@
1
+ export default {
2
+ addAgent: '添加助手',
3
+ search: {
4
+ placeholder: '搜索助手名称介绍或关键词...',
5
+ },
6
+ sidebar: {
7
+ comment: '讨论区',
8
+ prompt: '提示词',
9
+ title: '助手详情',
10
+ },
11
+ submitAgent: '提交助手',
12
+ };
@@ -1,6 +1,7 @@
1
1
  import common from '../../../locales/en_US/common.json';
2
2
  import empty from '../../../locales/en_US/empty.json';
3
3
  import error from '../../../locales/en_US/error.json';
4
+ import market from '../../../locales/en_US/market.json';
4
5
  import plugin from '../../../locales/en_US/plugin.json';
5
6
  import setting from '../../../locales/en_US/setting.json';
6
7
  import welcome from '../../../locales/en_US/welcome.json';
@@ -9,6 +10,7 @@ const resources = {
9
10
  common,
10
11
  empty,
11
12
  error,
13
+ market,
12
14
  plugin,
13
15
  setting,
14
16
  welcome,
@@ -1,6 +1,7 @@
1
1
  import common from '../../../locales/ru_RU/common.json';
2
2
  import empty from '../../../locales/ru_RU/empty.json';
3
3
  import error from '../../../locales/ru_RU/error.json';
4
+ import market from '../../../locales/ru_RU/market.json';
4
5
  import plugin from '../../../locales/ru_RU/plugin.json';
5
6
  import setting from '../../../locales/ru_RU/setting.json';
6
7
  import welcome from '../../../locales/ru_RU/welcome.json';
@@ -9,6 +10,7 @@ const resources = {
9
10
  common,
10
11
  empty,
11
12
  error,
13
+ market,
12
14
  plugin,
13
15
  setting,
14
16
  welcome,
@@ -1,6 +1,7 @@
1
1
  import common from '../default/common';
2
2
  import empty from '../default/empty';
3
3
  import error from '../default/error';
4
+ import market from '../default/market';
4
5
  import plugin from '../default/plugin';
5
6
  import setting from '../default/setting';
6
7
  import welcome from '../default/welcome';
@@ -9,6 +10,7 @@ const resources = {
9
10
  common,
10
11
  empty,
11
12
  error,
13
+ market,
12
14
  plugin,
13
15
  setting,
14
16
  welcome,
@@ -9,7 +9,7 @@ import { useSessionStore } from '@/store/session';
9
9
  import { agentSelectors } from '@/store/session/selectors';
10
10
  import { LanguageModel } from '@/types/llm';
11
11
 
12
- const InputActions = memo(() => {
12
+ const ActionLeft = memo(() => {
13
13
  const { t } = useTranslation('setting');
14
14
 
15
15
  const [model, temperature, historyCount, unlimited, updateAgentConfig] = useSessionStore((s) => {
@@ -107,4 +107,4 @@ const InputActions = memo(() => {
107
107
  );
108
108
  });
109
109
 
110
- export default InputActions;
110
+ export default ActionLeft;
@@ -0,0 +1,36 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { Popconfirm } from 'antd';
3
+ import { Eraser } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import { useSessionStore } from '@/store/session';
8
+
9
+ import SaveTopic from '../Footer/SaveTopic';
10
+
11
+ const ActionsRight = memo(() => {
12
+ const { t } = useTranslation('setting');
13
+ const [clearMessage] = useSessionStore((s) => [s.clearMessage, s.updateAgentConfig]);
14
+
15
+ return (
16
+ <>
17
+ <SaveTopic />
18
+ <Popconfirm
19
+ cancelText={t('cancel', { ns: 'common' })}
20
+ okButtonProps={{ danger: true }}
21
+ okText={t('ok', { ns: 'common' })}
22
+ onConfirm={() => clearMessage()}
23
+ placement={'topRight'}
24
+ title={t('confirmClearCurrentMessages', { ns: 'common' })}
25
+ >
26
+ <ActionIcon
27
+ icon={Eraser}
28
+ placement={'bottom'}
29
+ title={t('clearCurrentMessages', { ns: 'common' })}
30
+ />
31
+ </Popconfirm>
32
+ </>
33
+ );
34
+ });
35
+
36
+ export default ActionsRight;
@@ -0,0 +1,40 @@
1
+ import { DraggablePanel } from '@lobehub/ui';
2
+ import { ReactNode, memo } from 'react';
3
+
4
+ import { CHAT_TEXTAREA_HEIGHT, HEADER_HEIGHT } from '@/const/layoutTokens';
5
+ import { useGlobalStore } from '@/store/global';
6
+
7
+ interface ChatInputDesktopLayoutProps {
8
+ children: ReactNode;
9
+ expand?: boolean;
10
+ }
11
+
12
+ const ChatInputDesktopLayout = memo<ChatInputDesktopLayoutProps>(({ children, expand }) => {
13
+ const [inputHeight, updatePreference] = useGlobalStore((s) => [
14
+ s.preference.inputHeight,
15
+ s.updatePreference,
16
+ ]);
17
+
18
+ return (
19
+ <DraggablePanel
20
+ expandable={false}
21
+ fullscreen={expand}
22
+ headerHeight={HEADER_HEIGHT}
23
+ minHeight={CHAT_TEXTAREA_HEIGHT}
24
+ onSizeChange={(_, size) => {
25
+ if (!size) return;
26
+
27
+ updatePreference({
28
+ inputHeight: typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
29
+ });
30
+ }}
31
+ placement="bottom"
32
+ size={{ height: inputHeight, width: '100%' }}
33
+ style={{ zIndex: 10 }}
34
+ >
35
+ {children}
36
+ </DraggablePanel>
37
+ );
38
+ });
39
+
40
+ export default ChatInputDesktopLayout;