@lobehub/chat 0.120.2 → 0.120.4

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 (109) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +47 -22
  3. package/README.zh-CN.md +53 -26
  4. package/docs/Deployment/Upstream-Sync.md +8 -2
  5. package/docs/Deployment/Upstream-Sync.zh-CN.md +9 -3
  6. package/docs/Development/Add-New-Locale.md +9 -1
  7. package/docs/Development/Add-New-Locale.zh-CN.md +9 -1
  8. package/docs/Development/Contributing-Guidelines.md +83 -0
  9. package/docs/Development/Contributing-Guidelines.zh-CN.md +13 -0
  10. package/docs/Development/Internationalization-Implementation.md +125 -0
  11. package/docs/Development/Internationalization-Implementation.zh-CN.md +9 -1
  12. package/docs/Development/State-Management/State-Management-Intro.md +224 -0
  13. package/docs/Development/State-Management/{index.zh-CN.md → State-Management-Intro.zh-CN.md} +25 -28
  14. package/docs/Home.md +9 -6
  15. package/docs/_Sidebar.md +10 -7
  16. package/locales/ar/common.json +1 -0
  17. package/locales/ar/setting.json +1 -3
  18. package/locales/de-DE/common.json +1 -0
  19. package/locales/de-DE/setting.json +1 -3
  20. package/locales/en-US/common.json +1 -0
  21. package/locales/en-US/setting.json +1 -3
  22. package/locales/es-ES/common.json +1 -0
  23. package/locales/es-ES/setting.json +1 -3
  24. package/locales/fr-FR/common.json +1 -0
  25. package/locales/fr-FR/setting.json +1 -3
  26. package/locales/it-IT/common.json +1 -0
  27. package/locales/it-IT/setting.json +1 -3
  28. package/locales/ja-JP/common.json +1 -0
  29. package/locales/ja-JP/setting.json +1 -3
  30. package/locales/ko-KR/common.json +1 -0
  31. package/locales/ko-KR/setting.json +1 -3
  32. package/locales/nl-NL/common.json +1 -0
  33. package/locales/nl-NL/setting.json +1 -3
  34. package/locales/pl-PL/common.json +1 -0
  35. package/locales/pl-PL/setting.json +1 -3
  36. package/locales/pt-BR/common.json +1 -0
  37. package/locales/pt-BR/setting.json +1 -3
  38. package/locales/ru-RU/common.json +1 -0
  39. package/locales/ru-RU/setting.json +1 -3
  40. package/locales/tr-TR/common.json +1 -0
  41. package/locales/tr-TR/setting.json +1 -3
  42. package/locales/vi-VN/common.json +1 -0
  43. package/locales/vi-VN/setting.json +1 -3
  44. package/locales/zh-CN/common.json +1 -0
  45. package/locales/zh-CN/setting.json +1 -3
  46. package/locales/zh-TW/common.json +1 -0
  47. package/locales/zh-TW/setting.json +1 -3
  48. package/next.config.mjs +2 -2
  49. package/package.json +8 -7
  50. package/scripts/docsWorkflow/const.ts +1 -1
  51. package/scripts/docsWorkflow/index.ts +9 -3
  52. package/scripts/readmeWorkflow/syncAgentIndex.ts +2 -2
  53. package/scripts/readmeWorkflow/syncPluginIndex.ts +2 -2
  54. package/src/app/chat/(desktop)/features/ChatHeader.tsx +6 -6
  55. package/src/app/chat/{features → (desktop)/features/SideBar}/SystemRole/index.tsx +8 -5
  56. package/src/app/chat/(desktop)/features/{SideBar.tsx → SideBar/index.tsx} +2 -3
  57. package/src/app/chat/(mobile)/index.tsx +17 -4
  58. package/src/app/chat/(mobile)/mobile/page.tsx +30 -2
  59. package/src/app/chat/components/SessionHydration/index.tsx +44 -0
  60. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +6 -1
  61. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +0 -2
  62. package/src/app/chat/features/SessionListContent/List/index.tsx +7 -3
  63. package/src/app/chat/features/TopicListContent/Header.tsx +1 -1
  64. package/src/app/chat/page.tsx +7 -3
  65. package/src/app/home/Redirect.tsx +2 -2
  66. package/src/app/metadata.ts +6 -0
  67. package/src/app/settings/common/Common.tsx +5 -6
  68. package/src/app/welcome/features/Banner/index.tsx +6 -7
  69. package/src/components/StoreHydration/ChatHydration/index.tsx +33 -0
  70. package/src/config/server.ts +4 -0
  71. package/src/const/locale.ts +2 -2
  72. package/src/const/url.ts +1 -1
  73. package/src/database/models/__tests__/message.test.ts +3 -2
  74. package/src/database/models/message.ts +13 -9
  75. package/src/database/schemas/message.ts +1 -1
  76. package/src/features/ChatInput/ActionBar/Clear.tsx +0 -2
  77. package/src/features/ChatInput/useChatInput.ts +2 -6
  78. package/src/features/Conversation/hooks/useInitConversation.ts +0 -2
  79. package/src/features/Conversation/index.tsx +7 -2
  80. package/src/features/PluginDetailModal/index.tsx +0 -2
  81. package/src/features/PluginDevModal/index.tsx +0 -1
  82. package/src/features/SideBar/TopActions.tsx +5 -5
  83. package/src/layout/GlobalLayout/Locale.tsx +37 -10
  84. package/src/layout/GlobalLayout/StoreHydration.tsx +12 -11
  85. package/src/layout/GlobalLayout/index.tsx +1 -1
  86. package/src/locales/default/setting.ts +0 -2
  87. package/src/locales/resources.ts +9 -7
  88. package/src/store/chat/slices/message/action.ts +1 -1
  89. package/src/store/chat/store.ts +2 -34
  90. package/src/store/global/slices/common/action.ts +6 -0
  91. package/src/store/global/slices/common/initialState.ts +5 -0
  92. package/src/store/session/index.ts +0 -1
  93. package/src/store/session/initialState.ts +6 -2
  94. package/src/store/session/slices/agent/selectors.ts +3 -1
  95. package/src/store/session/slices/session/action.ts +13 -17
  96. package/src/store/session/slices/session/selectors/list.ts +7 -0
  97. package/src/store/session/store.ts +5 -46
  98. package/src/app/chat/(mobile)/layout.mobile.tsx +0 -17
  99. package/src/app/chat/(mobile)/mobile/index.tsx +0 -30
  100. package/src/app/chat/(mobile)/mobile/layout.mobile.tsx +0 -11
  101. package/src/store/session/hooks/index.ts +0 -4
  102. package/src/store/session/hooks/useEffectAfterHydrated.ts +0 -27
  103. package/src/store/session/hooks/useOnFinishHydrationSession.ts +0 -19
  104. package/src/store/session/hooks/useSessionChatInit.ts +0 -12
  105. package/src/store/session/hooks/useSessionHydrated.ts +0 -13
  106. /package/docs/Development/{index.md → Intro.md} +0 -0
  107. /package/docs/Development/{index.zh-CN.md → Intro.zh-CN.md} +0 -0
  108. /package/src/app/chat/{features → (desktop)/features/SideBar}/SystemRole/style.ts +0 -0
  109. /package/src/app/chat/{features → components}/SidebarHeader/index.tsx +0 -0
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Tamaño de fuente para el contenido del chat",
254
254
  "marks": {
255
- "large": "Grande",
256
- "normal": "Normal",
257
- "small": "Pequeño"
255
+ "normal": "Normal"
258
256
  },
259
257
  "title": "Tamaño de fuente"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Importer des données"
52
52
  },
53
53
  "lang": {
54
+ "ar": "arabe",
54
55
  "bn": "Bengali",
55
56
  "cs-CZ": "Tchèque",
56
57
  "da-DK": "Danois",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Taille de la police du contenu du chat",
254
254
  "marks": {
255
- "large": "Grand",
256
- "normal": "Normal",
257
- "small": "Petit"
255
+ "normal": "Normal"
258
256
  },
259
257
  "title": "Taille de la police"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Importa dati"
52
52
  },
53
53
  "lang": {
54
+ "ar": "Arabo",
54
55
  "bn": "Bengalese",
55
56
  "cs-CZ": "Ceco",
56
57
  "da-DK": "Danese",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Dimensione del carattere per i messaggi",
254
254
  "marks": {
255
- "large": "Grande",
256
- "normal": "Normale",
257
- "small": "Piccola"
255
+ "normal": "Normale"
258
256
  },
259
257
  "title": "Dimensione del carattere"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "データのインポート"
52
52
  },
53
53
  "lang": {
54
+ "ar": "アラビア語",
54
55
  "bn": "ベンガル語",
55
56
  "cs-CZ": "チェコ語",
56
57
  "da-DK": "デンマーク語",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "チャットのフォントサイズ",
254
254
  "marks": {
255
- "large": "大きい",
256
- "normal": "標準",
257
- "small": "小さい"
255
+ "normal": "標準"
258
256
  },
259
257
  "title": "フォントサイズ"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "데이터 가져오기"
52
52
  },
53
53
  "lang": {
54
+ "ar": "아랍어",
54
55
  "bn": "벵골어",
55
56
  "cs-CZ": "체코어",
56
57
  "da-DK": "덴마크어",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "채팅 내용의 글꼴 크기",
254
254
  "marks": {
255
- "large": "대",
256
- "normal": "표준",
257
- "small": "소"
255
+ "normal": "표준"
258
256
  },
259
257
  "title": "글꼴 크기"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Gegevens importeren"
52
52
  },
53
53
  "lang": {
54
+ "ar": "Arabisch",
54
55
  "bn": "Bengaals",
55
56
  "cs-CZ": "Tsjechisch",
56
57
  "da-DK": "Deens",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Lettergrootte van chatberichten",
254
254
  "marks": {
255
- "large": "Groot",
256
- "normal": "Normaal",
257
- "small": "Klein"
255
+ "normal": "Normaal"
258
256
  },
259
257
  "title": "Lettergrootte"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Import danych"
52
52
  },
53
53
  "lang": {
54
+ "ar": "arabski",
54
55
  "bn": "Bengalski",
55
56
  "cs-CZ": "Czeski",
56
57
  "da-DK": "Duński",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Rozmiar czcionki wiadomości",
254
254
  "marks": {
255
- "large": "Duży",
256
- "normal": "Standardowy",
257
- "small": "Mały"
255
+ "normal": "Standardowy"
258
256
  },
259
257
  "title": "Rozmiar czcionki"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Importar dados"
52
52
  },
53
53
  "lang": {
54
+ "ar": "árabe",
54
55
  "bn": "Bengali",
55
56
  "cs-CZ": "Tcheco",
56
57
  "da-DK": "Dinamarquês",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Tamanho da fonte do conteúdo do chat",
254
254
  "marks": {
255
- "large": "Grande",
256
- "normal": "Normal",
257
- "small": "Pequeno"
255
+ "normal": "Normal"
258
256
  },
259
257
  "title": "Tamanho da Fonte"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Импорт данных"
52
52
  },
53
53
  "lang": {
54
+ "ar": "арабский",
54
55
  "bn": "Бенгальский",
55
56
  "cs-CZ": "Чешский",
56
57
  "da-DK": "Датский",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Размер шрифта для чата",
254
254
  "marks": {
255
- "large": "большой",
256
- "normal": "нормальный",
257
- "small": "малый"
255
+ "normal": "нормальный"
258
256
  },
259
257
  "title": "Размер шрифта"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Veri İçe Aktar"
52
52
  },
53
53
  "lang": {
54
+ "ar": "Arapça",
54
55
  "bn": "Bengalce",
55
56
  "cs-CZ": "Çekçe",
56
57
  "da-DK": "Danca",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Sohbet içeriği için yazı boyutu",
254
254
  "marks": {
255
- "large": "Büyük",
256
- "normal": "Normal",
257
- "small": "Küçük"
255
+ "normal": "Normal"
258
256
  },
259
257
  "title": "Yazı Boyutu"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "Nhập dữ liệu"
52
52
  },
53
53
  "lang": {
54
+ "ar": "Tiếng Ả Rập",
54
55
  "bn": "Tiếng Bengal",
55
56
  "cs-CZ": "Tiếng Séc",
56
57
  "da-DK": "Tiếng Đan Mạch",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "Kích cỡ chữ của nội dung trò chuyện",
254
254
  "marks": {
255
- "large": "Lớn",
256
- "normal": "Bình thường",
257
- "small": "Nhỏ"
255
+ "normal": "Bình thường"
258
256
  },
259
257
  "title": "Kích cỡ chữ"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "导入数据"
52
52
  },
53
53
  "lang": {
54
+ "ar": "阿拉伯语",
54
55
  "bn": "孟加拉语",
55
56
  "cs-CZ": "捷克语",
56
57
  "da-DK": "丹麦语",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "聊天内容的字体大小",
254
254
  "marks": {
255
- "large": "大",
256
- "normal": "标准",
257
- "small": "小"
255
+ "normal": "标准"
258
256
  },
259
257
  "title": "字体大小"
260
258
  },
@@ -51,6 +51,7 @@
51
51
  "title": "匯入資料"
52
52
  },
53
53
  "lang": {
54
+ "ar": "阿拉伯語",
54
55
  "bn": "孟加拉語",
55
56
  "cs-CZ": "捷克語",
56
57
  "da-DK": "丹麥語",
@@ -252,9 +252,7 @@
252
252
  "fontSize": {
253
253
  "desc": "聊天內容的字體大小",
254
254
  "marks": {
255
- "large": "大",
256
- "normal": "標準",
257
- "small": "小"
255
+ "normal": "標準"
258
256
  },
259
257
  "title": "字體大小"
260
258
  },
package/next.config.mjs CHANGED
@@ -21,11 +21,11 @@ const nextConfig = {
21
21
  compress: isProd,
22
22
  experimental: {
23
23
  optimizePackageImports: [
24
- 'modern-screenshot',
25
24
  'emoji-mart',
26
25
  '@emoji-mart/react',
27
26
  '@emoji-mart/data',
28
27
  '@icons-pack/react-simple-icons',
28
+ '@lobehub/ui',
29
29
  'gpt-tokenizer',
30
30
  'chroma-js',
31
31
  ],
@@ -46,7 +46,7 @@ const nextConfig = {
46
46
 
47
47
  reactStrictMode: true,
48
48
 
49
- transpilePackages: ['antd-style', '@lobehub/ui', '@lobehub/tts'],
49
+ transpilePackages: ['antd-style'],
50
50
 
51
51
  webpack(config) {
52
52
  config.experiments = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.120.2",
3
+ "version": "0.120.4",
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",
@@ -32,7 +32,7 @@
32
32
  "dev": "next dev -p 3010",
33
33
  "docs": "lobe-i18n md && npm run workflow:docs",
34
34
  "i18n": "npm run workflow:i18n && lobe-i18n",
35
- "i18n-md": "lobe-i18n md",
35
+ "i18n:docs": "lobe-i18n md && npm run workflow:docs",
36
36
  "lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
37
37
  "lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true",
38
38
  "lint:md": "remark . --quiet --frail --output",
@@ -51,9 +51,9 @@
51
51
  "test:coverage-local": "vitest run --coverage",
52
52
  "test:update": "vitest -u",
53
53
  "type-check": "tsc --noEmit",
54
- "workflow:docs": "bun scripts/docsWorkflow/index.ts",
55
- "workflow:i18n": "ts-node --project ./tsconfig.json scripts/i18nWorkflow/index.ts",
56
- "workflow:readme": "bun scripts/readmeWorkflow/index.ts"
54
+ "workflow:docs": "tsx scripts/docsWorkflow/index.ts",
55
+ "workflow:i18n": "tsx scripts/i18nWorkflow/index.ts",
56
+ "workflow:readme": "tsx scripts/readmeWorkflow/index.ts"
57
57
  },
58
58
  "lint-staged": {
59
59
  "*.md": [
@@ -105,6 +105,7 @@
105
105
  "modern-screenshot": "^4",
106
106
  "nanoid": "^5",
107
107
  "next": "^14.0.2",
108
+ "nuqs": "^1.15.4",
108
109
  "openai": "^4.17.3",
109
110
  "polished": "^4",
110
111
  "posthog-js": "^1",
@@ -135,7 +136,7 @@
135
136
  "yaml": "^2",
136
137
  "zod": "^3",
137
138
  "zustand": "^4.4",
138
- "zustand-utils": "^1"
139
+ "zustand-utils": "^1.3.2"
139
140
  },
140
141
  "devDependencies": {
141
142
  "@commitlint/cli": "^18",
@@ -179,7 +180,7 @@
179
180
  "remark-parse": "^10",
180
181
  "semantic-release": "^21",
181
182
  "stylelint": "^15",
182
- "ts-node": "^10",
183
+ "tsx": "^4",
183
184
  "typescript": "^5",
184
185
  "unified": "^11",
185
186
  "unist-util-visit": "^5",
@@ -6,7 +6,7 @@ export const ROOT = resolve(__dirname, '../..');
6
6
  export const DOCS_DIR = resolve(ROOT, 'docs');
7
7
  export const HOME_PATH = resolve(DOCS_DIR, 'Home.md');
8
8
  export const SIDEBAR_PATH = resolve(DOCS_DIR, '_Sidebar.md');
9
- export const docsFiles = globSync(resolve(DOCS_DIR, '**/*.md'), {
9
+ export const docsFiles = globSync(resolve(DOCS_DIR, '**/*.md').replaceAll('\\', '/'), {
10
10
  ignore: ['Home.md', '_Sidebar.md', '_Footer.md'],
11
11
  nodir: true,
12
12
  }).filter((file) => !file.includes('.zh-CN.md'));
@@ -20,9 +20,15 @@ const run = async () => {
20
20
  const links = {
21
21
  cn: urlJoin(
22
22
  WIKI_URL,
23
- relative(DOCS_DIR, paths.cn).split('/')[1].replace('.zh-CN.md', '.zh-CN'),
23
+ relative(DOCS_DIR, paths.cn)
24
+ .replaceAll('\\', '/')
25
+ .split('/')[1]
26
+ .replace('.zh-CN.md', '.zh-CN'),
27
+ ),
28
+ en: urlJoin(
29
+ WIKI_URL,
30
+ relative(DOCS_DIR, paths.en).replaceAll('\\', '/').split('/')[1].replace('.md', ''),
24
31
  ),
25
- en: urlJoin(WIKI_URL, relative(DOCS_DIR, paths.en).split('/')[1].replace('.md', '')),
26
32
  };
27
33
  const titles = {
28
34
  cn: await getTitle(paths.cn),
@@ -59,7 +65,7 @@ const run = async () => {
59
65
  child.links.cn,
60
66
  )}\n`;
61
67
  sidebarContent += `- ${genMdLink(child.titles.en, child.links.en)} | ${genMdLink(
62
- '中文',
68
+ child.titles.cn,
63
69
  child.links.cn,
64
70
  )}\n`;
65
71
  });
@@ -10,7 +10,7 @@ const genAgentTable = (data: DataItem[], lang: string) => {
10
10
  const content = data.slice(0, 4).map((item) => [
11
11
  [
12
12
  genLink(
13
- item.meta.title,
13
+ item.meta.title.replaceAll('|', ','),
14
14
  qs.stringifyUrl({
15
15
  query: { agent: item.identifier },
16
16
  url: MARKET_URL,
@@ -18,7 +18,7 @@ const genAgentTable = (data: DataItem[], lang: string) => {
18
18
  ),
19
19
  `<sup>By **${genLink(item.author, item.homepage)}** on **${(item as any).createAt}**</sup>`,
20
20
  ].join('<br/>'),
21
- [item.meta.description, genTags(item.meta.tags)].join('<br/>'),
21
+ [item.meta.description.replaceAll('|', ','), genTags(item.meta.tags)].join('<br/>'),
22
22
  ]);
23
23
  return markdownTable([
24
24
  isCN ? ['最近新增', '助手说明'] : ['Recent Submits', 'Description'],
@@ -10,10 +10,10 @@ const genPluginTable = (data: DataItem[], lang: string) => {
10
10
  .slice(0, 4)
11
11
  .map((item) => [
12
12
  [
13
- genLink(item.meta.title, PLGUIN_URL),
13
+ genLink(item.meta.title.replaceAll('|', ','), PLGUIN_URL),
14
14
  `<sup>By **${item.author}** on **${item.createdAt}**</sup>`,
15
15
  ].join('<br/>'),
16
- [item.meta.description, genTags(item.meta.tags)].join('<br/>'),
16
+ [item.meta.description.replaceAll('|', ','), genTags(item.meta.tags)].join('<br/>'),
17
17
  ]);
18
18
  return markdownTable([
19
19
  isCN ? ['最近新增', '插件描述'] : ['Recent Submits', 'Description'],
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
11
11
  import { useGlobalStore } from '@/store/global';
12
- import { useSessionChatInit, useSessionStore } from '@/store/session';
12
+ import { useSessionStore } from '@/store/session';
13
13
  import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
14
14
  import { pathString } from '@/utils/url';
15
15
 
@@ -19,11 +19,12 @@ import ShareButton from '../../features/ChatHeader/ShareButton';
19
19
 
20
20
  const Left = memo(() => {
21
21
  const { t } = useTranslation('chat');
22
- const init = useSessionChatInit();
22
+
23
23
  const router = useRouter();
24
24
 
25
- const [isInbox, title, description, avatar, backgroundColor, model, plugins] = useSessionStore(
26
- (s) => [
25
+ const [init, isInbox, title, description, avatar, backgroundColor, model, plugins] =
26
+ useSessionStore((s) => [
27
+ sessionSelectors.isSomeSessionActive(s),
27
28
  sessionSelectors.isInboxSession(s),
28
29
  agentSelectors.currentAgentTitle(s),
29
30
  agentSelectors.currentAgentDescription(s),
@@ -31,8 +32,7 @@ const Left = memo(() => {
31
32
  agentSelectors.currentAgentBackgroundColor(s),
32
33
  agentSelectors.currentAgentModel(s),
33
34
  agentSelectors.currentAgentPlugins(s),
34
- ],
35
- );
35
+ ]);
36
36
 
37
37
  const displayTitle = isInbox ? t('inbox.title') : title;
38
38
  const displayDesc = isInbox ? t('inbox.desc') : description;
@@ -9,33 +9,36 @@ import useMergeState from 'use-merge-value';
9
9
 
10
10
  import AgentInfo from '@/features/AgentInfo';
11
11
  import { useGlobalStore } from '@/store/global';
12
- import { useSessionChatInit, useSessionStore } from '@/store/session';
13
- import { agentSelectors } from '@/store/session/selectors';
12
+ import { useSessionStore } from '@/store/session';
13
+ import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
14
14
  import { pathString } from '@/utils/url';
15
15
 
16
- import SidebarHeader from '../SidebarHeader';
16
+ import SidebarHeader from '../../../../components/SidebarHeader';
17
17
  import { useStyles } from './style';
18
18
 
19
19
  const SystemRole = memo(() => {
20
20
  const router = useRouter();
21
21
  const [editing, setEditing] = useState(false);
22
22
  const { styles } = useStyles();
23
- const [systemRole, meta, updateAgentConfig] = useSessionStore((s) => [
23
+
24
+ const [init, systemRole, meta, updateAgentConfig] = useSessionStore((s) => [
25
+ sessionSelectors.isSomeSessionActive(s),
24
26
  agentSelectors.currentAgentSystemRole(s),
25
27
  agentSelectors.currentAgentMeta(s),
26
28
  s.updateAgentConfig,
27
29
  ]);
30
+
28
31
  const [showSystemRole, toggleSystemRole] = useGlobalStore((s) => [
29
32
  s.preference.showSystemRole,
30
33
  s.toggleSystemRole,
31
34
  ]);
35
+
32
36
  const [open, setOpen] = useMergeState(false, {
33
37
  defaultValue: showSystemRole,
34
38
  onChange: toggleSystemRole,
35
39
  value: showSystemRole,
36
40
  });
37
41
 
38
- const init = useSessionChatInit();
39
42
  const { t } = useTranslation('common');
40
43
 
41
44
  const handleOpenWithEdit = () => {
@@ -3,15 +3,14 @@ import { createStyles } from 'antd-style';
3
3
  import dynamic from 'next/dynamic';
4
4
  import { memo } from 'react';
5
5
 
6
+ import TopicListContent from '@/app/chat/features/TopicListContent';
6
7
  import SafeSpacing from '@/components/SafeSpacing';
7
8
  import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
8
9
  import { useGlobalStore } from '@/store/global';
9
10
  import { useSessionStore } from '@/store/session';
10
11
  import { sessionSelectors } from '@/store/session/selectors';
11
12
 
12
- import TopicListContent from '../../features/TopicListContent';
13
-
14
- const SystemRole = dynamic(() => import('../../features/SystemRole'));
13
+ const SystemRole = dynamic(() => import('./SystemRole'));
15
14
 
16
15
  const useStyles = createStyles(({ css, token }) => ({
17
16
  content: css`
@@ -1,17 +1,30 @@
1
1
  'use client';
2
2
 
3
- import { memo } from 'react';
3
+ import { useRouter } from 'next/navigation';
4
+ import { memo, useEffect } from 'react';
5
+
6
+ import AppLayoutMobile from '@/layout/AppLayout.mobile';
7
+ import { useSwitchSideBarOnInit } from '@/store/global';
8
+ import { SidebarTabKey } from '@/store/global/slices/common/initialState';
4
9
 
5
10
  import PageTitle from '../features/PageTitle';
11
+ import SessionHeader from './features/SessionHeader';
6
12
  import SessionList from './features/SessionList';
7
- import Layout from './layout.mobile';
8
13
 
9
14
  const ChatMobilePage = memo(() => {
15
+ useSwitchSideBarOnInit(SidebarTabKey.Chat);
16
+
17
+ const router = useRouter();
18
+ useEffect(() => {
19
+ router.prefetch('/chat/mobile');
20
+ router.prefetch('/settings');
21
+ }, []);
22
+
10
23
  return (
11
- <Layout>
24
+ <AppLayoutMobile navBar={<SessionHeader />} showTabBar>
12
25
  <PageTitle />
13
26
  <SessionList />
14
- </Layout>
27
+ </AppLayoutMobile>
15
28
  );
16
29
  });
17
30
 
@@ -1,3 +1,31 @@
1
- import Index from './index';
1
+ 'use client';
2
2
 
3
- export default () => <Index />;
3
+ import dynamic from 'next/dynamic';
4
+ import { memo } from 'react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import ChatHeader from '@/app/chat/(mobile)/features/ChatHeader';
8
+ import Conversation from '@/features/Conversation';
9
+ import AppLayoutMobile from '@/layout/AppLayout.mobile';
10
+ import { useSessionStore } from '@/store/session';
11
+
12
+ import ChatInput from '../features/ChatInput';
13
+
14
+ const TopicList = dynamic(() => import('../features/TopicList'));
15
+
16
+ const Chat = memo(() => {
17
+ // due to mobile side don't have sessionList, so we need to fetch sessions here
18
+ // refs: https://github.com/lobehub/lobe-chat/pull/541
19
+ const useFetchSessions = useSessionStore((s) => s.useFetchSessions);
20
+ useFetchSessions();
21
+
22
+ return (
23
+ <AppLayoutMobile navBar={<ChatHeader />}>
24
+ <Flexbox height={'calc(100% - 44px)'} horizontal>
25
+ <Conversation chatInput={<ChatInput />} mobile />
26
+ <TopicList />
27
+ </Flexbox>
28
+ </AppLayoutMobile>
29
+ );
30
+ });
31
+ export default Chat;
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { useResponsive } from 'antd-style';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useQueryState } from 'nuqs';
6
+ import { parseAsString } from 'nuqs/parsers';
7
+ import { memo, useEffect } from 'react';
8
+ import { createStoreUpdater } from 'zustand-utils';
9
+
10
+ import { useSessionStore } from '@/store/session';
11
+
12
+ // sync outside state to useSessionStore
13
+ const SessionHydration = memo(() => {
14
+ const useStoreUpdater = createStoreUpdater(useSessionStore);
15
+
16
+ const { mobile } = useResponsive();
17
+ useStoreUpdater('isMobile', mobile);
18
+
19
+ const router = useRouter();
20
+ // TODO: 后续可以把 router 从 useSessionStore 移除
21
+ useStoreUpdater('router', router);
22
+
23
+ // two-way bindings the url and session store
24
+ const [session, setSession] = useQueryState(
25
+ 'session',
26
+ parseAsString.withDefault('inbox').withOptions({ history: 'replace', throttleMs: 500 }),
27
+ );
28
+ useStoreUpdater('activeId', session);
29
+
30
+ useEffect(() => {
31
+ const unsubscribe = useSessionStore.subscribe(
32
+ (s) => s.activeId,
33
+ (state) => setSession(state),
34
+ );
35
+
36
+ return () => {
37
+ unsubscribe();
38
+ };
39
+ }, []);
40
+
41
+ return null;
42
+ });
43
+
44
+ export default SessionHydration;
@@ -16,13 +16,18 @@ const SessionListContent = memo(() => {
16
16
  const { t } = useTranslation('chat');
17
17
  const unpinnedSessionList = useSessionStore(sessionSelectors.unpinnedSessionList, isEqual);
18
18
  const pinnedList = useSessionStore(sessionSelectors.pinnedSessionList, isEqual);
19
- const hasPinnedSessionList = useSessionStore(sessionSelectors.hasPinnedSessionList);
19
+ const [hasPinnedSessionList, useFetchSessions] = useSessionStore((s) => [
20
+ sessionSelectors.hasPinnedSessionList(s),
21
+ s.useFetchSessions,
22
+ ]);
20
23
 
21
24
  const [sessionGroupKeys, updatePreference] = useGlobalStore((s) => [
22
25
  preferenceSelectors.sessionGroupKeys(s),
23
26
  s.updatePreference,
24
27
  ]);
25
28
 
29
+ useFetchSessions();
30
+
26
31
  const items = [
27
32
  hasPinnedSessionList && {
28
33
  children: <SessionList dataSource={pinnedList} />,