@lobehub/lobehub 2.0.0-next.249 → 2.0.0-next.250
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +4 -0
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +5 -0
- package/locales/en-US/file.json +2 -0
- package/locales/zh-CN/discover.json +3 -0
- package/locales/zh-CN/file.json +2 -0
- package/package.json +3 -3
- package/packages/types/package.json +2 -2
- package/packages/types/src/discover/mcp.ts +3 -1
- package/src/app/[variants]/(main)/community/(list)/(home)/index.tsx +2 -0
- package/src/app/[variants]/(main)/community/(list)/features/SortButton/index.tsx +4 -0
- package/src/app/[variants]/(main)/community/(list)/mcp/features/Category/index.tsx +7 -3
- package/src/app/[variants]/(main)/community/(list)/mcp/index.tsx +2 -2
- package/src/app/[variants]/(main)/home/_layout/Body/Project/List/Editing.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Project/List/Item.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Project/List/index.tsx +1 -1
- package/src/app/[variants]/(main)/home/_layout/Body/Project/List/useDropdownMenu.tsx +1 -1
- package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/{List/Item → Item}/Editing.tsx +1 -1
- package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/{List/Item → Item}/index.tsx +1 -1
- package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/{List/Item → Item}/useDropdownMenu.tsx +1 -1
- package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/index.tsx +16 -6
- package/src/app/[variants]/(main)/resource/(home)/_layout/Body/{KnowledgeBase.tsx → index.tsx} +2 -3
- package/src/app/[variants]/(main)/resource/(home)/_layout/Sidebar.tsx +2 -2
- package/src/app/[variants]/(main)/resource/(home)/index.tsx +23 -10
- package/src/app/[variants]/(main)/resource/features/DndContextWrapper.tsx +12 -37
- package/src/app/[variants]/(main)/resource/features/hooks/useKnowledgeItem.ts +1 -1
- package/src/app/[variants]/(main)/resource/features/store/action.ts +9 -39
- package/src/app/[variants]/(main)/resource/library/_layout/Header/LibraryHead.tsx +1 -1
- package/src/app/[variants]/(main)/resource/library/index.tsx +13 -6
- package/src/features/LibraryModal/AddFilesToKnowledgeBase/SelectForm.tsx +1 -1
- package/src/features/LibraryModal/CreateNew/CreateForm.tsx +1 -1
- package/src/features/PageEditor/Header/Breadcrumb.tsx +1 -1
- package/src/features/PageEditor/store/action.ts +5 -2
- package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +5 -7
- package/src/features/ResourceManager/components/Explorer/Header/Breadcrumb.tsx +1 -1
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +57 -26
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +35 -6
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +20 -14
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +41 -31
- package/src/features/ResourceManager/components/Explorer/MasonryView/MasonryFileItem/index.tsx +1 -1
- package/src/features/ResourceManager/components/Explorer/MasonryView/Skeleton.tsx +6 -2
- package/src/features/ResourceManager/components/Explorer/MasonryView/index.tsx +29 -18
- package/src/features/ResourceManager/components/Explorer/MoveToFolderModal.tsx +7 -34
- package/src/features/ResourceManager/components/Explorer/ToolBar/BatchActionsDropdown.tsx +1 -1
- package/src/features/ResourceManager/components/Explorer/index.tsx +58 -18
- package/src/features/ResourceManager/components/Explorer/useCheckTaskStatus.ts +6 -4
- package/src/features/ResourceManager/components/Header/AddButton.tsx +58 -35
- package/src/features/ResourceManager/components/Header/hooks/useNotionImport.ts +5 -5
- package/src/features/ResourceManager/components/Tree/TreeSkeleton.tsx +19 -9
- package/src/features/ResourceManager/components/Tree/index.tsx +110 -5
- package/src/features/ResourceManager/components/UploadDock/index.tsx +2 -1
- package/src/features/ResourceManager/constants.ts +3 -0
- package/src/hooks/useMCPCategory.tsx +7 -0
- package/src/locales/default/discover.ts +3 -0
- package/src/locales/default/file.ts +2 -0
- package/src/services/file/index.ts +34 -1
- package/src/services/resource/index.ts +249 -0
- package/src/store/discover/slices/mcp/action.ts +1 -1
- package/src/store/file/slices/chat/action.ts +2 -1
- package/src/store/file/slices/document/action.ts +10 -7
- package/src/store/file/slices/fileManager/action.ts +14 -4
- package/src/store/file/slices/fileManager/initialState.ts +2 -0
- package/src/store/file/slices/resource/action.ts +432 -0
- package/src/store/file/slices/resource/hooks.ts +82 -0
- package/src/store/file/slices/resource/initialState.ts +67 -0
- package/src/store/file/slices/resource/syncEngine.ts +326 -0
- package/src/store/file/store.ts +6 -1
- package/src/store/{knowledgeBase → library}/initialState.ts +2 -2
- package/src/store/{knowledgeBase → library}/slices/content/action.test.ts +37 -51
- package/src/store/{knowledgeBase → library}/slices/content/action.ts +8 -4
- package/src/store/{knowledgeBase → library}/slices/crud/action.ts +1 -1
- package/src/store/{knowledgeBase → library}/slices/crud/selectors.ts +1 -1
- package/src/store/{knowledgeBase → library}/slices/ragEval/actions/dataset.ts +1 -1
- package/src/store/{knowledgeBase → library}/slices/ragEval/actions/evaluation.ts +1 -1
- package/src/store/{knowledgeBase → library}/slices/ragEval/actions/index.ts +1 -1
- package/src/types/resource.ts +133 -0
- package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/List/index.tsx +0 -25
- /package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/{List/Item → Item}/Actions.tsx +0 -0
- /package/src/store/{knowledgeBase → library}/index.ts +0 -0
- /package/src/store/{knowledgeBase → library}/selectors.ts +0 -0
- /package/src/store/{knowledgeBase → library}/slices/content/index.ts +0 -0
- /package/src/store/{knowledgeBase → library}/slices/crud/action.test.ts +0 -0
- /package/src/store/{knowledgeBase → library}/slices/crud/index.ts +0 -0
- /package/src/store/{knowledgeBase → library}/slices/crud/initialState.ts +0 -0
- /package/src/store/{knowledgeBase → library}/slices/ragEval/index.ts +0 -0
- /package/src/store/{knowledgeBase → library}/slices/ragEval/initialState.ts +0 -0
- /package/src/store/{knowledgeBase → library}/store.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.250](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.249...v2.0.0-next.250)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-09**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **community**: Recommended for home & added discover tab.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **community**: Recommended for home & added discover tab, closes [#11290](https://github.com/lobehub/lobe-chat/issues/11290) ([8db248c](https://github.com/lobehub/lobe-chat/commit/8db248c))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.249](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.248...v2.0.0-next.249)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-09**</sup>
|
package/changelog/v1.json
CHANGED
package/locales/en-US/file.json
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"empty": "No files or folders have been uploaded yet.",
|
|
19
19
|
"header.actions.builtInBlockList.filtered": "{{ignored}} files filtered (out of {{total}} total)",
|
|
20
20
|
"header.actions.connect": "Connect...",
|
|
21
|
+
"header.actions.createFolderError": "Failed to create folder",
|
|
22
|
+
"header.actions.creatingFolder": "Creating folder...",
|
|
21
23
|
"header.actions.gitignore.apply": "Apply Rules",
|
|
22
24
|
"header.actions.gitignore.cancel": "Ignore Rules",
|
|
23
25
|
"header.actions.gitignore.content": ".gitignore file detected ({{count}} files in total). Would you like to apply the ignore rules?",
|
|
@@ -152,6 +152,8 @@
|
|
|
152
152
|
"like": "喜欢",
|
|
153
153
|
"mcp.categories.all.description": "全部 MCP Servers",
|
|
154
154
|
"mcp.categories.all.name": "全部",
|
|
155
|
+
"mcp.categories.discover.description": "推荐和热门的 MCP 服务",
|
|
156
|
+
"mcp.categories.discover.name": "发现推荐",
|
|
155
157
|
"mcp.categories.business.description": "商业与企业服务",
|
|
156
158
|
"mcp.categories.business.name": "商业服务",
|
|
157
159
|
"mcp.categories.developer.description": "开发相关的技能与服务",
|
|
@@ -338,6 +340,7 @@
|
|
|
338
340
|
"mcp.hero.subTitle": "开源 & 开箱即用",
|
|
339
341
|
"mcp.hero.title": "面向 AI 的开源 MCP 社区",
|
|
340
342
|
"mcp.sorts.createdAt": "最近新增",
|
|
343
|
+
"mcp.sorts.discover": "推荐",
|
|
341
344
|
"mcp.sorts.installCount": "安装数",
|
|
342
345
|
"mcp.sorts.isFeatured": "推荐技能",
|
|
343
346
|
"mcp.sorts.isValidated": "已验证技能",
|
package/locales/zh-CN/file.json
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"empty": "这里还没有文件或文件夹。先上传一个文件,或创建文稿开始整理",
|
|
19
19
|
"header.actions.builtInBlockList.filtered": "已过滤 {{ignored}} 个文件(共 {{total}} 个文件)",
|
|
20
20
|
"header.actions.connect": "连接…",
|
|
21
|
+
"header.actions.createFolderError": "创建文件夹失败",
|
|
22
|
+
"header.actions.creatingFolder": "正在创建文件夹...",
|
|
21
23
|
"header.actions.gitignore.apply": "应用规则",
|
|
22
24
|
"header.actions.gitignore.cancel": "忽略规则",
|
|
23
25
|
"header.actions.gitignore.content": "检测到 .gitignore 文件(共 {{count}} 个文件),是否应用忽略规则?",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.250",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
"@lobehub/desktop-ipc-typings": "workspace:*",
|
|
205
205
|
"@lobehub/editor": "^3.8.0",
|
|
206
206
|
"@lobehub/icons": "^4.0.2",
|
|
207
|
-
"@lobehub/market-sdk": "0.28.
|
|
207
|
+
"@lobehub/market-sdk": "0.28.1",
|
|
208
208
|
"@lobehub/tts": "^4.0.2",
|
|
209
209
|
"@lobehub/ui": "^4.11.6",
|
|
210
210
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
@@ -366,7 +366,7 @@
|
|
|
366
366
|
"@lobechat/types": "workspace:*",
|
|
367
367
|
"@lobehub/i18n-cli": "^1.26.0",
|
|
368
368
|
"@lobehub/lint": "^1.26.3",
|
|
369
|
-
"@lobehub/market-types": "^1.
|
|
369
|
+
"@lobehub/market-types": "^1.12.3",
|
|
370
370
|
"@lobehub/seo-cli": "^1.7.0",
|
|
371
371
|
"@next/bundle-analyzer": "^16.1.1",
|
|
372
372
|
"@next/eslint-plugin-next": "^16.1.1",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"@lobechat/python-interpreter": "workspace:*",
|
|
9
9
|
"@lobechat/web-crawler": "workspace:*",
|
|
10
10
|
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
|
11
|
-
"@lobehub/market-sdk": "0.28.
|
|
12
|
-
"@lobehub/market-types": "^1.
|
|
11
|
+
"@lobehub/market-sdk": "0.28.1",
|
|
12
|
+
"@lobehub/market-types": "^1.12.3",
|
|
13
13
|
"model-bank": "workspace:*",
|
|
14
14
|
"type-fest": "^4.41.0",
|
|
15
15
|
"zustand": "5.0.4"
|
|
@@ -5,6 +5,7 @@ export enum McpCategory {
|
|
|
5
5
|
All = 'all',
|
|
6
6
|
Business = 'business',
|
|
7
7
|
Developer = 'developer',
|
|
8
|
+
Discover = 'discover',
|
|
8
9
|
GamingEntertainment = 'gaming-entertainment',
|
|
9
10
|
HealthWellness = 'health-wellness',
|
|
10
11
|
Lifestyle = 'lifestyle',
|
|
@@ -26,7 +27,8 @@ export enum McpSorts {
|
|
|
26
27
|
IsFeatured = 'isFeatured',
|
|
27
28
|
IsValidated = 'isValidated',
|
|
28
29
|
RatingCount = 'ratingCount',
|
|
29
|
-
|
|
30
|
+
Recommended = 'recommended',
|
|
31
|
+
UpdatedAt = 'updatedAt'
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export enum McpNavKey {
|
|
@@ -4,6 +4,7 @@ import { memo } from 'react';
|
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { useDiscoverStore } from '@/store/discover';
|
|
7
|
+
import { McpSorts } from '@/types/discover';
|
|
7
8
|
|
|
8
9
|
import Title from '../../components/Title';
|
|
9
10
|
import AssistantList from '../assistant/features/List';
|
|
@@ -23,6 +24,7 @@ const HomePage = memo(() => {
|
|
|
23
24
|
const { data: mcpList, isLoading: pluginLoading } = useMcpList({
|
|
24
25
|
page: 1,
|
|
25
26
|
pageSize: 12,
|
|
27
|
+
sort: McpSorts.Recommended,
|
|
26
28
|
});
|
|
27
29
|
|
|
28
30
|
if (assistantLoading || pluginLoading || !assistantList || !mcpList) return <Loading />;
|
|
@@ -10,13 +10,13 @@ import { withSuspense } from '@/components/withSuspense';
|
|
|
10
10
|
import { useCategory } from '@/hooks/useMCPCategory';
|
|
11
11
|
import { useQuery } from '@/hooks/useQuery';
|
|
12
12
|
import { useDiscoverStore } from '@/store/discover';
|
|
13
|
-
import { McpCategory } from '@/types/discover';
|
|
13
|
+
import { McpCategory, McpSorts } from '@/types/discover';
|
|
14
14
|
|
|
15
15
|
import CategoryMenu from '../../../../components/CategoryMenu';
|
|
16
16
|
|
|
17
17
|
const Category = memo(() => {
|
|
18
18
|
const useMcpCategories = useDiscoverStore((s) => s.useMcpCategories);
|
|
19
|
-
const { category =
|
|
19
|
+
const { category = McpCategory.Discover, q } = useQuery() as { category?: McpCategory; q?: string };
|
|
20
20
|
const { data: items = [] } = useMcpCategories({ q });
|
|
21
21
|
const navigate = useNavigate();
|
|
22
22
|
const cates = useCategory();
|
|
@@ -24,7 +24,11 @@ const Category = memo(() => {
|
|
|
24
24
|
const genUrl = (key: McpCategory) =>
|
|
25
25
|
qs.stringifyUrl(
|
|
26
26
|
{
|
|
27
|
-
query: {
|
|
27
|
+
query: {
|
|
28
|
+
category: [McpCategory.All, McpCategory.Discover].includes(key) ? null : key,
|
|
29
|
+
q,
|
|
30
|
+
sort: key === McpCategory.Discover ? McpSorts.Recommended : null,
|
|
31
|
+
},
|
|
28
32
|
url: '/community/mcp',
|
|
29
33
|
},
|
|
30
34
|
{ skipNull: true },
|
|
@@ -5,7 +5,7 @@ import { memo } from 'react';
|
|
|
5
5
|
|
|
6
6
|
import { useQuery } from '@/hooks/useQuery';
|
|
7
7
|
import { useDiscoverStore } from '@/store/discover';
|
|
8
|
-
import { DiscoverTab, type McpQueryParams } from '@/types/discover';
|
|
8
|
+
import { DiscoverTab, McpSorts, type McpQueryParams } from '@/types/discover';
|
|
9
9
|
|
|
10
10
|
import Pagination from '../features/Pagination';
|
|
11
11
|
import List from './features/List';
|
|
@@ -20,7 +20,7 @@ const McpPage = memo(() => {
|
|
|
20
20
|
page,
|
|
21
21
|
pageSize: 21,
|
|
22
22
|
q,
|
|
23
|
-
sort,
|
|
23
|
+
sort: sort ?? McpSorts.Recommended,
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
if (isLoading || !data) return <Loading />;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Input, Popover } from '@lobehub/ui';
|
|
2
2
|
import { memo, useCallback, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
4
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
5
5
|
|
|
6
6
|
interface EditingProps {
|
|
7
7
|
id: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BoxIcon } from 'lucide-react';
|
|
2
2
|
import { memo, useCallback } from 'react';
|
|
3
3
|
|
|
4
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
4
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
5
5
|
|
|
6
6
|
import NavItem from '../../../../../../../../features/NavPanel/components/NavItem';
|
|
7
7
|
import Actions from './Actions';
|
|
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
6
6
|
import { Link, useNavigate } from 'react-router-dom';
|
|
7
7
|
|
|
8
8
|
import { LIBRARY_URL } from '@/const/url';
|
|
9
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
9
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
10
10
|
|
|
11
11
|
import EmptyNavItem from '../../../../../../../../features/NavPanel/components/EmptyNavItem';
|
|
12
12
|
import SkeletonList from '../../../../../../../../features/NavPanel/components/SkeletonList';
|
|
@@ -4,7 +4,7 @@ import { PencilLine, Trash } from 'lucide-react';
|
|
|
4
4
|
import { useCallback } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
7
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
8
8
|
|
|
9
9
|
interface ProjectItemDropdownMenuProps {
|
|
10
10
|
id: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Input, Popover } from '@lobehub/ui';
|
|
2
2
|
import { memo, useCallback, useEffect, useState } from 'react';
|
|
3
3
|
|
|
4
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
4
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
5
5
|
|
|
6
6
|
interface EditingProps {
|
|
7
7
|
id: string;
|
|
@@ -7,7 +7,7 @@ import { useNavigate } from 'react-router-dom';
|
|
|
7
7
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
8
8
|
import RepoIcon from '@/components/LibIcon';
|
|
9
9
|
import NavItem from '@/features/NavPanel/components/NavItem';
|
|
10
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
10
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
11
11
|
|
|
12
12
|
import Actions from './Actions';
|
|
13
13
|
import Editing from './Editing';
|
|
@@ -4,7 +4,7 @@ import { PencilLine, Trash } from 'lucide-react';
|
|
|
4
4
|
import { useCallback } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
7
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
8
8
|
|
|
9
9
|
interface ActionProps {
|
|
10
10
|
id: string;
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { Flexbox } from '@lobehub/ui';
|
|
4
|
+
import { memo } from 'react';
|
|
4
5
|
import { useTranslation } from 'react-i18next';
|
|
5
6
|
import { useNavigate } from 'react-router-dom';
|
|
6
7
|
|
|
7
8
|
import { useCreateNewModal } from '@/features/LibraryModal';
|
|
8
9
|
import EmptyNavItem from '@/features/NavPanel/components/EmptyNavItem';
|
|
9
10
|
import SkeletonList from '@/features/NavPanel/components/SkeletonList';
|
|
10
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
11
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
11
12
|
|
|
12
|
-
import
|
|
13
|
+
import Item from './Item';
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Show library list in the sidebar
|
|
17
|
+
*/
|
|
18
|
+
const LibraryList = memo(() => {
|
|
15
19
|
const { t } = useTranslation('file');
|
|
16
20
|
const useFetchKnowledgeBaseList = useKnowledgeBaseStore((s) => s.useFetchKnowledgeBaseList);
|
|
17
21
|
const { data, isLoading } = useFetchKnowledgeBaseList();
|
|
@@ -32,7 +36,13 @@ const KnowledgeBaseList = memo(() => {
|
|
|
32
36
|
|
|
33
37
|
if (data?.length === 0) return <EmptyNavItem onClick={handleCreate} title={t('library.new')} />;
|
|
34
38
|
|
|
35
|
-
return
|
|
39
|
+
return (
|
|
40
|
+
<Flexbox gap={1} paddingInline={4}>
|
|
41
|
+
{data?.map((item) => (
|
|
42
|
+
<Item id={item.id} key={item.id} name={item.name} />
|
|
43
|
+
))}
|
|
44
|
+
</Flexbox>
|
|
45
|
+
);
|
|
36
46
|
});
|
|
37
47
|
|
|
38
|
-
export default
|
|
48
|
+
export default LibraryList;
|
package/src/app/[variants]/(main)/resource/(home)/_layout/Body/{KnowledgeBase.tsx → index.tsx}
RENAMED
|
@@ -8,7 +8,7 @@ import { useCreateNewModal } from '@/features/LibraryModal';
|
|
|
8
8
|
|
|
9
9
|
import LibraryList from './LibraryList';
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const SidebarBody = memo<{ itemKey: string }>(({ itemKey }) => {
|
|
12
12
|
const { t } = useTranslation('file');
|
|
13
13
|
const navigate = useNavigate();
|
|
14
14
|
|
|
@@ -29,7 +29,6 @@ const Collection = memo<{ itemKey: string }>(({ itemKey }) => {
|
|
|
29
29
|
icon={PlusIcon}
|
|
30
30
|
onClick={handleCreate}
|
|
31
31
|
size={'small'}
|
|
32
|
-
style={{ flex: 'none' }}
|
|
33
32
|
title={t('library.new')}
|
|
34
33
|
/>
|
|
35
34
|
}
|
|
@@ -47,4 +46,4 @@ const Collection = memo<{ itemKey: string }>(({ itemKey }) => {
|
|
|
47
46
|
);
|
|
48
47
|
});
|
|
49
48
|
|
|
50
|
-
export default
|
|
49
|
+
export default SidebarBody;
|
|
@@ -6,7 +6,7 @@ import { memo } from 'react';
|
|
|
6
6
|
import { NavPanelPortal } from '@/features/NavPanel';
|
|
7
7
|
import SideBarLayout from '@/features/NavPanel/SideBarLayout';
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import SidebarBody from './Body';
|
|
10
10
|
import Header from './Header';
|
|
11
11
|
|
|
12
12
|
export enum GroupKey {
|
|
@@ -20,7 +20,7 @@ const Sidebar = memo(() => {
|
|
|
20
20
|
body={
|
|
21
21
|
<Flexbox paddingBlock={8} paddingInline={4}>
|
|
22
22
|
<Accordion defaultExpandedKeys={[GroupKey.Library]} gap={8}>
|
|
23
|
-
<
|
|
23
|
+
<SidebarBody itemKey={GroupKey.Library} />
|
|
24
24
|
</Accordion>
|
|
25
25
|
</Flexbox>
|
|
26
26
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { memo, useEffect } from 'react';
|
|
4
|
-
import { useSearchParams } from 'react-router-dom';
|
|
3
|
+
import { memo, useEffect, useLayoutEffect } from 'react';
|
|
4
|
+
import { useLocation, useSearchParams } from 'react-router-dom';
|
|
5
5
|
|
|
6
6
|
import ResourceManager from '@/features/ResourceManager';
|
|
7
7
|
import { documentSelectors, useFileStore } from '@/store/file';
|
|
@@ -11,6 +11,7 @@ import { useResourceManagerStore } from '../features/store';
|
|
|
11
11
|
|
|
12
12
|
const ResourceHomePage = memo(() => {
|
|
13
13
|
const [searchParams] = useSearchParams();
|
|
14
|
+
const location = useLocation();
|
|
14
15
|
const [setMode, setCurrentViewItemId, setCategory, setLibraryId] = useResourceManagerStore(
|
|
15
16
|
(s) => [s.setMode, s.setCurrentViewItemId, s.setCategory, s.setLibraryId],
|
|
16
17
|
);
|
|
@@ -23,15 +24,27 @@ const ResourceHomePage = memo(() => {
|
|
|
23
24
|
const { data: fileData } = useFetchKnowledgeItem(fileId || undefined);
|
|
24
25
|
const documentData = useFileStore(documentSelectors.getDocumentById(fileId || undefined));
|
|
25
26
|
|
|
26
|
-
// Clear libraryId when on home route
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
// Clear libraryId when on home route using useLayoutEffect
|
|
28
|
+
// useLayoutEffect runs synchronously before browser paint, ensuring state is cleared
|
|
29
|
+
// before child components' useEffects run, while avoiding React's setState-in-render error
|
|
30
|
+
// IMPORTANT: Only depend on location.pathname, NOT currentLibraryId to avoid feedback loop
|
|
31
|
+
// When location changes to /resource, clear libraryId
|
|
32
|
+
// Don't clear when location is /library/* (even if this component is still mounted)
|
|
33
|
+
useLayoutEffect(() => {
|
|
34
|
+
const isOnHomeRoute = location.pathname === '/resource' || !location.pathname.includes('/library/');
|
|
35
|
+
if (isOnHomeRoute) {
|
|
36
|
+
setLibraryId(undefined);
|
|
37
|
+
}
|
|
38
|
+
}, [setLibraryId, location.pathname]);
|
|
30
39
|
|
|
31
|
-
// Sync category from URL
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
// Sync category from URL using useLayoutEffect
|
|
41
|
+
// IMPORTANT: Only sync if we're actually on the home route (not transitioning to library)
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
const isOnHomeRoute = location.pathname === '/resource' || !location.pathname.includes('/library/');
|
|
44
|
+
if (isOnHomeRoute) {
|
|
45
|
+
setCategory(categoryParam);
|
|
46
|
+
}
|
|
47
|
+
}, [categoryParam, setCategory, location.pathname]);
|
|
35
48
|
|
|
36
49
|
// Sync file view mode from URL
|
|
37
50
|
useEffect(() => {
|
|
@@ -60,6 +60,7 @@ interface DragState {
|
|
|
60
60
|
|
|
61
61
|
const DragStateContext = createContext<{
|
|
62
62
|
currentDrag: DragState | null;
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
64
|
setCurrentDrag: (state: DragState | null) => void;
|
|
64
65
|
}>({
|
|
65
66
|
currentDrag: null,
|
|
@@ -77,10 +78,8 @@ export const DndContextWrapper = memo<PropsWithChildren>(({ children }) => {
|
|
|
77
78
|
const { message } = App.useApp();
|
|
78
79
|
const [currentDrag, setCurrentDrag] = useState<DragState | null>(null);
|
|
79
80
|
const overlayRef = useRef<HTMLDivElement>(null);
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const refreshFileList = useFileStore((s) => s.refreshFileList);
|
|
83
|
-
const fileList = useFileStore((s) => s.fileList);
|
|
81
|
+
const moveResource = useFileStore((s) => s.moveResource);
|
|
82
|
+
const resourceList = useFileStore((s) => s.resourceList);
|
|
84
83
|
const selectedFileIds = useResourceManagerStore((s) => s.selectedFileIds);
|
|
85
84
|
const setSelectedFileIds = useResourceManagerStore((s) => s.setSelectedFileIds);
|
|
86
85
|
const libraryId = useResourceManagerStore((s) => s.libraryId);
|
|
@@ -143,8 +142,8 @@ export const DndContextWrapper = memo<PropsWithChildren>(({ children }) => {
|
|
|
143
142
|
}
|
|
144
143
|
|
|
145
144
|
// Save current drag data before clearing state
|
|
146
|
-
const draggedItemId = currentDrag.id;
|
|
147
|
-
const draggedItemData = currentDrag.data;
|
|
145
|
+
// const draggedItemId = currentDrag.id;
|
|
146
|
+
// const draggedItemData = currentDrag.data;
|
|
148
147
|
|
|
149
148
|
// Clear drag state immediately for better UX
|
|
150
149
|
setCurrentDrag(null);
|
|
@@ -153,36 +152,14 @@ export const DndContextWrapper = memo<PropsWithChildren>(({ children }) => {
|
|
|
153
152
|
const hideLoading = message.loading(t('FileManager.actions.moving'), 0);
|
|
154
153
|
|
|
155
154
|
try {
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const pools = itemsToMove.map((id) => {
|
|
160
|
-
const item = fileList.find((f) => f.id === id);
|
|
161
|
-
const itemData = item || (id === draggedItemId ? draggedItemData : null);
|
|
162
|
-
|
|
163
|
-
if (!itemData) return Promise.resolve();
|
|
164
|
-
|
|
165
|
-
// Track source folder ID
|
|
166
|
-
if (item?.parentId !== undefined) {
|
|
167
|
-
sourceFolderIds.add(item.parentId);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const isDocument =
|
|
171
|
-
itemData.sourceType === 'document' ||
|
|
172
|
-
itemData.fileType === 'custom/document' ||
|
|
173
|
-
itemData.fileType === 'custom/folder';
|
|
174
|
-
|
|
175
|
-
if (isDocument) {
|
|
176
|
-
return updateDocument(id, { parentId: targetParentId });
|
|
177
|
-
} else {
|
|
178
|
-
return moveFileToFolder(id, targetParentId);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
155
|
+
// Move all items using optimistic moveResource
|
|
156
|
+
const pools = itemsToMove.map((id) => moveResource(id, targetParentId));
|
|
181
157
|
|
|
182
158
|
await Promise.all(pools);
|
|
183
159
|
|
|
184
|
-
//
|
|
185
|
-
await
|
|
160
|
+
// Refetch resources to update the view (items should disappear from current folder)
|
|
161
|
+
const { revalidateResources } = await import('@/store/file/slices/resource/hooks');
|
|
162
|
+
await revalidateResources();
|
|
186
163
|
|
|
187
164
|
// Clear and reload all expanded folders in Tree's module-level cache
|
|
188
165
|
if (libraryId) {
|
|
@@ -230,10 +207,8 @@ export const DndContextWrapper = memo<PropsWithChildren>(({ children }) => {
|
|
|
230
207
|
}, [
|
|
231
208
|
currentDrag,
|
|
232
209
|
selectedFileIds,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
moveFileToFolder,
|
|
236
|
-
refreshFileList,
|
|
210
|
+
resourceList,
|
|
211
|
+
moveResource,
|
|
237
212
|
setSelectedFileIds,
|
|
238
213
|
message,
|
|
239
214
|
t,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useKnowledgeBaseStore } from '@/store/
|
|
1
|
+
import { useKnowledgeBaseStore } from '@/store/library';
|
|
2
2
|
|
|
3
3
|
export const useKnowledgeBaseItem = (id: string) => {
|
|
4
4
|
const useFetchKnowledgeBaseItem = useKnowledgeBaseStore((s) => s.useFetchKnowledgeBaseItem);
|
|
@@ -130,7 +130,7 @@ export const store: CreateStore = (publicState) => (set, get) => ({
|
|
|
130
130
|
onActionClick: async (type) => {
|
|
131
131
|
const { selectedFileIds, libraryId } = get();
|
|
132
132
|
const { useFileStore } = await import('@/store/file');
|
|
133
|
-
const { useKnowledgeBaseStore } = await import('@/store/
|
|
133
|
+
const { useKnowledgeBaseStore } = await import('@/store/library');
|
|
134
134
|
const { isChunkingUnsupported } = await import('@/utils/isChunkingUnsupported');
|
|
135
135
|
|
|
136
136
|
const fileStore = useFileStore.getState();
|
|
@@ -138,37 +138,9 @@ export const store: CreateStore = (publicState) => (set, get) => ({
|
|
|
138
138
|
|
|
139
139
|
switch (type) {
|
|
140
140
|
case 'delete': {
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
selectedFileIds.forEach((id) => {
|
|
146
|
-
const item = fileStore.fileList.find((f) => f.id === id);
|
|
147
|
-
if (!item) return;
|
|
148
|
-
|
|
149
|
-
const isPage = item.sourceType === 'document' || item.fileType === 'custom/document';
|
|
150
|
-
const isFolder = item.fileType === 'custom/folder';
|
|
151
|
-
|
|
152
|
-
if (isPage || isFolder) {
|
|
153
|
-
documentsToDelete.push(id);
|
|
154
|
-
} else {
|
|
155
|
-
filesToDelete.push(id);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Delete documents using batch delete endpoint
|
|
160
|
-
if (documentsToDelete.length > 0) {
|
|
161
|
-
const { documentService } = await import('@/services/document');
|
|
162
|
-
await documentService.deleteDocuments(documentsToDelete);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Delete regular files using file service
|
|
166
|
-
if (filesToDelete.length > 0) {
|
|
167
|
-
await fileStore.removeFiles(filesToDelete);
|
|
168
|
-
} else {
|
|
169
|
-
// If only documents were deleted, still refresh the file list
|
|
170
|
-
await fileStore.refreshFileList();
|
|
171
|
-
}
|
|
141
|
+
// Use optimistic deleteResource for instant batch delete
|
|
142
|
+
// All items disappear immediately, sync happens in background
|
|
143
|
+
await Promise.all(selectedFileIds.map((id) => fileStore.deleteResource(id)));
|
|
172
144
|
|
|
173
145
|
set({ selectedFileIds: [] });
|
|
174
146
|
return;
|
|
@@ -197,8 +169,8 @@ export const store: CreateStore = (publicState) => (set, get) => ({
|
|
|
197
169
|
|
|
198
170
|
case 'batchChunking': {
|
|
199
171
|
const chunkableFileIds = selectedFileIds.filter((id) => {
|
|
200
|
-
const
|
|
201
|
-
return
|
|
172
|
+
const resource = fileStore.resourceMap?.get(id);
|
|
173
|
+
return resource && !isChunkingUnsupported(resource.fileType);
|
|
202
174
|
});
|
|
203
175
|
await fileStore.parseFilesToChunks(chunkableFileIds, { skipExist: true });
|
|
204
176
|
set({ selectedFileIds: [] });
|
|
@@ -246,7 +218,7 @@ export const store: CreateStore = (publicState) => (set, get) => ({
|
|
|
246
218
|
set({ isTransitioning });
|
|
247
219
|
},
|
|
248
220
|
|
|
249
|
-
setLibraryId:
|
|
221
|
+
setLibraryId: (libraryId) => {
|
|
250
222
|
set({ libraryId });
|
|
251
223
|
|
|
252
224
|
// Reset pagination state when switching libraries to prevent showing stale data
|
|
@@ -255,10 +227,8 @@ export const store: CreateStore = (publicState) => (set, get) => ({
|
|
|
255
227
|
fileListOffset: 0,
|
|
256
228
|
});
|
|
257
229
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
const fileStore = useFileStore.getState();
|
|
261
|
-
await fileStore.refreshFileList();
|
|
230
|
+
// Note: No need to manually refresh - Explorer's useEffect will automatically
|
|
231
|
+
// call fetchResources when libraryId changes
|
|
262
232
|
},
|
|
263
233
|
|
|
264
234
|
setMode: (mode) => {
|
|
@@ -9,7 +9,7 @@ import { useNavigate } from 'react-router-dom';
|
|
|
9
9
|
import { useDragActive } from '@/app/[variants]/(main)/resource/features/DndContextWrapper';
|
|
10
10
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
11
11
|
import RepoIcon from '@/components/LibIcon';
|
|
12
|
-
import { knowledgeBaseSelectors, useKnowledgeBaseStore } from '@/store/
|
|
12
|
+
import { knowledgeBaseSelectors, useKnowledgeBaseStore } from '@/store/library';
|
|
13
13
|
|
|
14
14
|
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
15
15
|
clickableHeader: css`
|