@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,149 @@
1
+ import { Button, Tooltip } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { isNull } from 'lodash-es';
4
+ import { FileBoxIcon } from 'lucide-react';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import FileIcon from '@/components/FileIcon';
10
+ import { fileManagerSelectors, useFileStore } from '@/store/file';
11
+ import { AsyncTaskStatus, IAsyncTaskError } from '@/types/asyncTask';
12
+ import { formatSize } from '@/utils/format';
13
+ import { isChunkingUnsupported } from '@/utils/isChunkingUnsupported';
14
+
15
+ import ChunksBadge from '../FileListItem/ChunkTag';
16
+
17
+ const useStyles = createStyles(({ css, token }) => ({
18
+ floatingChunkBadge: css`
19
+ position: absolute;
20
+ z-index: 3;
21
+ inset-block-end: 8px;
22
+ inset-inline-end: 8px;
23
+
24
+ border-radius: ${token.borderRadius}px;
25
+
26
+ opacity: 0;
27
+ background: ${token.colorBgContainer};
28
+ box-shadow: ${token.boxShadow};
29
+
30
+ transition: opacity ${token.motionDurationMid};
31
+ `,
32
+ name: css`
33
+ overflow: hidden;
34
+ display: -webkit-box;
35
+ -webkit-box-orient: vertical;
36
+ -webkit-line-clamp: 2;
37
+
38
+ margin-block-end: 12px;
39
+
40
+ font-weight: ${token.fontWeightStrong};
41
+ color: ${token.colorText};
42
+ word-break: break-word;
43
+ `,
44
+ }));
45
+
46
+ interface DefaultFileItemProps {
47
+ chunkCount?: number | null;
48
+ chunkingError?: IAsyncTaskError | null;
49
+ chunkingStatus?: AsyncTaskStatus | null;
50
+ embeddingError?: IAsyncTaskError | null;
51
+ embeddingStatus?: AsyncTaskStatus | null;
52
+ fileType?: string;
53
+ finishEmbedding?: boolean;
54
+ id: string;
55
+ name: string;
56
+ size: number;
57
+ }
58
+
59
+ const DefaultFileItem = memo<DefaultFileItemProps>(
60
+ ({
61
+ chunkCount,
62
+ chunkingError,
63
+ chunkingStatus,
64
+ embeddingError,
65
+ embeddingStatus,
66
+ fileType,
67
+ finishEmbedding,
68
+ id,
69
+ name,
70
+ size,
71
+ }) => {
72
+ const { t } = useTranslation('components');
73
+ const { styles, cx } = useStyles();
74
+ const [isCreatingFileParseTask, parseFiles] = useFileStore((s) => [
75
+ fileManagerSelectors.isCreatingFileParseTask(id)(s),
76
+ s.parseFilesToChunks,
77
+ ]);
78
+
79
+ const isSupportedForChunking = !isChunkingUnsupported(fileType || '');
80
+
81
+ return (
82
+ <>
83
+ <Flexbox
84
+ align={'center'}
85
+ gap={12}
86
+ justify={'center'}
87
+ paddingBlock={24}
88
+ paddingInline={12}
89
+ style={{ minHeight: 180 }}
90
+ >
91
+ <FileIcon fileName={name} fileType={fileType} size={64} />
92
+ <div className={styles.name} style={{ textAlign: 'center' }}>
93
+ {name}
94
+ </div>
95
+ <div
96
+ style={{
97
+ color: 'var(--lobe-chat-text-tertiary)',
98
+ fontSize: 12,
99
+ textAlign: 'center',
100
+ }}
101
+ >
102
+ {formatSize(size)}
103
+ </div>
104
+ </Flexbox>
105
+ {/* Floating chunk badge or action button */}
106
+ {!isNull(chunkingStatus) && chunkingStatus ? (
107
+ <div
108
+ className={cx('floatingChunkBadge', styles.floatingChunkBadge)}
109
+ onClick={(e) => e.stopPropagation()}
110
+ >
111
+ <ChunksBadge
112
+ chunkCount={chunkCount}
113
+ chunkingError={chunkingError}
114
+ chunkingStatus={chunkingStatus}
115
+ embeddingError={embeddingError}
116
+ embeddingStatus={embeddingStatus}
117
+ finishEmbedding={finishEmbedding}
118
+ id={id}
119
+ />
120
+ </div>
121
+ ) : (
122
+ isSupportedForChunking && (
123
+ <Tooltip title={t('FileManager.actions.chunkingTooltip')}>
124
+ <div
125
+ className={cx('floatingChunkBadge', styles.floatingChunkBadge)}
126
+ onClick={(e) => {
127
+ e.stopPropagation();
128
+ if (!isCreatingFileParseTask) {
129
+ parseFiles([id]);
130
+ }
131
+ }}
132
+ style={{ cursor: 'pointer' }}
133
+ >
134
+ <Button
135
+ icon={FileBoxIcon}
136
+ loading={isCreatingFileParseTask}
137
+ size={'small'}
138
+ type={'text'}
139
+ />
140
+ </div>
141
+ </Tooltip>
142
+ )
143
+ )}
144
+ </>
145
+ );
146
+ },
147
+ );
148
+
149
+ export default DefaultFileItem;
@@ -0,0 +1,245 @@
1
+ import { Button, Tooltip } from '@lobehub/ui';
2
+ import { Image } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { isNull } from 'lodash-es';
5
+ import { FileBoxIcon } from 'lucide-react';
6
+ import { memo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import FileIcon from '@/components/FileIcon';
11
+ import { fileManagerSelectors, useFileStore } from '@/store/file';
12
+ import { AsyncTaskStatus, IAsyncTaskError } from '@/types/asyncTask';
13
+ import { formatSize } from '@/utils/format';
14
+ import { isChunkingUnsupported } from '@/utils/isChunkingUnsupported';
15
+
16
+ import ChunksBadge from '../FileListItem/ChunkTag';
17
+
18
+ const useStyles = createStyles(({ css, token }) => ({
19
+ floatingChunkBadge: css`
20
+ position: absolute;
21
+ z-index: 3;
22
+ inset-block-end: 8px;
23
+ inset-inline-end: 8px;
24
+
25
+ border-radius: ${token.borderRadius}px;
26
+
27
+ opacity: 0;
28
+ background: ${token.colorBgContainer};
29
+ box-shadow: ${token.boxShadow};
30
+
31
+ transition: opacity ${token.motionDurationMid};
32
+ `,
33
+ hoverOverlay: css`
34
+ position: absolute;
35
+ z-index: 1;
36
+ inset: 0;
37
+
38
+ display: flex;
39
+ flex-direction: column;
40
+ align-items: center;
41
+ justify-content: center;
42
+
43
+ padding: 16px;
44
+
45
+ opacity: 0;
46
+ background: ${token.colorBgMask};
47
+
48
+ transition: opacity ${token.motionDurationMid};
49
+
50
+ &:hover {
51
+ opacity: 1;
52
+ }
53
+ `,
54
+ imagePlaceholder: css`
55
+ display: flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ background: ${token.colorFillQuaternary};
59
+ `,
60
+ imageWrapper: css`
61
+ position: relative;
62
+ width: 100%;
63
+ background: ${token.colorFillQuaternary};
64
+
65
+ img {
66
+ display: block;
67
+ height: auto;
68
+ }
69
+ `,
70
+ name: css`
71
+ overflow: hidden;
72
+ display: -webkit-box;
73
+ -webkit-box-orient: vertical;
74
+ -webkit-line-clamp: 2;
75
+
76
+ margin-block-end: 12px;
77
+
78
+ font-weight: ${token.fontWeightStrong};
79
+ color: ${token.colorText};
80
+ word-break: break-word;
81
+ `,
82
+ overlaySize: css`
83
+ font-size: 12px;
84
+ color: ${token.colorTextLightSolid};
85
+ opacity: 0.9;
86
+ `,
87
+ overlayTitle: css`
88
+ overflow: hidden;
89
+ display: -webkit-box;
90
+ -webkit-box-orient: vertical;
91
+ -webkit-line-clamp: 3;
92
+
93
+ max-width: 100%;
94
+ margin-block-end: 8px;
95
+
96
+ font-size: 14px;
97
+ font-weight: ${token.fontWeightStrong};
98
+ color: ${token.colorTextLightSolid};
99
+ text-align: center;
100
+ word-break: break-word;
101
+ `,
102
+ }));
103
+
104
+ interface ImageFileItemProps {
105
+ chunkCount?: number | null;
106
+ chunkingError?: IAsyncTaskError | null;
107
+ chunkingStatus?: AsyncTaskStatus | null;
108
+ embeddingError?: IAsyncTaskError | null;
109
+ embeddingStatus?: AsyncTaskStatus | null;
110
+ fileType?: string;
111
+ finishEmbedding?: boolean;
112
+ id: string;
113
+ isInView: boolean;
114
+ name: string;
115
+ size: number;
116
+ url?: string;
117
+ }
118
+
119
+ const ImageFileItem = memo<ImageFileItemProps>(
120
+ ({
121
+ chunkCount,
122
+ chunkingError,
123
+ chunkingStatus,
124
+ embeddingError,
125
+ embeddingStatus,
126
+ fileType,
127
+ finishEmbedding,
128
+ id,
129
+ isInView,
130
+ name,
131
+ size,
132
+ url,
133
+ }) => {
134
+ const { t } = useTranslation('components');
135
+ const { styles, cx } = useStyles();
136
+ const [imageLoaded, setImageLoaded] = useState(false);
137
+ const [isCreatingFileParseTask, parseFiles] = useFileStore((s) => [
138
+ fileManagerSelectors.isCreatingFileParseTask(id)(s),
139
+ s.parseFilesToChunks,
140
+ ]);
141
+
142
+ const isSupportedForChunking = !isChunkingUnsupported(fileType || '');
143
+
144
+ return (
145
+ <>
146
+ <div className={styles.imageWrapper}>
147
+ {!imageLoaded && (
148
+ <div className={styles.imagePlaceholder}>
149
+ <Flexbox
150
+ align={'center'}
151
+ gap={12}
152
+ justify={'center'}
153
+ paddingBlock={24}
154
+ paddingInline={12}
155
+ >
156
+ <FileIcon fileName={name} fileType={fileType} size={48} />
157
+ <div className={styles.name} style={{ textAlign: 'center' }}>
158
+ {name}
159
+ </div>
160
+ <div
161
+ style={{
162
+ color: 'var(--lobe-chat-text-tertiary)',
163
+ fontSize: 12,
164
+ textAlign: 'center',
165
+ }}
166
+ >
167
+ {formatSize(size)}
168
+ </div>
169
+ </Flexbox>
170
+ </div>
171
+ )}
172
+ {isInView && url && (
173
+ <Image
174
+ alt={name}
175
+ loading="lazy"
176
+ onError={() => setImageLoaded(false)}
177
+ onLoad={() => setImageLoaded(true)}
178
+ preview={{
179
+ src: url,
180
+ }}
181
+ src={url}
182
+ style={{
183
+ display: 'block',
184
+ height: 'auto',
185
+ opacity: imageLoaded ? 1 : 0,
186
+ transition: 'opacity 0.3s',
187
+ width: '100%',
188
+ }}
189
+ wrapperStyle={{
190
+ display: 'block',
191
+ width: '100%',
192
+ }}
193
+ />
194
+ )}
195
+ {/* Hover overlay */}
196
+ <div className={styles.hoverOverlay}>
197
+ <div className={styles.overlayTitle}>{name}</div>
198
+ <div className={styles.overlaySize}>{formatSize(size)}</div>
199
+ </div>
200
+ </div>
201
+ {/* Floating chunk badge or action button */}
202
+ {!isNull(chunkingStatus) && chunkingStatus ? (
203
+ <div
204
+ className={cx('floatingChunkBadge', styles.floatingChunkBadge)}
205
+ onClick={(e) => e.stopPropagation()}
206
+ >
207
+ <ChunksBadge
208
+ chunkCount={chunkCount}
209
+ chunkingError={chunkingError}
210
+ chunkingStatus={chunkingStatus}
211
+ embeddingError={embeddingError}
212
+ embeddingStatus={embeddingStatus}
213
+ finishEmbedding={finishEmbedding}
214
+ id={id}
215
+ />
216
+ </div>
217
+ ) : (
218
+ isSupportedForChunking && (
219
+ <Tooltip title={t('FileManager.actions.chunkingTooltip')}>
220
+ <div
221
+ className={cx('floatingChunkBadge', styles.floatingChunkBadge)}
222
+ onClick={(e) => {
223
+ e.stopPropagation();
224
+ if (!isCreatingFileParseTask) {
225
+ parseFiles([id]);
226
+ }
227
+ }}
228
+ style={{ cursor: 'pointer' }}
229
+ >
230
+ <Button
231
+ icon={FileBoxIcon}
232
+ loading={isCreatingFileParseTask}
233
+ size={'small'}
234
+ type={'text'}
235
+ />
236
+ </div>
237
+ </Tooltip>
238
+ )
239
+ )}
240
+ </>
241
+ );
242
+ },
243
+ );
244
+
245
+ export default ImageFileItem;
@@ -0,0 +1,232 @@
1
+ import { Button, Tooltip } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { isNull } from 'lodash-es';
4
+ import { FileBoxIcon } from 'lucide-react';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+
8
+ import FileIcon from '@/components/FileIcon';
9
+ import { fileManagerSelectors, useFileStore } from '@/store/file';
10
+ import { AsyncTaskStatus, IAsyncTaskError } from '@/types/asyncTask';
11
+ import { formatSize } from '@/utils/format';
12
+ import { isChunkingUnsupported } from '@/utils/isChunkingUnsupported';
13
+
14
+ import ChunksBadge from '../FileListItem/ChunkTag';
15
+
16
+ const useStyles = createStyles(({ css, token }) => ({
17
+ floatingChunkBadge: css`
18
+ position: absolute;
19
+ z-index: 3;
20
+ inset-block-end: 8px;
21
+ inset-inline-end: 8px;
22
+
23
+ border-radius: ${token.borderRadius}px;
24
+
25
+ opacity: 0;
26
+ background: ${token.colorBgContainer};
27
+ box-shadow: ${token.boxShadow};
28
+
29
+ transition: opacity ${token.motionDurationMid};
30
+ `,
31
+ hoverOverlay: css`
32
+ position: absolute;
33
+ z-index: 1;
34
+ inset: 0;
35
+
36
+ display: flex;
37
+ flex-direction: column;
38
+ align-items: center;
39
+ justify-content: center;
40
+
41
+ padding: 16px;
42
+ border-radius: ${token.borderRadiusLG}px;
43
+
44
+ opacity: 0;
45
+ background: ${token.colorBgMask};
46
+
47
+ transition: opacity ${token.motionDurationMid};
48
+
49
+ &:hover {
50
+ opacity: 1;
51
+ }
52
+ `,
53
+ iconWrapper: css`
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+
58
+ height: 120px;
59
+ margin-block-end: 12px;
60
+ border-radius: ${token.borderRadius}px;
61
+
62
+ background: ${token.colorFillQuaternary};
63
+ `,
64
+ markdownLoading: css`
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: center;
68
+
69
+ min-height: 120px;
70
+ border-radius: ${token.borderRadiusLG}px;
71
+
72
+ font-size: 12px;
73
+ color: ${token.colorTextTertiary};
74
+
75
+ background: ${token.colorFillQuaternary};
76
+ `,
77
+ markdownPreview: css`
78
+ position: relative;
79
+
80
+ overflow: hidden;
81
+
82
+ width: 100%;
83
+ min-height: 120px;
84
+ max-height: 300px;
85
+ padding: 16px;
86
+ border-radius: ${token.borderRadiusLG}px;
87
+
88
+ font-size: 13px;
89
+ line-height: 1.6;
90
+ color: ${token.colorTextSecondary};
91
+ word-wrap: break-word;
92
+ white-space: pre-wrap;
93
+
94
+ background: ${token.colorFillQuaternary};
95
+
96
+ &::after {
97
+ pointer-events: none;
98
+ content: '';
99
+
100
+ position: absolute;
101
+ inset-block-end: 0;
102
+ inset-inline: 0;
103
+
104
+ height: 60px;
105
+
106
+ background: linear-gradient(to bottom, transparent, ${token.colorFillQuaternary});
107
+ }
108
+ `,
109
+ overlaySize: css`
110
+ font-size: 12px;
111
+ color: ${token.colorTextLightSolid};
112
+ opacity: 0.9;
113
+ `,
114
+ overlayTitle: css`
115
+ overflow: hidden;
116
+ display: -webkit-box;
117
+ -webkit-box-orient: vertical;
118
+ -webkit-line-clamp: 3;
119
+
120
+ max-width: 100%;
121
+ margin-block-end: 8px;
122
+
123
+ font-size: 14px;
124
+ font-weight: ${token.fontWeightStrong};
125
+ color: ${token.colorTextLightSolid};
126
+ text-align: center;
127
+ word-break: break-word;
128
+ `,
129
+ }));
130
+
131
+ interface MarkdownFileItemProps {
132
+ chunkCount?: number | null;
133
+ chunkingError?: IAsyncTaskError | null;
134
+ chunkingStatus?: AsyncTaskStatus | null;
135
+ embeddingError?: IAsyncTaskError | null;
136
+ embeddingStatus?: AsyncTaskStatus | null;
137
+ fileType?: string;
138
+ finishEmbedding?: boolean;
139
+ id: string;
140
+ isLoadingMarkdown: boolean;
141
+ markdownContent: string;
142
+ name: string;
143
+ size: number;
144
+ }
145
+
146
+ const MarkdownFileItem = memo<MarkdownFileItemProps>(
147
+ ({
148
+ chunkCount,
149
+ chunkingError,
150
+ chunkingStatus,
151
+ embeddingError,
152
+ embeddingStatus,
153
+ fileType,
154
+ finishEmbedding,
155
+ id,
156
+ isLoadingMarkdown,
157
+ markdownContent,
158
+ name,
159
+ size,
160
+ }) => {
161
+ const { t } = useTranslation('components');
162
+ const { styles, cx } = useStyles();
163
+ const [isCreatingFileParseTask, parseFiles] = useFileStore((s) => [
164
+ fileManagerSelectors.isCreatingFileParseTask(id)(s),
165
+ s.parseFilesToChunks,
166
+ ]);
167
+
168
+ const isSupportedForChunking = !isChunkingUnsupported(fileType || '');
169
+
170
+ return (
171
+ <>
172
+ <div style={{ position: 'relative' }}>
173
+ {isLoadingMarkdown ? (
174
+ <div className={styles.markdownLoading}>Loading preview...</div>
175
+ ) : markdownContent ? (
176
+ <div className={styles.markdownPreview}>{markdownContent}</div>
177
+ ) : (
178
+ <div className={styles.iconWrapper}>
179
+ <FileIcon fileName={name} fileType={fileType} size={64} />
180
+ </div>
181
+ )}
182
+ {/* Hover overlay */}
183
+ <div className={styles.hoverOverlay}>
184
+ <div className={styles.overlayTitle}>{name}</div>
185
+ <div className={styles.overlaySize}>{formatSize(size)}</div>
186
+ </div>
187
+ </div>
188
+ {/* Floating chunk badge or action button */}
189
+ {!isNull(chunkingStatus) && chunkingStatus ? (
190
+ <div
191
+ className={cx('floatingChunkBadge', styles.floatingChunkBadge)}
192
+ onClick={(e) => e.stopPropagation()}
193
+ >
194
+ <ChunksBadge
195
+ chunkCount={chunkCount}
196
+ chunkingError={chunkingError}
197
+ chunkingStatus={chunkingStatus}
198
+ embeddingError={embeddingError}
199
+ embeddingStatus={embeddingStatus}
200
+ finishEmbedding={finishEmbedding}
201
+ id={id}
202
+ />
203
+ </div>
204
+ ) : (
205
+ isSupportedForChunking && (
206
+ <Tooltip title={t('FileManager.actions.chunkingTooltip')}>
207
+ <div
208
+ className={cx('floatingChunkBadge', styles.floatingChunkBadge)}
209
+ onClick={(e) => {
210
+ e.stopPropagation();
211
+ if (!isCreatingFileParseTask) {
212
+ parseFiles([id]);
213
+ }
214
+ }}
215
+ style={{ cursor: 'pointer' }}
216
+ >
217
+ <Button
218
+ icon={FileBoxIcon}
219
+ loading={isCreatingFileParseTask}
220
+ size={'small'}
221
+ type={'text'}
222
+ />
223
+ </div>
224
+ </Tooltip>
225
+ )
226
+ )}
227
+ </>
228
+ );
229
+ },
230
+ );
231
+
232
+ export default MarkdownFileItem;