@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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -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/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/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/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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
type IEditor,
|
|
5
|
+
ReactCodePlugin,
|
|
6
|
+
ReactCodemirrorPlugin,
|
|
7
|
+
ReactHRPlugin,
|
|
8
|
+
ReactImagePlugin,
|
|
9
|
+
ReactLinkPlugin,
|
|
10
|
+
ReactListPlugin,
|
|
11
|
+
ReactLiteXmlPlugin,
|
|
12
|
+
ReactMathPlugin,
|
|
13
|
+
ReactTablePlugin,
|
|
14
|
+
ReactToolbarPlugin,
|
|
15
|
+
} from '@lobehub/editor';
|
|
16
|
+
import { Editor, useEditorState } from '@lobehub/editor/react';
|
|
17
|
+
import { memo, useEffect, useMemo } from 'react';
|
|
18
|
+
import { useTranslation } from 'react-i18next';
|
|
19
|
+
|
|
20
|
+
import type { EditorCanvasProps } from './EditorCanvas';
|
|
21
|
+
import InlineToolbar from './InlineToolbar';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Base plugins for the editor (without toolbar)
|
|
25
|
+
*/
|
|
26
|
+
const BASE_PLUGINS = [
|
|
27
|
+
ReactLiteXmlPlugin,
|
|
28
|
+
ReactListPlugin,
|
|
29
|
+
ReactCodePlugin,
|
|
30
|
+
ReactCodemirrorPlugin,
|
|
31
|
+
ReactHRPlugin,
|
|
32
|
+
ReactLinkPlugin,
|
|
33
|
+
ReactTablePlugin,
|
|
34
|
+
ReactMathPlugin,
|
|
35
|
+
Editor.withProps(ReactImagePlugin, {
|
|
36
|
+
defaultBlockImage: true,
|
|
37
|
+
}),
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export interface InternalEditorProps extends EditorCanvasProps {
|
|
41
|
+
/**
|
|
42
|
+
* Editor instance (required)
|
|
43
|
+
*/
|
|
44
|
+
editor: IEditor;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Internal EditorCanvas component that requires editor instance
|
|
49
|
+
*/
|
|
50
|
+
const InternalEditor = memo<InternalEditorProps>(
|
|
51
|
+
({
|
|
52
|
+
editor,
|
|
53
|
+
extraPlugins,
|
|
54
|
+
floatingToolbar = true,
|
|
55
|
+
onContentChange,
|
|
56
|
+
onInit,
|
|
57
|
+
placeholder,
|
|
58
|
+
plugins: customPlugins,
|
|
59
|
+
slashItems,
|
|
60
|
+
style,
|
|
61
|
+
toolbarExtraItems,
|
|
62
|
+
}) => {
|
|
63
|
+
const { t } = useTranslation('file');
|
|
64
|
+
const editorState = useEditorState(editor);
|
|
65
|
+
|
|
66
|
+
const finalPlaceholder = placeholder || t('pageEditor.editorPlaceholder');
|
|
67
|
+
|
|
68
|
+
// Build plugins array
|
|
69
|
+
const plugins = useMemo(() => {
|
|
70
|
+
// If custom plugins provided, use them directly
|
|
71
|
+
if (customPlugins) return customPlugins;
|
|
72
|
+
|
|
73
|
+
// Build base plugins with optional extra plugins prepended
|
|
74
|
+
const basePlugins = extraPlugins ? [...extraPlugins, ...BASE_PLUGINS] : BASE_PLUGINS;
|
|
75
|
+
|
|
76
|
+
// Add toolbar if enabled
|
|
77
|
+
if (floatingToolbar) {
|
|
78
|
+
return [
|
|
79
|
+
...basePlugins,
|
|
80
|
+
Editor.withProps(ReactToolbarPlugin, {
|
|
81
|
+
children: (
|
|
82
|
+
<InlineToolbar
|
|
83
|
+
editor={editor}
|
|
84
|
+
editorState={editorState}
|
|
85
|
+
extraItems={toolbarExtraItems}
|
|
86
|
+
floating
|
|
87
|
+
/>
|
|
88
|
+
),
|
|
89
|
+
}),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return basePlugins;
|
|
94
|
+
}, [customPlugins, editor, editorState, extraPlugins, floatingToolbar, toolbarExtraItems]);
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
// for easier debug, mount editor instance to window
|
|
98
|
+
if (editor) window.__editor = editor;
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
window.__editor = undefined;
|
|
102
|
+
};
|
|
103
|
+
}, [editor]);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div
|
|
107
|
+
onClick={(e) => {
|
|
108
|
+
e.stopPropagation();
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
<Editor
|
|
113
|
+
content={''}
|
|
114
|
+
editor={editor}
|
|
115
|
+
lineEmptyPlaceholder={finalPlaceholder}
|
|
116
|
+
onInit={onInit}
|
|
117
|
+
onTextChange={onContentChange}
|
|
118
|
+
placeholder={finalPlaceholder}
|
|
119
|
+
plugins={plugins}
|
|
120
|
+
slashOption={slashItems ? { items: slashItems } : undefined}
|
|
121
|
+
style={{
|
|
122
|
+
paddingBottom: 64,
|
|
123
|
+
...style,
|
|
124
|
+
}}
|
|
125
|
+
type={'text'}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
InternalEditor.displayName = 'InternalEditor';
|
|
133
|
+
|
|
134
|
+
export default InternalEditor;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opens a native file selector dialog
|
|
3
|
+
* @param handleFiles - Callback function to handle selected files
|
|
4
|
+
* @param accept - MIME type filter for accepted files (default: all files)
|
|
5
|
+
*/
|
|
1
6
|
export function openFileSelector(handleFiles: (files: FileList) => void, accept = '*/*') {
|
|
2
7
|
// Skip on server side
|
|
3
8
|
if (typeof document === 'undefined') {
|
|
@@ -7,19 +12,16 @@ export function openFileSelector(handleFiles: (files: FileList) => void, accept
|
|
|
7
12
|
// Create a hidden input element
|
|
8
13
|
const input = document.createElement('input');
|
|
9
14
|
input.type = 'file';
|
|
10
|
-
input.accept = accept;
|
|
11
|
-
input.multiple = false;
|
|
15
|
+
input.accept = accept;
|
|
16
|
+
input.multiple = false;
|
|
12
17
|
|
|
13
18
|
// Listen for file selection events
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// @ts-expect-error not error
|
|
17
|
-
const files = event.target?.files;
|
|
19
|
+
input.addEventListener('change', (event) => {
|
|
20
|
+
const files = (event.target as HTMLInputElement)?.files;
|
|
18
21
|
if (files && files.length > 0) {
|
|
19
|
-
// Handle selected files
|
|
20
22
|
handleFiles(files);
|
|
21
23
|
}
|
|
22
|
-
};
|
|
24
|
+
});
|
|
23
25
|
|
|
24
26
|
// Trigger file selector
|
|
25
27
|
input.click();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { openFileSelector } from './actions';
|
|
2
|
+
export { default as AutoSaveHint, type AutoSaveHintProps } from './AutoSaveHint';
|
|
3
|
+
export {
|
|
4
|
+
EditorCanvas,
|
|
5
|
+
type EditorCanvasProps,
|
|
6
|
+
type EditorCanvasWithEditorProps,
|
|
7
|
+
} from './EditorCanvas';
|
|
8
|
+
export { EditorErrorBoundary } from './ErrorBoundary';
|
|
9
|
+
export { default as InlineToolbar, type InlineToolbarProps } from './InlineToolbar';
|
|
@@ -1,76 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
ReactCodePlugin,
|
|
5
|
-
ReactCodemirrorPlugin,
|
|
6
|
-
ReactHRPlugin,
|
|
7
|
-
ReactImagePlugin,
|
|
8
|
-
ReactLinkPlugin,
|
|
9
|
-
ReactListPlugin,
|
|
10
|
-
ReactLiteXmlPlugin,
|
|
11
|
-
ReactMathPlugin,
|
|
12
|
-
ReactTablePlugin,
|
|
13
|
-
ReactToolbarPlugin,
|
|
14
|
-
} from '@lobehub/editor';
|
|
15
|
-
import { Editor } from '@lobehub/editor/react';
|
|
16
|
-
import { Alert } from '@lobehub/ui';
|
|
17
|
-
import { type CSSProperties, Component, type ErrorInfo, type ReactNode, memo } from 'react';
|
|
3
|
+
import { type CSSProperties, memo } from 'react';
|
|
18
4
|
import { useTranslation } from 'react-i18next';
|
|
19
5
|
|
|
6
|
+
import { EditorCanvas as SharedEditorCanvas } from '@/features/EditorCanvas';
|
|
7
|
+
|
|
20
8
|
import { usePageEditorStore } from '../store';
|
|
21
|
-
import
|
|
9
|
+
import { useAskCopilotItem } from './useAskCopilotItem';
|
|
22
10
|
import { useSlashItems } from './useSlashItems';
|
|
23
11
|
|
|
24
|
-
interface EditorErrorBoundaryState {
|
|
25
|
-
error: Error | null;
|
|
26
|
-
hasError: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* ErrorBoundary for EditorCanvas component.
|
|
31
|
-
* Catches rendering errors in the editor and displays a fallback error UI
|
|
32
|
-
* instead of crashing the entire page.
|
|
33
|
-
*/
|
|
34
|
-
class EditorErrorBoundary extends Component<{ children: ReactNode }, EditorErrorBoundaryState> {
|
|
35
|
-
public state: EditorErrorBoundaryState = {
|
|
36
|
-
error: null,
|
|
37
|
-
hasError: false,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
public static getDerivedStateFromError(error: Error): Partial<EditorErrorBoundaryState> {
|
|
41
|
-
return { error, hasError: true };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
|
|
45
|
-
console.error('[EditorErrorBoundary] Caught error in editor render:', {
|
|
46
|
-
componentStack: errorInfo.componentStack,
|
|
47
|
-
error: error.message,
|
|
48
|
-
stack: error.stack,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
public render() {
|
|
53
|
-
if (this.state.hasError) {
|
|
54
|
-
return (
|
|
55
|
-
<Alert
|
|
56
|
-
message={this.state.error?.message || 'An unknown error occurred in the editor'}
|
|
57
|
-
showIcon
|
|
58
|
-
style={{
|
|
59
|
-
margin: 16,
|
|
60
|
-
overflow: 'hidden',
|
|
61
|
-
position: 'relative',
|
|
62
|
-
width: '100%',
|
|
63
|
-
}}
|
|
64
|
-
title="Editor Error"
|
|
65
|
-
type="error"
|
|
66
|
-
/>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return this.props.children;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
12
|
interface EditorCanvasProps {
|
|
75
13
|
placeholder?: string;
|
|
76
14
|
style?: CSSProperties;
|
|
@@ -80,55 +18,20 @@ const EditorCanvas = memo<EditorCanvasProps>(({ placeholder, style }) => {
|
|
|
80
18
|
const { t } = useTranslation(['file', 'editor']);
|
|
81
19
|
|
|
82
20
|
const editor = usePageEditorStore((s) => s.editor);
|
|
83
|
-
const
|
|
84
|
-
const onEditorInit = usePageEditorStore((s) => s.onEditorInit);
|
|
21
|
+
const documentId = usePageEditorStore((s) => s.documentId);
|
|
85
22
|
|
|
86
23
|
const slashItems = useSlashItems(editor);
|
|
87
|
-
|
|
88
|
-
if (!editor) return null;
|
|
24
|
+
const askCopilotItem = useAskCopilotItem(editor);
|
|
89
25
|
|
|
90
26
|
return (
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
content={''}
|
|
100
|
-
editor={editor!}
|
|
101
|
-
lineEmptyPlaceholder={placeholder || t('pageEditor.editorPlaceholder')}
|
|
102
|
-
onInit={onEditorInit}
|
|
103
|
-
onTextChange={handleContentChange}
|
|
104
|
-
placeholder={placeholder || t('pageEditor.editorPlaceholder')}
|
|
105
|
-
plugins={[
|
|
106
|
-
ReactLiteXmlPlugin,
|
|
107
|
-
ReactListPlugin,
|
|
108
|
-
ReactCodePlugin,
|
|
109
|
-
ReactCodemirrorPlugin,
|
|
110
|
-
ReactHRPlugin,
|
|
111
|
-
ReactLinkPlugin,
|
|
112
|
-
ReactTablePlugin,
|
|
113
|
-
ReactMathPlugin,
|
|
114
|
-
Editor.withProps(ReactImagePlugin, {
|
|
115
|
-
defaultBlockImage: true,
|
|
116
|
-
}),
|
|
117
|
-
Editor.withProps(ReactToolbarPlugin, {
|
|
118
|
-
children: <InlineToolbar floating />,
|
|
119
|
-
}),
|
|
120
|
-
]}
|
|
121
|
-
slashOption={{
|
|
122
|
-
items: slashItems,
|
|
123
|
-
}}
|
|
124
|
-
style={{
|
|
125
|
-
paddingBottom: 64,
|
|
126
|
-
...style,
|
|
127
|
-
}}
|
|
128
|
-
type={'text'}
|
|
129
|
-
/>
|
|
130
|
-
</div>
|
|
131
|
-
</EditorErrorBoundary>
|
|
27
|
+
<SharedEditorCanvas
|
|
28
|
+
documentId={documentId}
|
|
29
|
+
editor={editor}
|
|
30
|
+
placeholder={placeholder || t('pageEditor.editorPlaceholder')}
|
|
31
|
+
slashItems={slashItems}
|
|
32
|
+
style={style}
|
|
33
|
+
toolbarExtraItems={askCopilotItem}
|
|
34
|
+
/>
|
|
132
35
|
);
|
|
133
36
|
});
|
|
134
37
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { nanoid } from '@lobechat/utils';
|
|
4
|
+
import { HIDE_TOOLBAR_COMMAND, type IEditor } from '@lobehub/editor';
|
|
5
|
+
import { type ChatInputActionsProps } from '@lobehub/editor/react';
|
|
6
|
+
import { Block } from '@lobehub/ui';
|
|
7
|
+
import { createStaticStyles, cssVar } from 'antd-style';
|
|
8
|
+
import { BotIcon } from 'lucide-react';
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
|
|
11
|
+
import { useFileStore } from '@/store/file';
|
|
12
|
+
import { useGlobalStore } from '@/store/global';
|
|
13
|
+
|
|
14
|
+
const styles = createStaticStyles(({ css }) => ({
|
|
15
|
+
askCopilot: css`
|
|
16
|
+
border-radius: 6px;
|
|
17
|
+
color: ${cssVar.colorTextDescription};
|
|
18
|
+
|
|
19
|
+
&:hover {
|
|
20
|
+
color: ${cssVar.colorTextSecondary};
|
|
21
|
+
}
|
|
22
|
+
`,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
export const useAskCopilotItem = (editor: IEditor | undefined): ChatInputActionsProps['items'] => {
|
|
26
|
+
const addSelectionContext = useFileStore((s) => s.addChatContextSelection);
|
|
27
|
+
|
|
28
|
+
return useMemo(() => {
|
|
29
|
+
if (!editor) return [];
|
|
30
|
+
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
children: (
|
|
34
|
+
<Block
|
|
35
|
+
align="center"
|
|
36
|
+
className={styles.askCopilot}
|
|
37
|
+
clickable
|
|
38
|
+
gap={8}
|
|
39
|
+
horizontal
|
|
40
|
+
onClick={() => {
|
|
41
|
+
const xml = (editor.getSelectionDocument?.('litexml') as string) || '';
|
|
42
|
+
const plainText = (editor.getSelectionDocument?.('text') as string) || '';
|
|
43
|
+
const content = xml.trim() || plainText.trim();
|
|
44
|
+
|
|
45
|
+
if (!content) return;
|
|
46
|
+
|
|
47
|
+
const format = xml.trim() ? 'xml' : 'text';
|
|
48
|
+
const preview =
|
|
49
|
+
(plainText || xml)
|
|
50
|
+
.replaceAll(/<[^>]*>/g, ' ')
|
|
51
|
+
.replaceAll(/\s+/g, ' ')
|
|
52
|
+
.trim() || undefined;
|
|
53
|
+
|
|
54
|
+
// Store action handles deduplication
|
|
55
|
+
addSelectionContext({
|
|
56
|
+
content,
|
|
57
|
+
format,
|
|
58
|
+
id: `selection-${nanoid(6)}`,
|
|
59
|
+
preview,
|
|
60
|
+
title: 'Selection',
|
|
61
|
+
type: 'text',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Open right panel if not opened
|
|
65
|
+
useGlobalStore.getState().toggleRightPanel(true);
|
|
66
|
+
|
|
67
|
+
// Focus on chat input after a short delay to ensure panel is opened
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
// Find the chat input editor within the right panel
|
|
70
|
+
// Query all lexical editors and get the last one (which should be the chat input)
|
|
71
|
+
const allEditors = [...document.querySelectorAll('[data-lexical-editor="true"]')];
|
|
72
|
+
const chatInputEditor = allEditors.at(-1) as HTMLElement;
|
|
73
|
+
if (chatInputEditor) {
|
|
74
|
+
chatInputEditor.focus();
|
|
75
|
+
}
|
|
76
|
+
}, 300);
|
|
77
|
+
|
|
78
|
+
editor.dispatchCommand(HIDE_TOOLBAR_COMMAND, undefined);
|
|
79
|
+
editor.blur();
|
|
80
|
+
}}
|
|
81
|
+
paddingBlock={6}
|
|
82
|
+
paddingInline={12}
|
|
83
|
+
variant="borderless"
|
|
84
|
+
>
|
|
85
|
+
<BotIcon />
|
|
86
|
+
<span>Ask Copilot</span>
|
|
87
|
+
</Block>
|
|
88
|
+
),
|
|
89
|
+
key: 'ask-copilot',
|
|
90
|
+
label: 'Ask Copilot',
|
|
91
|
+
onClick: () => {},
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
}, [addSelectionContext, editor]);
|
|
95
|
+
};
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import { useMemo } from 'react';
|
|
29
29
|
import { useTranslation } from 'react-i18next';
|
|
30
30
|
|
|
31
|
-
import { openFileSelector } from '@/features/
|
|
31
|
+
import { openFileSelector } from '@/features/EditorCanvas';
|
|
32
32
|
import { useFileStore } from '@/store/file';
|
|
33
33
|
|
|
34
34
|
export const useSlashItems = (editor: IEditor | undefined): SlashOptions['items'] => {
|
|
@@ -40,7 +40,7 @@ interface FolderCrumb {
|
|
|
40
40
|
const Breadcrumb = memo(() => {
|
|
41
41
|
const { t } = useTranslation('file');
|
|
42
42
|
|
|
43
|
-
const
|
|
43
|
+
const title = usePageEditorStore((s) => s.title);
|
|
44
44
|
const knowledgeBaseId = usePageEditorStore((s) => s.knowledgeBaseId);
|
|
45
45
|
const parentId = usePageEditorStore((s) => s.parentId);
|
|
46
46
|
|
|
@@ -61,7 +61,7 @@ const Breadcrumb = memo(() => {
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const documentTitle =
|
|
64
|
+
const documentTitle = title || t('pageEditor.titlePlaceholder');
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
67
|
<Flexbox align={'center'} className={styles.breadcrumb} flex={1} gap={0} horizontal>
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { ActionIcon, Avatar, Dropdown,
|
|
3
|
+
import { ActionIcon, Avatar, Dropdown, Text } from '@lobehub/ui';
|
|
4
4
|
import { ArrowLeftIcon, BotMessageSquareIcon, MoreHorizontal } from 'lucide-react';
|
|
5
5
|
import { memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
9
|
+
import { AutoSaveHint } from '@/features/EditorCanvas';
|
|
9
10
|
import NavHeader from '@/features/NavHeader';
|
|
10
11
|
import ToggleRightPanelButton from '@/features/RightPanel/ToggleRightPanelButton';
|
|
11
12
|
|
|
12
13
|
import { usePageEditorStore } from '../store';
|
|
13
|
-
import AutoSaveHint from './AutoSaveHint';
|
|
14
14
|
import Breadcrumb from './Breadcrumb';
|
|
15
15
|
import { useMenu } from './useMenu';
|
|
16
16
|
|
|
17
17
|
const Header = memo(() => {
|
|
18
18
|
const { t } = useTranslation('file');
|
|
19
|
-
const [
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const [documentId, emoji, title, parentId, onBack] = usePageEditorStore((s) => [
|
|
20
|
+
s.documentId,
|
|
21
|
+
s.emoji,
|
|
22
|
+
s.title,
|
|
23
|
+
s.parentId,
|
|
24
|
+
s.onBack,
|
|
25
|
+
]);
|
|
22
26
|
const { menuItems } = useMenu();
|
|
23
27
|
|
|
24
28
|
return (
|
|
@@ -32,22 +36,15 @@ const Header = memo(() => {
|
|
|
32
36
|
{!parentId && (
|
|
33
37
|
<>
|
|
34
38
|
{/* Icon */}
|
|
35
|
-
{
|
|
39
|
+
{emoji && <Avatar avatar={emoji} shape={'square'} size={28} />}
|
|
36
40
|
{/* Title */}
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
style={{ height: 20, marginLeft: 4, maxWidth: 200, width: 200 }}
|
|
41
|
-
/>
|
|
42
|
-
) : (
|
|
43
|
-
<Text ellipsis style={{ marginLeft: 4 }} weight={500}>
|
|
44
|
-
{currentTitle || t('pageEditor.titlePlaceholder')}
|
|
45
|
-
</Text>
|
|
46
|
-
)}
|
|
41
|
+
<Text ellipsis style={{ marginLeft: 4 }} weight={500}>
|
|
42
|
+
{title || t('pageEditor.titlePlaceholder')}
|
|
43
|
+
</Text>
|
|
47
44
|
</>
|
|
48
45
|
)}
|
|
49
|
-
{/* Auto Save Status
|
|
50
|
-
{
|
|
46
|
+
{/* Auto Save Status */}
|
|
47
|
+
{documentId && <AutoSaveHint documentId={documentId} style={{ marginLeft: 6 }} />}
|
|
51
48
|
</>
|
|
52
49
|
}
|
|
53
50
|
right={
|
|
@@ -6,6 +6,8 @@ import { CopyPlus, Download, Link2, Trash2 } from 'lucide-react';
|
|
|
6
6
|
import { useMemo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
|
|
9
|
+
import { useDocumentStore } from '@/store/document';
|
|
10
|
+
import { editorSelectors } from '@/store/document/slices/editor';
|
|
9
11
|
import { useFileStore } from '@/store/file';
|
|
10
12
|
import { useGlobalStore } from '@/store/global';
|
|
11
13
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
@@ -21,9 +23,12 @@ export const useMenu = (): { menuItems: any[] } => {
|
|
|
21
23
|
const storeApi = useStoreApi();
|
|
22
24
|
const { lg = true } = useResponsive();
|
|
23
25
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const documentId = usePageEditorStore((s) => s.documentId);
|
|
27
|
+
|
|
28
|
+
// Get lastUpdatedTime from DocumentStore
|
|
29
|
+
const lastUpdatedTime = useDocumentStore((s) =>
|
|
30
|
+
documentId ? editorSelectors.lastUpdatedTime(documentId)(s) : null,
|
|
31
|
+
);
|
|
27
32
|
|
|
28
33
|
const duplicateDocument = useFileStore((s) => s.duplicateDocument);
|
|
29
34
|
|
|
@@ -36,9 +41,9 @@ export const useMenu = (): { menuItems: any[] } => {
|
|
|
36
41
|
const showViewModeSwitch = lg;
|
|
37
42
|
|
|
38
43
|
const handleDuplicate = async () => {
|
|
39
|
-
if (!
|
|
44
|
+
if (!documentId) return;
|
|
40
45
|
try {
|
|
41
|
-
await duplicateDocument(
|
|
46
|
+
await duplicateDocument(documentId);
|
|
42
47
|
message.success(t('pageEditor.duplicateSuccess'));
|
|
43
48
|
} catch (error) {
|
|
44
49
|
console.error('Failed to duplicate page:', error);
|
|
@@ -48,7 +53,7 @@ export const useMenu = (): { menuItems: any[] } => {
|
|
|
48
53
|
|
|
49
54
|
const handleExportMarkdown = () => {
|
|
50
55
|
const state = storeApi.getState();
|
|
51
|
-
const { editor,
|
|
56
|
+
const { editor, title } = state;
|
|
52
57
|
|
|
53
58
|
if (!editor) return;
|
|
54
59
|
|
|
@@ -58,7 +63,7 @@ export const useMenu = (): { menuItems: any[] } => {
|
|
|
58
63
|
const url = URL.createObjectURL(blob);
|
|
59
64
|
const a = document.createElement('a');
|
|
60
65
|
a.href = url;
|
|
61
|
-
a.download = `${
|
|
66
|
+
a.download = `${title || 'Untitled'}.md`;
|
|
62
67
|
document.body.append(a);
|
|
63
68
|
a.click();
|
|
64
69
|
a.remove();
|
|
@@ -143,7 +148,6 @@ export const useMenu = (): { menuItems: any[] } => {
|
|
|
143
148
|
key: 'page-info',
|
|
144
149
|
label: (
|
|
145
150
|
<div style={{ color: cssVar.colorTextTertiary, fontSize: 12, lineHeight: 1.6 }}>
|
|
146
|
-
<div>{t('pageEditor.wordCount', { wordCount })}</div>
|
|
147
151
|
<div>
|
|
148
152
|
{lastUpdatedTime
|
|
149
153
|
? t('pageEditor.editedAt', {
|
|
@@ -156,7 +160,6 @@ export const useMenu = (): { menuItems: any[] } => {
|
|
|
156
160
|
},
|
|
157
161
|
],
|
|
158
162
|
[
|
|
159
|
-
wordCount,
|
|
160
163
|
lastUpdatedTime,
|
|
161
164
|
storeApi,
|
|
162
165
|
t,
|