@lobehub/lobehub 2.0.0-next.54 → 2.0.0-next.56
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/CHANGELOG.md +52 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/common.json +1 -0
- package/locales/ar/file.json +85 -2
- package/locales/bg-BG/common.json +1 -0
- package/locales/bg-BG/file.json +85 -2
- package/locales/de-DE/common.json +1 -0
- package/locales/de-DE/file.json +85 -2
- package/locales/en-US/common.json +1 -0
- package/locales/en-US/file.json +85 -2
- package/locales/es-ES/common.json +1 -0
- package/locales/es-ES/file.json +85 -2
- package/locales/fa-IR/common.json +1 -0
- package/locales/fa-IR/file.json +85 -2
- package/locales/fr-FR/common.json +1 -0
- package/locales/fr-FR/file.json +85 -2
- package/locales/it-IT/common.json +1 -0
- package/locales/it-IT/file.json +85 -2
- package/locales/ja-JP/common.json +1 -0
- package/locales/ja-JP/file.json +85 -2
- package/locales/ko-KR/common.json +1 -0
- package/locales/ko-KR/file.json +85 -2
- package/locales/nl-NL/common.json +1 -0
- package/locales/nl-NL/file.json +85 -2
- package/locales/pl-PL/common.json +1 -0
- package/locales/pl-PL/file.json +85 -2
- package/locales/pt-BR/common.json +1 -0
- package/locales/pt-BR/file.json +85 -2
- package/locales/ru-RU/common.json +1 -0
- package/locales/ru-RU/file.json +85 -2
- package/locales/tr-TR/common.json +1 -0
- package/locales/tr-TR/file.json +85 -2
- package/locales/vi-VN/common.json +1 -0
- package/locales/vi-VN/file.json +85 -2
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-CN/file.json +85 -2
- package/locales/zh-TW/common.json +1 -0
- package/locales/zh-TW/file.json +85 -2
- package/package.json +1 -1
- package/packages/database/src/models/__tests__/file.test.ts +94 -29
- package/packages/database/src/models/file.ts +15 -4
- package/packages/database/src/repositories/knowledge/index.test.ts +300 -0
- package/packages/database/src/repositories/knowledge/index.ts +420 -0
- package/packages/model-bank/src/aiModels/aihubmix.ts +1 -0
- package/packages/model-bank/src/aiModels/google.ts +9 -5
- package/packages/model-bank/src/aiModels/openai.ts +2 -35
- package/packages/model-bank/src/aiModels/openrouter.ts +1 -0
- package/packages/model-bank/src/aiModels/vertexai.ts +2 -0
- package/packages/model-bank/src/types/aiModel.ts +15 -2
- package/packages/model-runtime/src/core/usageConverters/index.ts +1 -0
- package/packages/model-runtime/src/core/usageConverters/utils/resolveImageSinglePrice.ts +34 -0
- package/packages/types/src/document/index.ts +14 -2
- package/packages/types/src/files/index.ts +2 -0
- package/packages/types/src/files/list.ts +10 -0
- package/packages/types/src/llm.ts +1 -1
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect/ImageModelItem.tsx +93 -0
- package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/{ModelSelect.tsx → ModelSelect/index.tsx} +17 -2
- package/src/app/[variants]/(main)/knowledge/KnowledgeRouter.tsx +2 -1
- package/src/app/[variants]/(main)/knowledge/components/KnowledgeBaseItem/index.tsx +0 -2
- package/src/app/[variants]/(main)/knowledge/hooks/useFileCategory.ts +6 -3
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/index.tsx +2 -2
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/menu/{MenuItems.tsx → CategoryMenu.tsx} +3 -3
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeBaseDetail/menu/Menu.tsx +2 -2
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/index.tsx +40 -18
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/layout/Container.tsx +1 -1
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/CategoryMenu.tsx +148 -0
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/KnowledgeBase.tsx +20 -7
- package/src/components/FileIcon/index.tsx +3 -1
- package/src/features/ChatInput/ActionBar/Knowledge/index.tsx +2 -2
- package/src/features/FileSidePanel/index.tsx +1 -1
- package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/MasonryItem.tsx +80 -0
- package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/MasonryItemWrapper.tsx +27 -0
- package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/List.tsx +104 -23
- package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/MasonrySkeleton.tsx +62 -0
- package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/index.tsx +3 -2
- package/src/features/KnowledgeBaseModal/CreateNew/CreateForm.tsx +1 -1
- package/src/features/KnowledgeManager/DocumentExplorer/DocumentActions.tsx +111 -0
- package/src/features/KnowledgeManager/DocumentExplorer/DocumentEditor.tsx +723 -0
- package/src/features/KnowledgeManager/DocumentExplorer/DocumentEditorPlaceholder.tsx +169 -0
- package/src/features/KnowledgeManager/DocumentExplorer/DocumentListItem.tsx +148 -0
- package/src/features/KnowledgeManager/DocumentExplorer/DocumentListSkeleton.tsx +39 -0
- package/src/features/KnowledgeManager/DocumentExplorer/NoteEditorModal.tsx +348 -0
- package/src/features/KnowledgeManager/DocumentExplorer/RenamePopover.tsx +163 -0
- package/src/features/KnowledgeManager/DocumentExplorer/index.tsx +318 -0
- package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileListItem/index.tsx +48 -9
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/DefaultFileItem.tsx +149 -0
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/ImageFileItem.tsx +245 -0
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/MarkdownFileItem.tsx +232 -0
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/NoteFileItem.tsx +230 -0
- package/src/features/KnowledgeManager/FileExplorer/MasonryFileItem/index.tsx +398 -0
- package/src/features/KnowledgeManager/FileExplorer/ToolBar/ViewSwitcher.tsx +45 -0
- package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/index.tsx +55 -16
- package/src/features/KnowledgeManager/Header/AddButton.tsx +118 -0
- package/src/features/KnowledgeManager/Header/NewNoteButton.tsx +33 -0
- package/src/features/{FileManager → KnowledgeManager}/Header/index.tsx +3 -9
- package/src/features/KnowledgeManager/Home/RecentDocumentCard.tsx +116 -0
- package/src/features/KnowledgeManager/Home/RecentDocuments.tsx +77 -0
- package/src/features/KnowledgeManager/Home/RecentFileCard.tsx +121 -0
- package/src/features/KnowledgeManager/Home/RecentFiles.tsx +73 -0
- package/src/features/KnowledgeManager/Home/RecentFilesSkeleton.tsx +81 -0
- package/src/features/KnowledgeManager/Home/UploadEntries.tsx +208 -0
- package/src/features/KnowledgeManager/Home/index.tsx +221 -0
- package/src/features/KnowledgeManager/index.tsx +75 -0
- package/src/features/Portal/FilePreview/Body/index.tsx +1 -1
- package/src/features/Portal/FilePreview/Header.tsx +1 -1
- package/src/locales/default/common.ts +1 -0
- package/src/locales/default/file.ts +87 -2
- package/src/server/routers/lambda/__tests__/file.test.ts +85 -6
- package/src/server/routers/lambda/document.ts +57 -0
- package/src/server/routers/lambda/file.ts +72 -0
- package/src/server/routers/lambda/knowledge.ts +94 -0
- package/src/server/services/document/index.ts +103 -0
- package/src/services/document/index.ts +44 -0
- package/src/services/file/index.ts +5 -3
- package/src/store/aiInfra/slices/aiProvider/__tests__/action.test.ts +125 -229
- package/src/store/aiInfra/slices/aiProvider/action.ts +113 -33
- package/src/store/file/initialState.ts +6 -1
- package/src/store/file/slices/chat/action.ts +3 -3
- package/src/store/file/slices/document/action.ts +359 -0
- package/src/store/file/slices/document/index.ts +3 -0
- package/src/store/file/slices/document/initialState.ts +22 -0
- package/src/store/file/slices/document/selectors.ts +25 -0
- package/src/store/file/slices/fileManager/action.test.ts +16 -9
- package/src/store/file/slices/fileManager/action.ts +11 -11
- package/src/store/file/store.ts +3 -0
- package/src/store/global/initialState.ts +3 -1
- package/src/app/[variants]/(main)/knowledge/routes/KnowledgeHome/menu/FileMenu.tsx +0 -75
- package/src/features/FileManager/FileList/MasonryFileItem/index.tsx +0 -582
- package/src/features/FileManager/index.tsx +0 -36
- /package/src/features/{FileManager/FileList/ToolBar → KnowledgeBaseModal/AssignKnowledgeBase}/ViewSwitcher.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/ChunkList/ChunkItem.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/ChunkList/index.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/Content.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/Loading/index.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/SimilaritySearchList/Item.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/SimilaritySearchList/index.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/ChunkDrawer/index.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/EmptyStatus.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileListItem/ChunkTag.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileListItem/DropdownMenu.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/FileSkeleton.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/MasonryFileItem/MasonryItemWrapper.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/MasonrySkeleton.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/ToolBar/Config.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/ToolBar/MultiSelectActions.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/ToolBar/index.tsx +0 -0
- /package/src/features/{FileManager/FileList → KnowledgeManager/FileExplorer}/useCheckTaskStatus.ts +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/Header/FilesSearchBar.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/Header/TogglePanelButton.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/Header/UploadFileButton.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/UploadDock/Item.tsx +0 -0
- /package/src/features/{FileManager → KnowledgeManager}/UploadDock/index.tsx +0 -0
|
@@ -11,6 +11,8 @@ export interface LobeDocument {
|
|
|
11
11
|
*/
|
|
12
12
|
createdAt: Date;
|
|
13
13
|
|
|
14
|
+
editorData: Record<string, any> | null;
|
|
15
|
+
|
|
14
16
|
/**
|
|
15
17
|
* File type or extension
|
|
16
18
|
*/
|
|
@@ -54,7 +56,12 @@ export interface LobeDocument {
|
|
|
54
56
|
source: string;
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
*
|
|
59
|
+
* 文档来源类型
|
|
60
|
+
*/
|
|
61
|
+
sourceType: DocumentSourceType;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 文档标题 (如果可用)。
|
|
58
65
|
*/
|
|
59
66
|
title?: string;
|
|
60
67
|
|
|
@@ -161,7 +168,12 @@ export enum DocumentSourceType {
|
|
|
161
168
|
API = 'api',
|
|
162
169
|
|
|
163
170
|
/**
|
|
164
|
-
*
|
|
171
|
+
* 编辑器创建的文档
|
|
172
|
+
*/
|
|
173
|
+
EDITOR = 'editor',
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 本地或上传的文件
|
|
165
177
|
*/
|
|
166
178
|
FILE = 'file',
|
|
167
179
|
|
|
@@ -6,14 +6,24 @@ export interface FileListItem {
|
|
|
6
6
|
chunkCount: number | null;
|
|
7
7
|
chunkingError: any | null;
|
|
8
8
|
chunkingStatus?: AsyncTaskStatus | null;
|
|
9
|
+
/**
|
|
10
|
+
* Text content of the document (for notes/documents)
|
|
11
|
+
*/
|
|
12
|
+
content?: string | null;
|
|
9
13
|
createdAt: Date;
|
|
14
|
+
editorData?: Record<string, any> | null;
|
|
10
15
|
embeddingError: any | null;
|
|
11
16
|
embeddingStatus?: AsyncTaskStatus | null;
|
|
12
17
|
fileType: string;
|
|
13
18
|
finishEmbedding: boolean;
|
|
14
19
|
id: string;
|
|
20
|
+
/**
|
|
21
|
+
* Metadata (for notes/documents)
|
|
22
|
+
*/
|
|
23
|
+
metadata?: Record<string, any> | null;
|
|
15
24
|
name: string;
|
|
16
25
|
size: number;
|
|
26
|
+
sourceType: string;
|
|
17
27
|
updatedAt: Date;
|
|
18
28
|
url: string;
|
|
19
29
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ModelIcon } from '@lobehub/icons';
|
|
2
|
+
import { Text } from '@lobehub/ui';
|
|
3
|
+
import { Popover } from 'antd';
|
|
4
|
+
import { createStyles } from 'antd-style';
|
|
5
|
+
import { AiModelForSelect } from 'model-bank';
|
|
6
|
+
import numeral from 'numeral';
|
|
7
|
+
import { memo, useMemo } from 'react';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
const POPOVER_MAX_WIDTH = 320;
|
|
11
|
+
|
|
12
|
+
const useStyles = createStyles(({ css, token, isDarkMode }) => ({
|
|
13
|
+
descriptionText: css`
|
|
14
|
+
color: ${isDarkMode ? token.colorText : token.colorTextSecondary};
|
|
15
|
+
`,
|
|
16
|
+
popover: css`
|
|
17
|
+
.ant-popover-inner {
|
|
18
|
+
background: ${isDarkMode ? token.colorBgSpotlight : token.colorBgElevated};
|
|
19
|
+
}
|
|
20
|
+
`,
|
|
21
|
+
priceText: css`
|
|
22
|
+
font-weight: 500;
|
|
23
|
+
color: ${isDarkMode ? token.colorTextLightSolid : token.colorTextTertiary};
|
|
24
|
+
`,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
type ImageModelItemProps = AiModelForSelect & {
|
|
28
|
+
/**
|
|
29
|
+
* Whether to show popover on hover
|
|
30
|
+
* @default true
|
|
31
|
+
*/
|
|
32
|
+
showPopover?: boolean;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const ImageModelItem = memo<ImageModelItemProps>(
|
|
36
|
+
({ approximatePricePerImage, description, pricePerImage, showPopover = true, ...model }) => {
|
|
37
|
+
const { styles } = useStyles();
|
|
38
|
+
|
|
39
|
+
const priceLabel = useMemo(() => {
|
|
40
|
+
// Priority 1: Use exact price
|
|
41
|
+
if (typeof pricePerImage === 'number') {
|
|
42
|
+
return `${numeral(pricePerImage).format('$0,0.00[000]')} / image`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Priority 2: Use approximate price with prefix
|
|
46
|
+
if (typeof approximatePricePerImage === 'number') {
|
|
47
|
+
return `~ ${numeral(approximatePricePerImage).format('$0,0.00[000]')} / image`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return undefined;
|
|
51
|
+
}, [approximatePricePerImage, pricePerImage]);
|
|
52
|
+
|
|
53
|
+
const popoverContent = useMemo(() => {
|
|
54
|
+
if (!description && !priceLabel) return null;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Flexbox gap={8} style={{ maxWidth: POPOVER_MAX_WIDTH }}>
|
|
58
|
+
{description && <Text className={styles.descriptionText}>{description}</Text>}
|
|
59
|
+
{priceLabel && <Text className={styles.priceText}>{priceLabel}</Text>}
|
|
60
|
+
</Flexbox>
|
|
61
|
+
);
|
|
62
|
+
}, [description, priceLabel, styles.descriptionText, styles.priceText]);
|
|
63
|
+
|
|
64
|
+
const content = (
|
|
65
|
+
<Flexbox align={'center'} gap={8} horizontal style={{ overflow: 'hidden' }}>
|
|
66
|
+
<ModelIcon model={model.id} size={20} />
|
|
67
|
+
<Text ellipsis title={model.displayName || model.id}>
|
|
68
|
+
{model.displayName || model.id}
|
|
69
|
+
</Text>
|
|
70
|
+
</Flexbox>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (!showPopover || !popoverContent) return content;
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Popover
|
|
77
|
+
align={{
|
|
78
|
+
offset: [24, -10],
|
|
79
|
+
}}
|
|
80
|
+
arrow={false}
|
|
81
|
+
classNames={{ root: styles.popover }}
|
|
82
|
+
content={popoverContent}
|
|
83
|
+
placement="rightTop"
|
|
84
|
+
>
|
|
85
|
+
{content}
|
|
86
|
+
</Popover>
|
|
87
|
+
);
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
ImageModelItem.displayName = 'ImageModelItem';
|
|
92
|
+
|
|
93
|
+
export default ImageModelItem;
|
|
@@ -7,7 +7,7 @@ import { memo, useMemo } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { ProviderItemRender } from '@/components/ModelSelect';
|
|
11
11
|
import { isDeprecatedEdition } from '@/const/version';
|
|
12
12
|
import { useAiInfraStore } from '@/store/aiInfra';
|
|
13
13
|
import { aiProviderSelectors } from '@/store/aiInfra/slices/aiProvider/selectors';
|
|
@@ -16,6 +16,8 @@ import { imageGenerationConfigSelectors } from '@/store/image/slices/generationC
|
|
|
16
16
|
import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
|
|
17
17
|
import { EnabledProviderWithModels } from '@/types/aiProvider';
|
|
18
18
|
|
|
19
|
+
import ImageModelItem from './ImageModelItem';
|
|
20
|
+
|
|
19
21
|
const useStyles = createStyles(({ css, prefixCls }) => ({
|
|
20
22
|
popup: css`
|
|
21
23
|
&.${prefixCls}-select-dropdown .${prefixCls}-select-item-option-grouped {
|
|
@@ -48,7 +50,7 @@ const ModelSelect = memo(() => {
|
|
|
48
50
|
const options = useMemo<SelectProps['options']>(() => {
|
|
49
51
|
const getImageModels = (provider: EnabledProviderWithModels) => {
|
|
50
52
|
const modelOptions = provider.children.map((model) => ({
|
|
51
|
-
label: <
|
|
53
|
+
label: <ImageModelItem {...model} />,
|
|
52
54
|
provider: provider.id,
|
|
53
55
|
value: `${provider.id}/${model.id}`,
|
|
54
56
|
}));
|
|
@@ -133,11 +135,24 @@ const ModelSelect = memo(() => {
|
|
|
133
135
|
}));
|
|
134
136
|
}, [enabledImageModelList, showLLM, t, theme.colorTextTertiary, router]);
|
|
135
137
|
|
|
138
|
+
const labelRender: SelectProps['labelRender'] = (props) => {
|
|
139
|
+
const modelInfo = enabledImageModelList
|
|
140
|
+
.flatMap((provider) =>
|
|
141
|
+
provider.children.map((model) => ({ ...model, providerId: provider.id })),
|
|
142
|
+
)
|
|
143
|
+
.find((model) => props.value === `${model.providerId}/${model.id}`);
|
|
144
|
+
|
|
145
|
+
if (!modelInfo) return props.label;
|
|
146
|
+
|
|
147
|
+
return <ImageModelItem {...modelInfo} showPopover={false} />;
|
|
148
|
+
};
|
|
149
|
+
|
|
136
150
|
return (
|
|
137
151
|
<Select
|
|
138
152
|
classNames={{
|
|
139
153
|
root: styles.popup,
|
|
140
154
|
}}
|
|
155
|
+
labelRender={labelRender}
|
|
141
156
|
onChange={(value, option) => {
|
|
142
157
|
// Skip onChange for disabled options (empty states)
|
|
143
158
|
if (value === 'no-provider' || value.includes('/empty')) return;
|
|
@@ -53,8 +53,9 @@ const KnowledgeRouter = memo(() => {
|
|
|
53
53
|
<MemoryRouter initialEntries={[getInitialPath()]} initialIndex={0}>
|
|
54
54
|
<UrlSynchronizer />
|
|
55
55
|
<Routes>
|
|
56
|
-
{/* Knowledge home
|
|
56
|
+
{/* Knowledge home */}
|
|
57
57
|
<Route element={<KnowledgeHomePage />} path="/" />
|
|
58
|
+
<Route element={<KnowledgeHomePage />} path="/:id" />
|
|
58
59
|
|
|
59
60
|
{/* Knowledge bases routes */}
|
|
60
61
|
<Route element={<KnowledgeBasesListPage />} path="/bases" />
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useSearchParams } from 'react-router-dom';
|
|
1
|
+
import { useParams, useSearchParams } from 'react-router-dom';
|
|
2
2
|
|
|
3
3
|
import { FilesTabs } from '@/types/files';
|
|
4
4
|
|
|
@@ -8,15 +8,18 @@ import { FilesTabs } from '@/types/files';
|
|
|
8
8
|
*/
|
|
9
9
|
export const useFileCategory = (): [string, (value: string) => void] => {
|
|
10
10
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
11
|
+
const { id } = useParams<{ id: string }>();
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// If there's an ID in the path, default to Pages instead of Home
|
|
14
|
+
const defaultCategory = id ? FilesTabs.Pages : FilesTabs.Home;
|
|
15
|
+
const category = searchParams.get('category') ?? defaultCategory;
|
|
13
16
|
|
|
14
17
|
const setCategory = (value: string) => {
|
|
15
18
|
setSearchParams(
|
|
16
19
|
(prev) => {
|
|
17
20
|
const newParams = new URLSearchParams(prev);
|
|
18
21
|
|
|
19
|
-
if (value === FilesTabs.
|
|
22
|
+
if (value === FilesTabs.Home) {
|
|
20
23
|
// Clear on default
|
|
21
24
|
newParams.delete('category');
|
|
22
25
|
} else {
|
|
@@ -6,8 +6,8 @@ import { useParams } from 'react-router-dom';
|
|
|
6
6
|
|
|
7
7
|
import FileModalQueryRoute from '@/app/[variants]/(main)/knowledge/shared/FileModalQueryRoute';
|
|
8
8
|
import { useSetFileModalId } from '@/app/[variants]/(main)/knowledge/shared/useFileQueryParam';
|
|
9
|
-
import FileManager from '@/features/FileManager';
|
|
10
9
|
import FilePanel from '@/features/FileSidePanel';
|
|
10
|
+
import KnowledgeItemManager from '@/features/KnowledgeManager';
|
|
11
11
|
import { knowledgeBaseSelectors, useKnowledgeBaseStore } from '@/store/knowledgeBase';
|
|
12
12
|
|
|
13
13
|
import { useKnowledgeBaseItem } from '../../hooks/useKnowledgeItem';
|
|
@@ -35,7 +35,7 @@ const KnowledgeBaseDetailPage = memo(() => {
|
|
|
35
35
|
<Menu id={id} />
|
|
36
36
|
</FilePanel>
|
|
37
37
|
<Flexbox flex={1} style={{ overflow: 'hidden', position: 'relative' }}>
|
|
38
|
-
<
|
|
38
|
+
<KnowledgeItemManager knowledgeBaseId={id} onOpenFile={setFileModalId} title={name} />
|
|
39
39
|
</Flexbox>
|
|
40
40
|
<FileModalQueryRoute />
|
|
41
41
|
</>
|
|
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
9
9
|
import Menu from '@/components/Menu';
|
|
10
10
|
import type { MenuProps } from '@/components/Menu';
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const CategoryMenu = () => {
|
|
13
13
|
const { t } = useTranslation('knowledgeBase');
|
|
14
14
|
|
|
15
15
|
const items: MenuProps['items'] = useMemo(
|
|
@@ -30,6 +30,6 @@ const MenuItems = () => {
|
|
|
30
30
|
);
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
CategoryMenu.displayName = 'MenuItems';
|
|
34
34
|
|
|
35
|
-
export default
|
|
35
|
+
export default CategoryMenu;
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
import { memo } from 'react';
|
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
|
+
import CategoryMenu from './CategoryMenu';
|
|
6
7
|
import Head from './Head';
|
|
7
|
-
import MenuItems from './MenuItems';
|
|
8
8
|
|
|
9
9
|
const Menu = memo<{ id: string }>(({ id }) => {
|
|
10
10
|
return (
|
|
11
11
|
<Flexbox gap={16} height={'100%'} paddingInline={12} style={{ paddingTop: 12 }}>
|
|
12
12
|
<Head id={id} />
|
|
13
|
-
<
|
|
13
|
+
<CategoryMenu />
|
|
14
14
|
</Flexbox>
|
|
15
15
|
);
|
|
16
16
|
});
|
|
@@ -5,11 +5,13 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
import { useMediaQuery } from 'react-responsive';
|
|
8
|
+
import { useParams } from 'react-router-dom';
|
|
8
9
|
|
|
9
10
|
import NProgress from '@/components/NProgress';
|
|
10
11
|
import PanelTitle from '@/components/PanelTitle';
|
|
11
|
-
import FileManager from '@/features/FileManager';
|
|
12
12
|
import FilePanel from '@/features/FileSidePanel';
|
|
13
|
+
import KnowledgeItemManager from '@/features/KnowledgeManager';
|
|
14
|
+
import TogglePanelButton from '@/features/KnowledgeManager/Header/TogglePanelButton';
|
|
13
15
|
import { useShowMobileWorkspace } from '@/hooks/useShowMobileWorkspace';
|
|
14
16
|
import { FilesTabs } from '@/types/files';
|
|
15
17
|
|
|
@@ -18,8 +20,8 @@ import FileModalQueryRoute from '../../shared/FileModalQueryRoute';
|
|
|
18
20
|
import { useSetFileModalId } from '../../shared/useFileQueryParam';
|
|
19
21
|
import Container from './layout/Container';
|
|
20
22
|
import RegisterHotkeys from './layout/RegisterHotkeys';
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
+
import CategoryMenu from './menu/CategoryMenu';
|
|
24
|
+
import Collection from './menu/KnowledgeBase';
|
|
23
25
|
|
|
24
26
|
const useStyles = createStyles(({ css, token }) => ({
|
|
25
27
|
main: css`
|
|
@@ -27,42 +29,63 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
27
29
|
overflow: hidden;
|
|
28
30
|
background: ${token.colorBgLayout};
|
|
29
31
|
`,
|
|
32
|
+
sidebar: css`
|
|
33
|
+
position: relative;
|
|
34
|
+
|
|
35
|
+
&:hover .toggle-button {
|
|
36
|
+
opacity: 1;
|
|
37
|
+
}
|
|
38
|
+
`,
|
|
39
|
+
toggleButton: css`
|
|
40
|
+
position: absolute;
|
|
41
|
+
z-index: 10;
|
|
42
|
+
inset-block-start: 8px;
|
|
43
|
+
inset-inline-end: 8px;
|
|
44
|
+
|
|
45
|
+
opacity: 0;
|
|
46
|
+
|
|
47
|
+
transition: opacity ${token.motionDurationSlow};
|
|
48
|
+
`,
|
|
30
49
|
}));
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
const MenuContent = memo(() => {
|
|
51
|
+
const Sidebar = memo(() => {
|
|
34
52
|
const { t } = useTranslation('file');
|
|
53
|
+
const { styles } = useStyles();
|
|
35
54
|
|
|
36
55
|
return (
|
|
37
|
-
<Flexbox gap={16} height={'100%'}>
|
|
56
|
+
<Flexbox className={styles.sidebar} gap={16} height={'100%'}>
|
|
57
|
+
<div className={`${styles.toggleButton} toggle-button`}>
|
|
58
|
+
<TogglePanelButton />
|
|
59
|
+
</div>
|
|
38
60
|
<Flexbox paddingInline={8}>
|
|
39
61
|
<PanelTitle desc={t('desc')} title={t('title')} />
|
|
40
|
-
<
|
|
62
|
+
<CategoryMenu />
|
|
41
63
|
</Flexbox>
|
|
42
|
-
<
|
|
64
|
+
<Collection />
|
|
43
65
|
</Flexbox>
|
|
44
66
|
);
|
|
45
67
|
});
|
|
46
68
|
|
|
47
|
-
|
|
69
|
+
Sidebar.displayName = 'Sidebar';
|
|
48
70
|
|
|
49
71
|
// Main files list component
|
|
50
|
-
const
|
|
72
|
+
const MainContent = memo(() => {
|
|
73
|
+
const { id } = useParams<{ id: string }>();
|
|
51
74
|
const [category] = useFileCategory();
|
|
52
75
|
const setFileModalId = useSetFileModalId();
|
|
53
76
|
|
|
54
77
|
return (
|
|
55
|
-
<
|
|
78
|
+
<KnowledgeItemManager
|
|
56
79
|
category={category}
|
|
80
|
+
documentId={id}
|
|
57
81
|
onOpenFile={setFileModalId}
|
|
58
82
|
title={`${category as FilesTabs}`}
|
|
59
83
|
/>
|
|
60
84
|
);
|
|
61
85
|
});
|
|
62
86
|
|
|
63
|
-
|
|
87
|
+
MainContent.displayName = 'FilesListPage';
|
|
64
88
|
|
|
65
|
-
// Desktop layout
|
|
66
89
|
const DesktopLayout = memo(() => {
|
|
67
90
|
return (
|
|
68
91
|
<>
|
|
@@ -74,10 +97,10 @@ const DesktopLayout = memo(() => {
|
|
|
74
97
|
width={'100%'}
|
|
75
98
|
>
|
|
76
99
|
<FilePanel>
|
|
77
|
-
<
|
|
100
|
+
<Sidebar />
|
|
78
101
|
</FilePanel>
|
|
79
102
|
<Container>
|
|
80
|
-
<
|
|
103
|
+
<MainContent />
|
|
81
104
|
</Container>
|
|
82
105
|
</Flexbox>
|
|
83
106
|
<RegisterHotkeys />
|
|
@@ -88,7 +111,6 @@ const DesktopLayout = memo(() => {
|
|
|
88
111
|
|
|
89
112
|
DesktopLayout.displayName = 'DesktopLayout';
|
|
90
113
|
|
|
91
|
-
// Mobile layout
|
|
92
114
|
const MobileLayout = memo(() => {
|
|
93
115
|
const showMobileWorkspace = useShowMobileWorkspace();
|
|
94
116
|
const { styles } = useStyles();
|
|
@@ -102,7 +124,7 @@ const MobileLayout = memo(() => {
|
|
|
102
124
|
style={showMobileWorkspace ? { display: 'none' } : undefined}
|
|
103
125
|
width="100%"
|
|
104
126
|
>
|
|
105
|
-
<
|
|
127
|
+
<Sidebar />
|
|
106
128
|
</Flexbox>
|
|
107
129
|
<Flexbox
|
|
108
130
|
className={styles.main}
|
|
@@ -110,7 +132,7 @@ const MobileLayout = memo(() => {
|
|
|
110
132
|
style={showMobileWorkspace ? undefined : { display: 'none' }}
|
|
111
133
|
width="100%"
|
|
112
134
|
>
|
|
113
|
-
<
|
|
135
|
+
<MainContent />
|
|
114
136
|
</Flexbox>
|
|
115
137
|
<FileModalQueryRoute />
|
|
116
138
|
</>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { CaretDownFilled } from '@ant-design/icons';
|
|
4
|
+
import { ActionIcon, Icon } from '@lobehub/ui';
|
|
5
|
+
import { createStyles } from 'antd-style';
|
|
6
|
+
import { motion } from 'framer-motion';
|
|
7
|
+
import { FilePenIcon, FileText, FolderOpen, ImageIcon, Mic2, SquarePlay } from 'lucide-react';
|
|
8
|
+
import { memo, useMemo, useState } from 'react';
|
|
9
|
+
import { useTranslation } from 'react-i18next';
|
|
10
|
+
import { Flexbox } from 'react-layout-kit';
|
|
11
|
+
import { useNavigate } from 'react-router-dom';
|
|
12
|
+
|
|
13
|
+
import Menu from '@/components/Menu';
|
|
14
|
+
import type { MenuProps } from '@/components/Menu';
|
|
15
|
+
import { FilesTabs } from '@/types/files';
|
|
16
|
+
|
|
17
|
+
import { useFileCategory } from '../../../hooks/useFileCategory';
|
|
18
|
+
|
|
19
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
20
|
+
header: css`
|
|
21
|
+
cursor: pointer;
|
|
22
|
+
border-radius: ${token.borderRadius}px;
|
|
23
|
+
color: ${token.colorTextSecondary};
|
|
24
|
+
transition: background-color 0.2s;
|
|
25
|
+
|
|
26
|
+
&:hover {
|
|
27
|
+
background-color: ${token.colorFillTertiary};
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
headerActive: css`
|
|
31
|
+
color: ${token.colorText};
|
|
32
|
+
background-color: ${token.colorFillSecondary};
|
|
33
|
+
`,
|
|
34
|
+
indentedMenu: css`
|
|
35
|
+
padding-inline-start: 24px;
|
|
36
|
+
`,
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
const CategoryMenu = memo(() => {
|
|
40
|
+
const { t } = useTranslation('file');
|
|
41
|
+
const { styles, cx } = useStyles();
|
|
42
|
+
const [activeKey] = useFileCategory();
|
|
43
|
+
const [showCollapsed, setShowCollapsed] = useState(activeKey !== FilesTabs.Home);
|
|
44
|
+
const navigate = useNavigate();
|
|
45
|
+
|
|
46
|
+
const collapsedItems: MenuProps['items'] = useMemo(
|
|
47
|
+
() => [
|
|
48
|
+
{
|
|
49
|
+
icon: <Icon icon={FolderOpen} />,
|
|
50
|
+
key: FilesTabs.All,
|
|
51
|
+
label: t('tab.all'),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
icon: <Icon icon={FileText} />,
|
|
55
|
+
key: FilesTabs.Documents,
|
|
56
|
+
label: t('tab.documents'),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
icon: <Icon icon={FilePenIcon} />,
|
|
60
|
+
key: FilesTabs.Pages,
|
|
61
|
+
label: t('tab.pages'),
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
icon: <Icon icon={ImageIcon} />,
|
|
65
|
+
key: FilesTabs.Images,
|
|
66
|
+
label: t('tab.images'),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
icon: <Icon icon={Mic2} />,
|
|
70
|
+
key: FilesTabs.Audios,
|
|
71
|
+
label: t('tab.audios'),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
icon: <Icon icon={SquarePlay} />,
|
|
75
|
+
key: FilesTabs.Videos,
|
|
76
|
+
label: t('tab.videos'),
|
|
77
|
+
},
|
|
78
|
+
// {
|
|
79
|
+
// icon: <Icon icon={Globe} />,
|
|
80
|
+
// key: FilesTabs.Websites,
|
|
81
|
+
// label: t('tab.websites'),
|
|
82
|
+
// },
|
|
83
|
+
],
|
|
84
|
+
[t],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const isHomeActive = activeKey === FilesTabs.Home;
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Flexbox gap={4}>
|
|
91
|
+
<Flexbox
|
|
92
|
+
align={'center'}
|
|
93
|
+
className={cx(styles.header, isHomeActive && styles.headerActive)}
|
|
94
|
+
horizontal
|
|
95
|
+
onClick={() => {
|
|
96
|
+
navigate('/', { replace: true });
|
|
97
|
+
}}
|
|
98
|
+
paddingBlock={6}
|
|
99
|
+
paddingInline={8}
|
|
100
|
+
>
|
|
101
|
+
<Flexbox align={'center'} flex={1} gap={8} horizontal>
|
|
102
|
+
<motion.div
|
|
103
|
+
animate={{ rotate: showCollapsed ? 0 : -90 }}
|
|
104
|
+
transition={{ duration: 0.2, ease: 'easeInOut' }}
|
|
105
|
+
>
|
|
106
|
+
<ActionIcon
|
|
107
|
+
icon={CaretDownFilled as any}
|
|
108
|
+
onClick={(e) => {
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
setShowCollapsed(!showCollapsed);
|
|
111
|
+
}}
|
|
112
|
+
size={'small'}
|
|
113
|
+
/>
|
|
114
|
+
</motion.div>
|
|
115
|
+
<div style={{ flex: 1, lineHeight: '14px' }}>{t('tab.home')}</div>
|
|
116
|
+
</Flexbox>
|
|
117
|
+
</Flexbox>
|
|
118
|
+
|
|
119
|
+
<motion.div
|
|
120
|
+
animate={{
|
|
121
|
+
height: showCollapsed ? 'auto' : 0,
|
|
122
|
+
opacity: showCollapsed ? 1 : 0,
|
|
123
|
+
}}
|
|
124
|
+
initial={false}
|
|
125
|
+
style={{ overflow: 'hidden' }}
|
|
126
|
+
transition={{ duration: 0.2, ease: 'easeInOut' }}
|
|
127
|
+
>
|
|
128
|
+
<Flexbox>
|
|
129
|
+
<Menu
|
|
130
|
+
compact
|
|
131
|
+
items={collapsedItems}
|
|
132
|
+
onClick={({ key }) => {
|
|
133
|
+
// Navigate to home route and set category
|
|
134
|
+
const categoryParam = key === FilesTabs.Home ? '' : `?category=${key}`;
|
|
135
|
+
navigate(`/${categoryParam}`, { replace: true });
|
|
136
|
+
}}
|
|
137
|
+
selectable
|
|
138
|
+
selectedKeys={[activeKey]}
|
|
139
|
+
/>
|
|
140
|
+
</Flexbox>
|
|
141
|
+
</motion.div>
|
|
142
|
+
</Flexbox>
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
CategoryMenu.displayName = 'CategoryMenu';
|
|
147
|
+
|
|
148
|
+
export default CategoryMenu;
|