@lobehub/lobehub 2.0.0-next.69 → 2.0.0-next.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/apps/desktop/package.json +18 -18
  3. package/changelog/v1.json +5 -0
  4. package/e2e/package.json +2 -2
  5. package/locales/en-US/chat.json +5 -0
  6. package/locales/zh-CN/chat.json +5 -0
  7. package/package.json +16 -16
  8. package/packages/agent-runtime/package.json +1 -1
  9. package/packages/const/package.json +1 -1
  10. package/packages/context-engine/package.json +2 -2
  11. package/packages/electron-client-ipc/package.json +1 -1
  12. package/packages/electron-server-ipc/package.json +2 -2
  13. package/packages/fetch-sse/package.json +1 -1
  14. package/packages/file-loaders/package.json +3 -3
  15. package/packages/memory-extract/package.json +2 -2
  16. package/packages/model-runtime/package.json +3 -3
  17. package/packages/obervability-otel/package.json +4 -4
  18. package/packages/prompts/package.json +1 -1
  19. package/packages/ssrf-safe-fetch/package.json +1 -1
  20. package/packages/types/src/agent/chatConfig.ts +8 -8
  21. package/packages/types/src/document/index.ts +4 -4
  22. package/packages/types/src/fetch.ts +9 -9
  23. package/packages/types/src/hotkey.ts +5 -5
  24. package/packages/types/src/importer.ts +2 -2
  25. package/packages/types/src/message/common/metadata.ts +2 -2
  26. package/packages/types/src/message/common/tools.ts +1 -1
  27. package/packages/types/src/meta.ts +3 -3
  28. package/packages/types/src/topic/topic.ts +1 -1
  29. package/packages/utils/package.json +2 -2
  30. package/packages/web-crawler/package.json +2 -2
  31. package/packages/web-crawler/src/type.ts +4 -4
  32. package/src/features/Portal/Artifacts/Body/Renderer/SVG.tsx +23 -6
  33. package/src/features/ShareModal/ShareImage/Preview.tsx +2 -2
  34. package/src/features/ShareModal/ShareImage/index.tsx +15 -1
  35. package/src/features/ShareModal/ShareImage/type.ts +6 -0
  36. package/src/features/ShareModal/style.ts +40 -30
  37. package/src/hooks/useScreenshot.ts +44 -28
  38. package/src/locales/default/chat.ts +5 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.70](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.69...v2.0.0-next.70)
6
+
7
+ <sup>Released on **2025-11-17**</sup>
8
+
9
+ <br/>
10
+
11
+ <details>
12
+ <summary><kbd>Improvements and Fixes</kbd></summary>
13
+
14
+ </details>
15
+
16
+ <div align="right">
17
+
18
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
19
+
20
+ </div>
21
+
5
22
  ## [Version 2.0.0-next.69](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.68...v2.0.0-next.69)
6
23
 
7
24
  <sup>Released on **2025-11-17**</sup>
@@ -32,33 +32,33 @@
32
32
  "electron-updater": "^6.6.2",
33
33
  "electron-window-state": "^5.0.3",
34
34
  "fetch-socks": "^1.3.2",
35
- "get-port-please": "^3.1.2",
35
+ "get-port-please": "^3.2.0",
36
36
  "pdfjs-dist": "4.10.38"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@electron-toolkit/eslint-config-prettier": "^3.0.0",
40
- "@electron-toolkit/eslint-config-ts": "^3.0.0",
41
- "@electron-toolkit/preload": "^3.0.1",
40
+ "@electron-toolkit/eslint-config-ts": "^3.1.0",
41
+ "@electron-toolkit/preload": "^3.0.2",
42
42
  "@electron-toolkit/tsconfig": "^2.0.0",
43
43
  "@electron-toolkit/utils": "^4.0.0",
44
44
  "@lobechat/electron-client-ipc": "workspace:*",
45
45
  "@lobechat/electron-server-ipc": "workspace:*",
46
46
  "@lobechat/file-loaders": "workspace:*",
47
- "@lobehub/i18n-cli": "^1.20.3",
48
- "@types/lodash": "^4.17.0",
47
+ "@lobehub/i18n-cli": "^1.25.1",
48
+ "@types/lodash": "^4.17.20",
49
49
  "@types/resolve": "^1.20.6",
50
- "@types/semver": "^7.7.0",
50
+ "@types/semver": "^7.7.1",
51
51
  "@types/set-cookie-parser": "^2.4.10",
52
52
  "@typescript/native-preview": "7.0.0-dev.20250711.1",
53
- "consola": "^3.1.0",
53
+ "consola": "^3.4.2",
54
54
  "cookie": "^1.0.2",
55
- "electron": "^38.6.0",
55
+ "electron": "^38.7.0",
56
56
  "electron-builder": "^26.0.12",
57
57
  "electron-is": "^3.0.0",
58
- "electron-log": "^5.3.3",
58
+ "electron-log": "^5.4.3",
59
59
  "electron-store": "^8.2.0",
60
- "electron-vite": "^3.0.0",
61
- "execa": "^9.5.2",
60
+ "electron-vite": "^3.1.0",
61
+ "execa": "^9.6.0",
62
62
  "fast-glob": "^3.3.3",
63
63
  "fix-path": "^5.0.0",
64
64
  "http-proxy-agent": "^7.0.2",
@@ -66,13 +66,13 @@
66
66
  "just-diff": "^6.0.2",
67
67
  "lodash": "^4.17.21",
68
68
  "lodash-es": "^4.17.21",
69
- "resolve": "^1.22.8",
70
- "semver": "^7.5.4",
71
- "set-cookie-parser": "^2.7.1",
72
- "tsx": "^4.19.3",
73
- "typescript": "^5.7.3",
74
- "undici": "^7.9.0",
75
- "vite": "^6.3.5",
69
+ "resolve": "^1.22.11",
70
+ "semver": "^7.7.3",
71
+ "set-cookie-parser": "^2.7.2",
72
+ "tsx": "^4.20.6",
73
+ "typescript": "^5.9.3",
74
+ "undici": "^7.16.0",
75
+ "vite": "^6.4.1",
76
76
  "vitest": "^3.2.4"
77
77
  },
78
78
  "pnpm": {
package/changelog/v1.json CHANGED
@@ -1,4 +1,9 @@
1
1
  [
2
+ {
3
+ "children": {},
4
+ "date": "2025-11-17",
5
+ "version": "2.0.0-next.70"
6
+ },
2
7
  {
3
8
  "children": {
4
9
  "improvements": [
package/e2e/package.json CHANGED
@@ -17,8 +17,8 @@
17
17
  "playwright": "^1.56.1"
18
18
  },
19
19
  "devDependencies": {
20
- "@types/node": "^22.10.5",
20
+ "@types/node": "^22.19.1",
21
21
  "tsx": "^4.20.6",
22
- "typescript": "^5.7.3"
22
+ "typescript": "^5.9.3"
23
23
  }
24
24
  }
@@ -330,6 +330,11 @@
330
330
  "screenshot": "Screenshot",
331
331
  "settings": "Export Settings",
332
332
  "text": "Text",
333
+ "widthMode": {
334
+ "label": "Width Mode",
335
+ "narrow": "Narrow",
336
+ "wide": "Wide"
337
+ },
333
338
  "withBackground": "Include Background Image",
334
339
  "withFooter": "Include Footer",
335
340
  "withPluginInfo": "Include Plugin Information",
@@ -330,6 +330,11 @@
330
330
  "screenshot": "截图",
331
331
  "settings": "导出设置",
332
332
  "text": "文本",
333
+ "widthMode": {
334
+ "label": "宽度模式",
335
+ "narrow": "窄屏模式",
336
+ "wide": "宽屏模式"
337
+ },
333
338
  "withBackground": "包含背景图片",
334
339
  "withFooter": "包含页脚",
335
340
  "withPluginInfo": "包含插件信息",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.69",
3
+ "version": "2.0.0-next.70",
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",
@@ -132,17 +132,17 @@
132
132
  "@azure-rest/ai-inference": "1.0.0-beta.5",
133
133
  "@azure/core-auth": "^1.10.1",
134
134
  "@cfworker/json-schema": "^4.1.1",
135
- "@clerk/localizations": "^3.27.2",
136
- "@clerk/nextjs": "^6.35.0",
137
- "@clerk/themes": "^2.4.35",
135
+ "@clerk/localizations": "^3.28.0",
136
+ "@clerk/nextjs": "^6.35.1",
137
+ "@clerk/themes": "^2.4.36",
138
138
  "@codesandbox/sandpack-react": "^2.20.0",
139
139
  "@cyntler/react-doc-viewer": "^1.17.1",
140
140
  "@electric-sql/pglite": "0.2.17",
141
141
  "@emotion/react": "^11.14.0",
142
142
  "@fal-ai/client": "^1.7.2",
143
143
  "@formkit/auto-animate": "^0.9.0",
144
- "@google/genai": "^1.29.0",
145
- "@huggingface/inference": "^4.13.2",
144
+ "@google/genai": "^1.29.1",
145
+ "@huggingface/inference": "^4.13.3",
146
146
  "@icons-pack/react-simple-icons": "^13.8.0",
147
147
  "@khmyznikov/pwa-install": "0.3.9",
148
148
  "@langchain/community": "^0.3.57",
@@ -165,14 +165,14 @@
165
165
  "@lobehub/charts": "^2.1.2",
166
166
  "@lobehub/chat-plugin-sdk": "^1.32.4",
167
167
  "@lobehub/chat-plugins-gateway": "^1.9.0",
168
- "@lobehub/editor": "^1.22.0",
168
+ "@lobehub/editor": "^1.23.1",
169
169
  "@lobehub/icons": "^2.43.1",
170
170
  "@lobehub/market-sdk": "^0.23.0",
171
171
  "@lobehub/tts": "^2.0.1",
172
- "@lobehub/ui": "^2.13.8",
172
+ "@lobehub/ui": "^2.16.2",
173
173
  "@modelcontextprotocol/sdk": "^1.22.0",
174
174
  "@neondatabase/serverless": "^1.0.2",
175
- "@next/third-parties": "^16.0.1",
175
+ "@next/third-parties": "^16.0.3",
176
176
  "@opentelemetry/exporter-jaeger": "^2.2.0",
177
177
  "@opentelemetry/winston-transport": "^0.18.0",
178
178
  "@react-pdf/renderer": "^4.3.1",
@@ -180,17 +180,18 @@
180
180
  "@saintno/comfyui-sdk": "^0.2.49",
181
181
  "@serwist/next": "^9.2.1",
182
182
  "@t3-oss/env-nextjs": "^0.13.8",
183
- "@tanstack/react-query": "^5.90.7",
183
+ "@tanstack/react-query": "^5.90.10",
184
184
  "@trpc/client": "^11.7.1",
185
185
  "@trpc/next": "^11.7.1",
186
186
  "@trpc/react-query": "^11.7.1",
187
187
  "@trpc/server": "^11.7.1",
188
188
  "@vercel/analytics": "^1.5.0",
189
189
  "@vercel/edge-config": "^1.4.3",
190
- "@vercel/functions": "^3.3.0",
190
+ "@vercel/functions": "^3.3.2",
191
191
  "@vercel/speed-insights": "^1.2.0",
192
192
  "@virtuoso.dev/masonry": "^1.3.5",
193
193
  "@xterm/xterm": "^5.5.0",
194
+ "@zumer/snapdom": "^1.9.14",
194
195
  "ahooks": "^3.9.6",
195
196
  "antd": "^5.28.1",
196
197
  "antd-style": "^3.7.1",
@@ -228,9 +229,8 @@
228
229
  "marked": "^16.4.2",
229
230
  "mdast-util-to-markdown": "^2.1.2",
230
231
  "model-bank": "workspace:*",
231
- "modern-screenshot": "^4.6.6",
232
232
  "nanoid": "^5.1.6",
233
- "next": "^16.0.1",
233
+ "next": "^16.0.3",
234
234
  "next-auth": "5.0.0-beta.30",
235
235
  "next-mdx-remote": "^5.0.0",
236
236
  "nextjs-toploader": "^3.9.17",
@@ -267,14 +267,14 @@
267
267
  "react-pdf": "^9.2.1",
268
268
  "react-responsive": "^10.0.1",
269
269
  "react-rnd": "^10.5.2",
270
- "react-router-dom": "^7.9.5",
270
+ "react-router-dom": "^7.9.6",
271
271
  "react-scan": "^0.4.3",
272
272
  "react-virtuoso": "^4.14.1",
273
273
  "react-wrap-balancer": "^1.1.1",
274
274
  "remark": "^15.0.1",
275
275
  "remark-gfm": "^4.0.1",
276
276
  "remark-html": "^16.0.1",
277
- "resolve-accept-language": "^3.1.13",
277
+ "resolve-accept-language": "^3.1.14",
278
278
  "rtl-detect": "^1.1.2",
279
279
  "semver": "^7.7.3",
280
280
  "sharp": "^0.34.5",
@@ -309,7 +309,7 @@
309
309
  "@lobehub/lint": "^1.26.2",
310
310
  "@lobehub/market-types": "^1.11.4",
311
311
  "@lobehub/seo-cli": "^1.7.0",
312
- "@next/bundle-analyzer": "^16.0.1",
312
+ "@next/bundle-analyzer": "^16.0.3",
313
313
  "@next/eslint-plugin-next": "^15.5.6",
314
314
  "@peculiar/webcrypto": "^1.5.0",
315
315
  "@playwright/test": "^1.56.1",
@@ -10,6 +10,6 @@
10
10
  },
11
11
  "dependencies": {},
12
12
  "devDependencies": {
13
- "openai": "^4.0.0"
13
+ "openai": "^4.104.0"
14
14
  }
15
15
  }
@@ -9,7 +9,7 @@
9
9
  "main": "./src/index.ts",
10
10
  "dependencies": {
11
11
  "model-bank": "workspace:*",
12
- "query-string": "^9.2.2",
12
+ "query-string": "^9.3.1",
13
13
  "url-join": "^5.0.0"
14
14
  }
15
15
  }
@@ -21,8 +21,8 @@
21
21
  "@lobechat/prompts": "workspace:*",
22
22
  "@lobechat/types": "workspace:*",
23
23
  "@lobechat/utils": "workspace:*",
24
- "debug": "^4.3.4",
25
- "immer": "^10.0.3",
24
+ "debug": "^4.4.3",
25
+ "immer": "^10.2.0",
26
26
  "lodash-es": "^4.17.21",
27
27
  "ts-md5": "^2.0.1"
28
28
  },
@@ -5,6 +5,6 @@
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
7
7
  "peerDependencies": {
8
- "react": "^19"
8
+ "react": "^19.2.0"
9
9
  }
10
10
  }
@@ -9,9 +9,9 @@
9
9
  "test:coverage": "vitest --coverage --silent='passed-only'"
10
10
  },
11
11
  "dependencies": {
12
- "debug": "^4.3.4"
12
+ "debug": "^4.4.3"
13
13
  },
14
14
  "devDependencies": {
15
- "@types/debug": "^4.1.7"
15
+ "@types/debug": "^4.1.12"
16
16
  }
17
17
  }
@@ -24,6 +24,6 @@
24
24
  "@lobechat/model-runtime": "workspace:*",
25
25
  "@lobechat/types": "workspace:*",
26
26
  "@lobechat/utils": "workspace:*",
27
- "i18next": "^24.2.1"
27
+ "i18next": "^24.2.3"
28
28
  }
29
29
  }
@@ -28,8 +28,8 @@
28
28
  "@napi-rs/canvas": "^0.1.70",
29
29
  "@xmldom/xmldom": "^0.9.8",
30
30
  "concat-stream": "^2.0.0",
31
- "debug": "^4.3.4",
32
- "mammoth": "^1.8.0",
31
+ "debug": "^4.4.3",
32
+ "mammoth": "^1.11.0",
33
33
  "officeparser": "5.1.1",
34
34
  "pdfjs-dist": "4.10.38",
35
35
  "word-extractor": "^1.0.4",
@@ -39,7 +39,7 @@
39
39
  "devDependencies": {
40
40
  "@types/concat-stream": "^2.0.3",
41
41
  "@types/yauzl": "^2.10.3",
42
- "typescript": "^5"
42
+ "typescript": "^5.9.3"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "typescript": ">=5"
@@ -13,9 +13,9 @@
13
13
  "ora": "^9.0.0"
14
14
  },
15
15
  "devDependencies": {
16
- "tsx": "^4.19.2"
16
+ "tsx": "^4.20.6"
17
17
  },
18
18
  "peerDependencies": {
19
- "zod": "^3"
19
+ "zod": "^3.25.76"
20
20
  }
21
21
  }
@@ -12,12 +12,12 @@
12
12
  "test:update": "vitest -u"
13
13
  },
14
14
  "dependencies": {
15
- "@aws-sdk/client-bedrock-runtime": "^3.862.0",
16
- "@huggingface/inference": "^4.11.3",
15
+ "@aws-sdk/client-bedrock-runtime": "^3.932.0",
16
+ "@huggingface/inference": "^4.13.3",
17
17
  "@lobechat/const": "workspace:*",
18
18
  "@lobechat/types": "workspace:*",
19
19
  "@lobechat/utils": "workspace:*",
20
- "debug": "^4.4.1",
20
+ "debug": "^4.4.3",
21
21
  "model-bank": "workspace:*",
22
22
  "openai": "^4.104.0"
23
23
  }
@@ -15,11 +15,11 @@
15
15
  "@opentelemetry/instrumentation": "^0.207.0",
16
16
  "@opentelemetry/instrumentation-http": "^0.207.0",
17
17
  "@opentelemetry/instrumentation-pg": "^0.60.0",
18
- "@opentelemetry/resources": "^2.0.1",
19
- "@opentelemetry/sdk-metrics": "^2.0.1",
18
+ "@opentelemetry/resources": "^2.2.0",
19
+ "@opentelemetry/sdk-metrics": "^2.2.0",
20
20
  "@opentelemetry/sdk-node": "^0.207.0",
21
- "@opentelemetry/sdk-trace-node": "^2.0.1",
22
- "@opentelemetry/semantic-conventions": "^1.36.0",
21
+ "@opentelemetry/sdk-trace-node": "^2.2.0",
22
+ "@opentelemetry/semantic-conventions": "^1.38.0",
23
23
  "@vercel/otel": "^2.1.0"
24
24
  }
25
25
  }
@@ -23,6 +23,6 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "promptfoo": "^0.118.17",
26
- "tsx": "^4.20.4"
26
+ "tsx": "^4.20.6"
27
27
  }
28
28
  }
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "node-fetch": "^3.3.2",
19
- "request-filtering-agent": "^3"
19
+ "request-filtering-agent": "^3.0.2"
20
20
  },
21
21
  "devDependencies": {
22
22
  "vitest": "^3.2.4"
@@ -17,16 +17,16 @@ export interface LobeAgentChatConfig {
17
17
  enableMaxTokens?: boolean;
18
18
 
19
19
  /**
20
- * 是否开启流式输出
20
+ * Whether to enable streaming output
21
21
  */
22
22
  enableStreaming?: boolean;
23
23
 
24
24
  /**
25
- * 是否开启推理
25
+ * Whether to enable reasoning
26
26
  */
27
27
  enableReasoning?: boolean;
28
28
  /**
29
- * 自定义推理强度
29
+ * Custom reasoning effort level
30
30
  */
31
31
  enableReasoningEffort?: boolean;
32
32
  reasoningBudgetToken?: number;
@@ -34,25 +34,25 @@ export interface LobeAgentChatConfig {
34
34
  gpt5ReasoningEffort?: 'minimal' | 'low' | 'medium' | 'high';
35
35
  gpt5_1ReasoningEffort?: 'none' | 'low' | 'medium' | 'high';
36
36
  /**
37
- * 输出文本详细程度控制
37
+ * Output text verbosity control
38
38
  */
39
39
  textVerbosity?: 'low' | 'medium' | 'high';
40
40
  thinking?: 'disabled' | 'auto' | 'enabled';
41
41
  thinkingBudget?: number;
42
42
  /**
43
- * 禁用上下文缓存
43
+ * Disable context caching
44
44
  */
45
45
  disableContextCaching?: boolean;
46
46
  /**
47
- * 历史消息条数
47
+ * Number of historical messages
48
48
  */
49
49
  historyCount?: number;
50
50
  /**
51
- * 开启历史记录条数
51
+ * Enable historical message count
52
52
  */
53
53
  enableHistoryCount?: boolean;
54
54
  /**
55
- * 历史消息长度压缩阈值
55
+ * Enable history message compression threshold
56
56
  */
57
57
  enableCompressHistory?: boolean;
58
58
 
@@ -56,12 +56,12 @@ export interface LobeDocument {
56
56
  source: string;
57
57
 
58
58
  /**
59
- * 文档来源类型
59
+ * Document source type
60
60
  */
61
61
  sourceType: DocumentSourceType;
62
62
 
63
63
  /**
64
- * 文档标题 (如果可用)
64
+ * Document title (if available)
65
65
  */
66
66
  title?: string;
67
67
 
@@ -168,12 +168,12 @@ export enum DocumentSourceType {
168
168
  API = 'api',
169
169
 
170
170
  /**
171
- * 编辑器创建的文档
171
+ * Document created in editor
172
172
  */
173
173
  EDITOR = 'editor',
174
174
 
175
175
  /**
176
- * 本地或上传的文件
176
+ * Local or uploaded file
177
177
  */
178
178
  FILE = 'file',
179
179
 
@@ -2,15 +2,15 @@
2
2
  import type { ILobeAgentRuntimeErrorType } from '@lobechat/model-runtime';
3
3
 
4
4
  export const ChatErrorType = {
5
- // ******* 业务错误语义 ******* //
5
+ // ******* Business Error Semantics ******* //
6
6
 
7
7
  InvalidAccessCode: 'InvalidAccessCode', // is in valid password
8
8
  InvalidClerkUser: 'InvalidClerkUser', // is not Clerk User
9
9
  FreePlanLimit: 'FreePlanLimit', // is not Clerk User
10
- SubscriptionPlanLimit: 'SubscriptionPlanLimit', // 订阅用户超限
11
- SubscriptionKeyMismatch: 'SubscriptionKeyMismatch', // 订阅 key 不匹配
10
+ SubscriptionPlanLimit: 'SubscriptionPlanLimit', // Subscription user limit exceeded
11
+ SubscriptionKeyMismatch: 'SubscriptionKeyMismatch', // Subscription key mismatch
12
12
 
13
- SupervisorDecisionFailed: 'SupervisorDecisionFailed', // 主持人决策失败
13
+ SupervisorDecisionFailed: 'SupervisorDecisionFailed', // Supervisor decision failed
14
14
 
15
15
  InvalidUserKey: 'InvalidUserKey', // is not valid User key
16
16
  CreateMessageError: 'CreateMessageError',
@@ -18,20 +18,20 @@ export const ChatErrorType = {
18
18
  * @deprecated
19
19
  */
20
20
  NoOpenAIAPIKey: 'NoOpenAIAPIKey',
21
- OllamaServiceUnavailable: 'OllamaServiceUnavailable', // 未启动/检测到 Ollama 服务
21
+ OllamaServiceUnavailable: 'OllamaServiceUnavailable', // Ollama service not started/detected
22
22
  PluginFailToTransformArguments: 'PluginFailToTransformArguments',
23
23
  UnknownChatFetchError: 'UnknownChatFetchError',
24
24
  SystemTimeNotMatchError: 'SystemTimeNotMatchError',
25
25
 
26
- // ******* 客户端错误 ******* //
26
+ // ******* Client Errors ******* //
27
27
  BadRequest: 400,
28
28
  Unauthorized: 401,
29
29
  Forbidden: 403,
30
- ContentNotFound: 404, // 没找到接口
31
- MethodNotAllowed: 405, // 不支持
30
+ ContentNotFound: 404, // Endpoint not found
31
+ MethodNotAllowed: 405, // Method not supported
32
32
  TooManyRequests: 429,
33
33
 
34
- // ******* 服务端错误 ******* //InvalidPluginArgumentsTransform
34
+ // ******* Server Errors ******* //InvalidPluginArgumentsTransform
35
35
  InternalServerError: 500,
36
36
  BadGateway: 502,
37
37
  ServiceUnavailable: 503,
@@ -84,7 +84,7 @@ export const HotkeyGroupEnum = {
84
84
  export const HotkeyScopeEnum = {
85
85
  Chat: 'chat',
86
86
  Files: 'files',
87
- // 默认全局注册的快捷键 scope
87
+ // Default globally registered hotkey scope
88
88
  // https://react-hotkeys-hook.vercel.app/docs/documentation/hotkeys-provider
89
89
  Global: 'global',
90
90
 
@@ -96,13 +96,13 @@ export type HotkeyGroupId = (typeof HotkeyGroupEnum)[keyof typeof HotkeyGroupEnu
96
96
  export type HotkeyScopeId = (typeof HotkeyScopeEnum)[keyof typeof HotkeyScopeEnum];
97
97
 
98
98
  export interface HotkeyItem {
99
- // 快捷键分组用于展示
99
+ // Hotkey grouping for display purposes
100
100
  group: HotkeyGroupId;
101
101
  id: HotkeyId;
102
102
  keys: string;
103
- // 是否为不可编辑的快捷键
103
+ // Whether the hotkey is non-editable
104
104
  nonEditable?: boolean;
105
- // 快捷键作用域
105
+ // Hotkey scope
106
106
  scopes?: HotkeyScopeId[];
107
107
  }
108
108
 
@@ -119,7 +119,7 @@ export interface DesktopHotkeyItem {
119
119
  id: DesktopHotkeyId;
120
120
 
121
121
  keys: string;
122
- // 是否为不可编辑的快捷键
122
+ // Whether the hotkey is non-editable
123
123
  nonEditable?: boolean;
124
124
  }
125
125
 
@@ -26,11 +26,11 @@ export interface ImportMessage {
26
26
  createdAt: number;
27
27
  error?: ChatMessageError;
28
28
 
29
- // 扩展字段
29
+ // Extended fields
30
30
  extra?: {
31
31
  model?: string;
32
32
  provider?: string;
33
- // 翻译
33
+ // Translation
34
34
  translate?: ChatTranslate | false | null;
35
35
  // TTS
36
36
  tts?: ChatTTS;
@@ -109,8 +109,8 @@ export interface MessageMetadata extends ModelUsage, ModelPerformance {
109
109
  activeBranchIndex?: number;
110
110
  activeColumn?: boolean;
111
111
  /**
112
- * 消息折叠状态
113
- * true: 折叠, false/undefined: 展开
112
+ * Message collapse state
113
+ * true: collapsed, false/undefined: expanded
114
114
  */
115
115
  collapsed?: boolean;
116
116
  compare?: boolean;
@@ -112,7 +112,7 @@ export const ChatToolPayloadSchema = z.object({
112
112
  });
113
113
 
114
114
  /**
115
- * 聊天消息错误对象
115
+ * Chat message error object
116
116
  */
117
117
  export interface ChatMessagePluginError {
118
118
  body?: any;
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
 
3
3
  export const LobeMetaDataSchema = z.object({
4
4
  /**
5
- * 角色头像
5
+ * Character avatar
6
6
  */
7
7
  avatar: z.string().optional(),
8
8
  /**
9
- * 背景色
9
+ * Background color
10
10
  */
11
11
  backgroundColor: z.string().optional(),
12
12
  description: z.string().optional(),
@@ -17,7 +17,7 @@ export const LobeMetaDataSchema = z.object({
17
17
 
18
18
  tags: z.array(z.string()).optional(),
19
19
  /**
20
- * 名称
20
+ * Name
21
21
  */
22
22
  title: z.string().optional(),
23
23
  });
@@ -1,6 +1,6 @@
1
1
  import type { BaseDataModel } from '../meta';
2
2
 
3
- // 类型定义
3
+ // Type definitions
4
4
  export type TimeGroupId =
5
5
  | 'today'
6
6
  | 'yesterday'
@@ -16,8 +16,8 @@
16
16
  "dependencies": {
17
17
  "@lobechat/const": "workspace:*",
18
18
  "@lobechat/types": "workspace:*",
19
- "dayjs": "^1.11.18",
20
- "dompurify": "^3.2.7"
19
+ "dayjs": "^1.11.19",
20
+ "dompurify": "^3.3.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "vitest-canvas-mock": "^0.3.3"
@@ -10,10 +10,10 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@mozilla/readability": "^0.6.0",
13
- "happy-dom": "^20.0.0",
13
+ "happy-dom": "^20.0.10",
14
14
  "node-html-markdown": "^1.3.0",
15
15
  "ssrf-safe-fetch": "workspace:*",
16
- "query-string": "^9.1.1",
16
+ "query-string": "^9.3.1",
17
17
  "url-join": "^5"
18
18
  }
19
19
  }
@@ -16,7 +16,7 @@ export interface CrawlErrorResult {
16
16
  }
17
17
 
18
18
  export interface FilterOptions {
19
- // 是否启用Readability
19
+ // Whether to enable Readability
20
20
  enableReadability?: boolean;
21
21
 
22
22
  pureText?: boolean;
@@ -34,12 +34,12 @@ export type CrawlImpl<Params = object> = (
34
34
  ) => Promise<CrawlSuccessResult | undefined>;
35
35
 
36
36
  export interface CrawlUrlRule {
37
- // 内容过滤配置(可选)
37
+ // Content filtering configuration (optional)
38
38
  filterOptions?: FilterOptions;
39
39
  impls?: CrawlImplType[];
40
- // URL匹配模式,仅支持正则表达式
40
+ // URL matching pattern, only supports regular expressions
41
41
  urlPattern: string;
42
- // URL转换模板(可选),如果提供则进行URL转换
42
+ // URL transformation template (optional), performs URL conversion if provided
43
43
  urlTransform?: string;
44
44
  }
45
45
 
@@ -1,9 +1,9 @@
1
1
  import { copyImageToClipboard, sanitizeSVGContent } from '@lobechat/utils/client';
2
2
  import { Button, Dropdown, Tooltip } from '@lobehub/ui';
3
+ import { snapdom } from '@zumer/snapdom';
3
4
  import { App, Space } from 'antd';
4
5
  import { css, cx } from 'antd-style';
5
6
  import { CopyIcon, DownloadIcon } from 'lucide-react';
6
- import { domToPng } from 'modern-screenshot';
7
7
  import { useMemo } from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
  import { Center, Flexbox } from 'react-layout-kit';
@@ -41,12 +41,29 @@ const SVGRenderer = ({ content }: SVGRendererProps) => {
41
41
  const sanitizedContent = useMemo(() => sanitizeSVGContent(content), [content]);
42
42
 
43
43
  const generatePng = async () => {
44
- return domToPng(document.querySelector(`#${DOM_ID}`) as HTMLDivElement, {
45
- features: {
46
- // 不启用移除控制符,否则会导致 safari emoji 报错
47
- removeControlCharacter: false,
48
- },
44
+ const blob = await snapdom.toBlob(document.querySelector(`#${DOM_ID}`) as HTMLDivElement, {
49
45
  scale: 2,
46
+ type: 'png',
47
+ });
48
+
49
+ if (!blob) {
50
+ throw new Error('Failed to generate PNG blob');
51
+ }
52
+
53
+ // Convert blob to data URL
54
+ return new Promise<string>((resolve, reject) => {
55
+ const reader = new FileReader();
56
+ reader.addEventListener('load', () => {
57
+ if (typeof reader.result === 'string') {
58
+ resolve(reader.result);
59
+ } else {
60
+ reject(new Error('FileReader result is not a string'));
61
+ }
62
+ });
63
+ reader.addEventListener('error', () =>
64
+ reject(reader.error || new Error('Failed to read blob as data URL')),
65
+ );
66
+ reader.readAsDataURL(blob);
50
67
  });
51
68
  };
52
69
 
@@ -19,7 +19,7 @@ import { useStyles } from './style';
19
19
  import { FieldType } from './type';
20
20
 
21
21
  const Preview = memo<FieldType & { title?: string }>(
22
- ({ title, withSystemRole, withBackground, withFooter }) => {
22
+ ({ title, withSystemRole, withBackground, withFooter, widthMode }) => {
23
23
  const [model, plugins, systemRole] = useAgentStore((s) => [
24
24
  agentSelectors.currentAgentModel(s),
25
25
  agentSelectors.displayableAgentPlugins(s),
@@ -34,7 +34,7 @@ const Preview = memo<FieldType & { title?: string }>(
34
34
 
35
35
  const { t } = useTranslation('chat');
36
36
  const { styles } = useStyles(withBackground);
37
- const { styles: containerStyles } = useContainerStyles();
37
+ const { styles: containerStyles } = useContainerStyles(widthMode);
38
38
 
39
39
  const displayTitle = isInbox ? t('inbox.title') : title;
40
40
  const displayDesc = isInbox ? t('inbox.desc') : description;
@@ -14,10 +14,11 @@ import { sessionMetaSelectors } from '@/store/session/selectors';
14
14
 
15
15
  import { useStyles } from '../style';
16
16
  import Preview from './Preview';
17
- import { FieldType } from './type';
17
+ import { FieldType, WidthMode } from './type';
18
18
 
19
19
  const DEFAULT_FIELD_VALUE: FieldType = {
20
20
  imageType: ImageType.JPG,
21
+ widthMode: WidthMode.Wide,
21
22
  withBackground: true,
22
23
  withFooter: true,
23
24
  withPluginInfo: false,
@@ -34,7 +35,20 @@ const ShareImage = memo<{ mobile?: boolean }>(() => {
34
35
  title: currentAgentTitle,
35
36
  });
36
37
  const { loading: copyLoading, onCopy } = useImgToClipboard();
38
+
39
+ const widthModeOptions = [
40
+ { label: t('shareModal.widthMode.wide'), value: WidthMode.Wide },
41
+ { label: t('shareModal.widthMode.narrow'), value: WidthMode.Narrow },
42
+ ];
43
+
37
44
  const settings: FormItemProps[] = [
45
+ {
46
+ children: <Segmented options={widthModeOptions} />,
47
+ label: t('shareModal.widthMode.label'),
48
+ layout: 'horizontal',
49
+ minWidth: undefined,
50
+ name: 'widthMode',
51
+ },
38
52
  {
39
53
  children: <Switch />,
40
54
  label: t('shareModal.withSystemRole'),
@@ -1,7 +1,13 @@
1
1
  import { ImageType } from '@/hooks/useScreenshot';
2
2
 
3
+ export enum WidthMode {
4
+ Narrow = 'narrow',
5
+ Wide = 'wide'
6
+ }
7
+
3
8
  export type FieldType = {
4
9
  imageType: ImageType;
10
+ widthMode: WidthMode;
5
11
  withBackground: boolean;
6
12
  withFooter: boolean;
7
13
  withPluginInfo: boolean;
@@ -1,36 +1,46 @@
1
1
  import { createStyles } from 'antd-style';
2
2
 
3
- export const useContainerStyles = createStyles(({ css, token, stylish, cx, responsive }) => ({
4
- preview: cx(
5
- stylish.noScrollbar,
6
- css`
7
- overflow: hidden scroll;
3
+ import { WidthMode } from './ShareImage/type';
8
4
 
9
- width: 100%;
10
- max-height: 70dvh;
11
- border: 1px solid ${token.colorBorder};
12
- border-radius: ${token.borderRadiusLG}px;
13
-
14
- background: ${token.colorBgLayout};
15
-
16
- /* stylelint-disable selector-class-pattern */
17
- .react-pdf__Document *,
18
- .react-pdf__Page * {
19
- pointer-events: none;
20
- }
21
- /* stylelint-enable selector-class-pattern */
22
-
23
- ::-webkit-scrollbar {
24
- width: 0 !important;
25
- height: 0 !important;
26
- }
27
-
28
- ${responsive.mobile} {
29
- max-height: 40dvh;
30
- }
31
- `,
32
- ),
33
- }));
5
+ export const useContainerStyles = createStyles(
6
+ ({ css, token, stylish, cx, responsive }, widthMode?: WidthMode) => {
7
+ const isNarrow = widthMode === WidthMode.Narrow;
8
+
9
+ return {
10
+ preview: cx(
11
+ stylish.noScrollbar,
12
+ css`
13
+ overflow: hidden scroll;
14
+
15
+ width: 100%;
16
+ max-width: ${isNarrow ? '480px' : 'none'};
17
+ max-height: 70dvh;
18
+ margin: ${isNarrow ? '0 auto' : '0'};
19
+ border: 1px solid ${token.colorBorder};
20
+ border-radius: ${token.borderRadiusLG}px;
21
+
22
+ background: ${token.colorBgLayout};
23
+
24
+ /* stylelint-disable selector-class-pattern */
25
+ .react-pdf__Document *,
26
+ .react-pdf__Page * {
27
+ pointer-events: none;
28
+ }
29
+ /* stylelint-enable selector-class-pattern */
30
+
31
+ ::-webkit-scrollbar {
32
+ width: 0 !important;
33
+ height: 0 !important;
34
+ }
35
+
36
+ ${responsive.mobile} {
37
+ max-height: 40dvh;
38
+ }
39
+ `,
40
+ ),
41
+ };
42
+ },
43
+ );
34
44
 
35
45
  export const useStyles = createStyles(({ responsive, token, css }) => ({
36
46
  body: css`
@@ -1,6 +1,6 @@
1
1
  import type { SegmentedProps } from '@lobehub/ui';
2
+ import { snapdom } from '@zumer/snapdom';
2
3
  import dayjs from 'dayjs';
3
- import { domToJpeg, domToPng, domToSvg, domToWebp } from 'modern-screenshot';
4
4
  import { useCallback, useState } from 'react';
5
5
 
6
6
  import { BRANDING_NAME } from '@/const/branding';
@@ -40,26 +40,6 @@ export const getImageUrl = async ({
40
40
  imageType: ImageType;
41
41
  width?: number;
42
42
  }) => {
43
- let screenshotFn: any;
44
- switch (imageType) {
45
- case ImageType.JPG: {
46
- screenshotFn = domToJpeg;
47
- break;
48
- }
49
- case ImageType.PNG: {
50
- screenshotFn = domToPng;
51
- break;
52
- }
53
- case ImageType.SVG: {
54
- screenshotFn = domToSvg;
55
- break;
56
- }
57
- case ImageType.WEBP: {
58
- screenshotFn = domToWebp;
59
- break;
60
- }
61
- }
62
-
63
43
  const dom: HTMLDivElement = document.querySelector(id) as HTMLDivElement;
64
44
  let copy: HTMLDivElement = dom;
65
45
 
@@ -69,18 +49,54 @@ export const getImageUrl = async ({
69
49
  document.body.append(copy);
70
50
  }
71
51
 
72
- const dataUrl = await screenshotFn(width ? copy : dom, {
73
- features: {
74
- // 不启用移除控制符,否则会导致 safari emoji 报错
75
- removeControlCharacter: false,
76
- },
52
+ const baseOptions = {
77
53
  scale: 2,
78
54
  width,
79
- });
55
+ };
56
+
57
+ let blob: Blob;
58
+
59
+ if (imageType === ImageType.SVG) {
60
+ // For SVG, we need to use the full snapdom API to get the raw SVG string
61
+ const result = await snapdom(width ? copy : dom, baseOptions);
62
+ const svgString = result.toRaw();
63
+ blob = new Blob([svgString], { type: 'image/svg+xml' });
64
+ } else {
65
+ // For raster formats, use toBlob directly with type option
66
+ const blobType = (imageType === ImageType.JPG ? 'jpg' : imageType) as 'png' | 'jpg' | 'webp';
67
+ const blobResult = await snapdom.toBlob(width ? copy : dom, {
68
+ type: blobType,
69
+ useProxy: 'https://proxy.corsfix.com/?',
70
+ });
71
+
72
+ if (!blobResult) {
73
+ throw new Error('Failed to generate blob from snapdom');
74
+ }
75
+
76
+ blob = blobResult;
77
+ }
80
78
 
81
79
  if (width && copy) copy?.remove();
82
80
 
83
- return dataUrl;
81
+ if (!blob) {
82
+ throw new Error('Blob is undefined');
83
+ }
84
+
85
+ // Convert blob to data URL using FileReader
86
+ return new Promise<string>((resolve, reject) => {
87
+ const reader = new FileReader();
88
+ reader.addEventListener('load', () => {
89
+ if (typeof reader.result === 'string') {
90
+ resolve(reader.result);
91
+ } else {
92
+ reject(new Error('FileReader result is not a string'));
93
+ }
94
+ });
95
+ reader.addEventListener('error', () =>
96
+ reject(reader.error || new Error('Failed to read blob as data URL')),
97
+ );
98
+ reader.readAsDataURL(blob);
99
+ });
84
100
  };
85
101
 
86
102
  export const useScreenshot = ({
@@ -361,6 +361,11 @@ export default {
361
361
  screenshot: '截图',
362
362
  settings: '导出设置',
363
363
  text: '文本',
364
+ widthMode: {
365
+ label: '宽度模式',
366
+ narrow: '窄屏模式',
367
+ wide: '宽屏模式',
368
+ },
364
369
  withBackground: '包含背景图片',
365
370
  withFooter: '包含页脚',
366
371
  withPluginInfo: '包含插件信息',