@lobehub/lobehub 2.0.0-next.232 → 2.0.0-next.234
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/bundle-analyzer.yml +1 -1
- package/.github/workflows/e2e.yml +62 -53
- package/.github/workflows/manual-build-desktop.yml +5 -5
- package/.github/workflows/pr-build-desktop.yml +4 -4
- package/.github/workflows/pr-build-docker.yml +2 -2
- package/.github/workflows/release-desktop-beta.yml +4 -4
- package/.github/workflows/release-docker.yml +2 -2
- package/.github/workflows/test.yml +44 -7
- package/CHANGELOG.md +59 -0
- package/CLAUDE.md +1 -1
- package/changelog/v1.json +14 -0
- package/docs/development/basic/feature-development.mdx +4 -5
- package/docs/development/basic/feature-development.zh-CN.mdx +4 -5
- package/docs/self-hosting/environment-variables/auth.mdx +7 -0
- package/docs/self-hosting/environment-variables/auth.zh-CN.mdx +7 -0
- package/e2e/README.md +6 -6
- package/e2e/src/features/community/detail-pages.feature +9 -9
- package/e2e/src/features/community/interactions.feature +13 -13
- package/e2e/src/features/community/smoke.feature +6 -6
- package/e2e/src/steps/agent/conversation-mgmt.steps.ts +196 -25
- package/e2e/src/steps/agent/conversation.steps.ts +58 -0
- package/e2e/src/steps/agent/message-ops.steps.ts +20 -15
- package/e2e/src/steps/community/detail-pages.steps.ts +60 -19
- package/e2e/src/steps/community/interactions.steps.ts +145 -32
- package/e2e/src/steps/hooks.ts +12 -2
- package/locales/en-US/setting.json +3 -0
- package/locales/zh-CN/file.json +4 -0
- package/locales/zh-CN/setting.json +3 -0
- package/package.json +5 -5
- package/packages/business/config/src/llm.ts +6 -1
- package/packages/const/src/index.ts +1 -0
- package/packages/const/src/lobehubSkill.ts +55 -0
- package/packages/const/src/settings/image.ts +1 -1
- package/packages/model-bank/src/aiModels/azure.ts +2 -2
- package/packages/model-bank/src/aiModels/google.ts +1 -0
- package/packages/model-bank/src/aiModels/lobehub.ts +33 -13
- package/packages/model-bank/src/aiModels/openai.ts +21 -4
- package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +4 -1
- package/packages/model-runtime/src/providers/openai/__snapshots__/index.test.ts.snap +1 -1
- package/packages/ssrf-safe-fetch/index.test.ts +5 -34
- package/packages/ssrf-safe-fetch/index.ts +12 -2
- package/packages/types/package.json +1 -1
- package/packages/types/src/files/upload.ts +11 -1
- package/packages/types/src/message/common/tools.ts +1 -1
- package/packages/types/src/serverConfig.ts +1 -0
- package/public/not-compatible.html +1296 -0
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/MultiImagesUpload/index.tsx +3 -3
- package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +3 -10
- package/src/app/[variants]/(main)/image/index.tsx +1 -1
- package/src/app/[variants]/(main)/resource/features/FileDetail.tsx +20 -12
- package/src/app/[variants]/(main)/resource/features/modal/FullscreenModal.tsx +2 -4
- package/src/app/[variants]/layout.tsx +50 -1
- package/src/envs/auth.ts +15 -0
- package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +304 -0
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +74 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/ToolTitle.tsx +9 -0
- package/src/features/FileViewer/Renderer/Code/index.tsx +224 -0
- package/src/features/FileViewer/Renderer/Image/index.tsx +8 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +3 -1
- package/src/features/FileViewer/Renderer/PDF/style.ts +2 -1
- package/src/features/FileViewer/index.tsx +135 -24
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +7 -4
- package/src/features/PageEditor/store/initialState.ts +2 -1
- package/src/features/ResourceManager/components/Editor/FileContent.tsx +1 -4
- package/src/features/ResourceManager/components/Editor/FileCopilot.tsx +64 -0
- package/src/features/ResourceManager/components/Editor/index.tsx +98 -31
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +3 -2
- package/src/features/ResourceManager/components/Explorer/ListView/ColumnResizeHandle.tsx +119 -0
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +67 -22
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +46 -11
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +140 -81
- package/src/features/ResourceManager/components/Explorer/ToolBar/SortDropdown.tsx +20 -12
- package/src/features/ResourceManager/components/Explorer/ToolBar/ViewSwitcher.tsx +18 -10
- package/src/features/ResourceManager/components/UploadDock/Item.tsx +38 -6
- package/src/features/ResourceManager/components/UploadDock/index.tsx +62 -41
- package/src/features/ResourceManager/index.tsx +1 -0
- package/src/helpers/toolEngineering/index.test.ts +3 -0
- package/src/helpers/toolEngineering/index.ts +12 -1
- package/src/hooks/useFetchAiImageConfig.ts +54 -10
- package/src/libs/trpc/utils/internalJwt.ts +2 -2
- package/src/locales/default/file.ts +4 -0
- package/src/locales/default/setting.ts +3 -0
- package/src/server/globalConfig/index.ts +1 -0
- package/src/server/modules/ModelRuntime/index.test.ts +214 -1
- package/src/server/modules/ModelRuntime/index.ts +43 -7
- package/src/server/routers/lambda/document.ts +44 -0
- package/src/server/routers/tools/market.ts +261 -0
- package/src/server/services/document/index.ts +22 -0
- package/src/services/document/index.ts +4 -0
- package/src/services/upload.ts +22 -2
- package/src/store/chat/slices/plugin/actions/internals.ts +15 -2
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +104 -0
- package/src/store/file/slices/fileManager/action.test.ts +9 -3
- package/src/store/file/slices/fileManager/action.ts +165 -70
- package/src/store/file/slices/upload/action.ts +3 -0
- package/src/store/global/actions/general.ts +15 -0
- package/src/store/global/initialState.ts +13 -0
- package/src/store/image/slices/generationConfig/initialState.ts +5 -5
- package/src/store/image/slices/generationConfig/selectors.test.ts +11 -4
- package/src/store/serverConfig/selectors.ts +1 -0
- package/src/store/tool/initialState.ts +11 -2
- package/src/store/tool/selectors/index.ts +1 -0
- package/src/store/tool/selectors/tool.ts +3 -1
- package/src/store/tool/slices/lobehubSkillStore/action.ts +361 -0
- package/src/store/tool/slices/lobehubSkillStore/index.ts +4 -0
- package/src/store/tool/slices/lobehubSkillStore/initialState.ts +24 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +145 -0
- package/src/store/tool/slices/lobehubSkillStore/types.ts +100 -0
- package/src/store/tool/store.ts +8 -2
- package/vitest.config.mts +11 -6
- package/src/features/FileViewer/Renderer/JavaScript/index.tsx +0 -66
- package/src/features/FileViewer/Renderer/TXT/index.tsx +0 -50
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
KLAVIS_SERVER_TYPES,
|
|
3
|
+
type KlavisServerType,
|
|
4
|
+
LOBEHUB_SKILL_PROVIDERS,
|
|
5
|
+
type LobehubSkillProviderType,
|
|
6
|
+
} from '@lobechat/const';
|
|
2
7
|
import { Avatar, Flexbox, Icon, Image, type ItemType } from '@lobehub/ui';
|
|
3
8
|
import { cssVar } from 'antd-style';
|
|
4
9
|
import isEqual from 'fast-deep-equal';
|
|
@@ -16,11 +21,13 @@ import { useToolStore } from '@/store/tool';
|
|
|
16
21
|
import {
|
|
17
22
|
builtinToolSelectors,
|
|
18
23
|
klavisStoreSelectors,
|
|
24
|
+
lobehubSkillStoreSelectors,
|
|
19
25
|
pluginSelectors,
|
|
20
26
|
} from '@/store/tool/selectors';
|
|
21
27
|
|
|
22
28
|
import { useAgentId } from '../../hooks/useAgentId';
|
|
23
29
|
import KlavisServerItem from './KlavisServerItem';
|
|
30
|
+
import LobehubSkillServerItem from './LobehubSkillServerItem';
|
|
24
31
|
import ToolItem from './ToolItem';
|
|
25
32
|
|
|
26
33
|
/**
|
|
@@ -39,6 +46,21 @@ const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label
|
|
|
39
46
|
|
|
40
47
|
KlavisIcon.displayName = 'KlavisIcon';
|
|
41
48
|
|
|
49
|
+
/**
|
|
50
|
+
* LobeHub Skill Provider 图标组件
|
|
51
|
+
*/
|
|
52
|
+
const LobehubSkillIcon = memo<Pick<LobehubSkillProviderType, 'icon' | 'label'>>(
|
|
53
|
+
({ icon, label }) => {
|
|
54
|
+
if (typeof icon === 'string') {
|
|
55
|
+
return <Image alt={label} height={18} src={icon} style={{ flex: 'none' }} width={18} />;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return <Icon fill={cssVar.colorText} icon={icon} size={18} />;
|
|
59
|
+
},
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
LobehubSkillIcon.displayName = 'LobehubSkillIcon';
|
|
63
|
+
|
|
42
64
|
export const useControls = ({
|
|
43
65
|
setModalOpen,
|
|
44
66
|
setUpdating,
|
|
@@ -66,10 +88,16 @@ export const useControls = ({
|
|
|
66
88
|
const allKlavisServers = useToolStore(klavisStoreSelectors.getServers, isEqual);
|
|
67
89
|
const isKlavisEnabledInEnv = useServerConfigStore(serverConfigSelectors.enableKlavis);
|
|
68
90
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
// LobeHub Skill 相关状态
|
|
92
|
+
const allLobehubSkillServers = useToolStore(lobehubSkillStoreSelectors.getServers, isEqual);
|
|
93
|
+
const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
|
|
94
|
+
|
|
95
|
+
const [useFetchPluginStore, useFetchUserKlavisServers, useFetchLobehubSkillConnections] =
|
|
96
|
+
useToolStore((s) => [
|
|
97
|
+
s.useFetchPluginStore,
|
|
98
|
+
s.useFetchUserKlavisServers,
|
|
99
|
+
s.useFetchLobehubSkillConnections,
|
|
100
|
+
]);
|
|
73
101
|
|
|
74
102
|
useFetchPluginStore();
|
|
75
103
|
useFetchInstalledPlugins();
|
|
@@ -78,6 +106,9 @@ export const useControls = ({
|
|
|
78
106
|
// 使用 SWR 加载用户的 Klavis 集成(从数据库)
|
|
79
107
|
useFetchUserKlavisServers(isKlavisEnabledInEnv);
|
|
80
108
|
|
|
109
|
+
// 使用 SWR 加载用户的 LobeHub Skill 连接
|
|
110
|
+
useFetchLobehubSkillConnections(isLobehubSkillEnabled);
|
|
111
|
+
|
|
81
112
|
// 根据 identifier 获取已连接的服务器
|
|
82
113
|
const getServerByName = (identifier: string) => {
|
|
83
114
|
return allKlavisServers.find((server) => server.identifier === identifier);
|
|
@@ -118,7 +149,20 @@ export const useControls = ({
|
|
|
118
149
|
[isKlavisEnabledInEnv, allKlavisServers],
|
|
119
150
|
);
|
|
120
151
|
|
|
121
|
-
//
|
|
152
|
+
// LobeHub Skill Provider 列表项
|
|
153
|
+
const lobehubSkillItems = useMemo(
|
|
154
|
+
() =>
|
|
155
|
+
isLobehubSkillEnabled
|
|
156
|
+
? LOBEHUB_SKILL_PROVIDERS.map((provider) => ({
|
|
157
|
+
icon: <LobehubSkillIcon icon={provider.icon} label={provider.label} />,
|
|
158
|
+
key: provider.id, // 使用 provider.id 作为 key,与 pluginId 保持一致
|
|
159
|
+
label: <LobehubSkillServerItem label={provider.label} provider={provider.id} />,
|
|
160
|
+
}))
|
|
161
|
+
: [],
|
|
162
|
+
[isLobehubSkillEnabled, allLobehubSkillServers],
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// 合并 builtin 工具、Klavis 服务器和 LobeHub Skill Provider
|
|
122
166
|
const builtinItems = useMemo(
|
|
123
167
|
() => [
|
|
124
168
|
// 原有的 builtin 工具
|
|
@@ -140,10 +184,12 @@ export const useControls = ({
|
|
|
140
184
|
/>
|
|
141
185
|
),
|
|
142
186
|
})),
|
|
187
|
+
// LobeHub Skill Providers
|
|
188
|
+
...lobehubSkillItems,
|
|
143
189
|
// Klavis 服务器
|
|
144
190
|
...klavisServerItems,
|
|
145
191
|
],
|
|
146
|
-
[filteredBuiltinList, klavisServerItems, checked, togglePlugin, setUpdating],
|
|
192
|
+
[filteredBuiltinList, klavisServerItems, lobehubSkillItems, checked, togglePlugin, setUpdating],
|
|
147
193
|
);
|
|
148
194
|
|
|
149
195
|
// 市场 tab 的 items
|
|
@@ -233,8 +279,17 @@ export const useControls = ({
|
|
|
233
279
|
checked.includes(item.key as string),
|
|
234
280
|
);
|
|
235
281
|
|
|
236
|
-
//
|
|
237
|
-
const
|
|
282
|
+
// 已连接的 LobeHub Skill Providers
|
|
283
|
+
const connectedLobehubSkillItems = lobehubSkillItems.filter((item) =>
|
|
284
|
+
checked.includes(item.key as string),
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// 合并 builtin、Klavis 和 LobeHub Skill
|
|
288
|
+
const allBuiltinItems = [
|
|
289
|
+
...enabledBuiltinItems,
|
|
290
|
+
...connectedKlavisItems,
|
|
291
|
+
...connectedLobehubSkillItems,
|
|
292
|
+
];
|
|
238
293
|
|
|
239
294
|
if (allBuiltinItems.length > 0) {
|
|
240
295
|
installedItems.push({
|
|
@@ -279,7 +334,16 @@ export const useControls = ({
|
|
|
279
334
|
}
|
|
280
335
|
|
|
281
336
|
return installedItems;
|
|
282
|
-
}, [
|
|
337
|
+
}, [
|
|
338
|
+
filteredBuiltinList,
|
|
339
|
+
list,
|
|
340
|
+
klavisServerItems,
|
|
341
|
+
lobehubSkillItems,
|
|
342
|
+
checked,
|
|
343
|
+
togglePlugin,
|
|
344
|
+
setUpdating,
|
|
345
|
+
t,
|
|
346
|
+
]);
|
|
283
347
|
|
|
284
348
|
return { installedPluginItems, marketItems };
|
|
285
349
|
};
|
|
@@ -38,6 +38,15 @@ const ToolTitle = memo<ToolTitleProps>(({ identifier, apiName, isLoading, isAbor
|
|
|
38
38
|
const isBuiltinPlugin = builtinToolIdentifiers.includes(identifier);
|
|
39
39
|
const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
|
|
40
40
|
|
|
41
|
+
// Debug logging for LobeHub Skill title issue
|
|
42
|
+
console.log('[ToolTitle Debug]', {
|
|
43
|
+
apiName,
|
|
44
|
+
identifier,
|
|
45
|
+
isBuiltinPlugin,
|
|
46
|
+
pluginMeta,
|
|
47
|
+
pluginTitle,
|
|
48
|
+
});
|
|
49
|
+
|
|
41
50
|
return (
|
|
42
51
|
<div
|
|
43
52
|
className={cx(
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Center, Flexbox, Highlighter } from '@lobehub/ui';
|
|
4
|
+
import { createStaticStyles } from 'antd-style';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
|
|
7
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
8
|
+
|
|
9
|
+
import { useTextFileLoader } from '../../hooks/useTextFileLoader';
|
|
10
|
+
|
|
11
|
+
const styles = createStaticStyles(({ css }) => ({
|
|
12
|
+
page: css`
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
padding-inline: 24px 4px;
|
|
16
|
+
`,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const getLanguage = (fileName?: string): string => {
|
|
20
|
+
if (!fileName) return 'txt';
|
|
21
|
+
|
|
22
|
+
const ext = fileName.toLowerCase().split('.').pop();
|
|
23
|
+
switch (ext) {
|
|
24
|
+
// JavaScript/TypeScript
|
|
25
|
+
case 'js':
|
|
26
|
+
case 'mjs':
|
|
27
|
+
case 'cjs': {
|
|
28
|
+
return 'javascript';
|
|
29
|
+
}
|
|
30
|
+
case 'ts': {
|
|
31
|
+
return 'typescript';
|
|
32
|
+
}
|
|
33
|
+
case 'tsx': {
|
|
34
|
+
return 'tsx';
|
|
35
|
+
}
|
|
36
|
+
case 'jsx': {
|
|
37
|
+
return 'jsx';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Python
|
|
41
|
+
case 'py':
|
|
42
|
+
case 'pyw': {
|
|
43
|
+
return 'python';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Java/JVM Languages
|
|
47
|
+
case 'java': {
|
|
48
|
+
return 'java';
|
|
49
|
+
}
|
|
50
|
+
case 'kt':
|
|
51
|
+
case 'kts': {
|
|
52
|
+
return 'kotlin';
|
|
53
|
+
}
|
|
54
|
+
case 'scala': {
|
|
55
|
+
return 'scala';
|
|
56
|
+
}
|
|
57
|
+
case 'groovy': {
|
|
58
|
+
return 'groovy';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// C/C++
|
|
62
|
+
case 'c':
|
|
63
|
+
case 'h': {
|
|
64
|
+
return 'c';
|
|
65
|
+
}
|
|
66
|
+
case 'cpp':
|
|
67
|
+
case 'cxx':
|
|
68
|
+
case 'cc':
|
|
69
|
+
case 'hpp':
|
|
70
|
+
case 'hxx': {
|
|
71
|
+
return 'cpp';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// C#
|
|
75
|
+
case 'cs': {
|
|
76
|
+
return 'csharp';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Go
|
|
80
|
+
case 'go': {
|
|
81
|
+
return 'go';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Rust
|
|
85
|
+
case 'rs': {
|
|
86
|
+
return 'rust';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Ruby
|
|
90
|
+
case 'rb': {
|
|
91
|
+
return 'ruby';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// PHP
|
|
95
|
+
case 'php': {
|
|
96
|
+
return 'php';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Swift
|
|
100
|
+
case 'swift': {
|
|
101
|
+
return 'swift';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Shell
|
|
105
|
+
case 'sh':
|
|
106
|
+
case 'bash':
|
|
107
|
+
case 'zsh': {
|
|
108
|
+
return 'bash';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Web
|
|
112
|
+
case 'html':
|
|
113
|
+
case 'htm': {
|
|
114
|
+
return 'html';
|
|
115
|
+
}
|
|
116
|
+
case 'css': {
|
|
117
|
+
return 'css';
|
|
118
|
+
}
|
|
119
|
+
case 'scss': {
|
|
120
|
+
return 'scss';
|
|
121
|
+
}
|
|
122
|
+
case 'sass': {
|
|
123
|
+
return 'sass';
|
|
124
|
+
}
|
|
125
|
+
case 'less': {
|
|
126
|
+
return 'less';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Data formats
|
|
130
|
+
case 'json': {
|
|
131
|
+
return 'json';
|
|
132
|
+
}
|
|
133
|
+
case 'xml': {
|
|
134
|
+
return 'xml';
|
|
135
|
+
}
|
|
136
|
+
case 'yaml':
|
|
137
|
+
case 'yml': {
|
|
138
|
+
return 'yaml';
|
|
139
|
+
}
|
|
140
|
+
case 'toml': {
|
|
141
|
+
return 'toml';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Markdown
|
|
145
|
+
case 'md':
|
|
146
|
+
case 'mdx': {
|
|
147
|
+
return 'markdown';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// SQL
|
|
151
|
+
case 'sql': {
|
|
152
|
+
return 'sql';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Other popular languages
|
|
156
|
+
case 'lua': {
|
|
157
|
+
return 'lua';
|
|
158
|
+
}
|
|
159
|
+
case 'r': {
|
|
160
|
+
return 'r';
|
|
161
|
+
}
|
|
162
|
+
case 'dart': {
|
|
163
|
+
return 'dart';
|
|
164
|
+
}
|
|
165
|
+
case 'elixir':
|
|
166
|
+
case 'ex':
|
|
167
|
+
case 'exs': {
|
|
168
|
+
return 'elixir';
|
|
169
|
+
}
|
|
170
|
+
case 'erl':
|
|
171
|
+
case 'hrl': {
|
|
172
|
+
return 'erlang';
|
|
173
|
+
}
|
|
174
|
+
case 'clj':
|
|
175
|
+
case 'cljs':
|
|
176
|
+
case 'cljc': {
|
|
177
|
+
return 'clojure';
|
|
178
|
+
}
|
|
179
|
+
case 'vim': {
|
|
180
|
+
return 'vim';
|
|
181
|
+
}
|
|
182
|
+
case 'dockerfile': {
|
|
183
|
+
return 'dockerfile';
|
|
184
|
+
}
|
|
185
|
+
case 'graphql':
|
|
186
|
+
case 'gql': {
|
|
187
|
+
return 'graphql';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
default: {
|
|
191
|
+
return 'txt';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
interface CodeViewerProps {
|
|
197
|
+
fileId: string;
|
|
198
|
+
fileName?: string;
|
|
199
|
+
url: string | null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Render any code file.
|
|
204
|
+
*/
|
|
205
|
+
const CodeViewer = memo<CodeViewerProps>(({ url, fileName }) => {
|
|
206
|
+
const { fileData, loading } = useTextFileLoader(url);
|
|
207
|
+
const language = getLanguage(fileName);
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<Flexbox className={styles.page}>
|
|
211
|
+
{!loading && fileData ? (
|
|
212
|
+
<Highlighter language={language} showLanguage={false} variant={'borderless'}>
|
|
213
|
+
{fileData}
|
|
214
|
+
</Highlighter>
|
|
215
|
+
) : (
|
|
216
|
+
<Center height={'100%'}>
|
|
217
|
+
<NeuralNetworkLoading size={36} />
|
|
218
|
+
</Center>
|
|
219
|
+
)}
|
|
220
|
+
</Flexbox>
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
export default CodeViewer;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Center } from '@lobehub/ui';
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { memo, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
5
7
|
|
|
6
8
|
interface ImageViewerProps {
|
|
7
9
|
fileId: string;
|
|
@@ -9,15 +11,20 @@ interface ImageViewerProps {
|
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
const ImageViewer = memo<ImageViewerProps>(({ url }) => {
|
|
14
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
15
|
+
|
|
12
16
|
if (!url) return null;
|
|
13
17
|
|
|
14
18
|
return (
|
|
15
19
|
<Center height={'100%'} width={'100%'}>
|
|
20
|
+
{!isLoaded && <NeuralNetworkLoading size={36} />}
|
|
16
21
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
17
22
|
<img
|
|
18
23
|
alt="Image preview"
|
|
24
|
+
onLoad={() => setIsLoaded(true)}
|
|
19
25
|
src={url}
|
|
20
26
|
style={{
|
|
27
|
+
display: isLoaded ? 'block' : 'none',
|
|
21
28
|
height: '100%',
|
|
22
29
|
objectFit: 'contain',
|
|
23
30
|
overflow: 'hidden',
|
|
@@ -7,6 +7,7 @@ import { Document, Page, pdfjs } from 'react-pdf';
|
|
|
7
7
|
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
|
|
8
8
|
import 'react-pdf/dist/esm/Page/TextLayer.css';
|
|
9
9
|
|
|
10
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
10
11
|
import { lambdaQuery } from '@/libs/trpc/client';
|
|
11
12
|
|
|
12
13
|
import HighlightLayer from './HighlightLayer';
|
|
@@ -62,13 +63,14 @@ const PDFViewer = memo<PDFViewerProps>(({ url, fileId }) => {
|
|
|
62
63
|
<Flexbox
|
|
63
64
|
align={'center'}
|
|
64
65
|
className={styles.documentContainer}
|
|
66
|
+
justify={isLoaded ? undefined : 'center'}
|
|
65
67
|
padding={24}
|
|
66
68
|
ref={setContainerRef}
|
|
67
|
-
style={{ height: isLoaded ? undefined : '100%' }}
|
|
68
69
|
>
|
|
69
70
|
<Document
|
|
70
71
|
className={styles.document}
|
|
71
72
|
file={url}
|
|
73
|
+
loading={<NeuralNetworkLoading size={36} />}
|
|
72
74
|
onLoadSuccess={onDocumentLoadSuccess}
|
|
73
75
|
options={options}
|
|
74
76
|
>
|
|
@@ -2,12 +2,13 @@ import { createStaticStyles } from 'antd-style';
|
|
|
2
2
|
|
|
3
3
|
export const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
4
4
|
container: css`
|
|
5
|
-
|
|
5
|
+
height: 100%;
|
|
6
6
|
`,
|
|
7
7
|
document: css`
|
|
8
8
|
position: relative;
|
|
9
9
|
`,
|
|
10
10
|
documentContainer: css`
|
|
11
|
+
flex: 1;
|
|
11
12
|
padding-block: 10px;
|
|
12
13
|
background-color: ${cssVar.colorBgLayout};
|
|
13
14
|
`,
|
|
@@ -5,12 +5,10 @@ import { type CSSProperties, memo } from 'react';
|
|
|
5
5
|
import { type FileListItem } from '@/types/files';
|
|
6
6
|
|
|
7
7
|
import NotSupport from './NotSupport';
|
|
8
|
+
import CodeViewer from './Renderer/Code';
|
|
8
9
|
import ImageViewer from './Renderer/Image';
|
|
9
|
-
import JavaScriptViewer from './Renderer/JavaScript';
|
|
10
10
|
import MSDocViewer from './Renderer/MSDoc';
|
|
11
|
-
import MarkdownViewer from './Renderer/Markdown';
|
|
12
11
|
import PDFViewer from './Renderer/PDF';
|
|
13
|
-
import TXTViewer from './Renderer/TXT';
|
|
14
12
|
import VideoViewer from './Renderer/Video';
|
|
15
13
|
|
|
16
14
|
// File type definitions
|
|
@@ -27,8 +25,79 @@ const IMAGE_MIME_TYPES = new Set([
|
|
|
27
25
|
const VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogg'];
|
|
28
26
|
const VIDEO_MIME_TYPES = new Set(['video/mp4', 'video/webm', 'video/ogg', 'mp4', 'webm', 'ogg']);
|
|
29
27
|
|
|
30
|
-
const
|
|
31
|
-
|
|
28
|
+
const CODE_EXTENSIONS = [
|
|
29
|
+
// JavaScript/TypeScript
|
|
30
|
+
'.js',
|
|
31
|
+
'.jsx',
|
|
32
|
+
'.ts',
|
|
33
|
+
'.tsx',
|
|
34
|
+
'.mjs',
|
|
35
|
+
'.cjs',
|
|
36
|
+
// Python
|
|
37
|
+
'.py',
|
|
38
|
+
'.pyw',
|
|
39
|
+
// Java/JVM
|
|
40
|
+
'.java',
|
|
41
|
+
'.kt',
|
|
42
|
+
'.kts',
|
|
43
|
+
'.scala',
|
|
44
|
+
'.groovy',
|
|
45
|
+
// C/C++
|
|
46
|
+
'.c',
|
|
47
|
+
'.h',
|
|
48
|
+
'.cpp',
|
|
49
|
+
'.cxx',
|
|
50
|
+
'.cc',
|
|
51
|
+
'.hpp',
|
|
52
|
+
'.hxx',
|
|
53
|
+
// Other compiled languages
|
|
54
|
+
'.cs',
|
|
55
|
+
'.go',
|
|
56
|
+
'.rs',
|
|
57
|
+
'.rb',
|
|
58
|
+
'.php',
|
|
59
|
+
'.swift',
|
|
60
|
+
'.lua',
|
|
61
|
+
'.r',
|
|
62
|
+
'.dart',
|
|
63
|
+
// Shell
|
|
64
|
+
'.sh',
|
|
65
|
+
'.bash',
|
|
66
|
+
'.zsh',
|
|
67
|
+
// Web
|
|
68
|
+
'.html',
|
|
69
|
+
'.htm',
|
|
70
|
+
'.css',
|
|
71
|
+
'.scss',
|
|
72
|
+
'.sass',
|
|
73
|
+
'.less',
|
|
74
|
+
// Data formats
|
|
75
|
+
'.json',
|
|
76
|
+
'.xml',
|
|
77
|
+
'.yaml',
|
|
78
|
+
'.yml',
|
|
79
|
+
'.toml',
|
|
80
|
+
'.sql',
|
|
81
|
+
// Functional languages
|
|
82
|
+
'.ex',
|
|
83
|
+
'.exs',
|
|
84
|
+
'.erl',
|
|
85
|
+
'.hrl',
|
|
86
|
+
'.clj',
|
|
87
|
+
'.cljs',
|
|
88
|
+
'.cljc',
|
|
89
|
+
// Markdown
|
|
90
|
+
'.md',
|
|
91
|
+
'.mdx',
|
|
92
|
+
// Other
|
|
93
|
+
'.vim',
|
|
94
|
+
'.graphql',
|
|
95
|
+
'.gql',
|
|
96
|
+
'.txt',
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const CODE_MIME_TYPES = new Set([
|
|
100
|
+
// JavaScript/TypeScript
|
|
32
101
|
'js',
|
|
33
102
|
'jsx',
|
|
34
103
|
'ts',
|
|
@@ -38,14 +107,66 @@ const JS_MIME_TYPES = new Set([
|
|
|
38
107
|
'text/javascript',
|
|
39
108
|
'application/typescript',
|
|
40
109
|
'text/typescript',
|
|
110
|
+
// Python
|
|
111
|
+
'python',
|
|
112
|
+
'text/x-python',
|
|
113
|
+
'application/x-python-code',
|
|
114
|
+
// Java/JVM
|
|
115
|
+
'java',
|
|
116
|
+
'text/x-java-source',
|
|
117
|
+
'kotlin',
|
|
118
|
+
'scala',
|
|
119
|
+
// C/C++
|
|
120
|
+
'c',
|
|
121
|
+
'text/x-c',
|
|
122
|
+
'cpp',
|
|
123
|
+
'text/x-c++',
|
|
124
|
+
// Other languages
|
|
125
|
+
'csharp',
|
|
126
|
+
'go',
|
|
127
|
+
'rust',
|
|
128
|
+
'ruby',
|
|
129
|
+
'php',
|
|
130
|
+
'text/x-php',
|
|
131
|
+
'swift',
|
|
132
|
+
'lua',
|
|
133
|
+
'r',
|
|
134
|
+
'dart',
|
|
135
|
+
// Shell
|
|
136
|
+
'bash',
|
|
137
|
+
'shell',
|
|
138
|
+
'text/x-shellscript',
|
|
139
|
+
// Web
|
|
140
|
+
'html',
|
|
141
|
+
'text/html',
|
|
142
|
+
'css',
|
|
143
|
+
'text/css',
|
|
144
|
+
'scss',
|
|
145
|
+
'sass',
|
|
146
|
+
'less',
|
|
147
|
+
// Data
|
|
148
|
+
'json',
|
|
149
|
+
'application/json',
|
|
150
|
+
'xml',
|
|
151
|
+
'text/xml',
|
|
152
|
+
'application/xml',
|
|
153
|
+
'yaml',
|
|
154
|
+
'text/yaml',
|
|
155
|
+
'application/x-yaml',
|
|
156
|
+
'toml',
|
|
157
|
+
'sql',
|
|
158
|
+
'text/x-sql',
|
|
159
|
+
// Markdown
|
|
160
|
+
'md',
|
|
161
|
+
'mdx',
|
|
162
|
+
'text/markdown',
|
|
163
|
+
'text/x-markdown',
|
|
164
|
+
// Other
|
|
165
|
+
'graphql',
|
|
166
|
+
'txt',
|
|
167
|
+
'text/plain',
|
|
41
168
|
]);
|
|
42
169
|
|
|
43
|
-
const MARKDOWN_EXTENSIONS = ['.md', '.mdx'];
|
|
44
|
-
const MARKDOWN_MIME_TYPES = new Set(['md', 'mdx', 'text/markdown', 'text/x-markdown']);
|
|
45
|
-
|
|
46
|
-
const TXT_EXTENSIONS = ['.txt'];
|
|
47
|
-
const TXT_MIME_TYPES = new Set(['txt', 'text/plain']);
|
|
48
|
-
|
|
49
170
|
const MSDOC_EXTENSIONS = ['.doc', '.docx', '.odt', '.ppt', '.pptx', '.xls', '.xlsx'];
|
|
50
171
|
const MSDOC_MIME_TYPES = new Set([
|
|
51
172
|
'doc',
|
|
@@ -117,19 +238,9 @@ const FileViewer = memo<FileViewerProps>(({ id, style, fileType, url, name }) =>
|
|
|
117
238
|
return <VideoViewer fileId={id} url={url} />;
|
|
118
239
|
}
|
|
119
240
|
|
|
120
|
-
// JavaScript
|
|
121
|
-
if (matchesFileType(fileType, name,
|
|
122
|
-
return <
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Markdown files
|
|
126
|
-
if (matchesFileType(fileType, name, MARKDOWN_EXTENSIONS, MARKDOWN_MIME_TYPES)) {
|
|
127
|
-
return <MarkdownViewer fileId={id} url={url} />;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Text files
|
|
131
|
-
if (matchesFileType(fileType, name, TXT_EXTENSIONS, TXT_MIME_TYPES)) {
|
|
132
|
-
return <TXTViewer fileId={id} url={url} />;
|
|
241
|
+
// Code files (JavaScript, TypeScript, Python, Java, C++, Go, Rust, Markdown, etc.)
|
|
242
|
+
if (matchesFileType(fileType, name, CODE_EXTENSIONS, CODE_MIME_TYPES)) {
|
|
243
|
+
return <CodeViewer fileId={id} fileName={name} url={url} />;
|
|
133
244
|
}
|
|
134
245
|
|
|
135
246
|
// Microsoft Office documents
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type IEditor,
|
|
3
3
|
INSERT_CHECK_LIST_COMMAND,
|
|
4
|
+
INSERT_CODEMIRROR_COMMAND,
|
|
4
5
|
INSERT_HEADING_COMMAND,
|
|
5
6
|
INSERT_HORIZONTAL_RULE_COMMAND,
|
|
6
7
|
INSERT_IMAGE_COMMAND,
|
|
@@ -28,12 +29,11 @@ import { useMemo } from 'react';
|
|
|
28
29
|
import { useTranslation } from 'react-i18next';
|
|
29
30
|
|
|
30
31
|
import { openFileSelector } from '@/features/PageEditor/EditorCanvas/actions';
|
|
31
|
-
import { usePageEditorStore } from '@/features/PageEditor/store';
|
|
32
32
|
import { useFileStore } from '@/store/file';
|
|
33
33
|
|
|
34
34
|
export const useSlashItems = (editor: IEditor | undefined): SlashOptions['items'] => {
|
|
35
35
|
const { t } = useTranslation('editor');
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
const uploadWithProgress = useFileStore((s) => s.uploadWithProgress);
|
|
38
38
|
|
|
39
39
|
const handleImageUpload = async (file: File) => {
|
|
@@ -179,8 +179,11 @@ export const useSlashItems = (editor: IEditor | undefined): SlashOptions['items'
|
|
|
179
179
|
icon: SquareDashedBottomCodeIcon,
|
|
180
180
|
key: 'codeblock',
|
|
181
181
|
label: t('typobar.codeblock'),
|
|
182
|
-
onSelect: () => {
|
|
183
|
-
|
|
182
|
+
onSelect: (editor) => {
|
|
183
|
+
editor.dispatchCommand(INSERT_CODEMIRROR_COMMAND, undefined);
|
|
184
|
+
queueMicrotask(() => {
|
|
185
|
+
editor.focus();
|
|
186
|
+
});
|
|
184
187
|
},
|
|
185
188
|
},
|
|
186
189
|
{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type IEditor } from '@lobehub/editor';
|
|
2
|
+
import { type EditorState } from '@lobehub/editor/react';
|
|
2
3
|
|
|
3
4
|
export interface PublicState {
|
|
4
5
|
autoSave?: boolean;
|
|
@@ -16,7 +17,7 @@ export interface State extends PublicState {
|
|
|
16
17
|
currentEmoji: string | undefined;
|
|
17
18
|
currentTitle: string;
|
|
18
19
|
editor?: IEditor;
|
|
19
|
-
editorState?:
|
|
20
|
+
editorState?: EditorState;
|
|
20
21
|
isDirty: boolean; // Track if there are unsaved changes
|
|
21
22
|
isLoadingContent: boolean; // Track if content is being loaded
|
|
22
23
|
lastSavedContent: string; // Last saved content hash for comparison
|