@lobehub/lobehub 2.0.0-next.254 → 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.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/zh-CN/plugin.json +1 -0
- package/package.json +1 -1
- package/packages/builtin-tool-notebook/src/client/Placeholder/CreateDocument.tsx +6 -6
- package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +23 -10
- package/packages/builtin-tool-notebook/src/client/Streaming/CreateDocument/index.tsx +1 -1
- package/packages/database/src/models/__tests__/agent.test.ts +91 -4
- package/packages/database/src/models/agent.ts +15 -7
- package/packages/editor-runtime/src/EditorRuntime.ts +1 -1
- package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +65 -4
- package/packages/editor-runtime/src/__tests__/__snapshots__/EditorRuntime.real.test.ts.snap +108 -17
- package/packages/editor-runtime/src/__tests__/fixtures/remove-then-add.json +636 -0
- package/packages/editor-runtime/src/__tests__/fixtures/remove.json +1 -0
- package/packages/types/src/agent/agentConfig.ts +8 -8
- package/src/app/[variants]/(main)/chat/features/Portal/_layout/Mobile.tsx +2 -1
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +2 -1
- package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +2 -2
- package/src/app/[variants]/(main)/page/{features/PageTitle → PageTitle}/index.tsx +0 -1
- package/src/app/[variants]/(main)/page/[id]/index.tsx +43 -1
- package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/Content.tsx +15 -15
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Editing.tsx +3 -3
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/index.tsx +7 -12
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/useDropdownMenu.tsx +5 -5
- package/src/app/[variants]/(main)/page/_layout/Body/List/index.tsx +7 -7
- package/src/app/[variants]/(main)/page/_layout/Body/index.tsx +15 -9
- package/src/app/[variants]/(main)/page/_layout/Body/useDropdownMenu.tsx +3 -3
- package/src/app/[variants]/(main)/page/_layout/DataSync.tsx +15 -0
- package/src/app/[variants]/(main)/page/_layout/Header/AddButton.tsx +2 -2
- package/src/app/[variants]/(main)/page/_layout/index.tsx +2 -0
- package/src/app/[variants]/(main)/page/index.tsx +3 -7
- package/src/components/Editor/AutoSaveHint.tsx +1 -1
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/Intervention/ApprovalActions.tsx +2 -2
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +18 -15
- package/src/features/Conversation/Messages/AssistantGroup/components/Group.tsx +3 -3
- package/src/features/Conversation/Messages/Contexts/MessageAggregationContext.ts +15 -0
- package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +3 -3
- package/src/features/Conversation/Messages/User/Actions/index.tsx +5 -1
- package/src/features/EditorCanvas/AutoSaveHint.tsx +37 -0
- package/src/features/{PageEditor → EditorCanvas}/DiffAllToolbar.tsx +57 -16
- package/src/features/EditorCanvas/DocumentIdMode.tsx +111 -0
- package/src/features/EditorCanvas/EditorCanvas.tsx +148 -0
- package/src/features/EditorCanvas/EditorDataMode.tsx +64 -0
- package/src/features/EditorCanvas/ErrorBoundary.tsx +66 -0
- package/src/features/EditorCanvas/InlineToolbar.tsx +245 -0
- package/src/features/EditorCanvas/InternalEditor.tsx +134 -0
- package/src/features/{PageEditor/EditorCanvas → EditorCanvas}/actions.ts +10 -8
- package/src/features/EditorCanvas/index.ts +9 -0
- package/src/features/PageEditor/EditorCanvas/index.tsx +14 -111
- package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +95 -0
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +1 -1
- package/src/features/PageEditor/Header/Breadcrumb.tsx +2 -2
- package/src/features/PageEditor/Header/index.tsx +15 -18
- package/src/features/PageEditor/Header/useMenu.tsx +12 -9
- package/src/features/PageEditor/PageEditor.tsx +45 -21
- package/src/features/PageEditor/PageEditorProvider.tsx +13 -1
- package/src/features/PageEditor/PageTitle/index.tsx +2 -2
- package/src/features/PageEditor/StoreUpdater.tsx +35 -308
- package/src/features/PageEditor/{Body/Title.tsx → TitleSection.tsx} +16 -16
- package/src/features/PageEditor/store/action.ts +96 -188
- package/src/features/PageEditor/store/initialState.ts +16 -21
- package/src/features/PageEditor/store/selectors.ts +3 -4
- package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +22 -14
- package/src/features/PageExplorer/index.tsx +34 -67
- package/src/features/Portal/Artifacts/index.ts +0 -2
- package/src/features/Portal/Document/AutoSaveHint.tsx +7 -6
- package/src/features/Portal/Document/Body.tsx +1 -3
- package/src/features/Portal/Document/EditorCanvas.tsx +7 -50
- package/src/features/Portal/Document/Header.tsx +13 -10
- package/src/features/Portal/Document/TodoList.tsx +6 -4
- package/src/features/Portal/Document/Wrapper.tsx +3 -11
- package/src/features/Portal/Document/index.ts +0 -2
- package/src/features/Portal/FilePreview/index.ts +0 -2
- package/src/features/Portal/GroupThread/index.ts +0 -3
- package/src/features/Portal/MessageDetail/index.ts +0 -2
- package/src/features/Portal/Notebook/index.ts +0 -2
- package/src/features/Portal/Plugins/index.ts +0 -2
- package/src/features/Portal/Thread/index.ts +0 -3
- package/src/features/Portal/components/Header.tsx +18 -6
- package/src/features/Portal/router.tsx +34 -97
- package/src/features/Portal/type.ts +0 -2
- package/src/libs/next/config/define-config.ts +4 -1
- package/src/locales/default/plugin.ts +1 -0
- package/src/store/chat/slices/portal/action.test.ts +218 -15
- package/src/store/chat/slices/portal/action.ts +194 -41
- package/src/store/chat/slices/portal/initialState.ts +40 -1
- package/src/store/chat/slices/portal/selectors/thread.ts +44 -3
- package/src/store/chat/slices/portal/selectors.test.ts +119 -17
- package/src/store/chat/slices/portal/selectors.ts +117 -36
- package/src/store/document/index.ts +17 -5
- package/src/store/document/slices/document/action.ts +209 -0
- package/src/store/document/slices/document/index.ts +6 -0
- package/src/store/document/slices/editor/action.test.ts +340 -0
- package/src/store/document/slices/editor/action.ts +133 -149
- package/src/store/document/slices/editor/index.ts +9 -2
- package/src/store/document/slices/editor/initialState.ts +66 -29
- package/src/store/document/slices/editor/reducer.test.ts +217 -0
- package/src/store/document/slices/editor/reducer.ts +67 -0
- package/src/store/document/slices/editor/selectors.test.ts +395 -0
- package/src/store/document/slices/editor/selectors.ts +107 -5
- package/src/store/document/store.ts +12 -13
- package/src/store/file/slices/document/action.ts +19 -188
- package/src/store/file/slices/document/initialState.ts +0 -30
- package/src/store/file/slices/document/selectors.ts +25 -59
- package/src/store/notebook/index.ts +5 -4
- package/src/store/page/index.ts +2 -0
- package/src/store/page/initialState.ts +92 -0
- package/src/store/page/selectors.ts +5 -0
- package/src/store/page/slices/crud/action.ts +477 -0
- package/src/store/page/slices/crud/index.ts +2 -0
- package/src/store/page/slices/crud/initialState.ts +7 -0
- package/src/store/page/slices/internal/action.ts +32 -0
- package/src/store/page/slices/internal/index.ts +2 -0
- package/src/store/page/slices/internal/reducer.ts +105 -0
- package/src/store/page/slices/list/action.ts +206 -0
- package/src/store/page/slices/list/index.ts +3 -0
- package/src/store/page/slices/list/initialState.ts +29 -0
- package/src/store/page/slices/list/selectors.ts +90 -0
- package/src/store/page/slices/selection/action.ts +67 -0
- package/src/store/page/slices/selection/index.ts +2 -0
- package/src/store/page/slices/selection/initialState.ts +11 -0
- package/src/store/page/store.ts +29 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +10 -20
- package/src/utils/identifier.ts +8 -2
- package/src/features/Conversation/Messages/AssistantGroup/components/GroupContext.ts +0 -15
- package/src/features/Conversation/Messages/Supervisor/components/GroupContext.ts +0 -15
- package/src/features/PageEditor/Body/index.tsx +0 -68
- package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +0 -316
- package/src/features/PageEditor/Header/AutoSaveHint.tsx +0 -27
- package/src/features/Portal/Artifacts/useEnable.ts +0 -4
- package/src/features/Portal/Document/DocumentEditorProvider.tsx +0 -34
- package/src/features/Portal/Document/StoreUpdater.tsx +0 -80
- package/src/features/Portal/Document/Title.tsx +0 -54
- package/src/features/Portal/Document/store/action.ts +0 -114
- package/src/features/Portal/Document/store/index.ts +0 -21
- package/src/features/Portal/Document/store/initialState.ts +0 -24
- package/src/features/Portal/Document/useEnable.ts +0 -8
- package/src/features/Portal/FilePreview/useEnable.ts +0 -6
- package/src/features/Portal/GroupThread/hook.ts +0 -9
- package/src/features/Portal/MessageDetail/useEnable.ts +0 -4
- package/src/features/Portal/Notebook/useEnable.ts +0 -6
- package/src/features/Portal/Plugins/useEnable.ts +0 -6
- package/src/features/Portal/Thread/hook.ts +0 -8
- package/src/store/document/slices/notebook/action.ts +0 -119
- package/src/store/document/slices/notebook/index.ts +0 -3
- package/src/store/document/slices/notebook/initialState.ts +0 -12
- package/src/store/document/slices/notebook/selectors.ts +0 -26
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { DiffAction, LITEXML_DIFFNODE_ALL_COMMAND } from '@lobehub/editor';
|
|
3
|
+
import { DiffAction, IEditor, LITEXML_DIFFNODE_ALL_COMMAND } from '@lobehub/editor';
|
|
4
4
|
import { Block, Icon } from '@lobehub/ui';
|
|
5
5
|
import { Button, Space } from 'antd';
|
|
6
6
|
import { createStaticStyles, cssVar, cx } from 'antd-style';
|
|
@@ -9,8 +9,7 @@ import { memo, useEffect, useState } from 'react';
|
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
11
11
|
import { useIsDark } from '@/hooks/useIsDark';
|
|
12
|
-
|
|
13
|
-
import { usePageEditorStore } from './store';
|
|
12
|
+
import { useDocumentStore } from '@/store/document';
|
|
14
13
|
|
|
15
14
|
const styles = createStaticStyles(({ css }) => ({
|
|
16
15
|
container: css`
|
|
@@ -36,18 +35,36 @@ const styles = createStaticStyles(({ css }) => ({
|
|
|
36
35
|
`,
|
|
37
36
|
}));
|
|
38
37
|
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
const useIsEditorInit = (editor: IEditor) => {
|
|
39
|
+
const [isEditInit, setEditInit] = useState<boolean>(!!editor.getLexicalEditor());
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (!editor) return;
|
|
43
|
+
|
|
44
|
+
const onInit = () => {
|
|
45
|
+
console.log('init: id', editor.getLexicalEditor()?._key);
|
|
46
|
+
setEditInit(true);
|
|
47
|
+
};
|
|
48
|
+
editor.on('initialized', onInit);
|
|
49
|
+
return () => {
|
|
50
|
+
editor.off('initialized', onInit);
|
|
51
|
+
};
|
|
52
|
+
}, [editor]);
|
|
53
|
+
|
|
54
|
+
return isEditInit;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const useEditorHasPendingDiffs = (editor: IEditor) => {
|
|
43
58
|
const [hasPendingDiffs, setHasPendingDiffs] = useState(false);
|
|
59
|
+
const isEditInit = useIsEditorInit(editor);
|
|
44
60
|
|
|
45
61
|
// Listen to editor state changes to detect diff nodes
|
|
46
62
|
useEffect(() => {
|
|
47
63
|
if (!editor) return;
|
|
48
64
|
|
|
49
65
|
const lexicalEditor = editor.getLexicalEditor();
|
|
50
|
-
|
|
66
|
+
|
|
67
|
+
if (!lexicalEditor || !isEditInit) return;
|
|
51
68
|
|
|
52
69
|
const checkForDiffNodes = () => {
|
|
53
70
|
const editorState = lexicalEditor.getEditorState();
|
|
@@ -67,15 +84,39 @@ const DiffAllToolbar = memo(() => {
|
|
|
67
84
|
// Check initially
|
|
68
85
|
checkForDiffNodes();
|
|
69
86
|
|
|
70
|
-
// Register update listener
|
|
71
87
|
const unregister = lexicalEditor.registerUpdateListener(() => {
|
|
72
88
|
checkForDiffNodes();
|
|
73
89
|
});
|
|
90
|
+
// Register update listener
|
|
91
|
+
return () => {
|
|
92
|
+
unregister();
|
|
93
|
+
};
|
|
94
|
+
}, [editor, isEditInit]);
|
|
74
95
|
|
|
75
|
-
|
|
76
|
-
|
|
96
|
+
return hasPendingDiffs;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
interface DiffAllToolbarProps {
|
|
100
|
+
documentId: string;
|
|
101
|
+
editor: IEditor;
|
|
102
|
+
}
|
|
103
|
+
const DiffAllToolbar = memo<DiffAllToolbarProps>(({ documentId }) => {
|
|
104
|
+
const { t } = useTranslation('editor');
|
|
105
|
+
const isDarkMode = useIsDark();
|
|
106
|
+
const [editor, performSave, markDirty] = useDocumentStore((s) => [
|
|
107
|
+
s.editor!,
|
|
108
|
+
s.performSave,
|
|
109
|
+
s.markDirty,
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
const hasPendingDiffs = useEditorHasPendingDiffs(editor);
|
|
113
|
+
|
|
114
|
+
if (!hasPendingDiffs) return null;
|
|
77
115
|
|
|
78
|
-
|
|
116
|
+
const handleSave = async () => {
|
|
117
|
+
markDirty(documentId);
|
|
118
|
+
await performSave();
|
|
119
|
+
};
|
|
79
120
|
|
|
80
121
|
return (
|
|
81
122
|
<div className={styles.container}>
|
|
@@ -90,10 +131,10 @@ const DiffAllToolbar = memo(() => {
|
|
|
90
131
|
<Space>
|
|
91
132
|
<Button
|
|
92
133
|
onClick={async () => {
|
|
93
|
-
editor
|
|
134
|
+
editor?.dispatchCommand(LITEXML_DIFFNODE_ALL_COMMAND, {
|
|
94
135
|
action: DiffAction.Reject,
|
|
95
136
|
});
|
|
96
|
-
await
|
|
137
|
+
await handleSave();
|
|
97
138
|
}}
|
|
98
139
|
size={'small'}
|
|
99
140
|
type="text"
|
|
@@ -104,10 +145,10 @@ const DiffAllToolbar = memo(() => {
|
|
|
104
145
|
<Button
|
|
105
146
|
color={'default'}
|
|
106
147
|
onClick={async () => {
|
|
107
|
-
editor
|
|
148
|
+
editor?.dispatchCommand(LITEXML_DIFFNODE_ALL_COMMAND, {
|
|
108
149
|
action: DiffAction.Accept,
|
|
109
150
|
});
|
|
110
|
-
await
|
|
151
|
+
await handleSave();
|
|
111
152
|
}}
|
|
112
153
|
size={'small'}
|
|
113
154
|
variant="filled"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type IEditor } from '@lobehub/editor';
|
|
4
|
+
import { Alert } from '@lobehub/ui';
|
|
5
|
+
import { Skeleton } from 'antd';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { createStoreUpdater } from 'zustand-utils';
|
|
9
|
+
|
|
10
|
+
import { useSaveDocumentHotkey } from '@/hooks/useHotkeys';
|
|
11
|
+
import { useDocumentStore } from '@/store/document';
|
|
12
|
+
import { editorSelectors } from '@/store/document/slices/editor';
|
|
13
|
+
|
|
14
|
+
import type { EditorCanvasProps } from './EditorCanvas';
|
|
15
|
+
import InternalEditor from './InternalEditor';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Loading skeleton for the editor
|
|
19
|
+
*/
|
|
20
|
+
const EditorSkeleton = memo(() => (
|
|
21
|
+
<div style={{ paddingBlock: 24 }}>
|
|
22
|
+
<Skeleton active paragraph={{ rows: 8 }} />
|
|
23
|
+
</div>
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Error display for fetch failures
|
|
28
|
+
*/
|
|
29
|
+
const EditorError = memo<{ error: Error }>(({ error }) => {
|
|
30
|
+
const { t } = useTranslation('file');
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Alert
|
|
34
|
+
description={error.message || t('pageEditor.loadError', 'Failed to load document')}
|
|
35
|
+
showIcon
|
|
36
|
+
style={{ margin: 16 }}
|
|
37
|
+
title={t('pageEditor.error', 'Error')}
|
|
38
|
+
type="error"
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export interface DocumentIdModeProps extends EditorCanvasProps {
|
|
44
|
+
documentId: string;
|
|
45
|
+
editor: IEditor | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* EditorCanvas with documentId mode - handles data fetching internally
|
|
50
|
+
*/
|
|
51
|
+
const DocumentIdMode = memo<DocumentIdModeProps>(
|
|
52
|
+
({
|
|
53
|
+
editor,
|
|
54
|
+
documentId,
|
|
55
|
+
autoSave = true,
|
|
56
|
+
sourceType = 'page',
|
|
57
|
+
onContentChange,
|
|
58
|
+
style,
|
|
59
|
+
...editorProps
|
|
60
|
+
}) => {
|
|
61
|
+
const { t } = useTranslation('file');
|
|
62
|
+
|
|
63
|
+
const storeUpdater = createStoreUpdater(useDocumentStore);
|
|
64
|
+
storeUpdater('activeDocumentId', documentId);
|
|
65
|
+
storeUpdater('editor', editor);
|
|
66
|
+
|
|
67
|
+
// Get document store actions
|
|
68
|
+
const [onEditorInit, handleContentChangeStore, useFetchDocument, flushSave] = useDocumentStore(
|
|
69
|
+
(s) => [s.onEditorInit, s.handleContentChange, s.useFetchDocument, s.flushSave],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
useSaveDocumentHotkey(flushSave);
|
|
73
|
+
|
|
74
|
+
// Use SWR hook for document fetching (auto-initializes via onSuccess in DocumentStore)
|
|
75
|
+
const { error } = useFetchDocument(documentId, { autoSave, editor, sourceType });
|
|
76
|
+
|
|
77
|
+
// Check loading state via selector (document not yet in store)
|
|
78
|
+
const isLoading = useDocumentStore(editorSelectors.isDocumentLoading(documentId));
|
|
79
|
+
|
|
80
|
+
// Handle content change
|
|
81
|
+
const handleChange = () => {
|
|
82
|
+
handleContentChangeStore();
|
|
83
|
+
onContentChange?.();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Show loading state
|
|
87
|
+
if (isLoading) {
|
|
88
|
+
return <EditorSkeleton />;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!editor) return null;
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
{error && <EditorError error={error as Error} />}
|
|
96
|
+
<InternalEditor
|
|
97
|
+
editor={editor}
|
|
98
|
+
onContentChange={handleChange}
|
|
99
|
+
onInit={onEditorInit}
|
|
100
|
+
placeholder={editorProps.placeholder || t('pageEditor.editorPlaceholder')}
|
|
101
|
+
style={style}
|
|
102
|
+
{...editorProps}
|
|
103
|
+
/>
|
|
104
|
+
</>
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
DocumentIdMode.displayName = 'DocumentIdMode';
|
|
110
|
+
|
|
111
|
+
export default DocumentIdMode;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type IEditor, type SlashOptions } from '@lobehub/editor';
|
|
4
|
+
import { type ChatInputActionsProps } from '@lobehub/editor/react';
|
|
5
|
+
import { Editor } from '@lobehub/editor/react';
|
|
6
|
+
import { type CSSProperties, memo } from 'react';
|
|
7
|
+
|
|
8
|
+
import DocumentIdMode from './DocumentIdMode';
|
|
9
|
+
import EditorDataMode from './EditorDataMode';
|
|
10
|
+
import { EditorErrorBoundary } from './ErrorBoundary';
|
|
11
|
+
import InternalEditor from './InternalEditor';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plugin type for the editor
|
|
15
|
+
* Allows any array of plugins that the Editor component accepts
|
|
16
|
+
*/
|
|
17
|
+
type EditorPlugins = Parameters<typeof Editor>[0]['plugins'];
|
|
18
|
+
|
|
19
|
+
export interface EditorCanvasProps {
|
|
20
|
+
/**
|
|
21
|
+
* Whether to enable auto-save in DocumentStore. Defaults to true.
|
|
22
|
+
* Only applies when documentId is provided.
|
|
23
|
+
*/
|
|
24
|
+
autoSave?: boolean;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Document ID to load from server.
|
|
28
|
+
* When provided, component will use useSWR to fetch document data.
|
|
29
|
+
*/
|
|
30
|
+
documentId?: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Editor data to render directly (skip fetch).
|
|
34
|
+
* Use this when you already have the content and don't need to fetch.
|
|
35
|
+
*/
|
|
36
|
+
editorData?: {
|
|
37
|
+
content?: string;
|
|
38
|
+
editorData?: unknown;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Extra plugins to prepend to BASE_PLUGINS (e.g., ReactLiteXmlPlugin)
|
|
43
|
+
*/
|
|
44
|
+
extraPlugins?: EditorPlugins;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Whether to show the floating toolbar. Defaults to true.
|
|
48
|
+
*/
|
|
49
|
+
floatingToolbar?: boolean;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Content change handler
|
|
53
|
+
*/
|
|
54
|
+
onContentChange?: () => void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Editor initialization handler
|
|
58
|
+
*/
|
|
59
|
+
onInit?: (editor: IEditor) => void;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Placeholder text for empty editor
|
|
63
|
+
*/
|
|
64
|
+
placeholder?: string;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Custom plugins for the editor. If provided, replaces BASE_PLUGINS entirely.
|
|
68
|
+
* Use this when you need complete control over plugins.
|
|
69
|
+
*/
|
|
70
|
+
plugins?: EditorPlugins;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Slash menu items
|
|
74
|
+
*/
|
|
75
|
+
slashItems?: SlashOptions['items'];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Source type for DocumentStore. Defaults to 'page'.
|
|
79
|
+
*/
|
|
80
|
+
sourceType?: 'page' | 'notebook';
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Custom styles for the editor
|
|
84
|
+
*/
|
|
85
|
+
style?: CSSProperties;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Extra items to add to the floating toolbar (e.g., "Ask Copilot" button)
|
|
89
|
+
*/
|
|
90
|
+
toolbarExtraItems?: ChatInputActionsProps['items'];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface EditorCanvasWithEditorProps extends EditorCanvasProps {
|
|
94
|
+
/**
|
|
95
|
+
* Editor instance
|
|
96
|
+
*/
|
|
97
|
+
editor: IEditor | undefined;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* EditorCanvas component that accepts editor as a prop
|
|
102
|
+
*
|
|
103
|
+
* Three modes of operation:
|
|
104
|
+
* 1. documentId mode: Pass documentId, component fetches data via useSWR, shows loading/error states
|
|
105
|
+
* 2. editorData mode: Pass editorData directly, skips fetch and renders immediately
|
|
106
|
+
* 3. Basic mode: No documentId or editorData, just renders the editor (original behavior)
|
|
107
|
+
*
|
|
108
|
+
* Features:
|
|
109
|
+
* - Internal ErrorBoundary for graceful error handling
|
|
110
|
+
* - Loading skeleton during fetch (documentId mode)
|
|
111
|
+
* - Error state display for fetch failures (documentId mode)
|
|
112
|
+
* - Auto-save integration with DocumentStore (documentId mode)
|
|
113
|
+
* - AutoSave hint display (documentId mode)
|
|
114
|
+
*/
|
|
115
|
+
export const EditorCanvas = memo<EditorCanvasWithEditorProps>(
|
|
116
|
+
({ editor, documentId, editorData, ...props }) => {
|
|
117
|
+
// documentId mode - fetch and render with loading/error states
|
|
118
|
+
if (documentId) {
|
|
119
|
+
return (
|
|
120
|
+
<EditorErrorBoundary>
|
|
121
|
+
<DocumentIdMode documentId={documentId} editor={editor} {...props} />
|
|
122
|
+
</EditorErrorBoundary>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// editorData mode - render with provided data
|
|
127
|
+
if (editorData) {
|
|
128
|
+
return (
|
|
129
|
+
<EditorErrorBoundary>
|
|
130
|
+
<EditorDataMode editor={editor} editorData={editorData} {...props} />
|
|
131
|
+
</EditorErrorBoundary>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Basic mode - original behavior
|
|
136
|
+
if (!editor) return null;
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<EditorErrorBoundary>
|
|
140
|
+
<InternalEditor editor={editor} {...props} />
|
|
141
|
+
</EditorErrorBoundary>
|
|
142
|
+
);
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
EditorCanvas.displayName = 'EditorCanvas';
|
|
147
|
+
|
|
148
|
+
export default EditorCanvas;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type IEditor } from '@lobehub/editor';
|
|
4
|
+
import { memo, useEffect, useState } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
import type { EditorCanvasProps } from './EditorCanvas';
|
|
8
|
+
import InternalEditor from './InternalEditor';
|
|
9
|
+
|
|
10
|
+
export interface EditorDataModeProps extends EditorCanvasProps {
|
|
11
|
+
editor: IEditor | undefined;
|
|
12
|
+
editorData: NonNullable<EditorCanvasProps['editorData']>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* EditorCanvas with editorData mode - uses provided data directly
|
|
17
|
+
*/
|
|
18
|
+
const EditorDataMode = memo<EditorDataModeProps>(
|
|
19
|
+
({ editor, editorData, onContentChange, style, ...editorProps }) => {
|
|
20
|
+
const { t } = useTranslation('file');
|
|
21
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
22
|
+
|
|
23
|
+
// Load content into editor on mount
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!editor || isInitialized) return;
|
|
26
|
+
|
|
27
|
+
const hasValidEditorData =
|
|
28
|
+
editorData.editorData &&
|
|
29
|
+
typeof editorData.editorData === 'object' &&
|
|
30
|
+
Object.keys(editorData.editorData as object).length > 0;
|
|
31
|
+
|
|
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);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
setIsInitialized(true);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error('[EditorCanvas] Failed to load content:', err);
|
|
44
|
+
}
|
|
45
|
+
}, [editorData, editor, isInitialized]);
|
|
46
|
+
|
|
47
|
+
if (!editor) return null;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div style={{ position: 'relative', ...style }}>
|
|
51
|
+
<InternalEditor
|
|
52
|
+
editor={editor}
|
|
53
|
+
onContentChange={onContentChange}
|
|
54
|
+
placeholder={editorProps.placeholder || t('pageEditor.editorPlaceholder')}
|
|
55
|
+
{...editorProps}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
EditorDataMode.displayName = 'EditorDataMode';
|
|
63
|
+
|
|
64
|
+
export default EditorDataMode;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Alert } from '@lobehub/ui';
|
|
4
|
+
import { Component, type ErrorInfo, type ReactNode } from 'react';
|
|
5
|
+
|
|
6
|
+
interface EditorErrorBoundaryState {
|
|
7
|
+
error: Error | null;
|
|
8
|
+
hasError: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface EditorErrorBoundaryProps {
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
fallback?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* ErrorBoundary for EditorCanvas component.
|
|
18
|
+
* Catches rendering errors in the editor and displays a fallback error UI
|
|
19
|
+
* instead of crashing the entire page.
|
|
20
|
+
*/
|
|
21
|
+
export class EditorErrorBoundary extends Component<
|
|
22
|
+
EditorErrorBoundaryProps,
|
|
23
|
+
EditorErrorBoundaryState
|
|
24
|
+
> {
|
|
25
|
+
public state: EditorErrorBoundaryState = {
|
|
26
|
+
error: null,
|
|
27
|
+
hasError: false,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
public static getDerivedStateFromError(error: Error): Partial<EditorErrorBoundaryState> {
|
|
31
|
+
return { error, hasError: true };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
35
|
+
console.error('[EditorErrorBoundary] Caught error in editor render:', {
|
|
36
|
+
componentStack: errorInfo.componentStack,
|
|
37
|
+
error: error.message,
|
|
38
|
+
stack: error.stack,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public render() {
|
|
43
|
+
if (this.state.hasError) {
|
|
44
|
+
if (this.props.fallback) {
|
|
45
|
+
return this.props.fallback;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Alert
|
|
50
|
+
message={this.state.error?.message || 'An unknown error occurred in the editor'}
|
|
51
|
+
showIcon
|
|
52
|
+
style={{
|
|
53
|
+
margin: 16,
|
|
54
|
+
overflow: 'hidden',
|
|
55
|
+
position: 'relative',
|
|
56
|
+
width: '100%',
|
|
57
|
+
}}
|
|
58
|
+
title="Editor Error"
|
|
59
|
+
type="error"
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return this.props.children;
|
|
65
|
+
}
|
|
66
|
+
}
|