@lobehub/lobehub 2.0.0-next.255 → 2.0.0-next.256

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 (140) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/locales/en-US/plugin.json +1 -0
  4. package/locales/zh-CN/plugin.json +1 -0
  5. package/package.json +1 -1
  6. package/packages/builtin-tool-notebook/src/client/Placeholder/CreateDocument.tsx +6 -6
  7. package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +23 -10
  8. package/packages/builtin-tool-notebook/src/client/Streaming/CreateDocument/index.tsx +1 -1
  9. package/packages/database/src/models/__tests__/agent.test.ts +91 -4
  10. package/packages/database/src/models/agent.ts +15 -7
  11. package/packages/editor-runtime/src/EditorRuntime.ts +1 -1
  12. package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +65 -4
  13. package/packages/editor-runtime/src/__tests__/__snapshots__/EditorRuntime.real.test.ts.snap +108 -17
  14. package/packages/editor-runtime/src/__tests__/fixtures/remove-then-add.json +636 -0
  15. package/packages/editor-runtime/src/__tests__/fixtures/remove.json +1 -0
  16. package/packages/types/src/agent/agentConfig.ts +8 -8
  17. package/src/app/[variants]/(main)/chat/features/Portal/_layout/Mobile.tsx +2 -1
  18. package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +2 -1
  19. package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +2 -2
  20. package/src/app/[variants]/(main)/page/{features/PageTitle → PageTitle}/index.tsx +0 -1
  21. package/src/app/[variants]/(main)/page/[id]/index.tsx +43 -1
  22. package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/Content.tsx +15 -15
  23. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Editing.tsx +3 -3
  24. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/index.tsx +7 -12
  25. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/useDropdownMenu.tsx +5 -5
  26. package/src/app/[variants]/(main)/page/_layout/Body/List/index.tsx +7 -7
  27. package/src/app/[variants]/(main)/page/_layout/Body/index.tsx +15 -9
  28. package/src/app/[variants]/(main)/page/_layout/Body/useDropdownMenu.tsx +3 -3
  29. package/src/app/[variants]/(main)/page/_layout/DataSync.tsx +15 -0
  30. package/src/app/[variants]/(main)/page/_layout/Header/AddButton.tsx +2 -2
  31. package/src/app/[variants]/(main)/page/_layout/index.tsx +2 -0
  32. package/src/app/[variants]/(main)/page/index.tsx +3 -7
  33. package/src/components/Editor/AutoSaveHint.tsx +1 -1
  34. package/src/features/Conversation/Messages/User/Actions/index.tsx +5 -1
  35. package/src/features/EditorCanvas/AutoSaveHint.tsx +37 -0
  36. package/src/features/{PageEditor → EditorCanvas}/DiffAllToolbar.tsx +57 -16
  37. package/src/features/EditorCanvas/DocumentIdMode.tsx +111 -0
  38. package/src/features/EditorCanvas/EditorCanvas.tsx +148 -0
  39. package/src/features/EditorCanvas/EditorDataMode.tsx +64 -0
  40. package/src/features/EditorCanvas/ErrorBoundary.tsx +66 -0
  41. package/src/features/EditorCanvas/InlineToolbar.tsx +245 -0
  42. package/src/features/EditorCanvas/InternalEditor.tsx +134 -0
  43. package/src/features/{PageEditor/EditorCanvas → EditorCanvas}/actions.ts +10 -8
  44. package/src/features/EditorCanvas/index.ts +9 -0
  45. package/src/features/PageEditor/EditorCanvas/index.tsx +14 -111
  46. package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +95 -0
  47. package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +1 -1
  48. package/src/features/PageEditor/Header/Breadcrumb.tsx +2 -2
  49. package/src/features/PageEditor/Header/index.tsx +15 -18
  50. package/src/features/PageEditor/Header/useMenu.tsx +12 -9
  51. package/src/features/PageEditor/PageEditor.tsx +45 -21
  52. package/src/features/PageEditor/PageEditorProvider.tsx +13 -1
  53. package/src/features/PageEditor/PageTitle/index.tsx +2 -2
  54. package/src/features/PageEditor/StoreUpdater.tsx +35 -308
  55. package/src/features/PageEditor/{Body/Title.tsx → TitleSection.tsx} +16 -16
  56. package/src/features/PageEditor/store/action.ts +96 -188
  57. package/src/features/PageEditor/store/initialState.ts +16 -21
  58. package/src/features/PageEditor/store/selectors.ts +3 -4
  59. package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +22 -14
  60. package/src/features/PageExplorer/index.tsx +34 -67
  61. package/src/features/Portal/Artifacts/index.ts +0 -2
  62. package/src/features/Portal/Document/AutoSaveHint.tsx +7 -6
  63. package/src/features/Portal/Document/Body.tsx +1 -3
  64. package/src/features/Portal/Document/EditorCanvas.tsx +7 -50
  65. package/src/features/Portal/Document/Header.tsx +13 -10
  66. package/src/features/Portal/Document/TodoList.tsx +6 -4
  67. package/src/features/Portal/Document/Wrapper.tsx +3 -11
  68. package/src/features/Portal/Document/index.ts +0 -2
  69. package/src/features/Portal/FilePreview/index.ts +0 -2
  70. package/src/features/Portal/GroupThread/index.ts +0 -3
  71. package/src/features/Portal/MessageDetail/index.ts +0 -2
  72. package/src/features/Portal/Notebook/index.ts +0 -2
  73. package/src/features/Portal/Plugins/index.ts +0 -2
  74. package/src/features/Portal/Thread/index.ts +0 -3
  75. package/src/features/Portal/components/Header.tsx +18 -6
  76. package/src/features/Portal/router.tsx +34 -97
  77. package/src/features/Portal/type.ts +0 -2
  78. package/src/locales/default/plugin.ts +1 -0
  79. package/src/store/chat/slices/portal/action.test.ts +218 -15
  80. package/src/store/chat/slices/portal/action.ts +194 -41
  81. package/src/store/chat/slices/portal/initialState.ts +40 -1
  82. package/src/store/chat/slices/portal/selectors/thread.ts +44 -3
  83. package/src/store/chat/slices/portal/selectors.test.ts +119 -17
  84. package/src/store/chat/slices/portal/selectors.ts +117 -36
  85. package/src/store/document/index.ts +17 -5
  86. package/src/store/document/slices/document/action.ts +209 -0
  87. package/src/store/document/slices/document/index.ts +6 -0
  88. package/src/store/document/slices/editor/action.test.ts +340 -0
  89. package/src/store/document/slices/editor/action.ts +133 -149
  90. package/src/store/document/slices/editor/index.ts +9 -2
  91. package/src/store/document/slices/editor/initialState.ts +66 -29
  92. package/src/store/document/slices/editor/reducer.test.ts +217 -0
  93. package/src/store/document/slices/editor/reducer.ts +67 -0
  94. package/src/store/document/slices/editor/selectors.test.ts +395 -0
  95. package/src/store/document/slices/editor/selectors.ts +107 -5
  96. package/src/store/document/store.ts +12 -13
  97. package/src/store/file/slices/document/action.ts +19 -188
  98. package/src/store/file/slices/document/initialState.ts +0 -30
  99. package/src/store/file/slices/document/selectors.ts +25 -59
  100. package/src/store/notebook/index.ts +5 -4
  101. package/src/store/page/index.ts +2 -0
  102. package/src/store/page/initialState.ts +92 -0
  103. package/src/store/page/selectors.ts +5 -0
  104. package/src/store/page/slices/crud/action.ts +477 -0
  105. package/src/store/page/slices/crud/index.ts +2 -0
  106. package/src/store/page/slices/crud/initialState.ts +7 -0
  107. package/src/store/page/slices/internal/action.ts +32 -0
  108. package/src/store/page/slices/internal/index.ts +2 -0
  109. package/src/store/page/slices/internal/reducer.ts +105 -0
  110. package/src/store/page/slices/list/action.ts +206 -0
  111. package/src/store/page/slices/list/index.ts +3 -0
  112. package/src/store/page/slices/list/initialState.ts +29 -0
  113. package/src/store/page/slices/list/selectors.ts +90 -0
  114. package/src/store/page/slices/selection/action.ts +67 -0
  115. package/src/store/page/slices/selection/index.ts +2 -0
  116. package/src/store/page/slices/selection/initialState.ts +11 -0
  117. package/src/store/page/store.ts +29 -0
  118. package/src/store/tool/slices/lobehubSkillStore/selectors.ts +10 -20
  119. package/src/utils/identifier.ts +8 -2
  120. package/src/features/PageEditor/Body/index.tsx +0 -68
  121. package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +0 -316
  122. package/src/features/PageEditor/Header/AutoSaveHint.tsx +0 -27
  123. package/src/features/Portal/Artifacts/useEnable.ts +0 -4
  124. package/src/features/Portal/Document/DocumentEditorProvider.tsx +0 -34
  125. package/src/features/Portal/Document/StoreUpdater.tsx +0 -80
  126. package/src/features/Portal/Document/Title.tsx +0 -54
  127. package/src/features/Portal/Document/store/action.ts +0 -114
  128. package/src/features/Portal/Document/store/index.ts +0 -21
  129. package/src/features/Portal/Document/store/initialState.ts +0 -24
  130. package/src/features/Portal/Document/useEnable.ts +0 -8
  131. package/src/features/Portal/FilePreview/useEnable.ts +0 -6
  132. package/src/features/Portal/GroupThread/hook.ts +0 -9
  133. package/src/features/Portal/MessageDetail/useEnable.ts +0 -4
  134. package/src/features/Portal/Notebook/useEnable.ts +0 -6
  135. package/src/features/Portal/Plugins/useEnable.ts +0 -6
  136. package/src/features/Portal/Thread/hook.ts +0 -8
  137. package/src/store/document/slices/notebook/action.ts +0 -119
  138. package/src/store/document/slices/notebook/index.ts +0 -3
  139. package/src/store/document/slices/notebook/initialState.ts +0 -12
  140. package/src/store/document/slices/notebook/selectors.ts +0 -26
@@ -2,15 +2,16 @@
2
2
 
3
3
  import { memo } from 'react';
4
4
 
5
- import AutoSaveHintBase from '@/components/Editor/AutoSaveHint';
6
-
7
- import { useDocumentEditorStore } from './store';
5
+ import { AutoSaveHint as SharedAutoSaveHint } from '@/features/EditorCanvas';
6
+ import { useChatStore } from '@/store/chat';
7
+ import { chatPortalSelectors } from '@/store/chat/selectors';
8
8
 
9
9
  const AutoSaveHint = memo(() => {
10
- const saveStatus = useDocumentEditorStore((s) => s.saveStatus);
11
- const lastUpdatedTime = useDocumentEditorStore((s) => s.lastUpdatedTime);
10
+ const documentId = useChatStore(chatPortalSelectors.portalDocumentId);
11
+
12
+ if (!documentId) return null;
12
13
 
13
- return <AutoSaveHintBase lastUpdatedTime={lastUpdatedTime} saveStatus={saveStatus} />;
14
+ return <SharedAutoSaveHint documentId={documentId} />;
14
15
  });
15
16
 
16
17
  export default AutoSaveHint;
@@ -5,14 +5,13 @@ import { createStaticStyles } from 'antd-style';
5
5
  import { memo } from 'react';
6
6
 
7
7
  import EditorCanvas from './EditorCanvas';
8
- import Title from './Title';
9
8
  import TodoList from './TodoList';
10
9
 
11
10
  const styles = createStaticStyles(({ css }) => ({
12
11
  content: css`
13
12
  overflow: auto;
14
13
  flex: 1;
15
- padding-inline: 12px;
14
+ padding-inline: 16px;
16
15
  `,
17
16
  todoContainer: css`
18
17
  flex-shrink: 0;
@@ -25,7 +24,6 @@ const DocumentBody = memo(() => {
25
24
  return (
26
25
  <Flexbox flex={1} height={'100%'} style={{ overflow: 'hidden' }}>
27
26
  <div className={styles.content}>
28
- <Title />
29
27
  <EditorCanvas />
30
28
  </div>
31
29
  <div className={styles.todoContainer}>
@@ -1,61 +1,18 @@
1
1
  'use client';
2
2
 
3
- import {
4
- ReactCodePlugin,
5
- ReactCodemirrorPlugin,
6
- ReactHRPlugin,
7
- ReactImagePlugin,
8
- ReactLinkPlugin,
9
- ReactListPlugin,
10
- ReactMathPlugin,
11
- ReactTablePlugin,
12
- } from '@lobehub/editor';
13
- import { Editor } from '@lobehub/editor/react';
3
+ import { useEditor } from '@lobehub/editor/react';
14
4
  import { memo } from 'react';
15
- import { useTranslation } from 'react-i18next';
16
5
 
17
- import { useDocumentEditorStore } from './store';
6
+ import { EditorCanvas as SharedEditorCanvas } from '@/features/EditorCanvas';
7
+ import { useChatStore } from '@/store/chat';
8
+ import { chatPortalSelectors } from '@/store/chat/selectors';
18
9
 
19
10
  const EditorCanvas = memo(() => {
20
- const { t } = useTranslation('file');
11
+ const editor = useEditor();
21
12
 
22
- const editor = useDocumentEditorStore((s) => s.editor);
23
- const handleContentChange = useDocumentEditorStore((s) => s.handleContentChange);
13
+ const documentId = useChatStore(chatPortalSelectors.portalDocumentId);
24
14
 
25
- if (!editor) return null;
26
-
27
- return (
28
- <div
29
- onClick={(e) => {
30
- e.stopPropagation();
31
- e.preventDefault();
32
- }}
33
- >
34
- <Editor
35
- content={''}
36
- editor={editor}
37
- lineEmptyPlaceholder={t('pageEditor.editorPlaceholder')}
38
- onTextChange={handleContentChange}
39
- placeholder={t('pageEditor.editorPlaceholder')}
40
- plugins={[
41
- ReactListPlugin,
42
- ReactCodePlugin,
43
- ReactCodemirrorPlugin,
44
- ReactHRPlugin,
45
- ReactLinkPlugin,
46
- ReactTablePlugin,
47
- ReactMathPlugin,
48
- Editor.withProps(ReactImagePlugin, {
49
- defaultBlockImage: true,
50
- }),
51
- ]}
52
- style={{
53
- paddingBottom: 64,
54
- }}
55
- type={'text'}
56
- />
57
- </div>
58
- );
15
+ return <SharedEditorCanvas documentId={documentId} editor={editor} sourceType="notebook" />;
59
16
  });
60
17
 
61
18
  export default EditorCanvas;
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { ActionIcon, Button, Flexbox, Text } from '@lobehub/ui';
3
+ import { Button, Flexbox, Text } from '@lobehub/ui';
4
4
  import { cx } from 'antd-style';
5
- import { ArrowLeft, ExternalLink } from 'lucide-react';
5
+ import { ExternalLink } from 'lucide-react';
6
6
  import { useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { useNavigate } from 'react-router-dom';
@@ -22,13 +22,17 @@ const Header = () => {
22
22
  const navigate = useNavigate();
23
23
  const [loading, setLoading] = useState(false);
24
24
 
25
- const [topicId, documentId, closeDocument] = useChatStore((s) => [
25
+ const [topicId, documentId] = useChatStore((s) => [
26
26
  s.activeTopicId,
27
27
  chatPortalSelectors.portalDocumentId(s),
28
- s.closeDocument,
29
28
  ]);
30
29
 
31
- const document = useNotebookStore(notebookSelectors.getDocumentById(topicId, documentId));
30
+ const [useFetchDocuments, title, fileType] = useNotebookStore((s) => [
31
+ s.useFetchDocuments,
32
+ notebookSelectors.getDocumentById(topicId, documentId)(s)?.title,
33
+ notebookSelectors.getDocumentById(topicId, documentId)(s)?.fileType,
34
+ ]);
35
+ useFetchDocuments(topicId);
32
36
 
33
37
  const handleOpenInPageEditor = async () => {
34
38
  if (!documentId) return;
@@ -49,19 +53,18 @@ const Header = () => {
49
53
  }
50
54
  };
51
55
 
52
- if (!document) return null;
56
+ if (!title) return null;
53
57
 
54
58
  return (
55
59
  <Flexbox align={'center'} flex={1} gap={12} horizontal justify={'space-between'} width={'100%'}>
56
- <Flexbox align={'center'} gap={4} horizontal>
57
- <ActionIcon icon={ArrowLeft} onClick={closeDocument} size={'small'} />
60
+ <Flexbox flex={1}>
58
61
  <Text className={cx(oneLineEllipsis)} type={'secondary'}>
59
- {document.title}
62
+ {title}
60
63
  </Text>
61
64
  </Flexbox>
62
65
  <Flexbox align={'center'} gap={8} horizontal>
63
66
  <AutoSaveHint />
64
- {document.fileType !== 'agent/plan' && (
67
+ {fileType !== 'agent/plan' && (
65
68
  <Button
66
69
  icon={<ExternalLink size={14} />}
67
70
  loading={loading}
@@ -6,11 +6,11 @@ import { ChevronDown, ChevronUp, ListTodo } from 'lucide-react';
6
6
  import { memo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
+ import { useChatStore } from '@/store/chat';
10
+ import { chatPortalSelectors } from '@/store/chat/selectors';
9
11
  import { useNotebookStore } from '@/store/notebook';
10
12
  import { notebookSelectors } from '@/store/notebook/selectors';
11
13
 
12
- import { useDocumentEditorStore } from './store';
13
-
14
14
  interface TodoItem {
15
15
  completed: boolean;
16
16
  text: string;
@@ -106,8 +106,10 @@ const TodoList = memo(() => {
106
106
  const { t } = useTranslation('portal');
107
107
  const [expanded, setExpanded] = useState(false);
108
108
 
109
- const documentId = useDocumentEditorStore((s) => s.documentId);
110
- const topicId = useDocumentEditorStore((s) => s.topicId);
109
+ const [topicId, documentId] = useChatStore((s) => [
110
+ s.activeTopicId,
111
+ chatPortalSelectors.portalDocumentId(s),
112
+ ]);
111
113
 
112
114
  const document = useNotebookStore(notebookSelectors.getDocumentById(topicId, documentId));
113
115
 
@@ -1,25 +1,17 @@
1
1
  'use client';
2
2
 
3
+ import { EditorProvider } from '@lobehub/editor/react';
3
4
  import { type PropsWithChildren, memo } from 'react';
4
5
 
5
6
  import { useChatStore } from '@/store/chat';
6
7
  import { chatPortalSelectors } from '@/store/chat/selectors';
7
8
 
8
- import { DocumentEditorProvider } from './DocumentEditorProvider';
9
-
10
9
  const Wrapper = memo<PropsWithChildren>(({ children }) => {
11
- const [topicId, documentId] = useChatStore((s) => [
12
- s.activeTopicId,
13
- chatPortalSelectors.portalDocumentId(s),
14
- ]);
10
+ const documentId = useChatStore(chatPortalSelectors.portalDocumentId);
15
11
 
16
12
  if (!documentId) return null;
17
13
 
18
- return (
19
- <DocumentEditorProvider documentId={documentId} topicId={topicId}>
20
- {children}
21
- </DocumentEditorProvider>
22
- );
14
+ return <EditorProvider>{children}</EditorProvider>;
23
15
  });
24
16
 
25
17
  export default Wrapper;
@@ -1,12 +1,10 @@
1
1
  import { type PortalImpl } from '../type';
2
2
  import Body from './Body';
3
3
  import Header from './Header';
4
- import { useEnable } from './useEnable';
5
4
  import Wrapper from './Wrapper';
6
5
 
7
6
  export const Document: PortalImpl = {
8
7
  Body,
9
8
  Title: Header,
10
9
  Wrapper,
11
- useEnable,
12
10
  };
@@ -1,10 +1,8 @@
1
1
  import { type PortalImpl } from '../type';
2
2
  import Body from './Body';
3
3
  import Title from './Title';
4
- import { useEnable } from './useEnable';
5
4
 
6
5
  export const FilePreview: PortalImpl = {
7
6
  Body,
8
7
  Title,
9
- useEnable,
10
8
  };
@@ -2,12 +2,9 @@ import { type PortalImpl } from '../type';
2
2
  import Body from './Body';
3
3
  import Header from './Header';
4
4
  import Title from './Title';
5
- import { onClose, useEnable } from './hook';
6
5
 
7
6
  export const GroupThread: PortalImpl = {
8
7
  Body,
9
8
  Header,
10
9
  Title,
11
- onClose,
12
- useEnable,
13
10
  };
@@ -1,10 +1,8 @@
1
1
  import { type PortalImpl } from '../type';
2
2
  import Body from './Body';
3
3
  import Title from './Title';
4
- import { useEnable } from './useEnable';
5
4
 
6
5
  export const MessageDetail: PortalImpl = {
7
6
  Body,
8
7
  Title,
9
- useEnable,
10
8
  };
@@ -1,10 +1,8 @@
1
1
  import { type PortalImpl } from '../type';
2
2
  import Body from './Body';
3
3
  import Title from './Title';
4
- import { useEnable } from './useEnable';
5
4
 
6
5
  export const Notebook: PortalImpl = {
7
6
  Body,
8
7
  Title,
9
- useEnable,
10
8
  };
@@ -1,10 +1,8 @@
1
1
  import { type PortalImpl } from '../type';
2
2
  import Body from './Body';
3
3
  import Title from './Title';
4
- import { useEnable } from './useEnable';
5
4
 
6
5
  export const Plugins: PortalImpl = {
7
6
  Body,
8
7
  Title,
9
- useEnable,
10
8
  };
@@ -1,12 +1,9 @@
1
1
  import { type PortalImpl } from '../type';
2
2
  import Chat from './Chat';
3
3
  import Header from './Header';
4
- import { onClose, useEnable } from './hook';
5
4
 
6
5
  export const Thread: PortalImpl = {
7
6
  Body: Chat,
8
7
  Header,
9
8
  Title: () => null,
10
- onClose,
11
- useEnable,
12
9
  };
@@ -1,24 +1,36 @@
1
1
  'use client';
2
2
 
3
3
  import { DESKTOP_HEADER_ICON_SIZE } from '@lobechat/const';
4
- import { ActionIcon } from '@lobehub/ui';
5
- import { PanelRightCloseIcon } from 'lucide-react';
4
+ import { ActionIcon, Flexbox } from '@lobehub/ui';
5
+ import { ArrowLeft, PanelRightCloseIcon } from 'lucide-react';
6
6
  import { type ReactNode, memo } from 'react';
7
7
 
8
8
  import NavHeader from '@/features/NavHeader';
9
9
  import { useChatStore } from '@/store/chat';
10
+ import { chatPortalSelectors } from '@/store/chat/selectors';
10
11
 
11
12
  const Header = memo<{ title: ReactNode }>(({ title }) => {
12
- const [toggleInspector] = useChatStore((s) => [s.togglePortal]);
13
+ const [canGoBack, goBack, togglePortal] = useChatStore((s) => [
14
+ chatPortalSelectors.canGoBack(s),
15
+ s.goBack,
16
+ s.togglePortal,
17
+ ]);
13
18
 
14
19
  return (
15
20
  <NavHeader
16
- left={title}
21
+ left={
22
+ <Flexbox align="center" gap={4} horizontal>
23
+ {canGoBack && (
24
+ <ActionIcon icon={ArrowLeft} onClick={goBack} size={DESKTOP_HEADER_ICON_SIZE} />
25
+ )}
26
+ {title}
27
+ </Flexbox>
28
+ }
17
29
  right={
18
30
  <ActionIcon
19
31
  icon={PanelRightCloseIcon}
20
32
  onClick={() => {
21
- toggleInspector(false);
33
+ togglePortal(false);
22
34
  }}
23
35
  size={DESKTOP_HEADER_ICON_SIZE}
24
36
  />
@@ -27,7 +39,7 @@ const Header = memo<{ title: ReactNode }>(({ title }) => {
27
39
  style={{ paddingBlock: 8, paddingInline: 8 }}
28
40
  styles={{
29
41
  left: {
30
- marginLeft: 6,
42
+ marginLeft: canGoBack ? 0 : 6,
31
43
  },
32
44
  }}
33
45
  />
@@ -2,6 +2,10 @@
2
2
 
3
3
  import React, { Fragment, memo } from 'react';
4
4
 
5
+ import { useChatStore } from '@/store/chat';
6
+ import { chatPortalSelectors } from '@/store/chat/selectors';
7
+ import { PortalViewType } from '@/store/chat/slices/portal/initialState';
8
+
5
9
  import { Artifacts } from './Artifacts';
6
10
  import { Document } from './Document';
7
11
  import { FilePreview } from './FilePreview';
@@ -14,72 +18,27 @@ import { Thread } from './Thread';
14
18
  import Header from './components/Header';
15
19
  import { type PortalImpl } from './type';
16
20
 
17
- // Keep GroupThread before Thread so group DM threads take precedence when enabled
18
- // Document should be before Notebook so detail view takes precedence
19
- const items: PortalImpl[] = [
20
- GroupThread,
21
- Thread,
22
- MessageDetail,
23
- Artifacts,
24
- Plugins,
25
- FilePreview,
26
- Document,
27
- Notebook,
28
- ];
29
-
30
- export const PortalTitle = memo(() => {
31
- const enabledList: boolean[] = [];
32
-
33
- for (const item of items) {
34
- const enabled = item.useEnable();
35
- enabledList.push(enabled);
36
- }
37
-
38
- for (const [i, element] of enabledList.entries()) {
39
- const Title = items[i].Title;
40
- if (element) {
41
- return <Title />;
42
- }
43
- }
44
-
45
- return <HomeTitle />;
46
- });
47
-
48
- export const PortalHeader = memo(() => {
49
- const enabledList: boolean[] = [];
50
-
51
- for (const item of items) {
52
- const enabled = item.useEnable();
53
- enabledList.push(enabled);
54
- }
55
-
56
- for (const [i, element] of enabledList.entries()) {
57
- const Header = items[i].Header;
58
- if (element && Header) {
59
- return <Header />;
60
- }
61
- }
62
-
63
- return <Header title={<PortalTitle />} />;
64
- });
65
-
66
- const PortalBody = memo(() => {
67
- const enabledList: boolean[] = [];
68
-
69
- for (const item of items) {
70
- const enabled = item.useEnable();
71
- enabledList.push(enabled);
72
- }
73
-
74
- for (const [i, element] of enabledList.entries()) {
75
- const Body = items[i].Body;
76
- if (element) {
77
- return <Body />;
78
- }
79
- }
80
-
81
- return <HomeBody />;
82
- });
21
+ // View type to component mapping
22
+ const VIEW_COMPONENTS: Record<PortalViewType, PortalImpl> = {
23
+ [PortalViewType.Home]: {
24
+ Body: HomeBody,
25
+ Title: HomeTitle,
26
+ },
27
+ [PortalViewType.Artifact]: Artifacts,
28
+ [PortalViewType.Document]: Document,
29
+ [PortalViewType.Notebook]: Notebook,
30
+ [PortalViewType.FilePreview]: FilePreview,
31
+ [PortalViewType.MessageDetail]: MessageDetail,
32
+ [PortalViewType.ToolUI]: Plugins,
33
+ [PortalViewType.Thread]: Thread,
34
+ [PortalViewType.GroupThread]: GroupThread,
35
+ };
36
+
37
+ // Default Home component
38
+ const HomeImpl: PortalImpl = {
39
+ Body: HomeBody,
40
+ Title: HomeTitle,
41
+ };
83
42
 
84
43
  interface PortalContentProps {
85
44
  renderBody?: (body: React.ReactNode) => React.ReactNode;
@@ -87,37 +46,18 @@ interface PortalContentProps {
87
46
 
88
47
  /**
89
48
  * Portal content with Wrapper support
90
- * When an enabled item has a Wrapper, it wraps both Header and Body
49
+ * Uses the view stack to determine which component to render
91
50
  */
92
- const PortalContent = memo<PortalContentProps>(({ renderBody }) => {
93
- const enabledList: boolean[] = [];
51
+ export const PortalContent = memo<PortalContentProps>(({ renderBody }) => {
52
+ const viewType = useChatStore(chatPortalSelectors.currentViewType);
53
+ const ViewImpl = viewType ? VIEW_COMPONENTS[viewType] : HomeImpl;
94
54
 
95
- for (const item of items) {
96
- const enabled = item.useEnable();
97
- enabledList.push(enabled);
98
- }
99
-
100
- // Find the first enabled item
101
- let enabledIndex = -1;
102
- for (const [i, element] of enabledList.entries()) {
103
- if (element) {
104
- enabledIndex = i;
105
- break;
106
- }
107
- }
108
-
109
- // Get components for the enabled item
110
- const enabledItem = enabledIndex >= 0 ? items[enabledIndex] : null;
111
- const Wrapper = enabledItem?.Wrapper || Fragment;
112
- const CustomHeader = enabledItem?.Header;
113
- const Body = enabledItem?.Body || HomeBody;
114
-
115
- const headerContent = CustomHeader ? (
116
- <CustomHeader />
117
- ) : (
118
- <Header title={enabledItem?.Title ? <enabledItem.Title /> : <HomeTitle />} />
119
- );
55
+ const Wrapper = ViewImpl?.Wrapper || Fragment;
56
+ const CustomHeader = ViewImpl?.Header;
57
+ const Body = ViewImpl?.Body || HomeBody;
58
+ const Title = ViewImpl?.Title || HomeTitle;
120
59
 
60
+ const headerContent = CustomHeader ? <CustomHeader /> : <Header title={<Title />} />;
121
61
  const bodyContent = <Body />;
122
62
 
123
63
  return (
@@ -127,6 +67,3 @@ const PortalContent = memo<PortalContentProps>(({ renderBody }) => {
127
67
  </Wrapper>
128
68
  );
129
69
  });
130
-
131
- export { PortalContent };
132
- export default PortalBody;
@@ -5,6 +5,4 @@ export interface PortalImpl {
5
5
  Header?: FC;
6
6
  Title: FC;
7
7
  Wrapper?: FC<PropsWithChildren>;
8
- onClose?: () => void;
9
- useEnable: () => boolean;
10
8
  }
@@ -93,6 +93,7 @@ export default {
93
93
  'builtins.lobe-local-system.inspector.rename.result':
94
94
  '<old>{{oldName}}</old> → <new>{{newName}}</new>',
95
95
  'builtins.lobe-local-system.title': 'Local System',
96
+ 'builtins.lobe-notebook.actions.collapse': 'Collapse',
96
97
  'builtins.lobe-notebook.actions.copy': 'Copy',
97
98
  'builtins.lobe-notebook.actions.creating': 'Creating document...',
98
99
  'builtins.lobe-notebook.actions.edit': 'Edit',