@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
import { Center, Flexbox, Icon } from '@lobehub/ui';
|
|
3
|
-
import {
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
4
|
import { FileImage, FileText, FileUpIcon } from 'lucide-react';
|
|
5
5
|
import { memo } from 'react';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
@@ -11,22 +11,22 @@ import { getContainer, useDragUpload } from './useDragUpload';
|
|
|
11
11
|
const BLOCK_SIZE = 64;
|
|
12
12
|
const ICON_SIZE = { size: 36, strokeWidth: 1.5 };
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
const useStyles = createStyles(({ css, token }) => {
|
|
15
15
|
return {
|
|
16
16
|
container: css`
|
|
17
17
|
width: 320px;
|
|
18
18
|
height: 200px;
|
|
19
|
-
padding: calc(${
|
|
19
|
+
padding: calc(${token.borderRadiusLG}px + 4px);
|
|
20
20
|
border-radius: 16px;
|
|
21
21
|
|
|
22
|
-
background: ${
|
|
22
|
+
background: ${token.geekblue};
|
|
23
23
|
`,
|
|
24
24
|
content: css`
|
|
25
25
|
width: 100%;
|
|
26
26
|
height: 100%;
|
|
27
27
|
padding: 16px;
|
|
28
28
|
border: 1.5px dashed #fff;
|
|
29
|
-
border-radius: ${
|
|
29
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
30
30
|
`,
|
|
31
31
|
desc: css`
|
|
32
32
|
font-size: 14px;
|
|
@@ -34,13 +34,25 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
34
34
|
color: #fff;
|
|
35
35
|
`,
|
|
36
36
|
icon: css`
|
|
37
|
-
border-radius: ${
|
|
38
|
-
color: color-mix(in srgb, ${
|
|
39
|
-
background: color-mix(in srgb, ${
|
|
37
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
38
|
+
color: color-mix(in srgb, ${token.geekblue} 95%, black);
|
|
39
|
+
background: color-mix(in srgb, ${token.geekblue} 38%, white);
|
|
40
40
|
`,
|
|
41
41
|
iconGroup: css`
|
|
42
42
|
margin-block-start: -44px;
|
|
43
43
|
`,
|
|
44
|
+
iconLeft: css`
|
|
45
|
+
transform: rotateZ(-20deg) translateX(10px);
|
|
46
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
47
|
+
color: color-mix(in srgb, ${token.geekblue} 95%, black);
|
|
48
|
+
background: color-mix(in srgb, ${token.geekblue} 68%, white);
|
|
49
|
+
`,
|
|
50
|
+
iconRight: css`
|
|
51
|
+
transform: rotateZ(20deg) translateX(-10px);
|
|
52
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
53
|
+
color: color-mix(in srgb, ${token.geekblue} 95%, black);
|
|
54
|
+
background: color-mix(in srgb, ${token.geekblue} 68%, white);
|
|
55
|
+
`,
|
|
44
56
|
title: css`
|
|
45
57
|
font-size: 20px;
|
|
46
58
|
font-weight: bold;
|
|
@@ -54,7 +66,7 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
54
66
|
width: 100%;
|
|
55
67
|
height: 100%;
|
|
56
68
|
|
|
57
|
-
background: ${
|
|
69
|
+
background: ${token.colorBgMask};
|
|
58
70
|
|
|
59
71
|
transition: all 0.3s ease-in-out;
|
|
60
72
|
`,
|
|
@@ -68,6 +80,7 @@ interface DragUploadProps {
|
|
|
68
80
|
|
|
69
81
|
const DragUpload = memo<DragUploadProps>(({ enabledFiles = true, onUploadFiles }) => {
|
|
70
82
|
const { t } = useTranslation('components');
|
|
83
|
+
const { styles } = useStyles();
|
|
71
84
|
|
|
72
85
|
const isDragging = useDragUpload(onUploadFiles);
|
|
73
86
|
|
|
@@ -78,15 +91,7 @@ const DragUpload = memo<DragUploadProps>(({ enabledFiles = true, onUploadFiles }
|
|
|
78
91
|
<div className={styles.container}>
|
|
79
92
|
<Center className={styles.content} gap={12}>
|
|
80
93
|
<Flexbox className={styles.iconGroup} horizontal>
|
|
81
|
-
<Center
|
|
82
|
-
className={styles.icon}
|
|
83
|
-
height={BLOCK_SIZE * 1.25}
|
|
84
|
-
style={{
|
|
85
|
-
background: `color-mix(in srgb, ${cssVar.geekblue} 68%, white)`,
|
|
86
|
-
transform: 'rotateZ(-20deg) translateX(10px)',
|
|
87
|
-
}}
|
|
88
|
-
width={BLOCK_SIZE}
|
|
89
|
-
>
|
|
94
|
+
<Center className={styles.iconLeft} height={BLOCK_SIZE * 1.25} width={BLOCK_SIZE}>
|
|
90
95
|
<Icon icon={FileImage} size={ICON_SIZE} />
|
|
91
96
|
</Center>
|
|
92
97
|
<Center
|
|
@@ -100,15 +105,7 @@ const DragUpload = memo<DragUploadProps>(({ enabledFiles = true, onUploadFiles }
|
|
|
100
105
|
>
|
|
101
106
|
<Icon icon={FileUpIcon} size={ICON_SIZE} />
|
|
102
107
|
</Center>
|
|
103
|
-
<Center
|
|
104
|
-
className={styles.icon}
|
|
105
|
-
height={BLOCK_SIZE * 1.25}
|
|
106
|
-
style={{
|
|
107
|
-
background: `color-mix(in srgb, ${cssVar.geekblue} 68%, white)`,
|
|
108
|
-
transform: 'rotateZ(20deg) translateX(-10px)',
|
|
109
|
-
}}
|
|
110
|
-
width={BLOCK_SIZE}
|
|
111
|
-
>
|
|
108
|
+
<Center className={styles.iconRight} height={BLOCK_SIZE * 1.25} width={BLOCK_SIZE}>
|
|
112
109
|
<Icon icon={FileText} size={ICON_SIZE} />
|
|
113
110
|
</Center>
|
|
114
111
|
</Flexbox>
|
|
@@ -162,11 +162,7 @@ export interface MemberSelectionModalProps {
|
|
|
162
162
|
*/
|
|
163
163
|
mode: MemberSelectionMode;
|
|
164
164
|
onCancel: () => void;
|
|
165
|
-
onConfirm: (
|
|
166
|
-
selectedAgents: string[],
|
|
167
|
-
hostConfig?: { model?: string; provider?: string },
|
|
168
|
-
enableSupervisor?: boolean,
|
|
169
|
-
) => void | Promise<void>;
|
|
165
|
+
onConfirm: (selectedAgents: string[]) => void | Promise<void>;
|
|
170
166
|
open: boolean;
|
|
171
167
|
/**
|
|
172
168
|
* Pre-selected agent IDs (useful for editing existing groups)
|
|
@@ -338,12 +334,7 @@ const MemberSelectionModal = memo<MemberSelectionModalProps>(
|
|
|
338
334
|
const handleConfirm = async () => {
|
|
339
335
|
try {
|
|
340
336
|
setIsAdding(true);
|
|
341
|
-
|
|
342
|
-
const shouldManageHost = !isHostCurrentlyEnabled;
|
|
343
|
-
const hostConfig =
|
|
344
|
-
shouldManageHost && !isHostRemoved ? normalizedHostModelConfig : undefined;
|
|
345
|
-
const enableSupervisor = shouldManageHost ? !isHostRemoved : undefined;
|
|
346
|
-
await onConfirm(selectedAgents, hostConfig, enableSupervisor);
|
|
337
|
+
await onConfirm(selectedAgents);
|
|
347
338
|
handleReset();
|
|
348
339
|
} catch (error) {
|
|
349
340
|
console.error('Failed to confirm action:', error);
|
|
@@ -169,21 +169,11 @@ export const useCommandMenu = () => {
|
|
|
169
169
|
const handleCreateAgentTeam = async () => {
|
|
170
170
|
closeCommandMenu();
|
|
171
171
|
openGroupWizard({
|
|
172
|
-
onCreateCustom: async (selectedAgents
|
|
173
|
-
await createGroupWithMembers(selectedAgents
|
|
172
|
+
onCreateCustom: async (selectedAgents) => {
|
|
173
|
+
await createGroupWithMembers(selectedAgents);
|
|
174
174
|
},
|
|
175
|
-
onCreateFromTemplate: async (
|
|
176
|
-
templateId,
|
|
177
|
-
hostConfig,
|
|
178
|
-
enableSupervisor,
|
|
179
|
-
selectedMemberTitles,
|
|
180
|
-
) => {
|
|
181
|
-
await createGroupFromTemplate(
|
|
182
|
-
templateId,
|
|
183
|
-
hostConfig,
|
|
184
|
-
enableSupervisor,
|
|
185
|
-
selectedMemberTitles,
|
|
186
|
-
);
|
|
175
|
+
onCreateFromTemplate: async (templateId, selectedMemberTitles) => {
|
|
176
|
+
await createGroupFromTemplate(templateId, selectedMemberTitles);
|
|
187
177
|
},
|
|
188
178
|
});
|
|
189
179
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { type FC } from 'react';
|
|
5
|
+
|
|
6
|
+
import { ProductLogo } from '@/components/Branding/ProductLogo';
|
|
7
|
+
import { electronStylish } from '@/styles/electron';
|
|
8
|
+
|
|
9
|
+
import { TITLE_BAR_HEIGHT } from './const';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A simple, minimal TitleBar for Electron windows.
|
|
13
|
+
* Provides draggable area without business logic (navigation, updates, etc.)
|
|
14
|
+
* Use this for secondary windows like onboarding, settings, etc.
|
|
15
|
+
*/
|
|
16
|
+
const SimpleTitleBar: FC = () => {
|
|
17
|
+
return (
|
|
18
|
+
<Flexbox
|
|
19
|
+
align={'center'}
|
|
20
|
+
className={electronStylish.draggable}
|
|
21
|
+
height={TITLE_BAR_HEIGHT}
|
|
22
|
+
horizontal
|
|
23
|
+
justify={'center'}
|
|
24
|
+
width={'100%'}
|
|
25
|
+
>
|
|
26
|
+
<ProductLogo size={16} type={'text'} />
|
|
27
|
+
</Flexbox>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default SimpleTitleBar;
|
|
@@ -4,7 +4,7 @@ import { BUILTIN_AGENT_SLUGS } from '@lobechat/builtin-agents';
|
|
|
4
4
|
import { ActionIcon, Flexbox } from '@lobehub/ui';
|
|
5
5
|
import { Modal } from 'antd';
|
|
6
6
|
import { cssVar, useTheme } from 'antd-style';
|
|
7
|
-
import { ArrowLeftIcon,
|
|
7
|
+
import { ArrowLeftIcon, DownloadIcon, InfoIcon } from 'lucide-react';
|
|
8
8
|
import { memo, useState } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
|
|
@@ -13,7 +13,6 @@ import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/featur
|
|
|
13
13
|
import Loading from '@/components/Loading/BrandTextLoading';
|
|
14
14
|
import NavHeader from '@/features/NavHeader';
|
|
15
15
|
import PageAgentProvider from '@/features/PageEditor/PageAgentProvider';
|
|
16
|
-
import ToggleRightPanelButton from '@/features/RightPanel/ToggleRightPanelButton';
|
|
17
16
|
import { useAgentStore } from '@/store/agent';
|
|
18
17
|
import { builtinAgentSelectors } from '@/store/agent/selectors';
|
|
19
18
|
import { fileManagerSelectors, useFileStore } from '@/store/file';
|
|
@@ -56,7 +55,7 @@ const FileEditorCanvas = memo<FileEditorProps>(({ onBack }) => {
|
|
|
56
55
|
}
|
|
57
56
|
right={
|
|
58
57
|
<Flexbox gap={8} horizontal>
|
|
59
|
-
<ToggleRightPanelButton icon={BotMessageSquareIcon} showActive={true} size={20} />
|
|
58
|
+
{/* <ToggleRightPanelButton icon={BotMessageSquareIcon} showActive={true} size={20} /> */}
|
|
60
59
|
{fileDetail?.url && (
|
|
61
60
|
<ActionIcon
|
|
62
61
|
icon={DownloadIcon}
|
|
@@ -29,18 +29,18 @@ const Header = memo(() => {
|
|
|
29
29
|
]);
|
|
30
30
|
const toggleCommandMenu = useGlobalStore((s) => s.toggleCommandMenu);
|
|
31
31
|
|
|
32
|
-
//
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
// If no libraryId, show category name or "Resource" for All
|
|
33
|
+
const leftContent = !libraryId ? (
|
|
34
|
+
<Flexbox style={{ marginLeft: 8 }}>
|
|
35
|
+
{category === FilesTabs.All
|
|
36
|
+
? t('resource', { defaultValue: 'Resource' })
|
|
37
|
+
: t(`tab.${category as FilesTabs}` as any)}
|
|
38
|
+
</Flexbox>
|
|
39
|
+
) : (
|
|
40
|
+
<Flexbox style={{ marginLeft: 8 }}>
|
|
41
|
+
<Breadcrumb category={category} knowledgeBaseId={libraryId} />
|
|
42
|
+
</Flexbox>
|
|
43
|
+
);
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
46
|
<NavHeader
|
|
@@ -49,11 +49,7 @@ const Header = memo(() => {
|
|
|
49
49
|
<>
|
|
50
50
|
<ActionIcon icon={SearchIcon} onClick={() => toggleCommandMenu(true)} />
|
|
51
51
|
<SortDropdown />
|
|
52
|
-
<BatchActionsDropdown
|
|
53
|
-
disabled={isBatchActionsDisabled}
|
|
54
|
-
onActionClick={onActionClick}
|
|
55
|
-
selectCount={selectFileIds.length}
|
|
56
|
-
/>
|
|
52
|
+
<BatchActionsDropdown onActionClick={onActionClick} selectCount={selectFileIds.length} />
|
|
57
53
|
<ViewSwitcher />
|
|
58
54
|
<Flexbox style={{ marginLeft: 8 }}>
|
|
59
55
|
<AddButton />
|
package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
15
15
|
import { shallow } from 'zustand/shallow';
|
|
16
16
|
|
|
17
17
|
import RepoIcon from '@/components/LibIcon';
|
|
18
|
-
import { clearTreeFolderCache } from '@/features/ResourceManager/components/
|
|
18
|
+
import { clearTreeFolderCache } from '@/features/ResourceManager/components/LibraryHierarchy';
|
|
19
19
|
import { PAGE_FILE_TYPE } from '@/features/ResourceManager/constants';
|
|
20
20
|
import { documentService } from '@/services/document';
|
|
21
21
|
import { useFileStore } from '@/store/file';
|
package/src/features/ResourceManager/components/Explorer/ListView/ListItem/TruncatedFileName.tsx
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { memo, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
interface TruncatedFileNameProps {
|
|
4
|
+
className?: string;
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Truncates file name from the center, preserving the extension at the end
|
|
10
|
+
* Similar to macOS Finder behavior
|
|
11
|
+
*/
|
|
12
|
+
const TruncatedFileName = memo<TruncatedFileNameProps>(({ name, className }) => {
|
|
13
|
+
const containerRef = useRef<HTMLSpanElement>(null);
|
|
14
|
+
const [displayName, setDisplayName] = useState(name);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const container = containerRef.current;
|
|
18
|
+
if (!container) return;
|
|
19
|
+
|
|
20
|
+
const updateTruncation = () => {
|
|
21
|
+
const containerWidth = container.offsetWidth;
|
|
22
|
+
if (containerWidth === 0) return;
|
|
23
|
+
|
|
24
|
+
// Create a temporary span to measure text width
|
|
25
|
+
const measureSpan = document.createElement('span');
|
|
26
|
+
measureSpan.style.visibility = 'hidden';
|
|
27
|
+
measureSpan.style.position = 'absolute';
|
|
28
|
+
measureSpan.style.whiteSpace = 'nowrap';
|
|
29
|
+
measureSpan.style.font = window.getComputedStyle(container).font;
|
|
30
|
+
document.body.append(measureSpan);
|
|
31
|
+
|
|
32
|
+
// Measure full name
|
|
33
|
+
measureSpan.textContent = name;
|
|
34
|
+
const fullWidth = measureSpan.offsetWidth;
|
|
35
|
+
|
|
36
|
+
// If it fits, show the full name
|
|
37
|
+
if (fullWidth <= containerWidth) {
|
|
38
|
+
setDisplayName(name);
|
|
39
|
+
measureSpan.remove();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Split filename and extension
|
|
44
|
+
const lastDotIndex = name.lastIndexOf('.');
|
|
45
|
+
let baseName = name;
|
|
46
|
+
let extension = '';
|
|
47
|
+
|
|
48
|
+
// Only treat as extension if dot is not at the start and there's content after it
|
|
49
|
+
if (lastDotIndex > 0 && lastDotIndex < name.length - 1) {
|
|
50
|
+
baseName = name.slice(0, lastDotIndex);
|
|
51
|
+
extension = name.slice(lastDotIndex); // includes the dot
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Measure ellipsis width
|
|
55
|
+
measureSpan.textContent = '...';
|
|
56
|
+
const ellipsisWidth = measureSpan.offsetWidth;
|
|
57
|
+
|
|
58
|
+
// Measure extension width
|
|
59
|
+
measureSpan.textContent = extension;
|
|
60
|
+
const extensionWidth = measureSpan.offsetWidth;
|
|
61
|
+
|
|
62
|
+
// Calculate available width for base name
|
|
63
|
+
const availableWidth = containerWidth - ellipsisWidth - extensionWidth;
|
|
64
|
+
|
|
65
|
+
if (availableWidth <= 0) {
|
|
66
|
+
// Not enough space, just show ellipsis + extension
|
|
67
|
+
setDisplayName(`...${extension}`);
|
|
68
|
+
measureSpan.remove();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Binary search to find the optimal split point
|
|
73
|
+
let left = 0;
|
|
74
|
+
let right = baseName.length;
|
|
75
|
+
let bestFit = '';
|
|
76
|
+
|
|
77
|
+
while (left <= right) {
|
|
78
|
+
const mid = Math.floor((left + right) / 2);
|
|
79
|
+
const startChars = Math.ceil(mid / 2);
|
|
80
|
+
const endChars = Math.floor(mid / 2);
|
|
81
|
+
|
|
82
|
+
const truncated =
|
|
83
|
+
baseName.slice(0, startChars) + (mid > 0 ? baseName.slice(-endChars) : '');
|
|
84
|
+
|
|
85
|
+
measureSpan.textContent = truncated;
|
|
86
|
+
const truncatedWidth = measureSpan.offsetWidth;
|
|
87
|
+
|
|
88
|
+
if (truncatedWidth <= availableWidth) {
|
|
89
|
+
bestFit = truncated;
|
|
90
|
+
left = mid + 1;
|
|
91
|
+
} else {
|
|
92
|
+
right = mid - 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
measureSpan.remove();
|
|
97
|
+
|
|
98
|
+
// Construct final truncated name
|
|
99
|
+
if (bestFit.length === 0) {
|
|
100
|
+
setDisplayName(`...${extension}`);
|
|
101
|
+
} else {
|
|
102
|
+
const startChars = Math.ceil(bestFit.length / 2);
|
|
103
|
+
const endChars = Math.floor(bestFit.length / 2);
|
|
104
|
+
setDisplayName(
|
|
105
|
+
`${baseName.slice(0, startChars)}...${baseName.slice(-endChars)}${extension}`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
updateTruncation();
|
|
111
|
+
|
|
112
|
+
// Use ResizeObserver to handle container size changes
|
|
113
|
+
const resizeObserver = new ResizeObserver(updateTruncation);
|
|
114
|
+
resizeObserver.observe(container);
|
|
115
|
+
|
|
116
|
+
return () => {
|
|
117
|
+
resizeObserver.disconnect();
|
|
118
|
+
};
|
|
119
|
+
}, [name]);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<span className={className} ref={containerRef} title={name}>
|
|
123
|
+
{displayName}
|
|
124
|
+
</span>
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
TruncatedFileName.displayName = 'TruncatedFileName';
|
|
129
|
+
|
|
130
|
+
export default TruncatedFileName;
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
|
|
18
18
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
19
19
|
import FileIcon from '@/components/FileIcon';
|
|
20
|
-
import { clearTreeFolderCache } from '@/features/ResourceManager/components/
|
|
20
|
+
import { clearTreeFolderCache } from '@/features/ResourceManager/components/LibraryHierarchy';
|
|
21
21
|
import { PAGE_FILE_TYPE } from '@/features/ResourceManager/constants';
|
|
22
22
|
import { fileManagerSelectors, useFileStore } from '@/store/file';
|
|
23
23
|
import { type FileListItem as FileListItemType } from '@/types/files';
|
|
@@ -27,6 +27,7 @@ import { isChunkingUnsupported } from '@/utils/isChunkingUnsupported';
|
|
|
27
27
|
import DropdownMenu from '../../ItemDropdown/DropdownMenu';
|
|
28
28
|
import { useFileItemDropdown } from '../../ItemDropdown/useFileItemDropdown';
|
|
29
29
|
import ChunksBadge from './ChunkTag';
|
|
30
|
+
import TruncatedFileName from './TruncatedFileName';
|
|
30
31
|
|
|
31
32
|
// Initialize dayjs plugin once at module level
|
|
32
33
|
dayjs.extend(relativeTime);
|
|
@@ -40,6 +41,7 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
40
41
|
cursor: pointer;
|
|
41
42
|
min-width: 800px;
|
|
42
43
|
|
|
44
|
+
/* Hover effect for individual rows */
|
|
43
45
|
&:hover {
|
|
44
46
|
background: ${cssVar.colorFillTertiary};
|
|
45
47
|
}
|
|
@@ -59,6 +61,25 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
59
61
|
opacity: 0.5;
|
|
60
62
|
`,
|
|
61
63
|
|
|
64
|
+
evenRow: css`
|
|
65
|
+
background: ${cssVar.colorFillQuaternary};
|
|
66
|
+
|
|
67
|
+
/* Hover effect overrides zebra striping on the hovered row only */
|
|
68
|
+
&:hover {
|
|
69
|
+
background: ${cssVar.colorFillTertiary};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Hide zebra striping when any row is hovered */
|
|
73
|
+
.any-row-hovered & {
|
|
74
|
+
background: transparent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* But keep hover effect on the actual hovered row */
|
|
78
|
+
.any-row-hovered &:hover {
|
|
79
|
+
background: ${cssVar.colorFillTertiary};
|
|
80
|
+
}
|
|
81
|
+
`,
|
|
82
|
+
|
|
62
83
|
hover: css`
|
|
63
84
|
opacity: 0;
|
|
64
85
|
|
|
@@ -80,7 +101,6 @@ const styles = createStaticStyles(({ css }) => {
|
|
|
80
101
|
margin-inline-start: 12px;
|
|
81
102
|
|
|
82
103
|
color: ${cssVar.colorText};
|
|
83
|
-
text-overflow: ellipsis;
|
|
84
104
|
white-space: nowrap;
|
|
85
105
|
`,
|
|
86
106
|
nameContainer: css`
|
|
@@ -105,6 +125,8 @@ interface FileListItemProps extends FileListItemType {
|
|
|
105
125
|
size: number;
|
|
106
126
|
};
|
|
107
127
|
index: number;
|
|
128
|
+
isAnyRowHovered: boolean;
|
|
129
|
+
onHoverChange: (isHovered: boolean) => void;
|
|
108
130
|
onSelectedChange: (id: string, selected: boolean, shiftKey: boolean, index: number) => void;
|
|
109
131
|
pendingRenameItemId?: string | null;
|
|
110
132
|
selected?: boolean;
|
|
@@ -133,6 +155,7 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
133
155
|
sourceType,
|
|
134
156
|
slug,
|
|
135
157
|
pendingRenameItemId,
|
|
158
|
+
onHoverChange,
|
|
136
159
|
}) => {
|
|
137
160
|
const { t } = useTranslation(['components', 'file']);
|
|
138
161
|
const { message } = App.useApp();
|
|
@@ -376,12 +399,14 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
376
399
|
className={cx(
|
|
377
400
|
styles.container,
|
|
378
401
|
'file-list-item-group',
|
|
402
|
+
index % 2 === 0 && styles.evenRow,
|
|
379
403
|
selected && styles.selected,
|
|
380
404
|
isDragging && styles.dragging,
|
|
381
405
|
isOver && styles.dragOver,
|
|
382
406
|
)}
|
|
383
407
|
data-drop-target-id={id}
|
|
384
408
|
data-is-folder={String(isFolder)}
|
|
409
|
+
data-row-index={index}
|
|
385
410
|
draggable={!!resourceManagerState.libraryId}
|
|
386
411
|
height={48}
|
|
387
412
|
horizontal
|
|
@@ -390,6 +415,8 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
390
415
|
onDragOver={handleDragOver}
|
|
391
416
|
onDragStart={handleDragStart}
|
|
392
417
|
onDrop={handleDrop}
|
|
418
|
+
onMouseEnter={() => onHoverChange(true)}
|
|
419
|
+
onMouseLeave={() => onHoverChange(false)}
|
|
393
420
|
paddingInline={8}
|
|
394
421
|
style={{
|
|
395
422
|
borderBlockEnd: `1px solid ${cssVar.colorBorderSecondary}`,
|
|
@@ -469,7 +496,10 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
469
496
|
value={renamingValue}
|
|
470
497
|
/>
|
|
471
498
|
) : (
|
|
472
|
-
<
|
|
499
|
+
<TruncatedFileName
|
|
500
|
+
className={styles.name}
|
|
501
|
+
name={name || t('file:pageList.untitled')}
|
|
502
|
+
/>
|
|
473
503
|
)}
|
|
474
504
|
</Flexbox>
|
|
475
505
|
<Flexbox
|
|
@@ -482,6 +512,7 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
482
512
|
onPointerDown={(e) => e.stopPropagation()}
|
|
483
513
|
>
|
|
484
514
|
{!isFolder &&
|
|
515
|
+
!isPage &&
|
|
485
516
|
(fileStoreState.isCreatingFileParseTask ||
|
|
486
517
|
isNull(chunkingStatus) ||
|
|
487
518
|
!chunkingStatus ? (
|
|
@@ -562,7 +593,8 @@ const FileListItem = memo<FileListItemProps>(
|
|
|
562
593
|
prevProps.url === nextProps.url &&
|
|
563
594
|
prevProps.columnWidths.name === nextProps.columnWidths.name &&
|
|
564
595
|
prevProps.columnWidths.date === nextProps.columnWidths.date &&
|
|
565
|
-
prevProps.columnWidths.size === nextProps.columnWidths.size
|
|
596
|
+
prevProps.columnWidths.size === nextProps.columnWidths.size &&
|
|
597
|
+
prevProps.isAnyRowHovered === nextProps.isAnyRowHovered
|
|
566
598
|
);
|
|
567
599
|
},
|
|
568
600
|
);
|
|
@@ -29,6 +29,7 @@ const ListViewSkeleton = ({
|
|
|
29
29
|
key={index}
|
|
30
30
|
paddingInline={8}
|
|
31
31
|
style={{
|
|
32
|
+
background: index % 2 === 0 ? cssVar.colorFillQuaternary : 'transparent',
|
|
32
33
|
borderBlockEnd: `1px solid ${cssVar.colorBorderSecondary}`,
|
|
33
34
|
opacity: getOpacity(index),
|
|
34
35
|
}}
|
|
@@ -39,21 +40,21 @@ const ListViewSkeleton = ({
|
|
|
39
40
|
<Flexbox
|
|
40
41
|
align={'center'}
|
|
41
42
|
horizontal
|
|
42
|
-
paddingInline={8}
|
|
43
43
|
style={{
|
|
44
44
|
flexShrink: 0,
|
|
45
45
|
maxWidth: columnWidths.name,
|
|
46
46
|
minWidth: columnWidths.name,
|
|
47
|
+
paddingInline: 8,
|
|
47
48
|
width: columnWidths.name,
|
|
48
49
|
}}
|
|
49
50
|
>
|
|
50
51
|
<Skeleton.Avatar active shape={'square'} size={24} style={{ marginInline: 8 }} />
|
|
51
52
|
<Skeleton.Button active style={{ height: 16, width: '60%' }} />
|
|
52
53
|
</Flexbox>
|
|
53
|
-
<Flexbox
|
|
54
|
+
<Flexbox style={{ flexShrink: 0, paddingInline: '0 24px' }} width={columnWidths.date}>
|
|
54
55
|
<Skeleton.Button active style={{ height: 16, width: '80%' }} />
|
|
55
56
|
</Flexbox>
|
|
56
|
-
<Flexbox
|
|
57
|
+
<Flexbox style={{ flexShrink: 0, paddingInline: '0 24px' }} width={columnWidths.size}>
|
|
57
58
|
<Skeleton.Button active style={{ height: 16, width: '60%' }} />
|
|
58
59
|
</Flexbox>
|
|
59
60
|
</Flexbox>
|