@lobehub/lobehub 2.0.0-next.255 → 2.0.0-next.257
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/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/Copilot/index.tsx +16 -1
- 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/features/RightPanel/index.tsx +11 -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/global/initialState.ts +2 -0
- package/src/store/global/selectors/systemStatus.ts +2 -0
- 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,206 @@
|
|
|
1
|
+
import type { SWRResponse } from 'swr';
|
|
2
|
+
import { type StateCreator } from 'zustand/vanilla';
|
|
3
|
+
|
|
4
|
+
import { useClientDataSWRWithSync } from '@/libs/swr';
|
|
5
|
+
import { documentService } from '@/services/document';
|
|
6
|
+
import { useGlobalStore } from '@/store/global';
|
|
7
|
+
import { type LobeDocument } from '@/types/document';
|
|
8
|
+
import { setNamespace } from '@/utils/storeDebug';
|
|
9
|
+
|
|
10
|
+
import { type PageQueryFilter } from '../../initialState';
|
|
11
|
+
import { type PageStore } from '../../store';
|
|
12
|
+
|
|
13
|
+
const n = setNamespace('page/list');
|
|
14
|
+
|
|
15
|
+
const ALLOWED_PAGE_SOURCE_TYPES = new Set(['editor', 'file', 'api']);
|
|
16
|
+
const ALLOWED_PAGE_FILE_TYPES = new Set(['custom/document', 'application/pdf']);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a page should be displayed in the page list
|
|
20
|
+
*/
|
|
21
|
+
const isAllowedPage = (page: { fileType: string; sourceType: string }) => {
|
|
22
|
+
return (
|
|
23
|
+
ALLOWED_PAGE_SOURCE_TYPES.has(page.sourceType) && ALLOWED_PAGE_FILE_TYPES.has(page.fileType)
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export interface ListAction {
|
|
28
|
+
/**
|
|
29
|
+
* Fetch documents from the server with pagination
|
|
30
|
+
*/
|
|
31
|
+
fetchDocuments: () => Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Load more documents (next page)
|
|
34
|
+
*/
|
|
35
|
+
loadMoreDocuments: () => Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Refresh document list (re-fetch from server)
|
|
38
|
+
*/
|
|
39
|
+
refreshDocuments: () => Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Set search keywords
|
|
42
|
+
*/
|
|
43
|
+
setSearchKeywords: (keywords: string) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Toggle filter to show only pages not in any library
|
|
46
|
+
*/
|
|
47
|
+
setShowOnlyPagesNotInLibrary: (show: boolean) => void;
|
|
48
|
+
/**
|
|
49
|
+
* SWR hook to fetch documents list with caching and auto-sync to store
|
|
50
|
+
*/
|
|
51
|
+
useFetchDocuments: () => SWRResponse<LobeDocument[]>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const createListSlice: StateCreator<
|
|
55
|
+
PageStore,
|
|
56
|
+
[['zustand/devtools', never]],
|
|
57
|
+
[],
|
|
58
|
+
ListAction
|
|
59
|
+
> = (set, get) => ({
|
|
60
|
+
fetchDocuments: async () => {
|
|
61
|
+
try {
|
|
62
|
+
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
63
|
+
const queryFilters: PageQueryFilter = {
|
|
64
|
+
fileTypes: Array.from(ALLOWED_PAGE_FILE_TYPES),
|
|
65
|
+
sourceTypes: Array.from(ALLOWED_PAGE_SOURCE_TYPES),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = await documentService.queryDocuments({
|
|
69
|
+
current: 0,
|
|
70
|
+
pageSize,
|
|
71
|
+
...queryFilters,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const documents = result.items.filter(isAllowedPage).map((doc) => ({
|
|
75
|
+
...doc,
|
|
76
|
+
filename: doc.filename ?? doc.title ?? 'Untitled',
|
|
77
|
+
})) as LobeDocument[];
|
|
78
|
+
|
|
79
|
+
const hasMore = result.items.length >= pageSize;
|
|
80
|
+
|
|
81
|
+
// Use internal dispatch to set documents
|
|
82
|
+
get().internal_dispatchDocuments({ documents, type: 'setDocuments' });
|
|
83
|
+
|
|
84
|
+
set(
|
|
85
|
+
{
|
|
86
|
+
currentPage: 0,
|
|
87
|
+
documentsTotal: result.total,
|
|
88
|
+
hasMoreDocuments: hasMore,
|
|
89
|
+
queryFilter: queryFilters,
|
|
90
|
+
},
|
|
91
|
+
false,
|
|
92
|
+
n('fetchDocuments/success'),
|
|
93
|
+
);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('Failed to fetch documents:', error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
loadMoreDocuments: async () => {
|
|
101
|
+
const { currentPage, isLoadingMoreDocuments, hasMoreDocuments, queryFilter, documents } = get();
|
|
102
|
+
|
|
103
|
+
if (isLoadingMoreDocuments || !hasMoreDocuments || !documents) return;
|
|
104
|
+
|
|
105
|
+
const nextPage = currentPage + 1;
|
|
106
|
+
|
|
107
|
+
set({ isLoadingMoreDocuments: true }, false, n('loadMoreDocuments/start'));
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
111
|
+
const queryParams = queryFilter
|
|
112
|
+
? { current: nextPage, pageSize, ...queryFilter }
|
|
113
|
+
: { current: nextPage, pageSize };
|
|
114
|
+
|
|
115
|
+
const result = await documentService.queryDocuments(queryParams);
|
|
116
|
+
|
|
117
|
+
const newDocuments = result.items.filter(isAllowedPage).map((doc) => ({
|
|
118
|
+
...doc,
|
|
119
|
+
filename: doc.filename ?? doc.title ?? 'Untitled',
|
|
120
|
+
})) as LobeDocument[];
|
|
121
|
+
|
|
122
|
+
const hasMore = result.items.length >= pageSize;
|
|
123
|
+
|
|
124
|
+
// Use internal dispatch to append documents
|
|
125
|
+
get().internal_dispatchDocuments({ documents: newDocuments, type: 'appendDocuments' });
|
|
126
|
+
|
|
127
|
+
set(
|
|
128
|
+
{
|
|
129
|
+
currentPage: nextPage,
|
|
130
|
+
documentsTotal: result.total,
|
|
131
|
+
hasMoreDocuments: hasMore,
|
|
132
|
+
isLoadingMoreDocuments: false,
|
|
133
|
+
},
|
|
134
|
+
false,
|
|
135
|
+
n('loadMoreDocuments/success'),
|
|
136
|
+
);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error('Failed to load more documents:', error);
|
|
139
|
+
set({ isLoadingMoreDocuments: false }, false, n('loadMoreDocuments/error'));
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
refreshDocuments: async () => {
|
|
144
|
+
await get().fetchDocuments();
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
setSearchKeywords: (keywords: string) => {
|
|
148
|
+
set({ searchKeywords: keywords }, false, n('setSearchKeywords'));
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
setShowOnlyPagesNotInLibrary: (show: boolean) => {
|
|
152
|
+
set({ showOnlyPagesNotInLibrary: show }, false, n('setShowOnlyPagesNotInLibrary'));
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
useFetchDocuments: () => {
|
|
156
|
+
return useClientDataSWRWithSync<LobeDocument[]>(
|
|
157
|
+
['pageDocuments'],
|
|
158
|
+
async () => {
|
|
159
|
+
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
160
|
+
const queryFilters: PageQueryFilter = {
|
|
161
|
+
fileTypes: Array.from(ALLOWED_PAGE_FILE_TYPES),
|
|
162
|
+
sourceTypes: Array.from(ALLOWED_PAGE_SOURCE_TYPES),
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const result = await documentService.queryDocuments({
|
|
166
|
+
current: 0,
|
|
167
|
+
pageSize,
|
|
168
|
+
...queryFilters,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const documents = result.items.filter(isAllowedPage).map((doc) => ({
|
|
172
|
+
...doc,
|
|
173
|
+
filename: doc.filename ?? doc.title ?? 'Untitled',
|
|
174
|
+
})) as LobeDocument[];
|
|
175
|
+
|
|
176
|
+
return documents;
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
onData: (documents) => {
|
|
180
|
+
if (!documents) return;
|
|
181
|
+
|
|
182
|
+
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
183
|
+
const hasMore = documents.length >= pageSize;
|
|
184
|
+
|
|
185
|
+
// Use internal dispatch to set documents
|
|
186
|
+
get().internal_dispatchDocuments({ documents, type: 'setDocuments' });
|
|
187
|
+
|
|
188
|
+
set(
|
|
189
|
+
{
|
|
190
|
+
currentPage: 0,
|
|
191
|
+
documentsTotal: documents.length,
|
|
192
|
+
hasMoreDocuments: hasMore,
|
|
193
|
+
queryFilter: {
|
|
194
|
+
fileTypes: Array.from(ALLOWED_PAGE_FILE_TYPES),
|
|
195
|
+
sourceTypes: Array.from(ALLOWED_PAGE_SOURCE_TYPES),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
false,
|
|
199
|
+
n('useFetchDocuments/onData'),
|
|
200
|
+
);
|
|
201
|
+
},
|
|
202
|
+
revalidateOnFocus: true,
|
|
203
|
+
},
|
|
204
|
+
);
|
|
205
|
+
},
|
|
206
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type LobeDocument } from '@/types/document';
|
|
2
|
+
|
|
3
|
+
import { type PageQueryFilter } from '../../initialState';
|
|
4
|
+
|
|
5
|
+
export interface ListState {
|
|
6
|
+
currentPage: number;
|
|
7
|
+
documents: LobeDocument[];
|
|
8
|
+
documentsTotal: number;
|
|
9
|
+
hasMoreDocuments: boolean;
|
|
10
|
+
isDocumentListLoading: boolean;
|
|
11
|
+
isLoadingMoreDocuments: boolean;
|
|
12
|
+
localPageMap: Map<string, LobeDocument>;
|
|
13
|
+
queryFilter?: PageQueryFilter;
|
|
14
|
+
searchKeywords: string;
|
|
15
|
+
showOnlyPagesNotInLibrary: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const initialListState: ListState = {
|
|
19
|
+
currentPage: 0,
|
|
20
|
+
documents: [],
|
|
21
|
+
documentsTotal: 0,
|
|
22
|
+
hasMoreDocuments: false,
|
|
23
|
+
isDocumentListLoading: false,
|
|
24
|
+
isLoadingMoreDocuments: false,
|
|
25
|
+
localPageMap: new Map(),
|
|
26
|
+
queryFilter: undefined,
|
|
27
|
+
searchKeywords: '',
|
|
28
|
+
showOnlyPagesNotInLibrary: false,
|
|
29
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { useGlobalStore } from '@/store/global';
|
|
2
|
+
import { type LobeDocument } from '@/types/document';
|
|
3
|
+
|
|
4
|
+
import { type PageState } from '../../initialState';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if documents are still loading (undefined means not yet loaded)
|
|
8
|
+
*/
|
|
9
|
+
const isDocumentsLoading = (s: PageState): boolean => s.documents === undefined;
|
|
10
|
+
|
|
11
|
+
const getFilteredDocuments = (s: PageState): LobeDocument[] => {
|
|
12
|
+
const docs = s.documents ?? [];
|
|
13
|
+
|
|
14
|
+
const { searchKeywords, showOnlyPagesNotInLibrary } = s;
|
|
15
|
+
|
|
16
|
+
let result = docs;
|
|
17
|
+
|
|
18
|
+
// Filter out documents with sourceType='file'
|
|
19
|
+
result = result.filter((doc: LobeDocument) => doc.sourceType !== 'file');
|
|
20
|
+
|
|
21
|
+
// Filter by library membership
|
|
22
|
+
if (showOnlyPagesNotInLibrary) {
|
|
23
|
+
result = result.filter((doc: LobeDocument) => {
|
|
24
|
+
// Show only pages that are NOT in any library
|
|
25
|
+
// Pages in a library have metadata.knowledgeBaseId set
|
|
26
|
+
return !doc.metadata?.knowledgeBaseId;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Filter by search keywords
|
|
31
|
+
if (searchKeywords.trim()) {
|
|
32
|
+
const lowerKeywords = searchKeywords.toLowerCase();
|
|
33
|
+
result = result.filter((doc: LobeDocument) => {
|
|
34
|
+
const content = doc.content?.toLowerCase() || '';
|
|
35
|
+
const title = doc.title?.toLowerCase() || '';
|
|
36
|
+
return content.includes(lowerKeywords) || title.includes(lowerKeywords);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Sort by creation date (newest first)
|
|
41
|
+
return result.sort((a: LobeDocument, b: LobeDocument) => {
|
|
42
|
+
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
43
|
+
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
44
|
+
return dateB - dateA;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Limited filtered documents for sidebar display
|
|
49
|
+
const getFilteredDocumentsLimited = (s: PageState): LobeDocument[] => {
|
|
50
|
+
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
51
|
+
const allDocs = getFilteredDocuments(s);
|
|
52
|
+
return allDocs.slice(0, pageSize);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const getDocumentById = (docId: string | undefined) => (s: PageState) => {
|
|
56
|
+
if (!docId) return undefined;
|
|
57
|
+
|
|
58
|
+
// Find in documents array
|
|
59
|
+
return s.documents?.find((doc) => doc.id === docId);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const hasMoreDocuments = (s: PageState): boolean => s.hasMoreDocuments;
|
|
63
|
+
|
|
64
|
+
const isLoadingMoreDocuments = (s: PageState): boolean => s.isLoadingMoreDocuments;
|
|
65
|
+
|
|
66
|
+
const documentsTotal = (s: PageState): number => s.documentsTotal;
|
|
67
|
+
|
|
68
|
+
// Check if filtered documents have more than displayed
|
|
69
|
+
const hasMoreFilteredDocuments = (s: PageState): boolean => {
|
|
70
|
+
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
71
|
+
const allDocs = getFilteredDocuments(s);
|
|
72
|
+
return allDocs.length > pageSize;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Get total count of filtered documents
|
|
76
|
+
const filteredDocumentsCount = (s: PageState): number => {
|
|
77
|
+
return getFilteredDocuments(s).length;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const listSelectors = {
|
|
81
|
+
documentsTotal,
|
|
82
|
+
filteredDocumentsCount,
|
|
83
|
+
getDocumentById,
|
|
84
|
+
getFilteredDocuments,
|
|
85
|
+
getFilteredDocumentsLimited,
|
|
86
|
+
hasMoreDocuments,
|
|
87
|
+
hasMoreFilteredDocuments,
|
|
88
|
+
isDocumentsLoading,
|
|
89
|
+
isLoadingMoreDocuments,
|
|
90
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type StateCreator } from 'zustand/vanilla';
|
|
2
|
+
|
|
3
|
+
import { setNamespace } from '@/utils/storeDebug';
|
|
4
|
+
|
|
5
|
+
import { type PageStore } from '../../store';
|
|
6
|
+
|
|
7
|
+
const n = setNamespace('page/selection');
|
|
8
|
+
|
|
9
|
+
export interface SelectionAction {
|
|
10
|
+
/**
|
|
11
|
+
* Close all pages drawer
|
|
12
|
+
*/
|
|
13
|
+
closeAllPagesDrawer: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Open all pages drawer
|
|
16
|
+
*/
|
|
17
|
+
openAllPagesDrawer: () => void;
|
|
18
|
+
/**
|
|
19
|
+
* Select a page (updates URL and state)
|
|
20
|
+
*/
|
|
21
|
+
selectPage: (pageId: string) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Set the ID of the page being renamed
|
|
24
|
+
*/
|
|
25
|
+
setRenamingPageId: (pageId: string | null) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Set selected page ID
|
|
28
|
+
*/
|
|
29
|
+
setSelectedPageId: (pageId: string | null, shouldNavigate?: boolean) => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const createSelectionSlice: StateCreator<
|
|
33
|
+
PageStore,
|
|
34
|
+
[['zustand/devtools', never]],
|
|
35
|
+
[],
|
|
36
|
+
SelectionAction
|
|
37
|
+
> = (set, get) => ({
|
|
38
|
+
closeAllPagesDrawer: () => {
|
|
39
|
+
set({ allPagesDrawerOpen: false }, false, n('closeAllPagesDrawer'));
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
openAllPagesDrawer: () => {
|
|
43
|
+
set({ allPagesDrawerOpen: true }, false, n('openAllPagesDrawer'));
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
selectPage: (pageId: string) => {
|
|
47
|
+
const { selectedPageId } = get();
|
|
48
|
+
|
|
49
|
+
// Don't allow deselecting the current page
|
|
50
|
+
if (selectedPageId === pageId) return;
|
|
51
|
+
|
|
52
|
+
// Select and navigate
|
|
53
|
+
set({ isCreatingNew: false, selectedPageId: pageId }, false, n('selectPage'));
|
|
54
|
+
get().navigateToPage(pageId);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
setRenamingPageId: (pageId: string | null) => {
|
|
58
|
+
set({ renamingPageId: pageId }, false, n('setRenamingPageId'));
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
setSelectedPageId: (pageId: string | null, shouldNavigate = true) => {
|
|
62
|
+
set({ selectedPageId: pageId }, false, n('setSelectedPageId'));
|
|
63
|
+
if (shouldNavigate) {
|
|
64
|
+
get().navigateToPage(pageId);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SelectionState {
|
|
2
|
+
allPagesDrawerOpen: boolean;
|
|
3
|
+
renamingPageId: string | null;
|
|
4
|
+
selectedPageId: string | null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const initialSelectionState: SelectionState = {
|
|
8
|
+
allPagesDrawerOpen: false,
|
|
9
|
+
renamingPageId: null,
|
|
10
|
+
selectedPageId: null,
|
|
11
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { shallow } from 'zustand/shallow';
|
|
2
|
+
import { createWithEqualityFn } from 'zustand/traditional';
|
|
3
|
+
import { type StateCreator } from 'zustand/vanilla';
|
|
4
|
+
|
|
5
|
+
import { createDevtools } from '../middleware/createDevtools';
|
|
6
|
+
import { type PageState, initialState } from './initialState';
|
|
7
|
+
import { type CrudAction, createCrudSlice } from './slices/crud';
|
|
8
|
+
import { type InternalAction, createInternalSlice } from './slices/internal';
|
|
9
|
+
import { type ListAction, createListSlice } from './slices/list';
|
|
10
|
+
import { type SelectionAction, createSelectionSlice } from './slices/selection';
|
|
11
|
+
|
|
12
|
+
// =============== Aggregate createStoreFn ============ //
|
|
13
|
+
|
|
14
|
+
export type PageStore = PageState & InternalAction & ListAction & SelectionAction & CrudAction;
|
|
15
|
+
|
|
16
|
+
const createStore: StateCreator<PageStore, [['zustand/devtools', never]]> = (...parameters) => ({
|
|
17
|
+
...initialState,
|
|
18
|
+
...createInternalSlice(...parameters),
|
|
19
|
+
...createListSlice(...parameters),
|
|
20
|
+
...createSelectionSlice(...parameters),
|
|
21
|
+
...createCrudSlice(...parameters),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// =============== Implement useStore ============ //
|
|
25
|
+
const devtools = createDevtools('page');
|
|
26
|
+
|
|
27
|
+
export const usePageStore = createWithEqualityFn<PageStore>()(devtools(createStore), shallow);
|
|
28
|
+
|
|
29
|
+
export const getPageStoreState = () => usePageStore.getState();
|
|
@@ -120,26 +120,16 @@ export const lobehubSkillStoreSelectors = {
|
|
|
120
120
|
*/
|
|
121
121
|
metaList: (s: ToolStoreState) => {
|
|
122
122
|
const servers = s.lobehubSkillServers || [];
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
return servers
|
|
124
125
|
.filter((server) => server.status === LobehubSkillStatus.CONNECTED)
|
|
125
|
-
.map((server) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
identifier: server.identifier,
|
|
135
|
-
meta: {
|
|
136
|
-
avatar: server.icon || '🔗',
|
|
137
|
-
description: `LobeHub Skill: ${server.name}`,
|
|
138
|
-
title: server.name,
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
});
|
|
142
|
-
console.log('[lobehubSkillStoreSelectors.metaList] result:', result);
|
|
143
|
-
return result;
|
|
126
|
+
.map((server) => ({
|
|
127
|
+
identifier: server.identifier,
|
|
128
|
+
meta: {
|
|
129
|
+
avatar: server.icon || '🔗',
|
|
130
|
+
description: `LobeHub Skill: ${server.name}`,
|
|
131
|
+
title: server.name,
|
|
132
|
+
},
|
|
133
|
+
}));
|
|
144
134
|
},
|
|
145
135
|
};
|
package/src/utils/identifier.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @returns The standardized identifier.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const standardizeIdentifier = (identifier: string, prefix?: 'docs' | 'agt') => {
|
|
7
|
+
export const standardizeIdentifier = (identifier: string, prefix?: 'docs' | 'agt') => {
|
|
8
8
|
if (identifier.includes('_')) {
|
|
9
9
|
return identifier.split('_')[1];
|
|
10
10
|
} else if (prefix) {
|
|
@@ -14,4 +14,10 @@ const standardizeIdentifier = (identifier: string, prefix?: 'docs' | 'agt') => {
|
|
|
14
14
|
return identifier;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export
|
|
17
|
+
export const getIdFromIdentifier = (identifier: string, prefix?: 'docs' | 'agt') => {
|
|
18
|
+
if (identifier.includes('_')) {
|
|
19
|
+
return identifier;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return `${prefix}_${identifier}`;
|
|
23
|
+
};
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { Flexbox, Skeleton } from '@lobehub/ui';
|
|
4
|
-
import { createStaticStyles } from 'antd-style';
|
|
5
|
-
import { memo, useEffect, useState } from 'react';
|
|
6
|
-
|
|
7
|
-
import EditorCanvas from '../EditorCanvas';
|
|
8
|
-
import { usePageEditorStore } from '../store';
|
|
9
|
-
import Title from './Title';
|
|
10
|
-
|
|
11
|
-
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
12
|
-
loadingOverlay: css`
|
|
13
|
-
position: absolute;
|
|
14
|
-
z-index: 10;
|
|
15
|
-
inset: 0;
|
|
16
|
-
|
|
17
|
-
padding: 24px;
|
|
18
|
-
|
|
19
|
-
background: ${cssVar.colorBgContainer};
|
|
20
|
-
`,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
const Body = memo(() => {
|
|
24
|
-
const isLoadingContent = usePageEditorStore((s) => s.isLoadingContent);
|
|
25
|
-
const [showSkeleton, setShowSkeleton] = useState(false);
|
|
26
|
-
|
|
27
|
-
// Only show skeleton if loading takes more than 1 second
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
// eslint-disable-next-line no-undef
|
|
30
|
-
let timer: NodeJS.Timeout;
|
|
31
|
-
|
|
32
|
-
if (isLoadingContent) {
|
|
33
|
-
timer = setTimeout(() => {
|
|
34
|
-
setShowSkeleton(true);
|
|
35
|
-
}, 1000);
|
|
36
|
-
} else {
|
|
37
|
-
setShowSkeleton(false);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return () => {
|
|
41
|
-
if (timer) clearTimeout(timer);
|
|
42
|
-
};
|
|
43
|
-
}, [isLoadingContent]);
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<Flexbox flex={1} style={{ overflowY: 'auto', position: 'relative' }}>
|
|
47
|
-
<Title />
|
|
48
|
-
<EditorCanvas />
|
|
49
|
-
{/* Show overlay immediately to hide old content */}
|
|
50
|
-
{isLoadingContent && (
|
|
51
|
-
<div className={styles.loadingOverlay}>
|
|
52
|
-
{/* Only show skeleton after 1 second */}
|
|
53
|
-
{showSkeleton && (
|
|
54
|
-
<Flexbox gap={16}>
|
|
55
|
-
<Skeleton.Title width="80%" />
|
|
56
|
-
<Skeleton.Title width="60%" />
|
|
57
|
-
<Skeleton.Title width="70%" />
|
|
58
|
-
<Skeleton.Title width="50%" />
|
|
59
|
-
<Skeleton.Title width="65%" />
|
|
60
|
-
</Flexbox>
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
63
|
-
)}
|
|
64
|
-
</Flexbox>
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
export default Body;
|