@lobehub/lobehub 2.0.0-next.273 → 2.0.0-next.275

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 (237) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/chat.json +7 -0
  4. package/locales/ar/models.json +2 -3
  5. package/locales/ar/plugin.json +22 -1
  6. package/locales/bg-BG/chat.json +7 -0
  7. package/locales/bg-BG/models.json +3 -3
  8. package/locales/bg-BG/plugin.json +22 -1
  9. package/locales/de-DE/chat.json +7 -0
  10. package/locales/de-DE/models.json +3 -4
  11. package/locales/de-DE/plugin.json +22 -1
  12. package/locales/en-US/chat.json +7 -0
  13. package/locales/en-US/models.json +5 -5
  14. package/locales/en-US/plugin.json +22 -1
  15. package/locales/es-ES/chat.json +7 -0
  16. package/locales/es-ES/models.json +3 -4
  17. package/locales/es-ES/plugin.json +22 -1
  18. package/locales/fa-IR/chat.json +7 -0
  19. package/locales/fa-IR/models.json +3 -4
  20. package/locales/fa-IR/plugin.json +22 -1
  21. package/locales/fr-FR/chat.json +7 -0
  22. package/locales/fr-FR/models.json +50 -3
  23. package/locales/fr-FR/plugin.json +22 -1
  24. package/locales/it-IT/chat.json +7 -0
  25. package/locales/it-IT/models.json +3 -3
  26. package/locales/it-IT/plugin.json +22 -1
  27. package/locales/ja-JP/chat.json +7 -0
  28. package/locales/ja-JP/models.json +43 -4
  29. package/locales/ja-JP/plugin.json +22 -1
  30. package/locales/ko-KR/chat.json +7 -0
  31. package/locales/ko-KR/models.json +3 -4
  32. package/locales/ko-KR/plugin.json +22 -1
  33. package/locales/nl-NL/chat.json +7 -0
  34. package/locales/nl-NL/models.json +51 -3
  35. package/locales/nl-NL/plugin.json +22 -1
  36. package/locales/pl-PL/chat.json +7 -0
  37. package/locales/pl-PL/models.json +3 -3
  38. package/locales/pl-PL/plugin.json +22 -1
  39. package/locales/pt-BR/chat.json +7 -0
  40. package/locales/pt-BR/models.json +3 -4
  41. package/locales/pt-BR/plugin.json +22 -1
  42. package/locales/ru-RU/chat.json +7 -0
  43. package/locales/ru-RU/models.json +3 -4
  44. package/locales/ru-RU/plugin.json +22 -1
  45. package/locales/tr-TR/chat.json +7 -0
  46. package/locales/tr-TR/models.json +3 -4
  47. package/locales/tr-TR/plugin.json +22 -1
  48. package/locales/vi-VN/chat.json +7 -0
  49. package/locales/vi-VN/models.json +3 -3
  50. package/locales/vi-VN/plugin.json +22 -1
  51. package/locales/zh-CN/chat.json +7 -0
  52. package/locales/zh-CN/models.json +54 -4
  53. package/locales/zh-CN/plugin.json +22 -1
  54. package/locales/zh-TW/chat.json +7 -0
  55. package/locales/zh-TW/models.json +43 -4
  56. package/locales/zh-TW/plugin.json +22 -1
  57. package/package.json +2 -2
  58. package/packages/builtin-tool-agent-builder/package.json +1 -0
  59. package/packages/builtin-tool-agent-builder/src/client/Inspector/GetAvailableModels/index.tsx +66 -0
  60. package/packages/builtin-tool-agent-builder/src/client/Inspector/InstallPlugin/index.tsx +63 -0
  61. package/packages/builtin-tool-agent-builder/src/client/Inspector/SearchMarketTools/index.tsx +64 -0
  62. package/packages/builtin-tool-agent-builder/src/client/Inspector/UpdateConfig/index.tsx +94 -0
  63. package/packages/builtin-tool-agent-builder/src/client/Inspector/UpdatePrompt/index.tsx +96 -0
  64. package/packages/builtin-tool-agent-builder/src/client/Inspector/index.ts +29 -0
  65. package/packages/builtin-tool-agent-builder/src/client/index.ts +13 -0
  66. package/packages/builtin-tool-agent-builder/src/executor.ts +132 -0
  67. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/ExecuteCode/index.tsx +5 -14
  68. package/packages/builtin-tool-cloud-sandbox/src/client/Inspector/RunCommand/index.tsx +5 -13
  69. package/packages/builtin-tool-group-agent-builder/package.json +7 -1
  70. package/packages/builtin-tool-group-agent-builder/src/ExecutionRuntime/index.ts +331 -87
  71. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/BatchCreateAgents/index.tsx +110 -0
  72. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/CreateAgent/index.tsx +72 -0
  73. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/InviteAgent/index.tsx +57 -0
  74. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/RemoveAgent/index.tsx +57 -0
  75. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/SearchAgent/index.tsx +66 -0
  76. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateAgentPrompt/index.tsx +120 -0
  77. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroup/index.tsx +87 -0
  78. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/UpdateGroupPrompt/index.tsx +99 -0
  79. package/packages/builtin-tool-group-agent-builder/src/client/Inspector/index.ts +52 -0
  80. package/packages/builtin-tool-group-agent-builder/src/client/Render/BatchCreateAgents.tsx +103 -0
  81. package/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateAgentPrompt/index.tsx +36 -0
  82. package/packages/builtin-tool-group-agent-builder/src/client/Render/UpdateGroupPrompt/index.tsx +36 -0
  83. package/packages/builtin-tool-group-agent-builder/src/client/Render/index.ts +16 -0
  84. package/packages/builtin-tool-group-agent-builder/src/client/Streaming/BatchCreateAgents/index.tsx +88 -0
  85. package/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateAgentPrompt/index.tsx +37 -0
  86. package/packages/builtin-tool-group-agent-builder/src/client/Streaming/UpdateGroupPrompt/index.tsx +35 -0
  87. package/packages/builtin-tool-group-agent-builder/src/client/Streaming/index.ts +22 -0
  88. package/packages/builtin-tool-group-agent-builder/src/client/index.ts +26 -0
  89. package/packages/builtin-tool-group-agent-builder/src/executor.ts +284 -0
  90. package/packages/builtin-tool-group-agent-builder/src/index.ts +1 -14
  91. package/packages/builtin-tool-group-agent-builder/src/manifest.ts +160 -15
  92. package/packages/builtin-tool-group-agent-builder/src/systemRole.ts +232 -46
  93. package/packages/builtin-tool-group-agent-builder/src/types.ts +191 -41
  94. package/packages/builtin-tool-group-management/src/client/Inspector/Broadcast/index.tsx +2 -2
  95. package/packages/builtin-tool-group-management/src/manifest.ts +1 -1
  96. package/packages/builtin-tool-gtd/src/client/Inspector/ClearTodos/index.tsx +5 -11
  97. package/packages/builtin-tool-gtd/src/client/Inspector/CompleteTodos/index.tsx +3 -9
  98. package/packages/builtin-tool-gtd/src/client/Inspector/CreatePlan/index.tsx +6 -15
  99. package/packages/builtin-tool-gtd/src/client/Inspector/CreateTodos/index.tsx +3 -9
  100. package/packages/builtin-tool-gtd/src/client/Inspector/ExecTask/index.tsx +6 -17
  101. package/packages/builtin-tool-gtd/src/client/Inspector/RemoveTodos/index.tsx +3 -9
  102. package/packages/builtin-tool-gtd/src/client/Inspector/UpdatePlan/index.tsx +3 -9
  103. package/packages/builtin-tool-gtd/src/client/Inspector/UpdateTodos/index.tsx +3 -9
  104. package/packages/builtin-tool-knowledge-base/src/client/Inspector/ReadKnowledge/index.tsx +4 -16
  105. package/packages/builtin-tool-knowledge-base/src/client/Inspector/SearchKnowledgeBase/index.tsx +5 -16
  106. package/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx +4 -12
  107. package/packages/builtin-tool-local-system/src/client/Inspector/GlobLocalFiles/index.tsx +5 -13
  108. package/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx +5 -16
  109. package/packages/builtin-tool-local-system/src/client/Inspector/ListLocalFiles/index.tsx +5 -16
  110. package/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx +5 -16
  111. package/packages/builtin-tool-local-system/src/client/Inspector/RenameLocalFile/index.tsx +5 -11
  112. package/packages/builtin-tool-local-system/src/client/Inspector/RunCommand/index.tsx +5 -13
  113. package/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx +5 -16
  114. package/packages/builtin-tool-local-system/src/client/Inspector/WriteLocalFile/index.tsx +6 -15
  115. package/packages/builtin-tool-notebook/src/client/Inspector/CreateDocument/index.tsx +7 -15
  116. package/packages/builtin-tool-page-agent/src/client/Inspector/EditTitle/index.tsx +5 -14
  117. package/packages/builtin-tool-page-agent/src/client/Inspector/GetPageContent/index.tsx +7 -8
  118. package/packages/builtin-tool-page-agent/src/client/Inspector/InitPage/index.tsx +4 -10
  119. package/packages/builtin-tool-page-agent/src/client/Inspector/ModifyNodes/index.tsx +3 -9
  120. package/packages/builtin-tool-page-agent/src/client/Inspector/ReplaceText/index.tsx +5 -11
  121. package/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlMultiPages/index.tsx +6 -15
  122. package/packages/builtin-tool-web-browsing/src/client/Inspector/CrawlSinglePage/index.tsx +6 -15
  123. package/packages/builtin-tool-web-browsing/src/client/Inspector/Search/index.tsx +4 -15
  124. package/packages/database/src/models/chatGroup.ts +1 -1
  125. package/packages/model-bank/src/aiModels/aihubmix.ts +2 -1
  126. package/packages/model-bank/src/aiModels/google.ts +2 -1
  127. package/packages/model-bank/src/aiModels/infiniai.ts +9 -6
  128. package/packages/model-bank/src/aiModels/minimax.ts +9 -5
  129. package/packages/model-bank/src/aiModels/ollamacloud.ts +4 -2
  130. package/packages/model-bank/src/aiModels/vertexai.ts +2 -1
  131. package/packages/types/src/agentGroup/index.ts +8 -0
  132. package/patches/@upstash__qstash.patch +13 -1
  133. package/src/app/[variants]/(main)/agent/_layout/Sidebar/Header/Nav.tsx +1 -1
  134. package/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx +4 -3
  135. package/src/app/[variants]/(main)/agent/profile/features/ProfileEditor/index.tsx +1 -1
  136. package/src/app/[variants]/(main)/agent/profile/features/store/action.ts +18 -21
  137. package/src/app/[variants]/(main)/community/(detail)/features/MakedownRender.tsx +8 -6
  138. package/src/app/[variants]/(main)/group/_layout/GroupIdSync.tsx +6 -1
  139. package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/AgentProfilePopup.tsx +29 -21
  140. package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +1 -0
  141. package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMemberItem.tsx +35 -18
  142. package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/AddTopicButon.tsx +2 -10
  143. package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/Nav.tsx +10 -2
  144. package/src/app/[variants]/(main)/group/_layout/Sidebar/Header/index.tsx +1 -2
  145. package/src/app/[variants]/(main)/group/profile/features/AgentBuilder/AgentBuilderProvider.tsx +1 -0
  146. package/src/app/[variants]/(main)/group/profile/features/AgentBuilder/TopicSelector.tsx +15 -9
  147. package/src/app/[variants]/(main)/group/profile/features/AgentBuilder/index.tsx +12 -6
  148. package/src/app/[variants]/(main)/group/profile/features/{ProfileEditor/AgentHeader.tsx → GroupProfile/GroupHeader.tsx} +22 -29
  149. package/src/app/[variants]/(main)/group/profile/features/GroupProfile/index.tsx +96 -0
  150. package/src/app/[variants]/(main)/group/profile/features/Header/AgentBuilderToggle.tsx +3 -4
  151. package/src/app/[variants]/(main)/group/profile/features/Header/AutoSaveHint.tsx +11 -7
  152. package/src/app/[variants]/(main)/group/profile/features/Header/ChromeTabs/index.tsx +147 -0
  153. package/src/app/[variants]/(main)/group/profile/features/Header/index.tsx +104 -13
  154. package/src/app/[variants]/(main)/group/profile/features/MemberProfile/AgentHeader.tsx +222 -0
  155. package/src/app/[variants]/(main)/group/profile/features/MemberProfile/index.tsx +155 -0
  156. package/src/app/[variants]/(main)/group/profile/features/ProfileHydration.tsx +63 -5
  157. package/src/app/[variants]/(main)/group/profile/index.tsx +34 -37
  158. package/src/app/[variants]/(main)/settings/proxy/features/ProxyForm.tsx +156 -253
  159. package/src/app/[variants]/(main)/settings/proxy/index.tsx +1 -3
  160. package/src/app/[variants]/(mobile)/(home)/_layout/SessionHydration.tsx +1 -1
  161. package/src/app/[variants]/(mobile)/(home)/features/SessionListContent/List/Item/index.tsx +1 -1
  162. package/src/features/AgentBuilder/index.tsx +16 -1
  163. package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/StatusIndicator.tsx +3 -2
  164. package/src/features/Conversation/Messages/User/useMarkdown.tsx +1 -0
  165. package/src/features/EditorCanvas/EditorCanvas.test.tsx +206 -0
  166. package/src/features/EditorCanvas/EditorDataMode.tsx +53 -19
  167. package/src/features/EditorModal/index.tsx +2 -2
  168. package/src/features/NavPanel/components/SessionHydration.tsx +1 -1
  169. package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +10 -6
  170. package/src/features/PageEditor/Header/index.tsx +12 -10
  171. package/src/features/PageEditor/Header/useMenu.tsx +45 -48
  172. package/src/features/RightPanel/ToggleRightPanelButton.tsx +3 -1
  173. package/src/features/ShareModal/ShareImage/ChatList/index.tsx +1 -1
  174. package/src/features/ShareModal/SharePdf/index.tsx +1 -1
  175. package/src/hooks/useBidirectionalQuerySync.ts +112 -0
  176. package/src/locales/default/chat.ts +10 -0
  177. package/src/locales/default/plugin.ts +22 -1
  178. package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +45 -45
  179. package/src/server/modules/KeyVaultsEncrypt/index.ts +6 -6
  180. package/src/server/modules/S3/index.ts +1 -1
  181. package/src/server/routers/lambda/agent.ts +24 -0
  182. package/src/server/routers/lambda/agentGroup.ts +39 -0
  183. package/src/services/agent.ts +22 -0
  184. package/src/services/chatGroup/index.ts +14 -0
  185. package/src/store/agent/selectors/selectors.ts +3 -0
  186. package/src/store/agentGroup/initialState.ts +6 -0
  187. package/src/store/agentGroup/selectors/byId.ts +3 -1
  188. package/src/store/agentGroup/selectors/current.ts +2 -2
  189. package/src/store/agentGroup/slices/lifecycle.ts +18 -0
  190. package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockStore.ts +1 -1
  191. package/src/store/chat/slices/aiAgent/actions/__tests__/agentGroup.test.ts +4 -1
  192. package/src/store/chat/slices/aiAgent/actions/agentGroup.ts +1 -1
  193. package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +65 -0
  194. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +2 -1
  195. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +1 -1
  196. package/src/store/chat/slices/builtinTool/actions/index.ts +1 -6
  197. package/src/store/chat/slices/message/action.test.ts +5 -5
  198. package/src/store/chat/slices/message/actions/publicApi.ts +5 -5
  199. package/src/store/chat/slices/message/initialState.ts +0 -5
  200. package/src/store/chat/slices/message/selectors/displayMessage.test.ts +4 -4
  201. package/src/store/chat/slices/plugin/action.test.ts +54 -19
  202. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +15 -21
  203. package/src/store/chat/slices/topic/action.test.ts +74 -24
  204. package/src/store/chat/slices/topic/action.ts +21 -13
  205. package/src/store/chat/slices/topic/selectors.test.ts +1 -1
  206. package/src/store/global/initialState.ts +10 -0
  207. package/src/store/global/selectors/systemStatus.ts +5 -0
  208. package/src/store/groupProfile/action.ts +168 -0
  209. package/src/store/groupProfile/index.ts +16 -0
  210. package/src/{app/[variants]/(main)/group/profile/features/store → store/groupProfile}/initialState.ts +17 -0
  211. package/src/store/groupProfile/selectors.ts +13 -0
  212. package/src/store/tool/slices/builtin/executors/index.ts +4 -0
  213. package/src/styles/text.ts +16 -0
  214. package/src/tools/inspectors.ts +13 -0
  215. package/src/tools/renders.ts +3 -0
  216. package/src/tools/streamings.ts +8 -0
  217. package/tests/mocks/lru_map.ts +40 -0
  218. package/vitest.config.mts +9 -1
  219. package/src/app/[variants]/(main)/group/profile/features/EditorCanvas/TypoBar.tsx +0 -129
  220. package/src/app/[variants]/(main)/group/profile/features/EditorCanvas/index.tsx +0 -138
  221. package/src/app/[variants]/(main)/group/profile/features/EditorCanvas/useSlashItems.tsx +0 -139
  222. package/src/app/[variants]/(main)/group/profile/features/ProfileEditor/index.tsx +0 -82
  223. package/src/app/[variants]/(main)/group/profile/features/ProfileProvider.tsx +0 -20
  224. package/src/app/[variants]/(main)/group/profile/features/StoreUpdater.tsx +0 -24
  225. package/src/app/[variants]/(main)/group/profile/features/store/action.ts +0 -163
  226. package/src/app/[variants]/(main)/group/profile/features/store/index.ts +0 -23
  227. package/src/app/[variants]/(main)/group/profile/features/store/selectors.ts +0 -7
  228. package/src/features/EditorModal/EditorCanvas.tsx +0 -84
  229. package/src/features/EditorModal/Typobar.tsx +0 -139
  230. package/src/store/chat/slices/builtinTool/actions/agentBuilder.ts +0 -192
  231. package/src/store/chat/slices/builtinTool/actions/groupAgentBuilder.ts +0 -242
  232. package/src/tools/executionRuntimes.ts +0 -14
  233. /package/src/app/[variants]/(main)/group/profile/features/{ProfileEditor → MemberProfile}/AgentTool.tsx +0 -0
  234. /package/src/app/[variants]/(main)/group/profile/features/{ProfileEditor → MemberProfile}/MentionList/MentionDropdown.tsx +0 -0
  235. /package/src/app/[variants]/(main)/group/profile/features/{ProfileEditor → MemberProfile}/MentionList/index.tsx +0 -0
  236. /package/src/app/[variants]/(main)/group/profile/features/{ProfileEditor → MemberProfile}/MentionList/types.ts +0 -0
  237. /package/src/app/[variants]/(main)/group/profile/features/{ProfileEditor → MemberProfile}/MentionList/useMentionItems.tsx +0 -0
@@ -9,9 +9,7 @@ const Page = () => {
9
9
  return (
10
10
  <>
11
11
  <SettingHeader title={t('tab.proxy')} />
12
- <div style={{ maxWidth: '1024px', width: '100%' }}>
13
- <ProxyForm />
14
- </div>
12
+ <ProxyForm />
15
13
  </>
16
14
  );
17
15
  };
@@ -22,7 +22,7 @@ const SessionHydration = memo(() => {
22
22
  });
23
23
 
24
24
  useStoreUpdater('activeId', session);
25
- useChatStoreUpdater('activeId', session);
25
+ useChatStoreUpdater('activeAgentId', session);
26
26
 
27
27
  useEffect(() => {
28
28
  const unsubscribe = useSessionStore.subscribe(
@@ -32,7 +32,7 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
32
32
 
33
33
  const [active] = useSessionStore((s) => [s.activeId === id]);
34
34
  const [loading] = useChatStore((s) => [
35
- operationSelectors.isAgentRuntimeRunning(s) && id === s.activeId,
35
+ operationSelectors.isAgentRuntimeRunning(s) && id === s.activeAgentId,
36
36
  ]);
37
37
 
38
38
  const [pin, title, avatar, avatarBackground, updateAt, members, model, group, sessionType] =
@@ -5,6 +5,8 @@ import Loading from '@/components/Loading/BrandTextLoading';
5
5
  import RightPanel from '@/features/RightPanel';
6
6
  import { useAgentStore } from '@/store/agent';
7
7
  import { builtinAgentSelectors } from '@/store/agent/selectors';
8
+ import { useGlobalStore } from '@/store/global';
9
+ import { systemStatusSelectors } from '@/store/global/selectors';
8
10
 
9
11
  import AgentBuilderConversation from './AgentBuilderConversation';
10
12
  import AgentBuilderProvider from './AgentBuilderProvider';
@@ -13,11 +15,24 @@ const AgentBuilder = memo(() => {
13
15
  const agentId = useAgentStore((s) => s.activeAgentId);
14
16
  const agentBuilderId = useAgentStore(builtinAgentSelectors.agentBuilderId);
15
17
 
18
+ const [width, updateSystemStatus] = useGlobalStore((s) => [
19
+ systemStatusSelectors.agentBuilderPanelWidth(s),
20
+ s.updateSystemStatus,
21
+ ]);
22
+
16
23
  const useInitBuiltinAgent = useAgentStore((s) => s.useInitBuiltinAgent);
17
24
  useInitBuiltinAgent(BUILTIN_AGENT_SLUGS.agentBuilder);
18
25
 
19
26
  return (
20
- <RightPanel>
27
+ <RightPanel
28
+ defaultWidth={width}
29
+ onSizeChange={(size) => {
30
+ if (size?.width) {
31
+ const w = typeof size.width === 'string' ? Number.parseInt(size.width) : size.width;
32
+ if (!!w) updateSystemStatus({ agentBuilderPanelWidth: w });
33
+ }
34
+ }}
35
+ >
21
36
  {agentId && agentBuilderId ? (
22
37
  <AgentBuilderProvider agentId={agentBuilderId}>
23
38
  <AgentBuilderConversation agentId={agentBuilderId} />
@@ -1,10 +1,11 @@
1
1
  import { type ToolIntervention } from '@lobechat/types';
2
2
  import { Block, Icon, Tooltip } from '@lobehub/ui';
3
3
  import { cssVar } from 'antd-style';
4
- import { Ban, Check, HandIcon, Loader2Icon, PauseIcon, X } from 'lucide-react';
4
+ import { Ban, Check, HandIcon, PauseIcon, X } from 'lucide-react';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
+ import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
8
9
  import { LOADING_FLAT } from '@/const/message';
9
10
 
10
11
  interface StatusIndicatorProps {
@@ -43,7 +44,7 @@ const StatusIndicator = memo<StatusIndicatorProps>(({ intervention, result }) =>
43
44
  } else if (hasResult) {
44
45
  icon = <Icon color={cssVar.colorSuccess} icon={Check} />;
45
46
  } else {
46
- icon = <Icon color={cssVar.colorTextDescription} icon={Loader2Icon} spin />;
47
+ icon = <NeuralNetworkLoading size={16} />;
47
48
  }
48
49
 
49
50
  return (
@@ -30,6 +30,7 @@ export const useMarkdown = (id: string): Partial<MarkdownProps> => {
30
30
  () =>
31
31
  ({
32
32
  components: Object.fromEntries(
33
+ // @ts-expect-error
33
34
  markdownElements.map((element) => {
34
35
  const Component = element.Component;
35
36
  return [element.tag, (props: any) => <Component {...props} id={id} />];
@@ -0,0 +1,206 @@
1
+ /**
2
+ * @vitest-environment happy-dom
3
+ */
4
+ import { type IEditor } from '@lobehub/editor';
5
+ import { cleanup, render, screen } from '@testing-library/react';
6
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
+
8
+ import { EditorCanvas } from './EditorCanvas';
9
+
10
+ // Mock DocumentIdMode
11
+ vi.mock('./DocumentIdMode', () => ({
12
+ default: vi.fn(({ documentId }) => (
13
+ <div data-testid="document-id-mode">DocumentIdMode: {documentId}</div>
14
+ )),
15
+ }));
16
+
17
+ // Mock EditorDataMode
18
+ vi.mock('./EditorDataMode', () => ({
19
+ default: vi.fn(({ editorData }) => (
20
+ <div data-testid="editor-data-mode">EditorDataMode: {editorData?.content}</div>
21
+ )),
22
+ }));
23
+
24
+ // Mock InternalEditor
25
+ vi.mock('./InternalEditor', () => ({
26
+ default: vi.fn(() => <div data-testid="internal-editor">InternalEditor</div>),
27
+ }));
28
+
29
+ // Mock ErrorBoundary to pass through children
30
+ vi.mock('./ErrorBoundary', () => ({
31
+ EditorErrorBoundary: vi.fn(({ children }) => <>{children}</>),
32
+ }));
33
+
34
+ describe('EditorCanvas', () => {
35
+ let mockEditor: IEditor;
36
+
37
+ beforeEach(() => {
38
+ mockEditor = {
39
+ getDocument: vi.fn(),
40
+ setDocument: vi.fn(),
41
+ focus: vi.fn(),
42
+ } as unknown as IEditor;
43
+ });
44
+
45
+ afterEach(() => {
46
+ cleanup();
47
+ vi.clearAllMocks();
48
+ });
49
+
50
+ describe('mode selection', () => {
51
+ it('should render DocumentIdMode when documentId is provided', () => {
52
+ render(<EditorCanvas documentId="doc-123" editor={mockEditor} />);
53
+
54
+ expect(screen.getByTestId('document-id-mode')).toBeInTheDocument();
55
+ expect(screen.getByText('DocumentIdMode: doc-123')).toBeInTheDocument();
56
+ expect(screen.queryByTestId('editor-data-mode')).not.toBeInTheDocument();
57
+ expect(screen.queryByTestId('internal-editor')).not.toBeInTheDocument();
58
+ });
59
+
60
+ it('should render EditorDataMode when editorData is provided', () => {
61
+ render(<EditorCanvas editor={mockEditor} editorData={{ content: 'test content' }} />);
62
+
63
+ expect(screen.getByTestId('editor-data-mode')).toBeInTheDocument();
64
+ expect(screen.getByText('EditorDataMode: test content')).toBeInTheDocument();
65
+ expect(screen.queryByTestId('document-id-mode')).not.toBeInTheDocument();
66
+ expect(screen.queryByTestId('internal-editor')).not.toBeInTheDocument();
67
+ });
68
+
69
+ it('should render InternalEditor in basic mode (no documentId or editorData)', () => {
70
+ render(<EditorCanvas editor={mockEditor} />);
71
+
72
+ expect(screen.getByTestId('internal-editor')).toBeInTheDocument();
73
+ expect(screen.queryByTestId('document-id-mode')).not.toBeInTheDocument();
74
+ expect(screen.queryByTestId('editor-data-mode')).not.toBeInTheDocument();
75
+ });
76
+
77
+ it('should return null in basic mode when editor is undefined', () => {
78
+ const { container } = render(<EditorCanvas editor={undefined} />);
79
+
80
+ expect(container.firstChild).toBeNull();
81
+ });
82
+ });
83
+
84
+ describe('mode priority', () => {
85
+ it('should prioritize documentId over editorData when both are provided', () => {
86
+ render(
87
+ <EditorCanvas
88
+ documentId="doc-123"
89
+ editor={mockEditor}
90
+ editorData={{ content: 'test content' }}
91
+ />,
92
+ );
93
+
94
+ expect(screen.getByTestId('document-id-mode')).toBeInTheDocument();
95
+ expect(screen.queryByTestId('editor-data-mode')).not.toBeInTheDocument();
96
+ });
97
+ });
98
+
99
+ describe('props forwarding', () => {
100
+ it('should forward props to DocumentIdMode', async () => {
101
+ const onContentChange = vi.fn();
102
+ const onInit = vi.fn();
103
+
104
+ render(
105
+ <EditorCanvas
106
+ autoSave={false}
107
+ documentId="doc-123"
108
+ editor={mockEditor}
109
+ onContentChange={onContentChange}
110
+ onInit={onInit}
111
+ placeholder="Custom placeholder"
112
+ sourceType="notebook"
113
+ />,
114
+ );
115
+
116
+ const DocumentIdMode = await vi.importMock('./DocumentIdMode');
117
+ const lastCall = (DocumentIdMode.default as ReturnType<typeof vi.fn>).mock.calls.at(-1);
118
+
119
+ expect(lastCall?.[0]).toMatchObject({
120
+ autoSave: false,
121
+ documentId: 'doc-123',
122
+ editor: mockEditor,
123
+ onContentChange,
124
+ onInit,
125
+ placeholder: 'Custom placeholder',
126
+ sourceType: 'notebook',
127
+ });
128
+ });
129
+
130
+ it('should forward props to EditorDataMode', async () => {
131
+ const onContentChange = vi.fn();
132
+ const onInit = vi.fn();
133
+ const editorData = { content: 'test', editorData: { blocks: [] } };
134
+
135
+ render(
136
+ <EditorCanvas
137
+ editor={mockEditor}
138
+ editorData={editorData}
139
+ onContentChange={onContentChange}
140
+ onInit={onInit}
141
+ placeholder="Custom placeholder"
142
+ />,
143
+ );
144
+
145
+ const EditorDataMode = await vi.importMock('./EditorDataMode');
146
+ const lastCall = (EditorDataMode.default as ReturnType<typeof vi.fn>).mock.calls.at(-1);
147
+
148
+ expect(lastCall?.[0]).toMatchObject({
149
+ editor: mockEditor,
150
+ editorData,
151
+ onContentChange,
152
+ onInit,
153
+ placeholder: 'Custom placeholder',
154
+ });
155
+ });
156
+
157
+ it('should forward props to InternalEditor in basic mode', async () => {
158
+ const onContentChange = vi.fn();
159
+ const onInit = vi.fn();
160
+
161
+ render(
162
+ <EditorCanvas
163
+ editor={mockEditor}
164
+ floatingToolbar={false}
165
+ onContentChange={onContentChange}
166
+ onInit={onInit}
167
+ placeholder="Custom placeholder"
168
+ />,
169
+ );
170
+
171
+ const InternalEditor = await vi.importMock('./InternalEditor');
172
+ const lastCall = (InternalEditor.default as ReturnType<typeof vi.fn>).mock.calls.at(-1);
173
+
174
+ expect(lastCall?.[0]).toMatchObject({
175
+ editor: mockEditor,
176
+ floatingToolbar: false,
177
+ onContentChange,
178
+ onInit,
179
+ placeholder: 'Custom placeholder',
180
+ });
181
+ });
182
+ });
183
+
184
+ describe('error boundary wrapping', () => {
185
+ it('should wrap DocumentIdMode with ErrorBoundary', async () => {
186
+ render(<EditorCanvas documentId="doc-123" editor={mockEditor} />);
187
+
188
+ const ErrorBoundary = await vi.importMock('./ErrorBoundary');
189
+ expect(ErrorBoundary.EditorErrorBoundary).toHaveBeenCalled();
190
+ });
191
+
192
+ it('should wrap EditorDataMode with ErrorBoundary', async () => {
193
+ render(<EditorCanvas editor={mockEditor} editorData={{ content: 'test' }} />);
194
+
195
+ const ErrorBoundary = await vi.importMock('./ErrorBoundary');
196
+ expect(ErrorBoundary.EditorErrorBoundary).toHaveBeenCalled();
197
+ });
198
+
199
+ it('should wrap InternalEditor with ErrorBoundary in basic mode', async () => {
200
+ render(<EditorCanvas editor={mockEditor} />);
201
+
202
+ const ErrorBoundary = await vi.importMock('./ErrorBoundary');
203
+ expect(ErrorBoundary.EditorErrorBoundary).toHaveBeenCalled();
204
+ });
205
+ });
206
+ });
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { type IEditor } from '@lobehub/editor';
4
- import { memo, useEffect, useState } from 'react';
4
+ import { memo, useCallback, useEffect, useRef } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import type { EditorCanvasProps } from './EditorCanvas';
@@ -12,37 +12,70 @@ export interface EditorDataModeProps extends EditorCanvasProps {
12
12
  editorData: NonNullable<EditorCanvasProps['editorData']>;
13
13
  }
14
14
 
15
+ const loadEditorContent = (
16
+ editorInstance: IEditor,
17
+ editorData: EditorDataModeProps['editorData'],
18
+ ): boolean => {
19
+ const hasValidEditorData =
20
+ editorData.editorData &&
21
+ typeof editorData.editorData === 'object' &&
22
+ Object.keys(editorData.editorData as object).length > 0;
23
+
24
+ if (hasValidEditorData) {
25
+ editorInstance.setDocument('json', JSON.stringify(editorData.editorData));
26
+ return true;
27
+ } else if (editorData.content?.trim()) {
28
+ editorInstance.setDocument('markdown', editorData.content, { keepId: true });
29
+ return true;
30
+ }
31
+ return false;
32
+ };
33
+
15
34
  /**
16
35
  * EditorCanvas with editorData mode - uses provided data directly
17
36
  */
18
37
  const EditorDataMode = memo<EditorDataModeProps>(
19
- ({ editor, editorData, onContentChange, style, ...editorProps }) => {
38
+ ({ editor, editorData, onContentChange, onInit, style, ...editorProps }) => {
20
39
  const { t } = useTranslation('file');
21
- const [isInitialized, setIsInitialized] = useState(false);
40
+ const isEditorReadyRef = useRef(false);
41
+ // Track loaded content to support re-loading when data changes
42
+ const loadedContentRef = useRef<string | undefined>(undefined);
22
43
 
23
- // Load content into editor on mount
24
- useEffect(() => {
25
- if (!editor || isInitialized) return;
44
+ // Check if content has actually changed
45
+ const hasDataChanged = loadedContentRef.current !== editorData.content;
26
46
 
27
- const hasValidEditorData =
28
- editorData.editorData &&
29
- typeof editorData.editorData === 'object' &&
30
- Object.keys(editorData.editorData as object).length > 0;
47
+ const handleInit = useCallback(
48
+ (editorInstance: IEditor) => {
49
+ isEditorReadyRef.current = true;
31
50
 
32
- try {
33
- if (hasValidEditorData) {
34
- editor.setDocument('json', JSON.stringify(editorData.editorData));
35
- } else if (editorData.content?.trim()) {
36
- editor.setDocument('markdown', editorData.content, { keepId: true });
37
- } else {
38
- console.error('[EditorCanvas] load content error:', editorData);
51
+ // Try to load content if editorData is available and hasn't been loaded yet
52
+ if (hasDataChanged) {
53
+ try {
54
+ if (loadEditorContent(editorInstance, editorData)) {
55
+ loadedContentRef.current = editorData.content;
56
+ }
57
+ } catch (err) {
58
+ console.error('[EditorCanvas] Failed to load content:', err);
59
+ }
39
60
  }
40
61
 
41
- setIsInitialized(true);
62
+ onInit?.(editorInstance);
63
+ },
64
+ [editorData, hasDataChanged, onInit],
65
+ );
66
+
67
+ // Load content when editorData changes after editor is ready
68
+ useEffect(() => {
69
+ if (!editor || !isEditorReadyRef.current || !hasDataChanged) return;
70
+
71
+ try {
72
+ if (loadEditorContent(editor, editorData)) {
73
+ loadedContentRef.current = editorData.content;
74
+ }
42
75
  } catch (err) {
43
76
  console.error('[EditorCanvas] Failed to load content:', err);
44
77
  }
45
- }, [editorData, editor, isInitialized]);
78
+ }, [editor, editorData, hasDataChanged]);
46
79
 
47
80
  if (!editor) return null;
48
81
 
@@ -51,6 +84,7 @@ const EditorDataMode = memo<EditorDataModeProps>(
51
84
  <InternalEditor
52
85
  editor={editor}
53
86
  onContentChange={onContentChange}
87
+ onInit={handleInit}
54
88
  placeholder={editorProps.placeholder || t('pageEditor.editorPlaceholder')}
55
89
  {...editorProps}
56
90
  />
@@ -3,7 +3,7 @@ import { Modal, ModalProps, createRawModal } from '@lobehub/ui';
3
3
  import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
- import EditorCanvas from './EditorCanvas';
6
+ import { EditorCanvas } from '@/features/EditorCanvas';
7
7
 
8
8
  interface EditorModalProps extends ModalProps {
9
9
  onConfirm?: (value: string) => Promise<void>;
@@ -43,7 +43,7 @@ export const EditorModal = memo<EditorModalProps>(({ value, onConfirm, ...rest }
43
43
  width={'min(90vw, 920px)'}
44
44
  {...rest}
45
45
  >
46
- <EditorCanvas defaultValue={value} editor={editor} />
46
+ <EditorCanvas editor={editor} editorData={{ content: value }} />
47
47
  </Modal>
48
48
  );
49
49
  });
@@ -22,7 +22,7 @@ const SessionHydration = memo(() => {
22
22
  });
23
23
 
24
24
  useStoreUpdater('activeId', session);
25
- useChatStoreUpdater('activeId', session);
25
+ useChatStoreUpdater('activeAgentId', session);
26
26
 
27
27
  useEffect(() => {
28
28
  const unsubscribe = useSessionStore.subscribe(
@@ -1,12 +1,13 @@
1
1
  'use client';
2
2
 
3
+ import { DEFAULT_INBOX_AVATAR } from '@lobechat/const';
3
4
  import { nanoid } from '@lobechat/utils';
4
5
  import { HIDE_TOOLBAR_COMMAND, type IEditor } from '@lobehub/editor';
5
6
  import { type ChatInputActionsProps } from '@lobehub/editor/react';
6
- import { Block } from '@lobehub/ui';
7
+ import { Avatar, Block } from '@lobehub/ui';
7
8
  import { createStaticStyles, cssVar } from 'antd-style';
8
- import { BotIcon } from 'lucide-react';
9
9
  import { useMemo } from 'react';
10
+ import { useTranslation } from 'react-i18next';
10
11
 
11
12
  import { useFileStore } from '@/store/file';
12
13
  import { useGlobalStore } from '@/store/global';
@@ -23,11 +24,14 @@ const styles = createStaticStyles(({ css }) => ({
23
24
  }));
24
25
 
25
26
  export const useAskCopilotItem = (editor: IEditor | undefined): ChatInputActionsProps['items'] => {
27
+ const { t } = useTranslation('common');
26
28
  const addSelectionContext = useFileStore((s) => s.addChatContextSelection);
27
29
 
28
30
  return useMemo(() => {
29
31
  if (!editor) return [];
30
32
 
33
+ const label = t('cmdk.askLobeAI');
34
+
31
35
  return [
32
36
  {
33
37
  children: (
@@ -82,14 +86,14 @@ export const useAskCopilotItem = (editor: IEditor | undefined): ChatInputActions
82
86
  paddingInline={12}
83
87
  variant="borderless"
84
88
  >
85
- <BotIcon />
86
- <span>Ask Copilot</span>
89
+ <Avatar avatar={DEFAULT_INBOX_AVATAR} shape="square" size={16} />
90
+ <span>{label}</span>
87
91
  </Block>
88
92
  ),
89
93
  key: 'ask-copilot',
90
- label: 'Ask Copilot',
94
+ label,
91
95
  onClick: () => {},
92
96
  },
93
97
  ];
94
- }, [addSelectionContext, editor]);
98
+ }, [addSelectionContext, editor, t]);
95
99
  };
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { ActionIcon, Avatar, Dropdown, Text } from '@lobehub/ui';
4
- import { ArrowLeftIcon, BotMessageSquareIcon, MoreHorizontal } from 'lucide-react';
3
+ import { ActionIcon, Avatar, DropdownMenu, Text } from '@lobehub/ui';
4
+ import { ArrowLeftIcon, MoreHorizontal } from 'lucide-react';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
@@ -49,18 +49,20 @@ const Header = memo(() => {
49
49
  }
50
50
  right={
51
51
  <>
52
- <ToggleRightPanelButton icon={BotMessageSquareIcon} showActive={true} />
53
52
  {/* Three-dot menu */}
54
- <Dropdown
55
- menu={{
56
- items: menuItems,
57
- style: { minWidth: 200 },
58
- }}
53
+ <DropdownMenu
54
+ items={menuItems}
55
+ nativeButton={false}
59
56
  placement="bottomRight"
60
- trigger={['click']}
57
+ popupProps={{
58
+ style: {
59
+ minWidth: 200,
60
+ },
61
+ }}
61
62
  >
62
63
  <ActionIcon icon={MoreHorizontal} size={DESKTOP_HEADER_ICON_SIZE} />
63
- </Dropdown>
64
+ </DropdownMenu>
65
+ <ToggleRightPanelButton hideWhenExpanded showActive={false} />
64
66
  </>
65
67
  }
66
68
  />
@@ -1,5 +1,5 @@
1
- import { Flexbox, Icon } from '@lobehub/ui';
2
- import { App, Switch } from 'antd';
1
+ import { type DropdownItem, Icon } from '@lobehub/ui';
2
+ import { App } from 'antd';
3
3
  import { cssVar, useResponsive } from 'antd-style';
4
4
  import dayjs from 'dayjs';
5
5
  import { CopyPlus, Download, Link2, Trash2 } from 'lucide-react';
@@ -75,25 +75,17 @@ export const useMenu = (): { menuItems: any[] } => {
75
75
  }
76
76
  };
77
77
 
78
- const menuItems = useMemo(
79
- () => [
78
+ const menuItems = useMemo<DropdownItem[]>(() => {
79
+ const items: DropdownItem[] = [
80
80
  ...(showViewModeSwitch
81
81
  ? [
82
82
  {
83
+ checked: wideScreen,
83
84
  key: 'full-width',
84
- label: (
85
- <Flexbox align="center" horizontal justify="space-between">
86
- <span>{t('viewMode.fullWidth', { ns: 'chat' })}</span>
87
- <Switch
88
- checked={wideScreen}
89
- onChange={toggleWideScreen}
90
- onClick={(checked, event) => {
91
- event.stopPropagation();
92
- }}
93
- size="small"
94
- />
95
- </Flexbox>
96
- ),
85
+ label: t('viewMode.fullWidth', { ns: 'chat' }),
86
+ onCheckedChange: toggleWideScreen,
87
+
88
+ type: 'checkbox' as const,
97
89
  },
98
90
  {
99
91
  type: 'divider' as const,
@@ -140,38 +132,43 @@ export const useMenu = (): { menuItems: any[] } => {
140
132
  key: 'export',
141
133
  label: t('pageEditor.menu.export'),
142
134
  },
143
- {
144
- type: 'divider' as const,
145
- },
146
- {
147
- disabled: true,
148
- key: 'page-info',
149
- label: (
150
- <div style={{ color: cssVar.colorTextTertiary, fontSize: 12, lineHeight: 1.6 }}>
151
- <div>
152
- {lastUpdatedTime
153
- ? t('pageEditor.editedAt', {
154
- time: dayjs(lastUpdatedTime).format('MMMM D, YYYY [at] h:mm A'),
155
- })
156
- : ''}
135
+ ];
136
+
137
+ if (lastUpdatedTime) {
138
+ items.push(
139
+ {
140
+ type: 'divider' as const,
141
+ },
142
+ {
143
+ disabled: true,
144
+ key: 'page-info',
145
+ label: (
146
+ <div style={{ color: cssVar.colorTextTertiary, fontSize: 12, lineHeight: 1.6 }}>
147
+ <div>
148
+ {lastUpdatedTime
149
+ ? t('pageEditor.editedAt', {
150
+ time: dayjs(lastUpdatedTime).format('MMMM D, YYYY [at] h:mm A'),
151
+ })
152
+ : ''}
153
+ </div>
157
154
  </div>
158
- </div>
159
- ),
160
- },
161
- ],
162
- [
163
- lastUpdatedTime,
164
- storeApi,
165
- t,
166
- message,
167
- modal,
168
- wideScreen,
169
- toggleWideScreen,
170
- showViewModeSwitch,
171
- handleDuplicate,
172
- handleExportMarkdown,
173
- ],
174
- );
155
+ ),
156
+ },
157
+ );
158
+ }
159
+ return items;
160
+ }, [
161
+ lastUpdatedTime,
162
+ storeApi,
163
+ t,
164
+ message,
165
+ modal,
166
+ wideScreen,
167
+ toggleWideScreen,
168
+ showViewModeSwitch,
169
+ handleDuplicate,
170
+ handleExportMarkdown,
171
+ ]);
175
172
 
176
173
  return { menuItems };
177
174
  };
@@ -15,6 +15,7 @@ import { HotkeyEnum } from '@/types/hotkey';
15
15
  export const TOGGLE_BUTTON_ID = 'toggle_right_panel_button';
16
16
 
17
17
  interface ToggleRightPanelButtonProps {
18
+ hideWhenExpanded?: boolean;
18
19
  icon?: ActionIconProps['icon'];
19
20
  showActive?: boolean;
20
21
  size?: ActionIconProps['size'];
@@ -22,7 +23,7 @@ interface ToggleRightPanelButtonProps {
22
23
  }
23
24
 
24
25
  const ToggleRightPanelButton = memo<ToggleRightPanelButtonProps>(
25
- ({ title, showActive, icon, size }) => {
26
+ ({ title, showActive, icon, hideWhenExpanded, size }) => {
26
27
  const [expand, togglePanel] = useGlobalStore((s) => [
27
28
  systemStatusSelectors.showRightPanel(s),
28
29
  s.toggleRightPanel,
@@ -31,6 +32,7 @@ const ToggleRightPanelButton = memo<ToggleRightPanelButtonProps>(
31
32
 
32
33
  const { t } = useTranslation(['chat', 'hotkey']);
33
34
 
35
+ if (hideWhenExpanded && expand) return null;
34
36
  return (
35
37
  <ActionIcon
36
38
  active={showActive ? expand : undefined}