@lobehub/lobehub 2.0.0-next.267 → 2.0.0-next.269
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/.cursor/rules/microcopy-cn.mdc +75 -63
- package/.cursor/rules/microcopy-en.mdc +4 -8
- package/CHANGELOG.md +50 -0
- package/README.md +8 -8
- package/README.zh-CN.md +8 -8
- package/apps/desktop/src/main/core/browser/Browser.ts +6 -0
- package/apps/desktop/src/main/locales/default/common.ts +2 -2
- package/changelog/v1.json +10 -0
- package/docs/development/database-schema.dbml +4 -0
- package/e2e/CLAUDE.md +9 -8
- package/e2e/cucumber.config.js +1 -0
- package/e2e/src/features/page/README.md +118 -0
- package/e2e/src/features/page/crud.feature +62 -0
- package/e2e/src/features/page/editor-content.feature +93 -0
- package/e2e/src/features/page/editor-meta.feature +60 -0
- package/e2e/src/steps/agent/conversation.steps.ts +4 -4
- package/e2e/src/steps/home/sidebarAgent.steps.ts +91 -94
- package/e2e/src/steps/home/sidebarGroup.steps.ts +4 -4
- package/e2e/src/steps/hooks.ts +2 -0
- package/e2e/src/steps/page/editor-content.steps.ts +344 -0
- package/e2e/src/steps/page/editor-meta.steps.ts +410 -0
- package/e2e/src/steps/page/page-crud.steps.ts +363 -0
- package/e2e/src/support/world.ts +12 -0
- package/locales/ar/file.json +2 -0
- package/locales/bg-BG/file.json +2 -0
- package/locales/de-DE/file.json +2 -0
- package/locales/en-US/auth.json +1 -1
- package/locales/en-US/file.json +2 -0
- package/locales/en-US/metadata.json +2 -2
- package/locales/es-ES/file.json +2 -0
- package/locales/fa-IR/file.json +2 -0
- package/locales/fr-FR/file.json +2 -0
- package/locales/it-IT/file.json +2 -0
- package/locales/ja-JP/file.json +2 -0
- package/locales/ko-KR/file.json +2 -0
- package/locales/nl-NL/file.json +2 -0
- package/locales/pl-PL/file.json +2 -0
- package/locales/pt-BR/file.json +2 -0
- package/locales/ru-RU/file.json +2 -0
- package/locales/tr-TR/file.json +2 -0
- package/locales/vi-VN/file.json +2 -0
- package/locales/zh-CN/file.json +2 -0
- package/locales/zh-TW/file.json +2 -0
- package/package.json +1 -1
- package/packages/builtin-agents/src/agents/agent-builder/index.ts +1 -1
- package/packages/builtin-agents/src/agents/group-agent-builder/index.ts +1 -1
- package/packages/builtin-agents/src/agents/page-agent/index.ts +1 -1
- package/packages/const/src/settings/group.ts +0 -10
- package/packages/database/migrations/0068_update_group_data.sql +4 -0
- package/packages/database/migrations/meta/0068_snapshot.json +9588 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/models/__tests__/chatGroup.test.ts +5 -7
- package/packages/database/src/models/__tests__/knowledgeBase.test.ts +185 -0
- package/packages/database/src/models/knowledgeBase.ts +67 -3
- package/packages/database/src/repositories/agentGroup/index.test.ts +23 -29
- package/packages/database/src/repositories/agentGroup/index.ts +4 -9
- package/packages/database/src/repositories/knowledge/index.ts +3 -3
- package/packages/database/src/schemas/chatGroup.ts +4 -3
- package/packages/database/src/types/chatGroup.ts +0 -7
- package/packages/types/src/agentGroup/index.ts +30 -9
- package/packages/utils/src/index.ts +1 -0
- package/packages/utils/src/platform.ts +35 -3
- package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +30 -22
- package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/style.ts +8 -5
- package/src/app/[variants]/(main)/_layout/DesktopLayoutContainer.tsx +2 -3
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/ModalProvider.tsx +9 -32
- package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +3 -37
- package/src/app/[variants]/(main)/home/_layout/hooks/useSessionGroupMenuItems.tsx +7 -53
- package/src/app/[variants]/(main)/home/features/RecentPage/List.tsx +2 -1
- package/src/app/[variants]/(main)/resource/features/DndContextWrapper.tsx +1 -1
- package/src/app/[variants]/(main)/resource/library/_layout/Sidebar.tsx +2 -2
- package/src/app/[variants]/(main)/resource/library/features/LibraryMenu.tsx +2 -2
- package/src/app/[variants]/(mobile)/chat/settings/features/SettingButton.tsx +2 -12
- package/src/components/ChatGroupWizard/ChatGroupWizard.tsx +5 -27
- package/src/components/DragUpload/index.tsx +24 -27
- package/src/components/MemberSelectionModal/MemberSelectionModal.tsx +2 -11
- package/src/features/CommandMenu/useCommandMenu.ts +4 -14
- package/src/features/ElectronTitlebar/SimpleTitleBar.tsx +31 -0
- package/src/features/ElectronTitlebar/index.tsx +1 -0
- package/src/features/ResourceManager/components/Editor/index.tsx +2 -3
- package/src/features/ResourceManager/components/Explorer/Header/index.tsx +13 -17
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +1 -1
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/TruncatedFileName.tsx +130 -0
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +36 -4
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +4 -3
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +58 -2
- package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +58 -6
- package/src/features/ResourceManager/components/Explorer/MoveToFolderModal.tsx +2 -5
- package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +9 -5
- package/src/features/ResourceManager/components/Explorer/index.tsx +11 -56
- package/src/features/ResourceManager/components/Header/AddButton.tsx +5 -6
- package/src/features/ResourceManager/components/LibraryHierarchy/HierarchyNode.tsx +382 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/index.tsx +396 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/styles.ts +19 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/treeState.ts +178 -0
- package/src/features/ResourceManager/components/LibraryHierarchy/types.ts +10 -0
- package/src/features/ResourceManager/index.tsx +3 -0
- package/src/layout/GlobalProvider/GroupWizardProvider.tsx +6 -29
- package/src/locales/default/auth.ts +1 -1
- package/src/locales/default/file.ts +2 -0
- package/src/locales/default/metadata.ts +2 -2
- package/src/server/modules/AgentRuntime/AgentRuntimeCoordinator.ts +30 -30
- package/src/server/modules/AgentRuntime/AgentStateManager.ts +23 -23
- package/src/server/modules/AgentRuntime/InMemoryAgentStateManager.ts +16 -16
- package/src/server/modules/AgentRuntime/InMemoryStreamEventManager.ts +13 -13
- package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +2 -2
- package/src/server/modules/AgentRuntime/StreamEventManager.ts +18 -18
- package/src/server/modules/AgentRuntime/types.ts +21 -21
- package/src/server/routers/lambda/__tests__/agentGroup.test.ts +8 -8
- package/src/server/routers/lambda/agentGroup.ts +10 -12
- package/src/server/services/document/index.ts +1 -0
- package/src/store/agentGroup/slices/curd.test.ts +4 -4
- package/src/store/file/slices/fileManager/action.ts +12 -4
- package/src/store/home/slices/homeInput/action.ts +0 -3
- package/src/store/session/slices/session/action.ts +5 -9
- package/src/utils/platform.ts +2 -0
- package/src/app/[variants]/(mobile)/chat/settings/features/AgentTeamSettings/index.tsx +0 -95
- package/src/features/GroupChatSettings/AgentCard.tsx +0 -154
- package/src/features/GroupChatSettings/AgentTeamChatSettings.tsx +0 -179
- package/src/features/GroupChatSettings/AgentTeamMembersSettings.tsx +0 -244
- package/src/features/GroupChatSettings/AgentTeamMetaSettings.tsx +0 -94
- package/src/features/GroupChatSettings/AgentTeamSettings.tsx +0 -54
- package/src/features/GroupChatSettings/GroupCategory/index.tsx +0 -30
- package/src/features/GroupChatSettings/GroupCategory/useGroupCategory.tsx +0 -42
- package/src/features/GroupChatSettings/GroupChatSettingsProvider.tsx +0 -19
- package/src/features/GroupChatSettings/HostMemberCard.tsx +0 -113
- package/src/features/GroupChatSettings/StoreUpdater.tsx +0 -34
- package/src/features/GroupChatSettings/hooks/useGroupChatSettings.ts +0 -25
- package/src/features/GroupChatSettings/index.ts +0 -16
- package/src/features/GroupChatSettings/store/action.ts +0 -105
- package/src/features/GroupChatSettings/store/index.ts +0 -18
- package/src/features/GroupChatSettings/store/initialState.ts +0 -23
- package/src/features/GroupChatSettings/store/selectors.ts +0 -13
- package/src/features/ResourceManager/components/Tree/index.tsx +0 -883
- /package/src/features/ResourceManager/components/{Tree → LibraryHierarchy}/TreeSkeleton.tsx +0 -0
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from '@/app/[variants]/(main)/resource/features/store';
|
|
16
16
|
import { sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
|
|
17
17
|
import { useFileStore } from '@/store/file';
|
|
18
|
+
import { useFetchResources } from '@/store/file/slices/resource/hooks';
|
|
18
19
|
import { useGlobalStore } from '@/store/global';
|
|
19
20
|
import { INITIAL_STATUS } from '@/store/global/initialState';
|
|
20
21
|
import { type AsyncTaskStatus } from '@/types/asyncTask';
|
|
@@ -53,8 +54,11 @@ const styles = createStaticStyles(({ css }) => ({
|
|
|
53
54
|
`,
|
|
54
55
|
}));
|
|
55
56
|
|
|
56
|
-
const ListView = memo(()
|
|
57
|
+
const ListView = memo(function ListView() {
|
|
57
58
|
const [
|
|
59
|
+
libraryId,
|
|
60
|
+
category,
|
|
61
|
+
searchQuery,
|
|
58
62
|
selectFileIds,
|
|
59
63
|
setSelectedFileIds,
|
|
60
64
|
pendingRenameItemId,
|
|
@@ -62,7 +66,11 @@ const ListView = memo(() => {
|
|
|
62
66
|
loadMoreKnowledgeItems,
|
|
63
67
|
sorter,
|
|
64
68
|
sortType,
|
|
69
|
+
storeIsTransitioning,
|
|
65
70
|
] = useResourceManagerStore((s) => [
|
|
71
|
+
s.libraryId,
|
|
72
|
+
s.category,
|
|
73
|
+
s.searchQuery,
|
|
66
74
|
s.selectedFileIds,
|
|
67
75
|
s.setSelectedFileIds,
|
|
68
76
|
s.pendingRenameItemId,
|
|
@@ -70,6 +78,7 @@ const ListView = memo(() => {
|
|
|
70
78
|
s.loadMoreKnowledgeItems,
|
|
71
79
|
s.sorter,
|
|
72
80
|
s.sortType,
|
|
81
|
+
s.isTransitioning,
|
|
73
82
|
]);
|
|
74
83
|
|
|
75
84
|
// Access column widths from Global store
|
|
@@ -83,6 +92,7 @@ const ListView = memo(() => {
|
|
|
83
92
|
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
84
93
|
const isDragActive = useDragActive();
|
|
85
94
|
const [isDropZoneActive, setIsDropZoneActive] = useState(false);
|
|
95
|
+
const [isAnyRowHovered, setIsAnyRowHovered] = useState(false);
|
|
86
96
|
const scrollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
87
97
|
const autoScrollIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
88
98
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -94,6 +104,33 @@ const ListView = memo(() => {
|
|
|
94
104
|
// Get current folder ID - either from breadcrumb or null for root
|
|
95
105
|
const currentFolderId = folderBreadcrumb?.at(-1)?.id || null;
|
|
96
106
|
|
|
107
|
+
const queryParams = useMemo(
|
|
108
|
+
() => ({
|
|
109
|
+
category: libraryId ? undefined : category,
|
|
110
|
+
libraryId,
|
|
111
|
+
parentId: currentFolderSlug || null,
|
|
112
|
+
q: searchQuery ?? undefined,
|
|
113
|
+
showFilesInKnowledgeBase: false,
|
|
114
|
+
sortType,
|
|
115
|
+
sorter,
|
|
116
|
+
}),
|
|
117
|
+
[category, currentFolderSlug, libraryId, searchQuery, sorter, sortType],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const { isLoading, isValidating } = useFetchResources(queryParams);
|
|
121
|
+
const { queryParams: currentQueryParams } = useFileStore();
|
|
122
|
+
|
|
123
|
+
const isNavigating = useMemo(() => {
|
|
124
|
+
if (!currentQueryParams || !queryParams) return false;
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
currentQueryParams.libraryId !== queryParams.libraryId ||
|
|
128
|
+
currentQueryParams.parentId !== queryParams.parentId ||
|
|
129
|
+
currentQueryParams.category !== queryParams.category ||
|
|
130
|
+
currentQueryParams.q !== queryParams.q
|
|
131
|
+
);
|
|
132
|
+
}, [currentQueryParams, queryParams]);
|
|
133
|
+
|
|
97
134
|
const resourceList = useFileStore((s) => s.resourceList);
|
|
98
135
|
|
|
99
136
|
// Map ResourceItem[] to FileListItem[] for compatibility
|
|
@@ -112,6 +149,17 @@ const ListView = memo(() => {
|
|
|
112
149
|
// Sort data using current sort settings
|
|
113
150
|
const data = sortFileList(rawData, sorter, sortType) || [];
|
|
114
151
|
|
|
152
|
+
const dataLength = data.length;
|
|
153
|
+
const effectiveIsLoading = isLoading ?? false;
|
|
154
|
+
const effectiveIsNavigating = isNavigating ?? false;
|
|
155
|
+
const effectiveIsTransitioning = storeIsTransitioning ?? false;
|
|
156
|
+
const effectiveIsValidating = isValidating ?? false;
|
|
157
|
+
|
|
158
|
+
const showSkeleton =
|
|
159
|
+
(effectiveIsLoading && dataLength === 0) ||
|
|
160
|
+
(effectiveIsNavigating && effectiveIsValidating) ||
|
|
161
|
+
effectiveIsTransitioning;
|
|
162
|
+
|
|
115
163
|
const dataRef = useRef<FileListItemType[]>(data);
|
|
116
164
|
|
|
117
165
|
useEffect(() => {
|
|
@@ -286,6 +334,8 @@ const ListView = memo(() => {
|
|
|
286
334
|
return <ListViewSkeleton columnWidths={columnWidths} />;
|
|
287
335
|
}, [isLoadingMore, fileListHasMore, columnWidths]);
|
|
288
336
|
|
|
337
|
+
if (showSkeleton) return <ListViewSkeleton columnWidths={columnWidths} />;
|
|
338
|
+
|
|
289
339
|
return (
|
|
290
340
|
<Flexbox height={'100%'}>
|
|
291
341
|
<div className={styles.scrollContainer}>
|
|
@@ -360,7 +410,11 @@ const ListView = memo(() => {
|
|
|
360
410
|
</Flexbox>
|
|
361
411
|
</Flexbox>
|
|
362
412
|
<div
|
|
363
|
-
className={cx(
|
|
413
|
+
className={cx(
|
|
414
|
+
styles.dropZone,
|
|
415
|
+
isDropZoneActive && styles.dropZoneActive,
|
|
416
|
+
isAnyRowHovered && 'any-row-hovered',
|
|
417
|
+
)}
|
|
364
418
|
data-drop-target-id={currentFolderId || undefined}
|
|
365
419
|
data-is-folder="true"
|
|
366
420
|
onDragLeave={handleDropZoneDragLeave}
|
|
@@ -385,7 +439,9 @@ const ListView = memo(() => {
|
|
|
385
439
|
<FileListItem
|
|
386
440
|
columnWidths={columnWidths}
|
|
387
441
|
index={index}
|
|
442
|
+
isAnyRowHovered={isAnyRowHovered}
|
|
388
443
|
key={item.id}
|
|
444
|
+
onHoverChange={setIsAnyRowHovered}
|
|
389
445
|
onSelectedChange={handleSelectionChange}
|
|
390
446
|
pendingRenameItemId={pendingRenameItemId}
|
|
391
447
|
selected={selectFileIds.includes(item.id)}
|
|
@@ -9,24 +9,31 @@ import { useTranslation } from 'react-i18next';
|
|
|
9
9
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
10
10
|
import { sortFileList } from '@/app/[variants]/(main)/resource/features/store/selectors';
|
|
11
11
|
import { useFileStore } from '@/store/file';
|
|
12
|
+
import { useFetchResources } from '@/store/file/slices/resource/hooks';
|
|
12
13
|
import { type FileListItem } from '@/types/files';
|
|
13
14
|
|
|
14
15
|
import { useMasonryColumnCount } from '../useMasonryColumnCount';
|
|
15
16
|
import MasonryItemWrapper from './MasonryFileItem/MasonryItemWrapper';
|
|
17
|
+
import MasonryViewSkeleton from './Skeleton';
|
|
16
18
|
|
|
17
|
-
const MasonryView = memo(()
|
|
19
|
+
const MasonryView = memo(function MasonryView() {
|
|
18
20
|
// Access all state from Resource Manager store
|
|
19
21
|
const [
|
|
20
22
|
libraryId,
|
|
23
|
+
category,
|
|
24
|
+
searchQuery,
|
|
21
25
|
selectedFileIds,
|
|
22
26
|
setSelectedFileIds,
|
|
23
27
|
loadMoreKnowledgeItems,
|
|
24
28
|
fileListHasMore,
|
|
25
|
-
|
|
29
|
+
storeIsMasonryReady,
|
|
26
30
|
sorter,
|
|
27
31
|
sortType,
|
|
32
|
+
storeIsTransitioning,
|
|
28
33
|
] = useResourceManagerStore((s) => [
|
|
29
34
|
s.libraryId,
|
|
35
|
+
s.category,
|
|
36
|
+
s.searchQuery,
|
|
30
37
|
s.selectedFileIds,
|
|
31
38
|
s.setSelectedFileIds,
|
|
32
39
|
s.loadMoreKnowledgeItems,
|
|
@@ -34,6 +41,7 @@ const MasonryView = memo(() => {
|
|
|
34
41
|
s.isMasonryReady,
|
|
35
42
|
s.sorter,
|
|
36
43
|
s.sortType,
|
|
44
|
+
s.isTransitioning,
|
|
37
45
|
]);
|
|
38
46
|
|
|
39
47
|
const { t } = useTranslation('file');
|
|
@@ -43,6 +51,33 @@ const MasonryView = memo(() => {
|
|
|
43
51
|
// NEW: Read from resource store instead of fetching independently
|
|
44
52
|
const resourceList = useFileStore((s) => s.resourceList);
|
|
45
53
|
|
|
54
|
+
const queryParams = useMemo(
|
|
55
|
+
() => ({
|
|
56
|
+
category: libraryId ? undefined : category,
|
|
57
|
+
libraryId,
|
|
58
|
+
parentId: null,
|
|
59
|
+
q: searchQuery ?? undefined,
|
|
60
|
+
showFilesInKnowledgeBase: false,
|
|
61
|
+
sortType,
|
|
62
|
+
sorter,
|
|
63
|
+
}),
|
|
64
|
+
[category, libraryId, searchQuery, sorter, sortType],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const { isLoading, isValidating } = useFetchResources(queryParams);
|
|
68
|
+
const { queryParams: currentQueryParams } = useFileStore();
|
|
69
|
+
|
|
70
|
+
const isNavigating = useMemo(() => {
|
|
71
|
+
if (!currentQueryParams || !queryParams) return false;
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
currentQueryParams.libraryId !== queryParams.libraryId ||
|
|
75
|
+
currentQueryParams.parentId !== queryParams.parentId ||
|
|
76
|
+
currentQueryParams.category !== queryParams.category ||
|
|
77
|
+
currentQueryParams.q !== queryParams.q
|
|
78
|
+
);
|
|
79
|
+
}, [currentQueryParams, queryParams]);
|
|
80
|
+
|
|
46
81
|
// Map ResourceItem[] to FileListItem[] for compatibility
|
|
47
82
|
const rawData = resourceList?.map(
|
|
48
83
|
(item): FileListItem => ({
|
|
@@ -69,7 +104,20 @@ const MasonryView = memo(() => {
|
|
|
69
104
|
);
|
|
70
105
|
|
|
71
106
|
// Sort data using current sort settings
|
|
72
|
-
const data = sortFileList(rawData, sorter, sortType);
|
|
107
|
+
const data = sortFileList(rawData, sorter, sortType) || [];
|
|
108
|
+
|
|
109
|
+
const dataLength = data.length;
|
|
110
|
+
const effectiveIsLoading = isLoading ?? false;
|
|
111
|
+
const effectiveIsNavigating = isNavigating ?? false;
|
|
112
|
+
const effectiveIsValidating = isValidating ?? false;
|
|
113
|
+
const effectiveIsTransitioning = storeIsTransitioning ?? false;
|
|
114
|
+
const effectiveIsMasonryReady = storeIsMasonryReady;
|
|
115
|
+
|
|
116
|
+
const showSkeleton =
|
|
117
|
+
(effectiveIsLoading && dataLength === 0) ||
|
|
118
|
+
(effectiveIsNavigating && effectiveIsValidating) ||
|
|
119
|
+
effectiveIsTransitioning ||
|
|
120
|
+
!effectiveIsMasonryReady;
|
|
73
121
|
|
|
74
122
|
const masonryContext = useMemo(
|
|
75
123
|
() => ({
|
|
@@ -108,13 +156,15 @@ const MasonryView = memo(() => {
|
|
|
108
156
|
[handleLoadMore],
|
|
109
157
|
);
|
|
110
158
|
|
|
111
|
-
return (
|
|
159
|
+
return showSkeleton ? (
|
|
160
|
+
<MasonryViewSkeleton columnCount={columnCount} />
|
|
161
|
+
) : (
|
|
112
162
|
<div
|
|
113
163
|
onScroll={handleScroll}
|
|
114
164
|
style={{
|
|
115
165
|
flex: 1,
|
|
116
166
|
height: '100%',
|
|
117
|
-
opacity:
|
|
167
|
+
opacity: effectiveIsMasonryReady ? 1 : 0,
|
|
118
168
|
overflowY: 'auto',
|
|
119
169
|
transition: 'opacity 0.2s ease-in-out',
|
|
120
170
|
}}
|
|
@@ -124,7 +174,7 @@ const MasonryView = memo(() => {
|
|
|
124
174
|
ItemContent={MasonryItemWrapper}
|
|
125
175
|
columnCount={columnCount}
|
|
126
176
|
context={masonryContext}
|
|
127
|
-
data={data
|
|
177
|
+
data={data}
|
|
128
178
|
style={{
|
|
129
179
|
gap: '16px',
|
|
130
180
|
overflow: 'hidden',
|
|
@@ -147,4 +197,6 @@ const MasonryView = memo(() => {
|
|
|
147
197
|
);
|
|
148
198
|
});
|
|
149
199
|
|
|
200
|
+
MasonryView.displayName = 'MasonryView';
|
|
201
|
+
|
|
150
202
|
export default MasonryView;
|
|
@@ -5,7 +5,7 @@ import { memo, useCallback, useEffect, useState } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import FolderTree, { type FolderTreeItem } from '@/features/ResourceManager/components/FolderTree';
|
|
8
|
-
import { clearTreeFolderCache } from '@/features/ResourceManager/components/
|
|
8
|
+
import { clearTreeFolderCache } from '@/features/ResourceManager/components/LibraryHierarchy';
|
|
9
9
|
import { fileService } from '@/services/file';
|
|
10
10
|
import { useFileStore } from '@/store/file';
|
|
11
11
|
|
|
@@ -28,10 +28,7 @@ const MoveToFolderModal = memo<MoveToFolderModalProps>(
|
|
|
28
28
|
const [loadedFolders, setLoadedFolders] = useState<Set<string>>(new Set());
|
|
29
29
|
const [isCreatingFolder, setIsCreatingFolder] = useState(false);
|
|
30
30
|
|
|
31
|
-
const [moveResource, createFolder] = useFileStore((s) => [
|
|
32
|
-
s.moveResource,
|
|
33
|
-
s.createFolder,
|
|
34
|
-
]);
|
|
31
|
+
const [moveResource, createFolder] = useFileStore((s) => [s.moveResource, s.createFolder]);
|
|
35
32
|
|
|
36
33
|
// Sort items: folders only
|
|
37
34
|
const sortItems = useCallback((items: FolderTreeItem[]): FolderTreeItem[] => {
|
|
@@ -25,13 +25,12 @@ export type MultiSelectActionType =
|
|
|
25
25
|
| 'removeFromKnowledgeBase';
|
|
26
26
|
|
|
27
27
|
interface BatchActionsDropdownProps {
|
|
28
|
-
disabled?: boolean;
|
|
29
28
|
onActionClick: (type: MultiSelectActionType) => Promise<void>;
|
|
30
29
|
selectCount: number;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
34
|
-
({ selectCount, onActionClick
|
|
33
|
+
({ selectCount, onActionClick }) => {
|
|
35
34
|
const { t } = useTranslation(['components', 'common', 'file', 'knowledgeBase']);
|
|
36
35
|
const { modal, message } = App.useApp();
|
|
37
36
|
|
|
@@ -54,7 +53,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
54
53
|
danger: true,
|
|
55
54
|
icon: <Icon icon={Trash2Icon} />,
|
|
56
55
|
key: 'deleteLibrary',
|
|
57
|
-
label: t('
|
|
56
|
+
label: t('header.actions.deleteLibrary', { ns: 'file' }),
|
|
58
57
|
onClick: async () => {
|
|
59
58
|
modal.confirm({
|
|
60
59
|
okButtonProps: {
|
|
@@ -74,6 +73,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
74
73
|
const availableKnowledgeBases = (knowledgeBases || []).filter((kb) => kb.id !== libraryId);
|
|
75
74
|
|
|
76
75
|
const addToKnowledgeBaseSubmenu: DropdownItem[] = availableKnowledgeBases.map((kb) => ({
|
|
76
|
+
disabled: selectCount === 0,
|
|
77
77
|
icon: <RepoIcon />,
|
|
78
78
|
key: `add-to-kb-${kb.id}`,
|
|
79
79
|
label: <span style={{ marginLeft: 8 }}>{kb.name}</span>,
|
|
@@ -95,6 +95,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
95
95
|
|
|
96
96
|
if (libraryId) {
|
|
97
97
|
items.push({
|
|
98
|
+
disabled: selectCount === 0,
|
|
98
99
|
icon: <Icon icon={BookMinusIcon} />,
|
|
99
100
|
key: 'removeFromKnowledgeBase',
|
|
100
101
|
label: t('FileManager.actions.removeFromKnowledgeBase'),
|
|
@@ -117,6 +118,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
117
118
|
if (availableKnowledgeBases.length > 0) {
|
|
118
119
|
items.push({
|
|
119
120
|
children: addToKnowledgeBaseSubmenu as any,
|
|
121
|
+
disabled: selectCount === 0,
|
|
120
122
|
icon: <Icon icon={BookPlusIcon} />,
|
|
121
123
|
key: 'addToOtherKnowledgeBase',
|
|
122
124
|
label: t('FileManager.actions.addToOtherKnowledgeBase'),
|
|
@@ -125,6 +127,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
125
127
|
} else if (availableKnowledgeBases.length > 0) {
|
|
126
128
|
items.push({
|
|
127
129
|
children: addToKnowledgeBaseSubmenu as any,
|
|
130
|
+
disabled: selectCount === 0,
|
|
128
131
|
icon: <Icon icon={BookPlusIcon} />,
|
|
129
132
|
key: 'addToKnowledgeBase',
|
|
130
133
|
label: t('FileManager.actions.addToKnowledgeBase'),
|
|
@@ -133,6 +136,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
133
136
|
|
|
134
137
|
items.push(
|
|
135
138
|
{
|
|
139
|
+
disabled: selectCount === 0,
|
|
136
140
|
icon: <Icon icon={FileBoxIcon} />,
|
|
137
141
|
key: 'batchChunking',
|
|
138
142
|
label: t('FileManager.actions.batchChunking'),
|
|
@@ -145,6 +149,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
145
149
|
},
|
|
146
150
|
{
|
|
147
151
|
danger: true,
|
|
152
|
+
disabled: selectCount === 0,
|
|
148
153
|
icon: <Icon icon={Trash2Icon} />,
|
|
149
154
|
key: 'delete',
|
|
150
155
|
label: t('delete', { ns: 'common' }),
|
|
@@ -177,9 +182,8 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(
|
|
|
177
182
|
]);
|
|
178
183
|
|
|
179
184
|
return (
|
|
180
|
-
<DropdownMenu items={menuItems} placement="bottomLeft"
|
|
185
|
+
<DropdownMenu items={menuItems} placement="bottomLeft">
|
|
181
186
|
<ActionIconWithChevron
|
|
182
|
-
disabled={disabled}
|
|
183
187
|
icon={CircleEllipsisIcon}
|
|
184
188
|
title={t('FileManager.actions.batchActions', 'Batch actions')}
|
|
185
189
|
/>
|
|
@@ -12,11 +12,8 @@ import { useFetchResources, useResourceStore } from '@/store/file/slices/resourc
|
|
|
12
12
|
import EmptyPlaceholder from './EmptyPlaceholder';
|
|
13
13
|
import Header from './Header';
|
|
14
14
|
import ListView from './ListView';
|
|
15
|
-
import ListViewSkeleton from './ListView/Skeleton';
|
|
16
15
|
import MasonryView from './MasonryView';
|
|
17
|
-
import MasonryViewSkeleton from './MasonryView/Skeleton';
|
|
18
16
|
import { useCheckTaskStatus } from './useCheckTaskStatus';
|
|
19
|
-
import { useMasonryColumnCount } from './useMasonryColumnCount';
|
|
20
17
|
import { useResourceExplorer } from './useResourceExplorer';
|
|
21
18
|
|
|
22
19
|
/**
|
|
@@ -32,27 +29,16 @@ const ResourceExplorer = memo(() => {
|
|
|
32
29
|
useResourceManagerUrlSync();
|
|
33
30
|
|
|
34
31
|
// Get state from Resource Manager store
|
|
35
|
-
const [
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
] = useResourceManagerStore((s) => [
|
|
46
|
-
s.libraryId,
|
|
47
|
-
s.category,
|
|
48
|
-
s.viewMode,
|
|
49
|
-
s.isTransitioning,
|
|
50
|
-
s.isMasonryReady,
|
|
51
|
-
s.searchQuery,
|
|
52
|
-
s.setSelectedFileIds,
|
|
53
|
-
s.sorter,
|
|
54
|
-
s.sortType,
|
|
55
|
-
]);
|
|
32
|
+
const [libraryId, category, viewMode, searchQuery, setSelectedFileIds, sorter, sortType] =
|
|
33
|
+
useResourceManagerStore((s) => [
|
|
34
|
+
s.libraryId,
|
|
35
|
+
s.category,
|
|
36
|
+
s.viewMode,
|
|
37
|
+
s.searchQuery,
|
|
38
|
+
s.setSelectedFileIds,
|
|
39
|
+
s.sorter,
|
|
40
|
+
s.sortType,
|
|
41
|
+
]);
|
|
56
42
|
|
|
57
43
|
// Get folder path for empty state check
|
|
58
44
|
const { currentFolderSlug } = useFolderPath();
|
|
@@ -77,19 +63,7 @@ const ResourceExplorer = memo(() => {
|
|
|
77
63
|
const { isLoading, isValidating } = useFetchResources(queryParams);
|
|
78
64
|
|
|
79
65
|
// Get resource data from store (updated by SWR hook)
|
|
80
|
-
const { resourceList
|
|
81
|
-
|
|
82
|
-
// Check if we're navigating to a different view (different query params)
|
|
83
|
-
const isNavigating = useMemo(() => {
|
|
84
|
-
if (!currentQueryParams || !queryParams) return false;
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
currentQueryParams.libraryId !== queryParams.libraryId ||
|
|
88
|
-
currentQueryParams.parentId !== queryParams.parentId ||
|
|
89
|
-
currentQueryParams.category !== queryParams.category ||
|
|
90
|
-
currentQueryParams.q !== queryParams.q
|
|
91
|
-
);
|
|
92
|
-
}, [currentQueryParams, queryParams]);
|
|
66
|
+
const { resourceList } = useResourceStore();
|
|
93
67
|
|
|
94
68
|
// Map ResourceItem[] to FileListItem[] for compatibility
|
|
95
69
|
// TODO: Eventually update all consumers to use ResourceItem directly
|
|
@@ -119,19 +93,6 @@ const ResourceExplorer = memo(() => {
|
|
|
119
93
|
setSelectedFileIds([]);
|
|
120
94
|
}, [category, libraryId, searchQuery, setSelectedFileIds]);
|
|
121
95
|
|
|
122
|
-
// Computed values
|
|
123
|
-
const columnCount = useMasonryColumnCount();
|
|
124
|
-
|
|
125
|
-
// Show skeleton when:
|
|
126
|
-
// 1. Initial load with no data (isLoading && no data)
|
|
127
|
-
// 2. Navigating to different folder/category (isNavigating && isValidating)
|
|
128
|
-
// 3. View mode transitions
|
|
129
|
-
const showSkeleton =
|
|
130
|
-
(isLoading && (!data || data.length >= 5)) ||
|
|
131
|
-
(isNavigating && isValidating) ||
|
|
132
|
-
(viewMode === 'list' && isTransitioning) ||
|
|
133
|
-
(viewMode === 'masonry' && (isTransitioning || !isMasonryReady));
|
|
134
|
-
|
|
135
96
|
const showEmptyStatus = !isLoading && !isValidating && data?.length === 0 && !currentFolderSlug;
|
|
136
97
|
|
|
137
98
|
return (
|
|
@@ -139,12 +100,6 @@ const ResourceExplorer = memo(() => {
|
|
|
139
100
|
<Header />
|
|
140
101
|
{showEmptyStatus ? (
|
|
141
102
|
<EmptyPlaceholder />
|
|
142
|
-
) : showSkeleton ? (
|
|
143
|
-
viewMode === 'list' ? (
|
|
144
|
-
<ListViewSkeleton />
|
|
145
|
-
) : (
|
|
146
|
-
<MasonryViewSkeleton columnCount={columnCount} />
|
|
147
|
-
)
|
|
148
103
|
) : viewMode === 'list' ? (
|
|
149
104
|
<ListView />
|
|
150
105
|
) : (
|
|
@@ -22,7 +22,6 @@ const AddButton = () => {
|
|
|
22
22
|
const { t } = useTranslation('file');
|
|
23
23
|
const pushDockFileList = useFileStore((s) => s.pushDockFileList);
|
|
24
24
|
const uploadFolderWithStructure = useFileStore((s) => s.uploadFolderWithStructure);
|
|
25
|
-
const createResource = useFileStore((s) => s.createResource);
|
|
26
25
|
const createResourceAndSync = useFileStore((s) => s.createResourceAndSync);
|
|
27
26
|
|
|
28
27
|
// TODO: Migrate Notion import to use createResource
|
|
@@ -39,9 +38,9 @@ const AddButton = () => {
|
|
|
39
38
|
]);
|
|
40
39
|
|
|
41
40
|
const handleOpenPageEditor = useCallback(async () => {
|
|
42
|
-
// Create a new page
|
|
41
|
+
// Create a new page and wait for server sync - ensures page editor can load the document
|
|
43
42
|
const untitledTitle = t('pageList.untitled');
|
|
44
|
-
const
|
|
43
|
+
const realId = await createResourceAndSync({
|
|
45
44
|
content: '',
|
|
46
45
|
fileType: 'custom/document',
|
|
47
46
|
knowledgeBaseId: libraryId,
|
|
@@ -50,10 +49,10 @@ const AddButton = () => {
|
|
|
50
49
|
title: untitledTitle,
|
|
51
50
|
});
|
|
52
51
|
|
|
53
|
-
// Switch to page view mode
|
|
54
|
-
setCurrentViewItemId(
|
|
52
|
+
// Switch to page view mode with real ID
|
|
53
|
+
setCurrentViewItemId(realId);
|
|
55
54
|
setMode('page');
|
|
56
|
-
}, [
|
|
55
|
+
}, [createResourceAndSync, currentFolderId, libraryId, setCurrentViewItemId, setMode, t]);
|
|
57
56
|
|
|
58
57
|
const handleCreateFolder = useCallback(async () => {
|
|
59
58
|
// Create folder and wait for sync to complete before triggering rename
|