@lobehub/chat 1.82.0 → 1.82.2

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 (102) hide show
  1. package/.cursor/rules/desktop-local-tools-implement.mdc +80 -0
  2. package/.env.desktop +2 -1
  3. package/.github/scripts/pr-comment.js +4 -9
  4. package/CHANGELOG.md +51 -0
  5. package/changelog/v1.json +18 -0
  6. package/locales/ar/electron.json +38 -2
  7. package/locales/ar/plugin.json +51 -31
  8. package/locales/bg-BG/electron.json +38 -2
  9. package/locales/bg-BG/plugin.json +51 -31
  10. package/locales/de-DE/electron.json +38 -2
  11. package/locales/de-DE/plugin.json +29 -9
  12. package/locales/en-US/electron.json +38 -2
  13. package/locales/en-US/plugin.json +29 -9
  14. package/locales/es-ES/electron.json +38 -2
  15. package/locales/es-ES/plugin.json +51 -31
  16. package/locales/fa-IR/electron.json +38 -2
  17. package/locales/fa-IR/plugin.json +51 -31
  18. package/locales/fr-FR/electron.json +38 -2
  19. package/locales/fr-FR/plugin.json +51 -31
  20. package/locales/it-IT/electron.json +38 -2
  21. package/locales/it-IT/plugin.json +51 -31
  22. package/locales/ja-JP/electron.json +38 -2
  23. package/locales/ja-JP/plugin.json +51 -31
  24. package/locales/ko-KR/electron.json +38 -2
  25. package/locales/ko-KR/plugin.json +29 -9
  26. package/locales/nl-NL/electron.json +38 -2
  27. package/locales/nl-NL/plugin.json +51 -31
  28. package/locales/pl-PL/electron.json +38 -2
  29. package/locales/pl-PL/plugin.json +29 -9
  30. package/locales/pt-BR/electron.json +38 -2
  31. package/locales/pt-BR/plugin.json +51 -31
  32. package/locales/ru-RU/electron.json +38 -2
  33. package/locales/ru-RU/plugin.json +51 -31
  34. package/locales/tr-TR/electron.json +38 -2
  35. package/locales/tr-TR/plugin.json +51 -31
  36. package/locales/vi-VN/electron.json +38 -2
  37. package/locales/vi-VN/plugin.json +29 -9
  38. package/locales/zh-CN/electron.json +38 -2
  39. package/locales/zh-CN/plugin.json +30 -10
  40. package/locales/zh-TW/electron.json +38 -2
  41. package/locales/zh-TW/plugin.json +51 -31
  42. package/package.json +1 -1
  43. package/packages/electron-client-ipc/src/events/update.ts +3 -3
  44. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/Mode.tsx +222 -0
  45. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/Option.tsx +104 -0
  46. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/Sync.tsx +42 -0
  47. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/Waiting.tsx +203 -0
  48. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/Connection/index.tsx +57 -0
  49. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/UpdateModal.tsx +242 -0
  50. package/src/app/[variants]/(main)/_layout/Desktop/ElectronTitlebar/UpdateNotification.tsx +193 -0
  51. package/src/app/[variants]/(main)/_layout/Desktop/{Titlebar.tsx → ElectronTitlebar/index.tsx} +15 -1
  52. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/BottomActions.tsx +3 -2
  53. package/src/app/[variants]/(main)/_layout/Desktop/index.tsx +1 -1
  54. package/src/app/[variants]/layout.tsx +2 -1
  55. package/src/config/aiModels/openrouter.ts +6 -6
  56. package/src/features/Conversation/components/MarkdownElements/LocalFile/Render/LocalFile.tsx +65 -0
  57. package/src/features/Conversation/components/MarkdownElements/LocalFile/Render/index.tsx +29 -0
  58. package/src/features/Conversation/components/MarkdownElements/LocalFile/index.ts +16 -0
  59. package/src/features/Conversation/components/MarkdownElements/index.ts +7 -1
  60. package/src/features/Conversation/components/MarkdownElements/remarkPlugins/__snapshots__/createRemarkSelfClosingTagPlugin.test.ts.snap +260 -0
  61. package/src/features/Conversation/components/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +204 -0
  62. package/src/features/Conversation/components/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +133 -0
  63. package/src/features/Conversation/components/MarkdownElements/type.ts +5 -1
  64. package/src/features/PluginDevModal/MCPManifestForm/ArgsInput.tsx +20 -0
  65. package/src/features/PluginDevModal/MCPManifestForm/MCPTypeSelect.tsx +176 -0
  66. package/src/features/PluginDevModal/MCPManifestForm/index.tsx +289 -0
  67. package/src/features/PluginDevModal/MCPManifestForm/utils.test.ts +262 -0
  68. package/src/features/PluginDevModal/MCPManifestForm/utils.ts +151 -0
  69. package/src/features/PluginDevModal/index.tsx +31 -22
  70. package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +1 -1
  71. package/src/libs/mcp/__tests__/__snapshots__/index.test.ts.snap +0 -56
  72. package/src/locales/default/electron.ts +38 -2
  73. package/src/locales/default/plugin.ts +28 -8
  74. package/src/server/modules/ElectronIPCClient/index.ts +36 -0
  75. package/src/server/routers/lambda/session.ts +2 -6
  76. package/src/server/routers/tools/mcp.ts +6 -0
  77. package/src/server/services/file/impls/index.ts +9 -1
  78. package/src/server/services/file/impls/local.test.ts +299 -0
  79. package/src/server/services/file/impls/local.ts +183 -0
  80. package/src/server/services/mcp/index.ts +26 -0
  81. package/src/services/aiModel/index.ts +5 -1
  82. package/src/services/aiProvider/index.ts +5 -1
  83. package/src/services/electron/autoUpdate.ts +4 -0
  84. package/src/services/file/index.ts +5 -1
  85. package/src/services/mcp.ts +13 -2
  86. package/src/services/message/index.ts +5 -1
  87. package/src/services/plugin/index.ts +5 -1
  88. package/src/services/session/index.ts +5 -1
  89. package/src/services/tableViewer/desktop.ts +15 -0
  90. package/src/services/tableViewer/index.ts +4 -1
  91. package/src/services/thread/index.ts +5 -1
  92. package/src/services/topic/index.ts +5 -1
  93. package/src/services/user/index.ts +5 -1
  94. package/src/store/electron/actions/app.ts +59 -0
  95. package/src/store/electron/actions/sync.ts +5 -1
  96. package/src/store/electron/initialState.ts +3 -1
  97. package/src/store/electron/store.ts +6 -1
  98. package/src/store/tool/slices/customPlugin/action.ts +16 -4
  99. package/src/utils/client/GlobalAgentContextManager.ts +85 -0
  100. package/src/utils/promptTemplate.test.ts +78 -0
  101. package/src/utils/promptTemplate.ts +17 -0
  102. package/src/features/PluginDevModal/MCPManifestForm.tsx +0 -164
@@ -1,11 +1,20 @@
1
1
  import { memo } from 'react';
2
2
  import { Flexbox } from 'react-layout-kit';
3
3
 
4
+ import { useElectronStore } from '@/store/electron';
4
5
  import { electronStylish } from '@/styles/electron';
5
6
 
7
+ import Connection from './Connection';
8
+ import { UpdateModal } from './UpdateModal';
9
+ import { UpdateNotification } from './UpdateNotification';
10
+
6
11
  export const TITLE_BAR_HEIGHT = 36;
7
12
 
8
13
  const TitleBar = memo(() => {
14
+ const initElectronAppState = useElectronStore((s) => s.useInitElectronAppState);
15
+
16
+ initElectronAppState();
17
+
9
18
  return (
10
19
  <Flexbox
11
20
  align={'center'}
@@ -19,7 +28,12 @@ const TitleBar = memo(() => {
19
28
  >
20
29
  <div />
21
30
  <div>{/* TODO */}</div>
22
- <div>{/* TODO */}</div>
31
+
32
+ <Flexbox className={electronStylish.nodrag} gap={8} horizontal>
33
+ <UpdateNotification />
34
+ <Connection />
35
+ </Flexbox>
36
+ <UpdateModal />
23
37
  </Flexbox>
24
38
  );
25
39
  });
@@ -3,6 +3,7 @@ import { Book, Github } from 'lucide-react';
3
3
  import Link from 'next/link';
4
4
  import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
6
7
 
7
8
  import { DOCUMENTS_REFER_URL, GITHUB } from '@/const/url';
8
9
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
@@ -12,7 +13,7 @@ const BottomActions = memo(() => {
12
13
  const { hideGitHub, hideDocs } = useServerConfigStore(featureFlagsSelectors);
13
14
 
14
15
  return (
15
- <>
16
+ <Flexbox gap={8}>
16
17
  {!hideGitHub && (
17
18
  <Link aria-label={'GitHub'} href={GITHUB} target={'_blank'}>
18
19
  <ActionIcon icon={Github} placement={'right'} title={'GitHub'} />
@@ -23,7 +24,7 @@ const BottomActions = memo(() => {
23
24
  <ActionIcon icon={Book} placement={'right'} title={t('document')} />
24
25
  </Link>
25
26
  )}
26
- </>
27
+ </Flexbox>
27
28
  );
28
29
  });
29
30
 
@@ -14,9 +14,9 @@ import { usePlatform } from '@/hooks/usePlatform';
14
14
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
15
15
  import { HotkeyScopeEnum } from '@/types/hotkey';
16
16
 
17
+ import TitleBar, { TITLE_BAR_HEIGHT } from './ElectronTitlebar';
17
18
  import RegisterHotkeys from './RegisterHotkeys';
18
19
  import SideBar from './SideBar';
19
- import TitleBar, { TITLE_BAR_HEIGHT } from './Titlebar';
20
20
 
21
21
  const CloudBanner = dynamic(() => import('@/features/AlertBanner/CloudBanner'));
22
22
 
@@ -7,6 +7,7 @@ import { isRtlLang } from 'rtl-detect';
7
7
 
8
8
  import Analytics from '@/components/Analytics';
9
9
  import { DEFAULT_LANG } from '@/const/locale';
10
+ import { isDesktop } from '@/const/version';
10
11
  import PWAInstall from '@/features/PWAInstall';
11
12
  import AuthProvider from '@/layout/AuthProvider';
12
13
  import GlobalProvider from '@/layout/GlobalProvider';
@@ -78,7 +79,7 @@ export const generateViewport = async (props: DynamicLayoutProps): ResolvingView
78
79
 
79
80
  export const generateStaticParams = () => {
80
81
  const themes: ThemeAppearance[] = ['dark', 'light'];
81
- const mobileOptions = [true, false];
82
+ const mobileOptions = isDesktop ? [false] : [true, false];
82
83
  // only static for serveral page, other go to dynamtic
83
84
  const staticLocales: Locales[] = [DEFAULT_LANG, 'zh-CN'];
84
85
 
@@ -20,7 +20,7 @@ const openrouterChatModels: AIChatModelCard[] = [
20
20
  description:
21
21
  'o3 是一款全能强大的模型,在多个领域表现出色。它为数学、科学、编程和视觉推理任务树立了新标杆。它也擅长技术写作和指令遵循。用户可利用它分析文本、代码和图像,解决多步骤的复杂问题。',
22
22
  displayName: 'o3',
23
- id: 'o3',
23
+ id: 'openai/o3',
24
24
  maxOutput: 100_000,
25
25
  pricing: {
26
26
  cachedInput: 2.5,
@@ -79,7 +79,7 @@ const openrouterChatModels: AIChatModelCard[] = [
79
79
  description:
80
80
  'GPT-4.1 是我们用于复杂任务的旗舰模型。它非常适合跨领域解决问题。',
81
81
  displayName: 'GPT-4.1',
82
- id: 'gpt-4.1',
82
+ id: 'openai/gpt-4.1',
83
83
  maxOutput: 32_768,
84
84
  pricing: {
85
85
  cachedInput: 0.5,
@@ -98,7 +98,7 @@ const openrouterChatModels: AIChatModelCard[] = [
98
98
  description:
99
99
  'GPT-4.1 mini 提供了智能、速度和成本之间的平衡,使其成为许多用例中有吸引力的模型。',
100
100
  displayName: 'GPT-4.1 mini',
101
- id: 'gpt-4.1-mini',
101
+ id: 'openai/gpt-4.1-mini',
102
102
  maxOutput: 32_768,
103
103
  pricing: {
104
104
  cachedInput: 0.1,
@@ -117,7 +117,7 @@ const openrouterChatModels: AIChatModelCard[] = [
117
117
  description:
118
118
  'GPT-4.1 nano 是最快,最具成本效益的GPT-4.1模型。',
119
119
  displayName: 'GPT-4.1 nano',
120
- id: 'gpt-4.1-nano',
120
+ id: 'openai/gpt-4.1-nano',
121
121
  maxOutput: 32_768,
122
122
  pricing: {
123
123
  cachedInput: 0.025,
@@ -136,7 +136,7 @@ const openrouterChatModels: AIChatModelCard[] = [
136
136
  description:
137
137
  'o3-mini 高推理等级版,在与 o1-mini 相同的成本和延迟目标下提供高智能。',
138
138
  displayName: 'o3-mini (high)',
139
- id: 'o3-mini-high',
139
+ id: 'openai/o3-mini-high',
140
140
  maxOutput: 100_000,
141
141
  pricing: {
142
142
  cachedInput: 0.55,
@@ -155,7 +155,7 @@ const openrouterChatModels: AIChatModelCard[] = [
155
155
  description:
156
156
  'o3-mini 在与 o1-mini 相同的成本和延迟目标下提供高智能。',
157
157
  displayName: 'o3-mini',
158
- id: 'o3-mini',
158
+ id: 'openai/o3-mini',
159
159
  maxOutput: 100_000,
160
160
  pricing: {
161
161
  cachedInput: 0.55,
@@ -0,0 +1,65 @@
1
+ import { createStyles } from 'antd-style';
2
+ import React from 'react';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ import FileIcon from '@/components/FileIcon';
6
+ import { localFileService } from '@/services/electron/localFileService';
7
+
8
+ const useStyles = createStyles(({ css, token }) => ({
9
+ container: css`
10
+ cursor: pointer;
11
+
12
+ padding-block: 2px;
13
+ padding-inline: 4px 8px;
14
+ border-radius: 4px;
15
+
16
+ color: ${token.colorTextSecondary};
17
+
18
+ :hover {
19
+ color: ${token.colorText};
20
+ background: ${token.colorFillTertiary};
21
+ }
22
+ `,
23
+ title: css`
24
+ overflow: hidden;
25
+ display: block;
26
+
27
+ line-height: 20px;
28
+ color: inherit;
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ `,
32
+ }));
33
+
34
+ const LocalFile = ({
35
+ name,
36
+ path,
37
+ isDirectory,
38
+ }: {
39
+ isDirectory: boolean;
40
+ name: string;
41
+ path: string;
42
+ }) => {
43
+ const { styles } = useStyles();
44
+ const handleClick = () => {
45
+ localFileService.openLocalFileOrFolder(path, isDirectory);
46
+ };
47
+
48
+ return (
49
+ <Flexbox
50
+ align={'center'}
51
+ className={styles.container}
52
+ gap={4}
53
+ horizontal
54
+ onClick={handleClick}
55
+ style={{ display: 'inline-flex', verticalAlign: 'middle' }}
56
+ >
57
+ <FileIcon fileName={name} isDirectory={isDirectory} size={22} variant={'pure'} />
58
+ <Flexbox align={'baseline'} gap={4} horizontal style={{ overflow: 'hidden', width: '100%' }}>
59
+ <div className={styles.title}>{name}</div>
60
+ </Flexbox>
61
+ </Flexbox>
62
+ );
63
+ };
64
+
65
+ export default LocalFile;
@@ -0,0 +1,29 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import React, { memo } from 'react';
3
+
4
+ import { MarkdownElementProps } from '../../type';
5
+ import LocalFile from './LocalFile';
6
+
7
+ interface LocalFileProps {
8
+ isDirectory: boolean;
9
+ name: string;
10
+ path: string;
11
+ }
12
+
13
+ const Render = memo<MarkdownElementProps<LocalFileProps>>(({ node }) => {
14
+ // 从 node.properties 中提取属性
15
+ const { name, path, isDirectory } = node?.properties || {};
16
+
17
+ if (!name || !path) {
18
+ // 如果缺少必要属性,可以选择渲染错误提示或 null
19
+ console.error('LocalFile Render component missing required properties:', node?.properties);
20
+ return null; // 或者返回一个错误占位符
21
+ }
22
+
23
+ // isDirectory 属性可能为 true (来自插件) 或 undefined,我们需要确保它是 boolean
24
+ const isDir = isDirectory === true;
25
+
26
+ return <LocalFile isDirectory={isDir} name={name} path={path} />;
27
+ }, isEqual);
28
+
29
+ export default Render;
@@ -0,0 +1,16 @@
1
+ import { FC } from 'react';
2
+
3
+ import { createRemarkSelfClosingTagPlugin } from '../remarkPlugins/createRemarkSelfClosingTagPlugin';
4
+ import { MarkdownElement, MarkdownElementProps } from '../type';
5
+ import RenderComponent from './Render';
6
+
7
+ // 定义此元素的标签名
8
+ const tag = 'localFile';
9
+
10
+ const LocalFileElement: MarkdownElement = {
11
+ Component: RenderComponent as FC<MarkdownElementProps>,
12
+ remarkPlugin: createRemarkSelfClosingTagPlugin(tag),
13
+ tag,
14
+ };
15
+
16
+ export default LocalFileElement;
@@ -1,6 +1,12 @@
1
1
  import LobeArtifact from './LobeArtifact';
2
2
  import LobeThinking from './LobeThinking';
3
+ import LocalFile from './LocalFile';
3
4
  import Thinking from './Thinking';
4
5
  import { MarkdownElement } from './type';
5
6
 
6
- export const markdownElements: MarkdownElement[] = [Thinking, LobeArtifact, LobeThinking];
7
+ export const markdownElements: MarkdownElement[] = [
8
+ Thinking,
9
+ LobeArtifact,
10
+ LobeThinking,
11
+ LocalFile,
12
+ ];
@@ -0,0 +1,260 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`createRemarkSelfClosingTagPlugin > should handle tag within a list item and generate snapshot 1`] = `
4
+ {
5
+ "children": [
6
+ {
7
+ "children": [
8
+ {
9
+ "checked": null,
10
+ "children": [
11
+ {
12
+ "children": [
13
+ {
14
+ "position": {
15
+ "end": {
16
+ "column": 26,
17
+ "line": 2,
18
+ "offset": 26,
19
+ },
20
+ "start": {
21
+ "column": 4,
22
+ "line": 2,
23
+ "offset": 4,
24
+ },
25
+ },
26
+ "type": "text",
27
+ "value": "文件名:飞机全书 一部明晰可见的历史.pdf",
28
+ },
29
+ ],
30
+ "position": {
31
+ "end": {
32
+ "column": 26,
33
+ "line": 2,
34
+ "offset": 26,
35
+ },
36
+ "start": {
37
+ "column": 4,
38
+ "line": 2,
39
+ "offset": 4,
40
+ },
41
+ },
42
+ "type": "paragraph",
43
+ },
44
+ {
45
+ "children": [
46
+ {
47
+ "checked": null,
48
+ "children": [
49
+ {
50
+ "children": [
51
+ {
52
+ "position": {
53
+ "end": {
54
+ "column": 10,
55
+ "line": 3,
56
+ "offset": 36,
57
+ },
58
+ "start": {
59
+ "column": 6,
60
+ "line": 3,
61
+ "offset": 32,
62
+ },
63
+ },
64
+ "type": "text",
65
+ "value": "路径1:",
66
+ },
67
+ {
68
+ "data": {
69
+ "hName": "localFile",
70
+ "hProperties": {
71
+ "name": "飞机全书 一部明晰可见的历史.pdf",
72
+ "path": "/Users/abc/Zotero/storage/ASBMAURK/飞机全书 一部明晰可见的历史.pdf",
73
+ },
74
+ },
75
+ "type": "localFile",
76
+ },
77
+ ],
78
+ "position": {
79
+ "end": {
80
+ "column": 110,
81
+ "line": 3,
82
+ "offset": 136,
83
+ },
84
+ "start": {
85
+ "column": 6,
86
+ "line": 3,
87
+ "offset": 32,
88
+ },
89
+ },
90
+ "type": "paragraph",
91
+ },
92
+ ],
93
+ "position": {
94
+ "end": {
95
+ "column": 110,
96
+ "line": 3,
97
+ "offset": 136,
98
+ },
99
+ "start": {
100
+ "column": 4,
101
+ "line": 3,
102
+ "offset": 30,
103
+ },
104
+ },
105
+ "spread": false,
106
+ "type": "listItem",
107
+ },
108
+ {
109
+ "checked": null,
110
+ "children": [
111
+ {
112
+ "children": [
113
+ {
114
+ "position": {
115
+ "end": {
116
+ "column": 56,
117
+ "line": 4,
118
+ "offset": 192,
119
+ },
120
+ "start": {
121
+ "column": 6,
122
+ "line": 4,
123
+ "offset": 142,
124
+ },
125
+ },
126
+ "type": "text",
127
+ "value": "路径2:/Users/abc/Downloads/测试 PDF/飞机全书 一部明晰可见的历史.pdf",
128
+ },
129
+ ],
130
+ "position": {
131
+ "end": {
132
+ "column": 56,
133
+ "line": 4,
134
+ "offset": 192,
135
+ },
136
+ "start": {
137
+ "column": 6,
138
+ "line": 4,
139
+ "offset": 142,
140
+ },
141
+ },
142
+ "type": "paragraph",
143
+ },
144
+ ],
145
+ "position": {
146
+ "end": {
147
+ "column": 56,
148
+ "line": 4,
149
+ "offset": 192,
150
+ },
151
+ "start": {
152
+ "column": 4,
153
+ "line": 4,
154
+ "offset": 140,
155
+ },
156
+ },
157
+ "spread": false,
158
+ "type": "listItem",
159
+ },
160
+ ],
161
+ "ordered": false,
162
+ "position": {
163
+ "end": {
164
+ "column": 56,
165
+ "line": 4,
166
+ "offset": 192,
167
+ },
168
+ "start": {
169
+ "column": 4,
170
+ "line": 3,
171
+ "offset": 30,
172
+ },
173
+ },
174
+ "spread": false,
175
+ "start": null,
176
+ "type": "list",
177
+ },
178
+ ],
179
+ "position": {
180
+ "end": {
181
+ "column": 56,
182
+ "line": 4,
183
+ "offset": 192,
184
+ },
185
+ "start": {
186
+ "column": 1,
187
+ "line": 2,
188
+ "offset": 1,
189
+ },
190
+ },
191
+ "spread": false,
192
+ "type": "listItem",
193
+ },
194
+ ],
195
+ "ordered": true,
196
+ "position": {
197
+ "end": {
198
+ "column": 56,
199
+ "line": 4,
200
+ "offset": 192,
201
+ },
202
+ "start": {
203
+ "column": 1,
204
+ "line": 2,
205
+ "offset": 1,
206
+ },
207
+ },
208
+ "spread": false,
209
+ "start": 1,
210
+ "type": "list",
211
+ },
212
+ {
213
+ "children": [
214
+ {
215
+ "position": {
216
+ "end": {
217
+ "column": 75,
218
+ "line": 6,
219
+ "offset": 268,
220
+ },
221
+ "start": {
222
+ "column": 1,
223
+ "line": 6,
224
+ "offset": 194,
225
+ },
226
+ },
227
+ "type": "text",
228
+ "value": "这是一本 PDF 格式的书,并且在你的 Zotero 和 Downloads 文件夹里都能找到。如果需要进一步操作,比如阅读或者提取内容,可以告诉我",
229
+ },
230
+ ],
231
+ "position": {
232
+ "end": {
233
+ "column": 75,
234
+ "line": 6,
235
+ "offset": 268,
236
+ },
237
+ "start": {
238
+ "column": 1,
239
+ "line": 6,
240
+ "offset": 194,
241
+ },
242
+ },
243
+ "type": "paragraph",
244
+ },
245
+ ],
246
+ "position": {
247
+ "end": {
248
+ "column": 1,
249
+ "line": 7,
250
+ "offset": 269,
251
+ },
252
+ "start": {
253
+ "column": 1,
254
+ "line": 1,
255
+ "offset": 0,
256
+ },
257
+ },
258
+ "type": "root",
259
+ }
260
+ `;