@lobehub/lobehub 2.0.0-next.53 → 2.0.0-next.55

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.
Files changed (165) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/ar/common.json +1 -0
  4. package/locales/ar/file.json +85 -2
  5. package/locales/bg-BG/common.json +1 -0
  6. package/locales/bg-BG/file.json +85 -2
  7. package/locales/de-DE/common.json +1 -0
  8. package/locales/de-DE/file.json +85 -2
  9. package/locales/en-US/common.json +1 -0
  10. package/locales/en-US/file.json +85 -2
  11. package/locales/es-ES/common.json +1 -0
  12. package/locales/es-ES/file.json +85 -2
  13. package/locales/fa-IR/common.json +1 -0
  14. package/locales/fa-IR/file.json +85 -2
  15. package/locales/fr-FR/common.json +1 -0
  16. package/locales/fr-FR/file.json +85 -2
  17. package/locales/it-IT/common.json +1 -0
  18. package/locales/it-IT/file.json +85 -2
  19. package/locales/ja-JP/common.json +1 -0
  20. package/locales/ja-JP/file.json +85 -2
  21. package/locales/ko-KR/common.json +1 -0
  22. package/locales/ko-KR/file.json +85 -2
  23. package/locales/nl-NL/common.json +1 -0
  24. package/locales/nl-NL/file.json +85 -2
  25. package/locales/pl-PL/common.json +1 -0
  26. package/locales/pl-PL/file.json +85 -2
  27. package/locales/pt-BR/common.json +1 -0
  28. package/locales/pt-BR/file.json +85 -2
  29. package/locales/ru-RU/common.json +1 -0
  30. package/locales/ru-RU/file.json +85 -2
  31. package/locales/tr-TR/common.json +1 -0
  32. package/locales/tr-TR/file.json +85 -2
  33. package/locales/vi-VN/common.json +1 -0
  34. package/locales/vi-VN/file.json +85 -2
  35. package/locales/zh-CN/common.json +1 -0
  36. package/locales/zh-CN/file.json +85 -2
  37. package/locales/zh-TW/common.json +1 -0
  38. package/locales/zh-TW/file.json +85 -2
  39. package/package.json +1 -1
  40. package/packages/database/src/models/__tests__/file.test.ts +94 -29
  41. package/packages/database/src/models/file.ts +15 -4
  42. package/packages/database/src/repositories/knowledge/index.test.ts +300 -0
  43. package/packages/database/src/repositories/knowledge/index.ts +420 -0
  44. package/packages/model-bank/src/aiModels/aihubmix.ts +1 -0
  45. package/packages/model-bank/src/aiModels/google.ts +9 -5
  46. package/packages/model-bank/src/aiModels/openai.ts +2 -35
  47. package/packages/model-bank/src/aiModels/openrouter.ts +1 -0
  48. package/packages/model-bank/src/aiModels/vertexai.ts +2 -0
  49. package/packages/model-bank/src/types/aiModel.ts +15 -2
  50. package/packages/model-runtime/src/core/usageConverters/index.ts +1 -0
  51. package/packages/model-runtime/src/core/usageConverters/utils/resolveImageSinglePrice.ts +34 -0
  52. package/packages/types/src/document/index.ts +14 -2
  53. package/packages/types/src/files/index.ts +2 -0
  54. package/packages/types/src/files/list.ts +10 -0
  55. package/packages/types/src/llm.ts +1 -1
  56. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect/ImageModelItem.tsx +93 -0
  57. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/{ModelSelect.tsx → ModelSelect/index.tsx} +17 -2
  58. package/src/app/[variants]/(main)/knowledge/KnowledgeRouter.tsx +2 -1
  59. package/src/app/[variants]/(main)/knowledge/components/KnowledgeBaseItem/index.tsx +0 -2
  60. package/src/app/[variants]/(main)/knowledge/hooks/useFileCategory.ts +6 -3
  61. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/index.tsx +2 -2
  62. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/menu/{MenuItems.tsx → CategoryMenu.tsx} +3 -3
  63. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/menu/Menu.tsx +2 -2
  64. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/index.tsx +40 -18
  65. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/layout/Container.tsx +1 -1
  66. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/CategoryMenu.tsx +148 -0
  67. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/KnowledgeBase.tsx +20 -7
  68. package/src/components/FileIcon/index.tsx +3 -1
  69. package/src/features/ChatInput/ActionBar/Knowledge/index.tsx +2 -2
  70. package/src/features/Conversation/Messages/Assistant/index.tsx +7 -1
  71. package/src/features/FileSidePanel/index.tsx +1 -1
  72. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/MasonryItem.tsx +80 -0
  73. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/MasonryItemWrapper.tsx +27 -0
  74. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/List.tsx +104 -23
  75. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/MasonrySkeleton.tsx +62 -0
  76. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/index.tsx +3 -2
  77. package/src/features/KnowledgeBaseModal/CreateNew/CreateForm.tsx +1 -1
  78. package/src/features/KnowledgeManager/DocumentExplorer/DocumentActions.tsx +111 -0
  79. package/src/features/KnowledgeManager/DocumentExplorer/DocumentEditor.tsx +723 -0
  80. package/src/features/KnowledgeManager/DocumentExplorer/DocumentEditorPlaceholder.tsx +169 -0
  81. package/src/features/KnowledgeManager/DocumentExplorer/DocumentListItem.tsx +148 -0
  82. package/src/features/KnowledgeManager/DocumentExplorer/DocumentListSkeleton.tsx +39 -0
  83. package/src/features/KnowledgeManager/DocumentExplorer/NoteEditorModal.tsx +348 -0
  84. package/src/features/KnowledgeManager/DocumentExplorer/RenamePopover.tsx +163 -0
  85. package/src/features/KnowledgeManager/DocumentExplorer/index.tsx +318 -0
  86. package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileListItem/index.tsx +48 -9
  87. package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/DefaultFileItem.tsx +149 -0
  88. package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/ImageFileItem.tsx +245 -0
  89. package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/MarkdownFileItem.tsx +232 -0
  90. package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/NoteFileItem.tsx +230 -0
  91. package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/index.tsx +398 -0
  92. package/src/features/KnowledgeManager/FileExplorer/ToolBar/ViewSwitcher.tsx +45 -0
  93. package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/index.tsx +68 -16
  94. package/src/features/KnowledgeManager/Header/AddButton.tsx +107 -0
  95. package/src/features/KnowledgeManager/Header/NewNoteButton.tsx +33 -0
  96. package/src/features/{FileManager → KnowledgeManager}/Header/index.tsx +3 -9
  97. package/src/features/KnowledgeManager/Home/RecentDocumentCard.tsx +116 -0
  98. package/src/features/KnowledgeManager/Home/RecentDocuments.tsx +77 -0
  99. package/src/features/KnowledgeManager/Home/RecentFileCard.tsx +121 -0
  100. package/src/features/KnowledgeManager/Home/RecentFiles.tsx +73 -0
  101. package/src/features/KnowledgeManager/Home/RecentFilesSkeleton.tsx +83 -0
  102. package/src/features/KnowledgeManager/Home/UploadEntries.tsx +208 -0
  103. package/src/features/KnowledgeManager/Home/index.tsx +221 -0
  104. package/src/features/KnowledgeManager/index.tsx +75 -0
  105. package/src/features/Portal/FilePreview/Body/index.tsx +1 -1
  106. package/src/features/Portal/FilePreview/Header.tsx +1 -1
  107. package/src/locales/default/common.ts +1 -0
  108. package/src/locales/default/file.ts +85 -2
  109. package/src/locales/default/tool.ts +8 -0
  110. package/src/server/routers/lambda/__tests__/file.test.ts +85 -6
  111. package/src/server/routers/lambda/document.ts +57 -0
  112. package/src/server/routers/lambda/file.ts +72 -0
  113. package/src/server/routers/lambda/knowledge.ts +94 -0
  114. package/src/server/services/document/index.ts +103 -0
  115. package/src/services/document/index.ts +44 -0
  116. package/src/services/file/index.ts +5 -3
  117. package/src/store/aiInfra/slices/aiProvider/__tests__/action.test.ts +125 -229
  118. package/src/store/aiInfra/slices/aiProvider/action.ts +113 -33
  119. package/src/store/chat/slices/builtinTool/actions/localSystem.ts +1 -1
  120. package/src/store/file/initialState.ts +6 -1
  121. package/src/store/file/slices/chat/action.ts +3 -3
  122. package/src/store/file/slices/document/action.ts +359 -0
  123. package/src/store/file/slices/document/index.ts +3 -0
  124. package/src/store/file/slices/document/initialState.ts +22 -0
  125. package/src/store/file/slices/document/selectors.ts +25 -0
  126. package/src/store/file/slices/fileManager/action.test.ts +16 -9
  127. package/src/store/file/slices/fileManager/action.ts +11 -11
  128. package/src/store/file/store.ts +3 -0
  129. package/src/store/global/initialState.ts +3 -1
  130. package/src/tools/interventions.ts +3 -5
  131. package/src/tools/local-system/Intervention/MoveLocalFiles/MoveFileItem.tsx +56 -0
  132. package/src/tools/local-system/Intervention/MoveLocalFiles/index.tsx +26 -0
  133. package/src/tools/local-system/Intervention/RunCommand/index.tsx +1 -2
  134. package/src/tools/local-system/Intervention/index.ts +11 -0
  135. package/src/tools/local-system/Render/MoveLocalFiles/MoveFileItem.tsx +56 -0
  136. package/src/tools/local-system/Render/MoveLocalFiles/index.tsx +26 -0
  137. package/src/tools/local-system/Render/index.ts +21 -0
  138. package/src/tools/renders.ts +6 -24
  139. package/src/tools/web-browsing/Render/index.ts +13 -0
  140. package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/FileMenu.tsx +0 -75
  141. package/src/features/FileManager/FileList/MasonryFileItem/index.tsx +0 -582
  142. package/src/features/FileManager/index.tsx +0 -36
  143. /package/src/features/{FileManager/FileList/ToolBar → KnowledgeBaseModal/AssignKnowledgeBase}/ViewSwitcher.tsx +0 -0
  144. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/ChunkList/ChunkItem.tsx +0 -0
  145. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/ChunkList/index.tsx +0 -0
  146. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/Content.tsx +0 -0
  147. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/Loading/index.tsx +0 -0
  148. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/SimilaritySearchList/Item.tsx +0 -0
  149. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/SimilaritySearchList/index.tsx +0 -0
  150. /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/index.tsx +0 -0
  151. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/EmptyStatus.tsx +0 -0
  152. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileListItem/ChunkTag.tsx +0 -0
  153. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileListItem/DropdownMenu.tsx +0 -0
  154. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileSkeleton.tsx +0 -0
  155. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/MasonryFileItem/MasonryItemWrapper.tsx +0 -0
  156. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/MasonrySkeleton.tsx +0 -0
  157. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/ToolBar/Config.tsx +0 -0
  158. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/ToolBar/MultiSelectActions.tsx +0 -0
  159. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/ToolBar/index.tsx +0 -0
  160. /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/useCheckTaskStatus.ts +0 -0
  161. /package/src/features/{FileManager → KnowledgeManager}/Header/FilesSearchBar.tsx +0 -0
  162. /package/src/features/{FileManager → KnowledgeManager}/Header/TogglePanelButton.tsx +0 -0
  163. /package/src/features/{FileManager → KnowledgeManager}/Header/UploadFileButton.tsx +0 -0
  164. /package/src/features/{FileManager → KnowledgeManager}/UploadDock/Item.tsx +0 -0
  165. /package/src/features/{FileManager → KnowledgeManager}/UploadDock/index.tsx +0 -0
@@ -0,0 +1,73 @@
1
+ 'use client';
2
+
3
+ import { createStyles } from 'antd-style';
4
+ import { memo } from 'react';
5
+
6
+ import { FileListItem } from '@/types/files';
7
+
8
+ import RecentFileCard from './RecentFileCard';
9
+ import RecentFilesSkeleton from './RecentFilesSkeleton';
10
+
11
+ const useStyles = createStyles(({ css, token }) => ({
12
+ container: css`
13
+ position: relative;
14
+ overflow: hidden;
15
+ `,
16
+ fadeEdge: css`
17
+ pointer-events: none;
18
+
19
+ position: absolute;
20
+ inset-block: 0 0;
21
+ inset-inline-end: 0;
22
+
23
+ width: 80px;
24
+
25
+ background: linear-gradient(to left, ${token.colorBgContainerSecondary}, transparent);
26
+ `,
27
+ scrollContainer: css`
28
+ scroll-behavior: smooth;
29
+
30
+ /* Hide scrollbar */
31
+ scrollbar-width: none;
32
+
33
+ overflow: auto hidden;
34
+ display: flex;
35
+ gap: 16px;
36
+
37
+ padding-block-end: 8px;
38
+ padding-inline-end: 80px;
39
+
40
+ -ms-overflow-style: none;
41
+
42
+ &::-webkit-scrollbar {
43
+ display: none;
44
+ }
45
+ `,
46
+ }));
47
+
48
+ interface RecentFilesProps {
49
+ files: FileListItem[];
50
+ isLoading?: boolean;
51
+ onOpenFile: (id: string) => void;
52
+ }
53
+
54
+ const RecentFiles = memo<RecentFilesProps>(({ files, isLoading, onOpenFile }) => {
55
+ const { styles } = useStyles();
56
+
57
+ if (isLoading) {
58
+ return <RecentFilesSkeleton />;
59
+ }
60
+
61
+ return (
62
+ <div className={styles.container}>
63
+ <div className={styles.scrollContainer}>
64
+ {files.map((file) => (
65
+ <RecentFileCard file={file} key={file.id} onClick={() => onOpenFile(file.id)} />
66
+ ))}
67
+ </div>
68
+ <div className={styles.fadeEdge} />
69
+ </div>
70
+ );
71
+ });
72
+
73
+ export default RecentFiles;
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+
3
+ import { Skeleton } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import { memo } from 'react';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ const useStyles = createStyles(({ css, token }) => ({
9
+ card: css`
10
+ flex-shrink: 0;
11
+
12
+ width: 280px;
13
+ padding: 12px;
14
+ border: 1px solid ${token.colorBorderSecondary};
15
+ border-radius: ${token.borderRadiusLG}px;
16
+
17
+ background: ${token.colorBgContainer};
18
+ `,
19
+ container: css`
20
+ position: relative;
21
+ overflow: hidden;
22
+ `,
23
+ fadeEdge: css`
24
+ pointer-events: none;
25
+
26
+ position: absolute;
27
+ inset-block: 0 0;
28
+ inset-inline-end: 0;
29
+
30
+ width: 80px;
31
+
32
+ background: linear-gradient(to left, ${token.colorBgLayout}, transparent);
33
+ `,
34
+ previewSkeleton: css`
35
+ width: 100%;
36
+ height: 160px;
37
+ margin-block-end: 12px;
38
+ border-radius: ${token.borderRadius}px;
39
+
40
+ background: ${token.colorFillQuaternary};
41
+ `,
42
+ scrollContainer: css`
43
+
44
+ /* Hide scrollbar */
45
+ scrollbar-width: none;
46
+
47
+ overflow-x: auto;
48
+ display: flex;
49
+ gap: 16px;
50
+
51
+ padding-block-end: 8px;
52
+
53
+ -ms-overflow-style: none;
54
+
55
+ &::-webkit-scrollbar {
56
+ display: none;
57
+ }
58
+ `,
59
+ }));
60
+
61
+ const RecentFilesSkeleton = memo(() => {
62
+ const { styles } = useStyles();
63
+
64
+ return (
65
+ <div className={styles.container}>
66
+ <div className={styles.scrollContainer}>
67
+ {Array.from({ length: 10 }).map((_, index) => (
68
+ <Flexbox className={styles.card} gap={12} key={index}>
69
+ <div className={styles.previewSkeleton} />
70
+ <Flexbox gap={6}>
71
+ <Skeleton active paragraph={false} title={{ width: '90%' }} />
72
+ <Skeleton active paragraph={false} title={{ style: { height: 12 }, width: '70%' }} />
73
+ </Flexbox>
74
+ </Flexbox>
75
+ ))}
76
+ </div>
77
+ <div className={styles.fadeEdge} />
78
+ </div>
79
+ );
80
+ });
81
+
82
+ export default RecentFilesSkeleton;
83
+
@@ -0,0 +1,208 @@
1
+ 'use client';
2
+
3
+ import { FileTypeIcon, Icon } from '@lobehub/ui';
4
+ import { Upload } from 'antd';
5
+ import { createStyles, useTheme } from 'antd-style';
6
+ import { ArrowUpIcon, FolderUp, PlusIcon } from 'lucide-react';
7
+ import { memo } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { Flexbox } from 'react-layout-kit';
10
+ import { useNavigate } from 'react-router-dom';
11
+
12
+ import { useFileStore } from '@/store/file';
13
+
14
+ const ICON_SIZE = 64;
15
+
16
+ const useStyles = createStyles(({ css, token }) => ({
17
+ actionTitle: css`
18
+ margin-block-start: 12px;
19
+ font-size: 14px;
20
+ font-weight: 500;
21
+ color: ${token.colorTextSecondary};
22
+ `,
23
+ card: css`
24
+ cursor: pointer;
25
+
26
+ position: relative;
27
+
28
+ overflow: hidden;
29
+
30
+ width: 200px;
31
+ min-height: 120px;
32
+ padding: 16px;
33
+ border: 1px solid ${token.colorBorderSecondary};
34
+ border-radius: ${token.borderRadiusLG}px;
35
+
36
+ font-weight: 500;
37
+ text-align: center;
38
+
39
+ background: ${token.colorBgContainer};
40
+
41
+ transition: all ${token.motionDurationMid} ${token.motionEaseInOut};
42
+
43
+ &:hover {
44
+ border-color: ${token.colorPrimary};
45
+ box-shadow: ${token.boxShadowTertiary};
46
+ }
47
+ `,
48
+ glow: css`
49
+ position: absolute;
50
+ inset-block-end: -12px;
51
+ inset-inline-end: 0;
52
+
53
+ width: 48px;
54
+ height: 48px;
55
+
56
+ opacity: 0.3;
57
+ filter: blur(24px);
58
+ `,
59
+ grid: css`
60
+ display: flex;
61
+ flex-wrap: wrap;
62
+ gap: 16px;
63
+ justify-content: left;
64
+ `,
65
+ icon: css`
66
+ position: absolute;
67
+ z-index: 1;
68
+ inset-block-end: -16px;
69
+ inset-inline-end: 8px;
70
+
71
+ flex: none;
72
+ `,
73
+ uploadWrapper: css`
74
+ /* Make the wrapper transparent so hover passes through to the card */
75
+ & > span {
76
+ display: block;
77
+ }
78
+ `,
79
+ }));
80
+
81
+ interface UploadEntriesProps {
82
+ knowledgeBaseId?: string;
83
+ }
84
+
85
+ const UploadEntries = memo<UploadEntriesProps>(({ knowledgeBaseId }) => {
86
+ const { t } = useTranslation('file');
87
+ const theme = useTheme();
88
+ const { styles } = useStyles();
89
+ const navigate = useNavigate();
90
+
91
+ const createDocument = useFileStore((s) => s.createDocument);
92
+ const pushDockFileList = useFileStore((s) => s.pushDockFileList);
93
+ // const { open } = useCreateNewModal();
94
+
95
+ const handleCreateNote = async () => {
96
+ try {
97
+ const newDoc = await createDocument({
98
+ content: '',
99
+ knowledgeBaseId,
100
+ title: t('home.uploadEntries.newDocument.title'),
101
+ });
102
+ // Navigate to the newly created document
103
+ // The KnowledgeHomePage will automatically set category to 'documents' when it detects the id param
104
+ navigate(`/${newDoc.id}`);
105
+ } catch (error) {
106
+ console.error('Failed to create document:', error);
107
+ }
108
+ };
109
+
110
+ // const handleCreateKnowledgeBase = () => {
111
+ // open({
112
+ // onSuccess: (id) => {
113
+ // navigate(`/bases/${id}`);
114
+ // },
115
+ // });
116
+ // };
117
+
118
+ const handleUploadFiles = async (file: File) => {
119
+ try {
120
+ await pushDockFileList([file], knowledgeBaseId);
121
+ } catch (error) {
122
+ console.error('Failed to upload file:', error);
123
+ }
124
+ return false;
125
+ };
126
+
127
+ const handleUploadFolder = async (file: File) => {
128
+ try {
129
+ await pushDockFileList([file], knowledgeBaseId);
130
+ } catch (error) {
131
+ console.error('Failed to upload folder:', error);
132
+ }
133
+ return false;
134
+ };
135
+
136
+ return (
137
+ <div className={styles.grid}>
138
+ {/* Create New Note */}
139
+ <Flexbox className={styles.card} onClick={handleCreateNote} padding={16}>
140
+ <span className={styles.actionTitle}>{t('home.uploadEntries.newDocument.title')}</span>
141
+ <div className={styles.glow} style={{ background: theme.purple }} />
142
+ <FileTypeIcon
143
+ className={styles.icon}
144
+ color={theme.purple}
145
+ icon={<Icon color={'#fff'} icon={PlusIcon} />}
146
+ size={ICON_SIZE}
147
+ type={'file'}
148
+ />
149
+ </Flexbox>
150
+
151
+ {/* Create Knowledge Base */}
152
+ {/* <Flexbox className={styles.card} onClick={handleCreateKnowledgeBase} padding={16}>
153
+ <span className={styles.actionTitle}>{t('home.uploadEntries.knowledgeBase.title')}</span>
154
+ <div className={styles.glow} style={{ background: theme.colorPrimary }} />
155
+ <FileTypeIcon
156
+ className={styles.icon}
157
+ color={theme.colorPrimary}
158
+ icon={<Icon color={'#fff'} icon={LibraryBig} />}
159
+ size={ICON_SIZE}
160
+ type={'file'}
161
+ />
162
+ </Flexbox> */}
163
+
164
+ {/* Upload Files */}
165
+ <Upload
166
+ beforeUpload={handleUploadFiles}
167
+ className={styles.uploadWrapper}
168
+ multiple
169
+ showUploadList={false}
170
+ >
171
+ <Flexbox className={styles.card} padding={16}>
172
+ <span className={styles.actionTitle}>{t('home.uploadEntries.files.title')}</span>
173
+ <div className={styles.glow} style={{ background: theme.geekblue }} />
174
+ <FileTypeIcon
175
+ className={styles.icon}
176
+ color={theme.geekblue}
177
+ icon={<Icon color={'#fff'} icon={ArrowUpIcon} />}
178
+ size={ICON_SIZE}
179
+ type={'file'}
180
+ />
181
+ </Flexbox>
182
+ </Upload>
183
+
184
+ {/* Upload Folder */}
185
+ <Upload
186
+ beforeUpload={handleUploadFolder}
187
+ className={styles.uploadWrapper}
188
+ directory
189
+ multiple
190
+ showUploadList={false}
191
+ >
192
+ <Flexbox className={styles.card} padding={16}>
193
+ <span className={styles.actionTitle}>{t('home.uploadEntries.folder.title')}</span>
194
+ <div className={styles.glow} style={{ background: theme.green }} />
195
+ <FileTypeIcon
196
+ className={styles.icon}
197
+ color={theme.green}
198
+ icon={<Icon color={'#fff'} icon={FolderUp} />}
199
+ size={ICON_SIZE}
200
+ type={'file'}
201
+ />
202
+ </Flexbox>
203
+ </Upload>
204
+ </div>
205
+ );
206
+ });
207
+
208
+ export default UploadEntries;
@@ -0,0 +1,221 @@
1
+ 'use client';
2
+
3
+ import { ActionIcon, Dropdown, Text } from '@lobehub/ui';
4
+ import { createStyles } from 'antd-style';
5
+ import { Clock, FileTextIcon, MoreHorizontal } from 'lucide-react';
6
+ import { memo, useMemo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+ import { useNavigate } from 'react-router-dom';
10
+
11
+ import { useFileCategory } from '@/app/[variants]/(main)/knowledge/hooks/useFileCategory';
12
+ import { useFileStore } from '@/store/file';
13
+ import { FilesTabs } from '@/types/files';
14
+
15
+ import RecentDocuments from './RecentDocuments';
16
+ import RecentFiles from './RecentFiles';
17
+ import UploadEntries from './UploadEntries';
18
+
19
+ const useStyles = createStyles(({ css, token }) => ({
20
+ container: css`
21
+ padding-block: 20px 48px;
22
+ padding-inline: 24px;
23
+ `,
24
+ content: css`
25
+ width: 100%;
26
+ max-width: 1200px;
27
+ margin-block: 0;
28
+ margin-inline: auto;
29
+ `,
30
+ greeting: css`
31
+ font-size: 24px;
32
+ font-weight: 600;
33
+ color: ${token.colorText};
34
+ text-align: start;
35
+ `,
36
+ section: css`
37
+ margin-block: 6px 36px;
38
+ `,
39
+ sectionActions: css`
40
+ transition: opacity ${token.motionDurationMid} ${token.motionEaseInOut};
41
+ `,
42
+ sectionTitle: css`
43
+ display: flex;
44
+ gap: 8px;
45
+ align-items: center;
46
+
47
+ margin-block-end: 24px;
48
+
49
+ font-size: 18px;
50
+ font-weight: 600;
51
+ color: ${token.colorTextSecondary};
52
+ text-align: start;
53
+ `,
54
+ sectionTitleWrapper: css`
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: space-between;
58
+
59
+ min-height: 36px;
60
+ margin-block-end: 24px;
61
+ `,
62
+ }));
63
+
64
+ interface HomeProps {
65
+ knowledgeBaseId?: string;
66
+ onOpenFile: (id: string) => void;
67
+ }
68
+
69
+ const Home = memo<HomeProps>(({ knowledgeBaseId, onOpenFile }) => {
70
+ const { t } = useTranslation('file');
71
+ const { styles } = useStyles();
72
+ const navigate = useNavigate();
73
+ const [, setActiveKey] = useFileCategory();
74
+ const [isDocumentsMenuOpen, setIsDocumentsMenuOpen] = useState(false);
75
+ const [isDocumentsSectionHovered, setIsDocumentsSectionHovered] = useState(false);
76
+ const [isFilesMenuOpen, setIsFilesMenuOpen] = useState(false);
77
+ const [isFilesSectionHovered, setIsFilesSectionHovered] = useState(false);
78
+
79
+ const useFetchKnowledgeItems = useFileStore((s) => s.useFetchKnowledgeItems);
80
+
81
+ // Fetch all items (all categories, sorted by updatedAt)
82
+ const { data: allItems, isLoading } = useFetchKnowledgeItems({
83
+ category: FilesTabs.All,
84
+ knowledgeBaseId,
85
+ sortType: 'desc',
86
+ sorter: 'createdAt',
87
+ });
88
+
89
+ // Get top 10 recent files (filter by sourceType === 'file')
90
+ const topRecentFiles = useMemo(() => {
91
+ if (!allItems) return [];
92
+ const files = allItems.filter((item) => item.sourceType === 'file');
93
+ return files.slice(0, 10);
94
+ }, [allItems]);
95
+
96
+ // Get top 10 recent documents (filter by sourceType === 'document')
97
+ const topRecentDocuments = useMemo(() => {
98
+ if (!allItems) return [];
99
+ const documents = allItems.filter((item) => item.sourceType === 'document');
100
+ return documents.slice(0, 10);
101
+ }, [allItems]);
102
+
103
+ // Handle document click - navigate to document explorer
104
+ const handleDocumentClick = (documentId: string) => {
105
+ // Navigate to the document in the explorer
106
+ // The KnowledgeHomePage will automatically set category to 'documents' when it detects the id param
107
+ navigate(`/${documentId}`);
108
+ };
109
+
110
+ return (
111
+ <div className={styles.container}>
112
+ <Flexbox className={styles.content}>
113
+ {/* Greeting Section */}
114
+ <Flexbox className={styles.section}>
115
+ <Text className={styles.greeting}>{t('home.greeting')}</Text>
116
+ </Flexbox>
117
+
118
+ {/* Upload Entries Section */}
119
+ <Flexbox className={styles.section}>
120
+ <UploadEntries knowledgeBaseId={knowledgeBaseId} />
121
+ </Flexbox>
122
+
123
+ {/* Recent Files Section */}
124
+ {(isLoading || topRecentFiles.length > 0) && (
125
+ <div
126
+ className={styles.section}
127
+ onMouseEnter={() => {
128
+ setIsFilesSectionHovered(true);
129
+ }}
130
+ onMouseLeave={() => {
131
+ setIsFilesSectionHovered(false);
132
+ }}
133
+ >
134
+ <div className={styles.sectionTitleWrapper}>
135
+ <Text className={styles.sectionTitle} style={{ marginBottom: 0 }}>
136
+ <Clock size={18} />
137
+ {t('home.recentFiles')}
138
+ </Text>
139
+ <div
140
+ className={styles.sectionActions}
141
+ style={{
142
+ opacity: isFilesSectionHovered || isFilesMenuOpen ? 1 : 0,
143
+ }}
144
+ >
145
+ <Dropdown
146
+ menu={{
147
+ items: [
148
+ {
149
+ key: 'all-files',
150
+ label: t('menu.allFiles'),
151
+ onClick: () => {
152
+ setActiveKey(FilesTabs.All);
153
+ },
154
+ },
155
+ ],
156
+ }}
157
+ onOpenChange={setIsFilesMenuOpen}
158
+ open={isFilesMenuOpen}
159
+ >
160
+ <ActionIcon icon={MoreHorizontal} size="small" />
161
+ </Dropdown>
162
+ </div>
163
+ </div>
164
+ <RecentFiles files={topRecentFiles} isLoading={isLoading} onOpenFile={onOpenFile} />
165
+ </div>
166
+ )}
167
+
168
+ {/* Recent Documents Section */}
169
+ {(isLoading || topRecentDocuments.length > 0) && (
170
+ <div
171
+ className={styles.section}
172
+ onMouseEnter={() => {
173
+ setIsDocumentsSectionHovered(true);
174
+ }}
175
+ onMouseLeave={() => {
176
+ setIsDocumentsSectionHovered(false);
177
+ }}
178
+ >
179
+ <div className={styles.sectionTitleWrapper}>
180
+ <Text className={styles.sectionTitle} style={{ marginBottom: 0 }}>
181
+ <FileTextIcon size={18} />
182
+ {t('home.recentDocuments')}
183
+ </Text>
184
+ <div
185
+ className={styles.sectionActions}
186
+ style={{
187
+ opacity: isDocumentsSectionHovered || isDocumentsMenuOpen ? 1 : 0,
188
+ }}
189
+ >
190
+ <Dropdown
191
+ menu={{
192
+ items: [
193
+ {
194
+ key: 'all-documents',
195
+ label: t('menu.allDocuments'),
196
+ onClick: () => {
197
+ setActiveKey(FilesTabs.Documents);
198
+ },
199
+ },
200
+ ],
201
+ }}
202
+ onOpenChange={setIsDocumentsMenuOpen}
203
+ open={isDocumentsMenuOpen}
204
+ >
205
+ <ActionIcon icon={MoreHorizontal} size="small" />
206
+ </Dropdown>
207
+ </div>
208
+ </div>
209
+ <RecentDocuments
210
+ documents={topRecentDocuments}
211
+ isLoading={isLoading}
212
+ onOpenDocument={handleDocumentClick}
213
+ />
214
+ </div>
215
+ )}
216
+ </Flexbox>
217
+ </div>
218
+ );
219
+ });
220
+
221
+ export default Home;
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+
3
+ import { Text } from '@lobehub/ui';
4
+ import dynamic from 'next/dynamic';
5
+ import { memo } from 'react';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { FilesTabs } from '@/types/files';
9
+
10
+ import DocumentExplorer from './DocumentExplorer';
11
+ import FileExplorer from './FileExplorer';
12
+ import Header from './Header';
13
+ import Home from './Home';
14
+ import UploadDock from './UploadDock';
15
+
16
+ const ChunkDrawer = dynamic(() => import('./ChunkDrawer'), { ssr: false });
17
+
18
+ interface KnowledgeManagerProps {
19
+ category?: string;
20
+ // Directly open a document if provided
21
+ documentId?: string;
22
+ knowledgeBaseId?: string;
23
+ onOpenFile: (id: string) => void;
24
+ title: string;
25
+ }
26
+
27
+ /**
28
+ * Manage knowledge items. Can be all items or certian knowledge base.
29
+ *
30
+ * DocumentExplorer: For the "documents" category.
31
+ * Home: For the "home" category.
32
+ * FileExplorer: For other categories.
33
+ */
34
+ const KnowledgeManager = memo<KnowledgeManagerProps>(
35
+ ({ title, knowledgeBaseId, category, onOpenFile, documentId }) => {
36
+ const isDocumentsView = category === FilesTabs.Pages;
37
+ const isHomeView = category === FilesTabs.Home;
38
+
39
+ if (isHomeView) {
40
+ return (
41
+ <>
42
+ <Home knowledgeBaseId={knowledgeBaseId} onOpenFile={onOpenFile} />
43
+ <UploadDock />
44
+ <ChunkDrawer />
45
+ </>
46
+ );
47
+ }
48
+
49
+ return (
50
+ <>
51
+ {!isDocumentsView && <Header knowledgeBaseId={knowledgeBaseId} />}
52
+ <Flexbox gap={12} height={'100%'}>
53
+ {!isDocumentsView && (
54
+ <Text strong style={{ fontSize: 16, marginBlock: 16, marginInline: 24 }}>
55
+ {title}
56
+ </Text>
57
+ )}
58
+ {isDocumentsView ? (
59
+ <DocumentExplorer documentId={documentId} knowledgeBaseId={knowledgeBaseId} />
60
+ ) : (
61
+ <FileExplorer
62
+ category={category}
63
+ knowledgeBaseId={knowledgeBaseId}
64
+ onOpenFile={onOpenFile}
65
+ />
66
+ )}
67
+ </Flexbox>
68
+ <UploadDock />
69
+ <ChunkDrawer />
70
+ </>
71
+ );
72
+ },
73
+ );
74
+
75
+ export default KnowledgeManager;
@@ -18,7 +18,7 @@ enum FilePreviewTab {
18
18
  const FilePreview = () => {
19
19
  const previewFileId = useChatStore(chatPortalSelectors.previewFileId);
20
20
  const chunkText = useChatStore(chatPortalSelectors.chunkText);
21
- const useFetchFileItem = useFileStore((s) => s.useFetchFileItem);
21
+ const useFetchFileItem = useFileStore((s) => s.useFetchKnowledgeItem);
22
22
  const { t } = useTranslation('portal');
23
23
 
24
24
  const [tab, setTab] = useState<FilePreviewTab>(FilePreviewTab.File);
@@ -14,7 +14,7 @@ const Header = () => {
14
14
  chatPortalSelectors.previewFileId(s),
15
15
  ]);
16
16
 
17
- const useFetchFileItem = useFileStore((s) => s.useFetchFileItem);
17
+ const useFetchFileItem = useFileStore((s) => s.useFetchKnowledgeItem);
18
18
 
19
19
  const { data, isLoading } = useFetchFileItem(previewFileId);
20
20
 
@@ -289,6 +289,7 @@ export default {
289
289
  oauth: 'SSO 登录',
290
290
  officialSite: '官方网站',
291
291
  ok: '确定',
292
+ or: '或',
292
293
  password: '密码',
293
294
  pin: '置顶',
294
295
  pinOff: '取消置顶',