@lobehub/lobehub 2.0.0-next.233 → 2.0.0-next.235
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/.github/workflows/e2e.yml +6 -12
- package/.github/workflows/test.yml +3 -3
- package/CHANGELOG.md +59 -0
- package/CLAUDE.md +1 -1
- package/changelog/v1.json +18 -0
- package/docs/development/basic/feature-development.mdx +4 -5
- package/docs/development/basic/feature-development.zh-CN.mdx +4 -5
- package/e2e/README.md +6 -6
- package/e2e/src/features/community/detail-pages.feature +9 -9
- package/e2e/src/features/community/interactions.feature +13 -13
- package/e2e/src/features/community/smoke.feature +6 -6
- package/e2e/src/steps/agent/conversation-mgmt.steps.ts +196 -25
- package/e2e/src/steps/agent/conversation.steps.ts +58 -0
- package/e2e/src/steps/agent/message-ops.steps.ts +20 -15
- package/e2e/src/steps/community/detail-pages.steps.ts +60 -19
- package/e2e/src/steps/community/interactions.steps.ts +145 -32
- package/e2e/src/steps/hooks.ts +12 -2
- package/locales/ar/components.json +1 -0
- package/locales/ar/file.json +4 -0
- package/locales/ar/models.json +29 -0
- package/locales/ar/setting.json +7 -0
- package/locales/bg-BG/components.json +1 -0
- package/locales/bg-BG/file.json +4 -0
- package/locales/bg-BG/models.json +1 -0
- package/locales/bg-BG/setting.json +7 -0
- package/locales/de-DE/components.json +1 -0
- package/locales/de-DE/file.json +4 -0
- package/locales/de-DE/models.json +29 -0
- package/locales/de-DE/setting.json +7 -0
- package/locales/en-US/common.json +0 -1
- package/locales/en-US/components.json +1 -0
- package/locales/en-US/file.json +4 -0
- package/locales/en-US/models.json +1 -0
- package/locales/en-US/setting.json +3 -0
- package/locales/es-ES/components.json +1 -0
- package/locales/es-ES/file.json +4 -0
- package/locales/es-ES/models.json +43 -0
- package/locales/es-ES/setting.json +7 -0
- package/locales/fa-IR/components.json +1 -0
- package/locales/fa-IR/file.json +4 -0
- package/locales/fa-IR/models.json +54 -0
- package/locales/fa-IR/setting.json +7 -0
- package/locales/fr-FR/components.json +1 -0
- package/locales/fr-FR/file.json +4 -0
- package/locales/fr-FR/models.json +31 -0
- package/locales/fr-FR/setting.json +7 -0
- package/locales/it-IT/components.json +1 -0
- package/locales/it-IT/file.json +4 -0
- package/locales/it-IT/models.json +43 -0
- package/locales/it-IT/setting.json +7 -0
- package/locales/ja-JP/components.json +1 -0
- package/locales/ja-JP/file.json +4 -0
- package/locales/ja-JP/models.json +28 -0
- package/locales/ja-JP/setting.json +7 -0
- package/locales/ko-KR/components.json +1 -0
- package/locales/ko-KR/file.json +4 -0
- package/locales/ko-KR/models.json +37 -0
- package/locales/ko-KR/setting.json +7 -0
- package/locales/nl-NL/components.json +1 -0
- package/locales/nl-NL/file.json +4 -0
- package/locales/nl-NL/models.json +13 -0
- package/locales/nl-NL/setting.json +7 -0
- package/locales/pl-PL/components.json +1 -0
- package/locales/pl-PL/file.json +4 -0
- package/locales/pl-PL/models.json +13 -0
- package/locales/pl-PL/setting.json +7 -0
- package/locales/pt-BR/components.json +1 -0
- package/locales/pt-BR/file.json +4 -0
- package/locales/pt-BR/models.json +29 -0
- package/locales/pt-BR/setting.json +7 -0
- package/locales/ru-RU/components.json +1 -0
- package/locales/ru-RU/file.json +4 -0
- package/locales/ru-RU/models.json +1 -0
- package/locales/ru-RU/setting.json +7 -0
- package/locales/tr-TR/components.json +1 -0
- package/locales/tr-TR/file.json +4 -0
- package/locales/tr-TR/models.json +29 -0
- package/locales/tr-TR/setting.json +7 -0
- package/locales/vi-VN/components.json +1 -0
- package/locales/vi-VN/file.json +4 -0
- package/locales/vi-VN/models.json +1 -0
- package/locales/vi-VN/setting.json +7 -0
- package/locales/zh-CN/file.json +4 -0
- package/locales/zh-CN/models.json +46 -0
- package/locales/zh-CN/setting.json +3 -0
- package/locales/zh-TW/components.json +1 -0
- package/locales/zh-TW/file.json +4 -0
- package/locales/zh-TW/models.json +35 -0
- package/locales/zh-TW/setting.json +7 -0
- package/package.json +5 -5
- package/packages/const/src/index.ts +1 -0
- package/packages/const/src/lobehubSkill.ts +55 -0
- package/packages/types/package.json +1 -1
- package/packages/types/src/files/upload.ts +11 -1
- package/packages/types/src/message/common/tools.ts +1 -1
- package/packages/types/src/serverConfig.ts +1 -0
- package/public/not-compatible.html +1296 -0
- package/src/app/[variants]/(main)/resource/features/FileDetail.tsx +20 -12
- package/src/app/[variants]/(main)/resource/features/modal/FullscreenModal.tsx +2 -4
- package/src/app/[variants]/layout.tsx +50 -1
- package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +304 -0
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +74 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/ToolTitle.tsx +9 -0
- package/src/features/FileViewer/Renderer/Code/index.tsx +224 -0
- package/src/features/FileViewer/Renderer/Image/index.tsx +8 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +3 -1
- package/src/features/FileViewer/Renderer/PDF/style.ts +2 -1
- package/src/features/FileViewer/index.tsx +135 -24
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +7 -4
- package/src/features/PageEditor/store/initialState.ts +2 -1
- package/src/features/ResourceManager/components/Editor/FileContent.tsx +1 -4
- package/src/features/ResourceManager/components/Editor/FileCopilot.tsx +64 -0
- package/src/features/ResourceManager/components/Editor/index.tsx +98 -31
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +3 -2
- package/src/features/ResourceManager/components/Explorer/ListView/ColumnResizeHandle.tsx +119 -0
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +67 -22
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +46 -11
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +140 -81
- package/src/features/ResourceManager/components/Explorer/ToolBar/SortDropdown.tsx +20 -12
- package/src/features/ResourceManager/components/Explorer/ToolBar/ViewSwitcher.tsx +18 -10
- package/src/features/ResourceManager/components/UploadDock/Item.tsx +38 -6
- package/src/features/ResourceManager/components/UploadDock/index.tsx +62 -41
- package/src/features/ResourceManager/index.tsx +1 -0
- package/src/helpers/toolEngineering/index.test.ts +3 -0
- package/src/helpers/toolEngineering/index.ts +12 -1
- package/src/locales/default/file.ts +4 -0
- package/src/locales/default/setting.ts +3 -0
- package/src/server/globalConfig/index.ts +1 -0
- package/src/server/modules/ModelRuntime/index.test.ts +214 -1
- package/src/server/modules/ModelRuntime/index.ts +43 -7
- package/src/server/routers/lambda/_helpers/resolveContext.ts +8 -8
- package/src/server/routers/lambda/agent.ts +1 -1
- package/src/server/routers/lambda/aiModel.ts +1 -1
- package/src/server/routers/lambda/comfyui.ts +1 -1
- package/src/server/routers/lambda/document.ts +44 -0
- package/src/server/routers/lambda/exporter.ts +1 -1
- package/src/server/routers/lambda/image.ts +13 -13
- package/src/server/routers/lambda/klavis.ts +10 -10
- package/src/server/routers/lambda/market/index.ts +6 -6
- package/src/server/routers/lambda/message.ts +2 -2
- package/src/server/routers/lambda/plugin.ts +1 -1
- package/src/server/routers/lambda/ragEval.ts +2 -2
- package/src/server/routers/lambda/topic.ts +3 -3
- package/src/server/routers/lambda/user.ts +10 -10
- package/src/server/routers/lambda/userMemories.ts +6 -6
- package/src/server/routers/tools/market.ts +261 -0
- package/src/server/services/document/index.ts +22 -0
- package/src/services/document/index.ts +4 -0
- package/src/services/upload.ts +22 -2
- package/src/store/chat/slices/plugin/actions/internals.ts +15 -2
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +104 -0
- package/src/store/file/slices/fileManager/action.test.ts +9 -3
- package/src/store/file/slices/fileManager/action.ts +165 -70
- package/src/store/file/slices/upload/action.ts +3 -0
- package/src/store/global/actions/general.ts +15 -0
- package/src/store/global/initialState.ts +13 -0
- package/src/store/serverConfig/selectors.ts +1 -0
- package/src/store/tool/initialState.ts +11 -2
- package/src/store/tool/selectors/index.ts +1 -0
- package/src/store/tool/selectors/tool.ts +3 -1
- package/src/store/tool/slices/lobehubSkillStore/action.ts +361 -0
- package/src/store/tool/slices/lobehubSkillStore/index.ts +4 -0
- package/src/store/tool/slices/lobehubSkillStore/initialState.ts +24 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +145 -0
- package/src/store/tool/slices/lobehubSkillStore/types.ts +100 -0
- package/src/store/tool/store.ts +8 -2
- package/vitest.config.mts +1 -0
- package/src/features/FileViewer/Renderer/JavaScript/index.tsx +0 -66
- package/src/features/FileViewer/Renderer/TXT/index.tsx +0 -50
|
@@ -1,22 +1,107 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { BUILTIN_AGENT_SLUGS } from '@lobechat/builtin-agents';
|
|
3
4
|
import { ActionIcon, Flexbox } from '@lobehub/ui';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { Modal } from 'antd';
|
|
6
|
+
import { cssVar, useTheme } from 'antd-style';
|
|
7
|
+
import { ArrowLeftIcon, BotMessageSquareIcon, DownloadIcon, InfoIcon } from 'lucide-react';
|
|
8
|
+
import { memo, useState } from 'react';
|
|
7
9
|
import { useTranslation } from 'react-i18next';
|
|
8
10
|
|
|
11
|
+
import FileDetailComponent from '@/app/[variants]/(main)/resource/features/FileDetail';
|
|
9
12
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
13
|
+
import Loading from '@/components/Loading/BrandTextLoading';
|
|
10
14
|
import NavHeader from '@/features/NavHeader';
|
|
15
|
+
import PageAgentProvider from '@/features/PageEditor/PageAgentProvider';
|
|
16
|
+
import ToggleRightPanelButton from '@/features/RightPanel/ToggleRightPanelButton';
|
|
17
|
+
import { useAgentStore } from '@/store/agent';
|
|
18
|
+
import { builtinAgentSelectors } from '@/store/agent/selectors';
|
|
11
19
|
import { fileManagerSelectors, useFileStore } from '@/store/file';
|
|
20
|
+
import { downloadFile } from '@/utils/client/downloadFile';
|
|
12
21
|
|
|
13
|
-
import Breadcrumb from '../Explorer/Header/Breadcrumb';
|
|
14
22
|
import FileContent from './FileContent';
|
|
23
|
+
import FileCopilot from './FileCopilot';
|
|
15
24
|
|
|
16
25
|
interface FileEditorProps {
|
|
17
26
|
onBack?: () => void;
|
|
18
27
|
}
|
|
19
28
|
|
|
29
|
+
const FileEditorCanvas = memo<FileEditorProps>(({ onBack }) => {
|
|
30
|
+
const { t } = useTranslation(['common', 'file']);
|
|
31
|
+
const theme = useTheme();
|
|
32
|
+
const [isDetailModalOpen, setIsDetailModalOpen] = useState(false);
|
|
33
|
+
|
|
34
|
+
const currentViewItemId = useResourceManagerStore((s) => s.currentViewItemId);
|
|
35
|
+
|
|
36
|
+
const fileDetail = useFileStore(fileManagerSelectors.getFileById(currentViewItemId));
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<>
|
|
40
|
+
<Flexbox height={'100%'} horizontal width={'100%'}>
|
|
41
|
+
<Flexbox flex={1} height={'100%'}>
|
|
42
|
+
<NavHeader
|
|
43
|
+
left={
|
|
44
|
+
<Flexbox align={'center'} gap={12} horizontal style={{ minHeight: 32 }}>
|
|
45
|
+
<ActionIcon icon={ArrowLeftIcon} onClick={onBack} title={t('back')} />
|
|
46
|
+
<span
|
|
47
|
+
style={{
|
|
48
|
+
color: theme.colorText,
|
|
49
|
+
fontSize: 14,
|
|
50
|
+
fontWeight: 500,
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
53
|
+
{fileDetail?.name}
|
|
54
|
+
</span>
|
|
55
|
+
</Flexbox>
|
|
56
|
+
}
|
|
57
|
+
right={
|
|
58
|
+
<Flexbox gap={8} horizontal>
|
|
59
|
+
<ToggleRightPanelButton icon={BotMessageSquareIcon} showActive={true} size={20} />
|
|
60
|
+
{fileDetail?.url && (
|
|
61
|
+
<ActionIcon
|
|
62
|
+
icon={DownloadIcon}
|
|
63
|
+
onClick={() => {
|
|
64
|
+
if (fileDetail?.url && fileDetail?.name) {
|
|
65
|
+
downloadFile(fileDetail.url, fileDetail.name);
|
|
66
|
+
}
|
|
67
|
+
}}
|
|
68
|
+
title={t('download', { ns: 'common' })}
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
<ActionIcon icon={InfoIcon} onClick={() => setIsDetailModalOpen(true)} />
|
|
72
|
+
</Flexbox>
|
|
73
|
+
}
|
|
74
|
+
style={{
|
|
75
|
+
borderBottom: `1px solid ${cssVar.colorBorderSecondary}`,
|
|
76
|
+
}}
|
|
77
|
+
styles={{
|
|
78
|
+
left: { padding: 0 },
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
|
|
82
|
+
<FileContent fileId={currentViewItemId} />
|
|
83
|
+
</Flexbox>
|
|
84
|
+
</Flexbox>
|
|
85
|
+
<FileCopilot />
|
|
86
|
+
</Flexbox>
|
|
87
|
+
|
|
88
|
+
<Modal
|
|
89
|
+
footer={null}
|
|
90
|
+
onCancel={() => setIsDetailModalOpen(false)}
|
|
91
|
+
open={isDetailModalOpen}
|
|
92
|
+
title={t('detail.basic.title', { ns: 'file' })}
|
|
93
|
+
width={400}
|
|
94
|
+
>
|
|
95
|
+
{fileDetail && (
|
|
96
|
+
<FileDetailComponent {...fileDetail} showDownloadButton={false} showTitle={false} />
|
|
97
|
+
)}
|
|
98
|
+
</Modal>
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
FileEditorCanvas.displayName = 'FileEditorCanvas';
|
|
104
|
+
|
|
20
105
|
/**
|
|
21
106
|
* View or Edit a file
|
|
22
107
|
*
|
|
@@ -24,38 +109,20 @@ interface FileEditorProps {
|
|
|
24
109
|
* So we depend on context, not props.
|
|
25
110
|
*/
|
|
26
111
|
const FileEditor = memo<FileEditorProps>(({ onBack }) => {
|
|
27
|
-
const
|
|
112
|
+
const useInitBuiltinAgent = useAgentStore((s) => s.useInitBuiltinAgent);
|
|
113
|
+
const pageAgentId = useAgentStore(builtinAgentSelectors.pageAgentId);
|
|
28
114
|
|
|
29
|
-
|
|
30
|
-
s.currentViewItemId,
|
|
31
|
-
s.category,
|
|
32
|
-
]);
|
|
115
|
+
useInitBuiltinAgent(BUILTIN_AGENT_SLUGS.pageAgent);
|
|
33
116
|
|
|
34
|
-
|
|
117
|
+
if (!pageAgentId) return <Loading debugId="FileEditor > PageAgent Init" />;
|
|
35
118
|
|
|
36
119
|
return (
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
<Flexbox align={'center'} gap={4} horizontal style={{ minHeight: 32 }}>
|
|
41
|
-
<ActionIcon icon={ArrowLeftIcon} onClick={onBack} title={t('back')} />
|
|
42
|
-
<Flexbox align={'center'} style={{ marginLeft: 8 }}>
|
|
43
|
-
<Breadcrumb category={category} fileName={fileDetail?.name} />
|
|
44
|
-
</Flexbox>
|
|
45
|
-
</Flexbox>
|
|
46
|
-
}
|
|
47
|
-
style={{
|
|
48
|
-
borderBottom: `1px solid ${cssVar.colorBorderSecondary}`,
|
|
49
|
-
}}
|
|
50
|
-
styles={{
|
|
51
|
-
left: { padding: 0 },
|
|
52
|
-
}}
|
|
53
|
-
/>
|
|
54
|
-
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
|
|
55
|
-
<FileContent fileId={currentViewItemId} />
|
|
56
|
-
</Flexbox>
|
|
57
|
-
</Flexbox>
|
|
120
|
+
<PageAgentProvider pageAgentId={pageAgentId}>
|
|
121
|
+
<FileEditorCanvas onBack={onBack} />
|
|
122
|
+
</PageAgentProvider>
|
|
58
123
|
);
|
|
59
124
|
});
|
|
60
125
|
|
|
126
|
+
FileEditor.displayName = 'FileEditor';
|
|
127
|
+
|
|
61
128
|
export default FileEditor;
|
package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx
CHANGED
|
@@ -56,8 +56,9 @@ export const useFileItemDropdown = ({
|
|
|
56
56
|
s.useFetchKnowledgeBaseList,
|
|
57
57
|
]);
|
|
58
58
|
|
|
59
|
-
//
|
|
60
|
-
//
|
|
59
|
+
// Fetch knowledge bases - SWR caches this across all dropdown instances
|
|
60
|
+
// Only the first call fetches from server, subsequent calls use cache
|
|
61
|
+
// The expensive menu computation is deferred until dropdown opens (menuItems is a function)
|
|
61
62
|
const { data: knowledgeBases } = useFetchKnowledgeBaseList();
|
|
62
63
|
|
|
63
64
|
const inKnowledgeBase = !!knowledgeBaseId;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createStaticStyles, cssVar } from 'antd-style';
|
|
4
|
+
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
const styles = createStaticStyles(({ css }) => ({
|
|
7
|
+
handle: css`
|
|
8
|
+
cursor: col-resize;
|
|
9
|
+
user-select: none;
|
|
10
|
+
|
|
11
|
+
position: absolute;
|
|
12
|
+
z-index: 1;
|
|
13
|
+
inset-block: 0 0;
|
|
14
|
+
inset-inline-end: 0;
|
|
15
|
+
transform: translateX(-4px);
|
|
16
|
+
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
|
|
21
|
+
width: 16px;
|
|
22
|
+
|
|
23
|
+
&::after {
|
|
24
|
+
content: '';
|
|
25
|
+
|
|
26
|
+
width: 1.5px;
|
|
27
|
+
height: calc(100% - 16px);
|
|
28
|
+
border-radius: 1px;
|
|
29
|
+
|
|
30
|
+
background-color: ${cssVar.colorBorder};
|
|
31
|
+
|
|
32
|
+
transition: all 0.2s;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&:hover::after {
|
|
36
|
+
width: 3px;
|
|
37
|
+
background-color: ${cssVar.colorPrimary};
|
|
38
|
+
}
|
|
39
|
+
`,
|
|
40
|
+
handleDragging: css`
|
|
41
|
+
&::after {
|
|
42
|
+
width: 3px !important;
|
|
43
|
+
background-color: ${cssVar.colorPrimary} !important;
|
|
44
|
+
}
|
|
45
|
+
`,
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
interface ColumnResizeHandleProps {
|
|
49
|
+
column: 'name' | 'date' | 'size';
|
|
50
|
+
currentWidth: number;
|
|
51
|
+
maxWidth: number;
|
|
52
|
+
minWidth: number;
|
|
53
|
+
onResize: (width: number) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ColumnResizeHandle = memo<ColumnResizeHandleProps>(
|
|
57
|
+
({ currentWidth, minWidth, maxWidth, onResize }) => {
|
|
58
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
59
|
+
const startXRef = useRef(0);
|
|
60
|
+
const startWidthRef = useRef(0);
|
|
61
|
+
|
|
62
|
+
const handleMouseMove = useCallback(
|
|
63
|
+
(e: MouseEvent) => {
|
|
64
|
+
const delta = e.clientX - startXRef.current;
|
|
65
|
+
const newWidth = Math.max(minWidth, Math.min(maxWidth, startWidthRef.current + delta));
|
|
66
|
+
|
|
67
|
+
// Update width in real-time during drag
|
|
68
|
+
onResize(newWidth);
|
|
69
|
+
},
|
|
70
|
+
[minWidth, maxWidth, onResize],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const handleMouseUp = useCallback(() => {
|
|
74
|
+
setIsDragging(false);
|
|
75
|
+
}, []);
|
|
76
|
+
|
|
77
|
+
const handleMouseDown = useCallback(
|
|
78
|
+
(e: React.MouseEvent) => {
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
e.stopPropagation();
|
|
81
|
+
|
|
82
|
+
setIsDragging(true);
|
|
83
|
+
startXRef.current = e.clientX;
|
|
84
|
+
startWidthRef.current = currentWidth;
|
|
85
|
+
},
|
|
86
|
+
[currentWidth],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Attach document-level event listeners when dragging
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (isDragging) {
|
|
92
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
93
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
94
|
+
|
|
95
|
+
// Disable text selection and lock cursor during drag
|
|
96
|
+
document.body.style.userSelect = 'none';
|
|
97
|
+
document.body.style.cursor = 'col-resize';
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
101
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
102
|
+
document.body.style.userSelect = '';
|
|
103
|
+
document.body.style.cursor = '';
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}, [isDragging, handleMouseMove, handleMouseUp]);
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
className={`${styles.handle} ${isDragging ? styles.handleDragging : ''}`}
|
|
111
|
+
onMouseDown={handleMouseDown}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
},
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
ColumnResizeHandle.displayName = 'ColumnResizeHandle';
|
|
118
|
+
|
|
119
|
+
export default ColumnResizeHandle;
|
|
@@ -26,6 +26,7 @@ import DropdownMenu from '../../ItemDropdown/DropdownMenu';
|
|
|
26
26
|
import { useFileItemDropdown } from '../../ItemDropdown/useFileItemDropdown';
|
|
27
27
|
import ChunksBadge from './ChunkTag';
|
|
28
28
|
|
|
29
|
+
// Initialize dayjs plugin once at module level
|
|
29
30
|
dayjs.extend(relativeTime);
|
|
30
31
|
|
|
31
32
|
export const FILE_DATE_WIDTH = 160;
|
|
@@ -35,6 +36,7 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
35
36
|
return {
|
|
36
37
|
container: css`
|
|
37
38
|
cursor: pointer;
|
|
39
|
+
min-width: 800px;
|
|
38
40
|
|
|
39
41
|
&:hover {
|
|
40
42
|
background: ${cssVar.colorFillTertiary};
|
|
@@ -83,7 +85,6 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
83
85
|
overflow: hidden;
|
|
84
86
|
flex: 1;
|
|
85
87
|
min-width: 0;
|
|
86
|
-
max-width: 600px;
|
|
87
88
|
`,
|
|
88
89
|
selected: css`
|
|
89
90
|
background: ${cssVar.colorFillTertiary};
|
|
@@ -96,6 +97,11 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
96
97
|
});
|
|
97
98
|
|
|
98
99
|
interface FileListItemProps extends FileListItemType {
|
|
100
|
+
columnWidths: {
|
|
101
|
+
date: number;
|
|
102
|
+
name: number;
|
|
103
|
+
size: number;
|
|
104
|
+
};
|
|
99
105
|
index: number;
|
|
100
106
|
onSelectedChange: (id: string, selected: boolean, shiftKey: boolean, index: number) => void;
|
|
101
107
|
pendingRenameItemId?: string | null;
|
|
@@ -107,6 +113,7 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
107
113
|
({
|
|
108
114
|
size,
|
|
109
115
|
chunkingError,
|
|
116
|
+
columnWidths,
|
|
110
117
|
embeddingError,
|
|
111
118
|
embeddingStatus,
|
|
112
119
|
finishEmbedding,
|
|
@@ -242,7 +249,7 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
242
249
|
[createdAt],
|
|
243
250
|
);
|
|
244
251
|
|
|
245
|
-
const handleRenameStart = () => {
|
|
252
|
+
const handleRenameStart = useCallback(() => {
|
|
246
253
|
setIsRenaming(true);
|
|
247
254
|
setRenamingValue(name);
|
|
248
255
|
// Focus input after render
|
|
@@ -250,9 +257,9 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
250
257
|
inputRef.current?.focus();
|
|
251
258
|
inputRef.current?.select();
|
|
252
259
|
}, 0);
|
|
253
|
-
};
|
|
260
|
+
}, [name]);
|
|
254
261
|
|
|
255
|
-
const handleRenameConfirm = async () => {
|
|
262
|
+
const handleRenameConfirm = useCallback(async () => {
|
|
256
263
|
if (!renamingValue.trim()) {
|
|
257
264
|
message.error(t('FileManager.actions.renameError'));
|
|
258
265
|
return;
|
|
@@ -271,12 +278,12 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
271
278
|
console.error('Rename error:', error);
|
|
272
279
|
message.error(t('FileManager.actions.renameError'));
|
|
273
280
|
}
|
|
274
|
-
};
|
|
281
|
+
}, [renamingValue, name, fileStoreState.renameFolder, id, message, t]);
|
|
275
282
|
|
|
276
|
-
const handleRenameCancel = () => {
|
|
283
|
+
const handleRenameCancel = useCallback(() => {
|
|
277
284
|
setIsRenaming(false);
|
|
278
285
|
setRenamingValue(name);
|
|
279
|
-
};
|
|
286
|
+
}, [name]);
|
|
280
287
|
|
|
281
288
|
// Memoize click handler to prevent recreation on every render
|
|
282
289
|
const handleItemClick = useCallback(() => {
|
|
@@ -357,29 +364,42 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
357
364
|
paddingInline={8}
|
|
358
365
|
style={{
|
|
359
366
|
borderBlockEnd: `1px solid ${cssVar.colorBorderSecondary}`,
|
|
367
|
+
userSelect: 'none',
|
|
360
368
|
}}
|
|
361
369
|
>
|
|
370
|
+
<Center
|
|
371
|
+
height={40}
|
|
372
|
+
onClick={(e) => {
|
|
373
|
+
e.stopPropagation();
|
|
374
|
+
|
|
375
|
+
onSelectedChange(id, !selected, e.shiftKey, index);
|
|
376
|
+
}}
|
|
377
|
+
onPointerDown={(e) => {
|
|
378
|
+
e.stopPropagation();
|
|
379
|
+
// Prevent text selection when shift-clicking for batch selection
|
|
380
|
+
if (e.shiftKey) {
|
|
381
|
+
e.preventDefault();
|
|
382
|
+
}
|
|
383
|
+
}}
|
|
384
|
+
style={{ paddingInline: 4 }}
|
|
385
|
+
>
|
|
386
|
+
<Checkbox checked={selected} />
|
|
387
|
+
</Center>
|
|
362
388
|
<Flexbox
|
|
363
389
|
align={'center'}
|
|
364
390
|
className={styles.item}
|
|
365
391
|
distribution={'space-between'}
|
|
366
|
-
flex={1}
|
|
367
392
|
horizontal
|
|
368
393
|
onClick={handleItemClick}
|
|
394
|
+
style={{
|
|
395
|
+
flexShrink: 0,
|
|
396
|
+
maxWidth: columnWidths.name,
|
|
397
|
+
minWidth: columnWidths.name,
|
|
398
|
+
paddingInline: 8,
|
|
399
|
+
width: columnWidths.name,
|
|
400
|
+
}}
|
|
369
401
|
>
|
|
370
402
|
<Flexbox align={'center'} className={styles.nameContainer} horizontal>
|
|
371
|
-
<Center
|
|
372
|
-
height={48}
|
|
373
|
-
onClick={(e) => {
|
|
374
|
-
e.stopPropagation();
|
|
375
|
-
|
|
376
|
-
onSelectedChange(id, !selected, e.shiftKey, index);
|
|
377
|
-
}}
|
|
378
|
-
onPointerDown={(e) => e.stopPropagation()}
|
|
379
|
-
style={{ paddingInline: 4 }}
|
|
380
|
-
>
|
|
381
|
-
<Checkbox checked={selected} />
|
|
382
|
-
</Center>
|
|
383
403
|
<Flexbox
|
|
384
404
|
align={'center'}
|
|
385
405
|
justify={'center'}
|
|
@@ -479,10 +499,10 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
479
499
|
</Flexbox>
|
|
480
500
|
{!isDragging && (
|
|
481
501
|
<>
|
|
482
|
-
<Flexbox className={styles.item} width={
|
|
502
|
+
<Flexbox className={styles.item} style={{ flexShrink: 0 }} width={columnWidths.date}>
|
|
483
503
|
{displayTime}
|
|
484
504
|
</Flexbox>
|
|
485
|
-
<Flexbox className={styles.item} width={
|
|
505
|
+
<Flexbox className={styles.item} style={{ flexShrink: 0 }} width={columnWidths.size}>
|
|
486
506
|
{isFolder || isPage ? '-' : formatSize(size)}
|
|
487
507
|
</Flexbox>
|
|
488
508
|
</>
|
|
@@ -491,6 +511,31 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
491
511
|
</ContextMenuTrigger>
|
|
492
512
|
);
|
|
493
513
|
},
|
|
514
|
+
// Custom comparison function to prevent unnecessary re-renders
|
|
515
|
+
(prevProps, nextProps) => {
|
|
516
|
+
// Only re-render if these critical props change
|
|
517
|
+
return (
|
|
518
|
+
prevProps.id === nextProps.id &&
|
|
519
|
+
prevProps.name === nextProps.name &&
|
|
520
|
+
prevProps.selected === nextProps.selected &&
|
|
521
|
+
prevProps.chunkingStatus === nextProps.chunkingStatus &&
|
|
522
|
+
prevProps.embeddingStatus === nextProps.embeddingStatus &&
|
|
523
|
+
prevProps.chunkCount === nextProps.chunkCount &&
|
|
524
|
+
prevProps.chunkingError === nextProps.chunkingError &&
|
|
525
|
+
prevProps.embeddingError === nextProps.embeddingError &&
|
|
526
|
+
prevProps.finishEmbedding === nextProps.finishEmbedding &&
|
|
527
|
+
prevProps.pendingRenameItemId === nextProps.pendingRenameItemId &&
|
|
528
|
+
prevProps.size === nextProps.size &&
|
|
529
|
+
prevProps.createdAt === nextProps.createdAt &&
|
|
530
|
+
prevProps.fileType === nextProps.fileType &&
|
|
531
|
+
prevProps.sourceType === nextProps.sourceType &&
|
|
532
|
+
prevProps.slug === nextProps.slug &&
|
|
533
|
+
prevProps.url === nextProps.url &&
|
|
534
|
+
prevProps.columnWidths.name === nextProps.columnWidths.name &&
|
|
535
|
+
prevProps.columnWidths.date === nextProps.columnWidths.date &&
|
|
536
|
+
prevProps.columnWidths.size === nextProps.columnWidths.size
|
|
537
|
+
);
|
|
538
|
+
},
|
|
494
539
|
);
|
|
495
540
|
|
|
496
541
|
FileListItem.displayName = 'FileListItem';
|
|
@@ -1,20 +1,55 @@
|
|
|
1
|
-
import { Flexbox, Skeleton } from '@lobehub/ui';
|
|
1
|
+
import { Center, Checkbox, Flexbox, Skeleton } from '@lobehub/ui';
|
|
2
|
+
import { cssVar } from 'antd-style';
|
|
2
3
|
|
|
3
4
|
import { FILE_DATE_WIDTH, FILE_SIZE_WIDTH } from './ListItem';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
interface ListViewSkeletonProps {
|
|
7
|
+
columnWidths?: {
|
|
8
|
+
date: number;
|
|
9
|
+
name: number;
|
|
10
|
+
size: number;
|
|
11
|
+
};
|
|
12
|
+
count?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ListViewSkeleton = ({
|
|
16
|
+
columnWidths = { date: FILE_DATE_WIDTH, name: 400, size: FILE_SIZE_WIDTH },
|
|
17
|
+
count = 3,
|
|
18
|
+
}: ListViewSkeletonProps) => (
|
|
19
|
+
<Flexbox>
|
|
20
|
+
{Array.from({ length: count }).map((_, index) => (
|
|
21
|
+
<Flexbox
|
|
22
|
+
align={'center'}
|
|
23
|
+
height={48}
|
|
24
|
+
horizontal
|
|
25
|
+
key={index}
|
|
26
|
+
paddingInline={8}
|
|
27
|
+
style={{
|
|
28
|
+
borderBlockEnd: `1px solid ${cssVar.colorBorderSecondary}`,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<Center height={40} style={{ paddingInline: 4 }}>
|
|
32
|
+
<Checkbox disabled />
|
|
33
|
+
</Center>
|
|
34
|
+
<Flexbox
|
|
35
|
+
align={'center'}
|
|
36
|
+
horizontal
|
|
37
|
+
paddingInline={8}
|
|
38
|
+
style={{
|
|
39
|
+
flexShrink: 0,
|
|
40
|
+
maxWidth: columnWidths.name,
|
|
41
|
+
minWidth: columnWidths.name,
|
|
42
|
+
width: columnWidths.name,
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
10
45
|
<Skeleton.Avatar active shape={'square'} size={24} style={{ marginInline: 8 }} />
|
|
11
|
-
<Skeleton.Button active style={{ height: 16, width:
|
|
46
|
+
<Skeleton.Button active style={{ height: 16, width: '60%' }} />
|
|
12
47
|
</Flexbox>
|
|
13
|
-
<Flexbox paddingInline={24} width={
|
|
14
|
-
<Skeleton.Button active style={{ height: 16 }} />
|
|
48
|
+
<Flexbox paddingInline={24} style={{ flexShrink: 0 }} width={columnWidths.date}>
|
|
49
|
+
<Skeleton.Button active style={{ height: 16, width: '80%' }} />
|
|
15
50
|
</Flexbox>
|
|
16
|
-
<Flexbox paddingInline={24} width={
|
|
17
|
-
<Skeleton.Button active style={{ height: 16 }} />
|
|
51
|
+
<Flexbox paddingInline={24} style={{ flexShrink: 0 }} width={columnWidths.size}>
|
|
52
|
+
<Skeleton.Button active style={{ height: 16, width: '60%' }} />
|
|
18
53
|
</Flexbox>
|
|
19
54
|
</Flexbox>
|
|
20
55
|
))}
|