@lobehub/lobehub 2.0.0-next.281 → 2.0.0-next.282

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.282](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.281...v2.0.0-next.282)
6
+
7
+ <sup>Released on **2026-01-14**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Update readFile content.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Update readFile content, closes [#11485](https://github.com/lobehub/lobe-chat/issues/11485) ([050499b](https://github.com/lobehub/lobe-chat/commit/050499b))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.281](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.280...v2.0.0-next.281)
6
31
 
7
32
  <sup>Released on **2026-01-14**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Update readFile content."
6
+ ]
7
+ },
8
+ "date": "2026-01-14",
9
+ "version": "2.0.0-next.282"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.281",
3
+ "version": "2.0.0-next.282",
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",
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { type LocalReadFileParams } from '@lobechat/electron-client-ipc';
4
4
  import { type BuiltinInspectorProps } from '@lobechat/types';
5
- import { cx } from 'antd-style';
6
- import { memo } from 'react';
5
+ import { createStaticStyles, cx } from 'antd-style';
6
+ import { memo, useMemo } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
 
9
9
  import { inspectorTextStyles, shinyTextStyles } from '@/styles';
@@ -11,12 +11,28 @@ import { inspectorTextStyles, shinyTextStyles } from '@/styles';
11
11
  import { type LocalReadFileState } from '../../..';
12
12
  import { FilePathDisplay } from '../../components/FilePathDisplay';
13
13
 
14
+ const styles = createStaticStyles(({ css }) => ({
15
+ lineRange: css`
16
+ flex-shrink: 0;
17
+ margin-inline-start: 4px;
18
+ opacity: 0.7;
19
+ `,
20
+ }));
21
+
14
22
  export const ReadLocalFileInspector = memo<
15
23
  BuiltinInspectorProps<LocalReadFileParams, LocalReadFileState>
16
24
  >(({ args, partialArgs, isArgumentsStreaming, isLoading }) => {
17
25
  const { t } = useTranslation('plugin');
18
26
 
19
27
  const filePath = args?.path || partialArgs?.path || '';
28
+ const loc = args?.loc || partialArgs?.loc;
29
+
30
+ // Format line range display, e.g., "L1-L200"
31
+ const lineRangeText = useMemo(() => {
32
+ if (!loc || loc.length !== 2) return null;
33
+ const [start, end] = loc;
34
+ return `L${start + 1}-L${end}`;
35
+ }, [loc]);
20
36
 
21
37
  // During argument streaming
22
38
  if (isArgumentsStreaming) {
@@ -31,6 +47,7 @@ export const ReadLocalFileInspector = memo<
31
47
  <div className={cx(inspectorTextStyles.root, shinyTextStyles.shinyText)}>
32
48
  <span>{t('builtins.lobe-local-system.apiName.readLocalFile')}: </span>
33
49
  <FilePathDisplay filePath={filePath} />
50
+ {lineRangeText && <span className={styles.lineRange}>{lineRangeText}</span>}
34
51
  </div>
35
52
  );
36
53
  }
@@ -39,6 +56,7 @@ export const ReadLocalFileInspector = memo<
39
56
  <div className={cx(inspectorTextStyles.root, isLoading && shinyTextStyles.shinyText)}>
40
57
  <span>{t('builtins.lobe-local-system.apiName.readLocalFile')}: </span>
41
58
  <FilePathDisplay filePath={filePath} />
59
+ {lineRangeText && <span className={styles.lineRange}>{lineRangeText}</span>}
42
60
  </div>
43
61
  );
44
62
  });
@@ -60,16 +60,53 @@ export function attributesCommon(): DetectedResourceAttributes {
60
60
  }
61
61
  }
62
62
 
63
- export function register(options?: { debug?: true | DiagLogLevel; version?: string }) {
63
+ function debugLogLevelFromString(level?: string | null): DiagLogLevel | undefined {
64
+ if (!level) {
65
+ return undefined;
66
+ }
67
+ if (typeof level !== 'string') {
68
+ return undefined;
69
+ }
70
+
71
+ switch (level.toLowerCase()) {
72
+ case 'none':
73
+ return DiagLogLevel.NONE;
74
+ case 'error':
75
+ return DiagLogLevel.ERROR;
76
+ case 'warn':
77
+ return DiagLogLevel.WARN;
78
+ case 'info':
79
+ return DiagLogLevel.INFO;
80
+ case 'debug':
81
+ return DiagLogLevel.DEBUG;
82
+ case 'verbose':
83
+ return DiagLogLevel.VERBOSE;
84
+ case 'all':
85
+ return DiagLogLevel.ALL;
86
+ default:
87
+ return undefined;
88
+ }
89
+ }
90
+
91
+ export function register(options?: { debug?: true | DiagLogLevel; name?: string; version?: string }) {
64
92
  const attributes = attributesCommon();
65
93
 
94
+ if (typeof options?.name !== 'undefined') {
95
+ attributes[ATTR_SERVICE_NAME] = options.name;
96
+ }
66
97
  if (typeof options?.version !== 'undefined') {
67
98
  attributes[ATTR_SERVICE_VERSION] = options.version;
68
99
  }
69
- if (typeof options?.debug !== 'undefined') {
100
+ if (typeof options?.debug !== 'undefined' || env.OTEL_JS_LOBEHUB_DIAG) {
101
+ const levelFromEnv = debugLogLevelFromString(env.OTEL_JS_LOBEHUB_DIAG);
102
+
70
103
  diag.setLogger(
71
104
  new DiagConsoleLogger(),
72
- options.debug === true ? DiagLogLevel.DEBUG : options.debug,
105
+ !!levelFromEnv
106
+ ? levelFromEnv
107
+ : options?.debug === true
108
+ ? DiagLogLevel.DEBUG
109
+ : options?.debug,
73
110
  );
74
111
  }
75
112
 
@@ -0,0 +1,62 @@
1
+ import {
2
+ IEditor,
3
+ ReactCodePlugin,
4
+ ReactCodemirrorPlugin,
5
+ ReactHRPlugin,
6
+ ReactLinkPlugin,
7
+ ReactListPlugin,
8
+ ReactMathPlugin,
9
+ ReactTablePlugin,
10
+ } from '@lobehub/editor';
11
+ import { Editor } from '@lobehub/editor/react';
12
+ import { Flexbox } from '@lobehub/ui';
13
+ import { FC } from 'react';
14
+
15
+ import TypoBar from './Typobar';
16
+
17
+ interface EditorCanvasProps {
18
+ defaultValue?: string;
19
+ editor?: IEditor;
20
+ }
21
+
22
+ const EditorCanvas: FC<EditorCanvasProps> = ({ defaultValue, editor }) => {
23
+ return (
24
+ <>
25
+ <TypoBar editor={editor} />
26
+ <Flexbox
27
+ padding={16}
28
+ style={{ cursor: 'text', maxHeight: '80vh', minHeight: '50vh', overflowY: 'auto' }}
29
+ >
30
+ <Editor
31
+ autoFocus
32
+ content={''}
33
+ editor={editor}
34
+ onInit={(editor) => {
35
+ if (!editor || !defaultValue) return;
36
+ try {
37
+ editor?.setDocument('markdown', defaultValue);
38
+ } catch (e) {
39
+ console.error('setDocument error:', e);
40
+ }
41
+ }}
42
+ plugins={[
43
+ ReactListPlugin,
44
+ ReactCodePlugin,
45
+ ReactCodemirrorPlugin,
46
+ ReactHRPlugin,
47
+ ReactLinkPlugin,
48
+ ReactTablePlugin,
49
+ ReactMathPlugin,
50
+ ]}
51
+ style={{
52
+ paddingBottom: 120,
53
+ }}
54
+ type={'text'}
55
+ variant={'chat'}
56
+ />
57
+ </Flexbox>
58
+ </>
59
+ );
60
+ };
61
+
62
+ export default EditorCanvas;
@@ -0,0 +1,30 @@
1
+ import { TextArea } from '@lobehub/ui';
2
+ import { FC } from 'react';
3
+
4
+ interface EditorCanvasProps {
5
+ defaultValue?: string;
6
+ onChange?: (value: string) => void;
7
+ value?: string;
8
+ }
9
+
10
+ const EditorCanvas: FC<EditorCanvasProps> = ({ defaultValue, value, onChange }) => {
11
+ return (
12
+ <TextArea
13
+ defaultValue={defaultValue}
14
+ onChange={(e) => {
15
+ onChange?.(e.target.value);
16
+ }}
17
+ style={{
18
+ cursor: 'text',
19
+ maxHeight: '80vh',
20
+ minHeight: '50vh',
21
+ overflowY: 'auto',
22
+ padding: 16,
23
+ }}
24
+ value={value}
25
+ variant={'borderless'}
26
+ />
27
+ );
28
+ };
29
+
30
+ export default EditorCanvas;
@@ -0,0 +1,139 @@
1
+ import { HotkeyEnum, type IEditor, getHotkeyById } from '@lobehub/editor';
2
+ import { useEditorState } from '@lobehub/editor/react';
3
+ import {
4
+ ChatInputActionBar,
5
+ ChatInputActions,
6
+ type ChatInputActionsProps,
7
+ } from '@lobehub/editor/react';
8
+ import { cssVar } from 'antd-style';
9
+ import {
10
+ BoldIcon,
11
+ CodeXmlIcon,
12
+ ItalicIcon,
13
+ ListIcon,
14
+ ListOrderedIcon,
15
+ ListTodoIcon,
16
+ MessageSquareQuote,
17
+ SigmaIcon,
18
+ SquareDashedBottomCodeIcon,
19
+ StrikethroughIcon,
20
+ UnderlineIcon,
21
+ } from 'lucide-react';
22
+ import { memo, useMemo } from 'react';
23
+ import { useTranslation } from 'react-i18next';
24
+
25
+ const TypoBar = memo<{ editor?: IEditor }>(({ editor }) => {
26
+ const { t } = useTranslation('editor');
27
+ const editorState = useEditorState(editor);
28
+
29
+ const items: ChatInputActionsProps['items'] = useMemo(
30
+ () =>
31
+ [
32
+ {
33
+ active: editorState.isBold,
34
+ icon: BoldIcon,
35
+ key: 'bold',
36
+ label: t('typobar.bold'),
37
+ onClick: editorState.bold,
38
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Bold).keys },
39
+ },
40
+ {
41
+ active: editorState.isItalic,
42
+ icon: ItalicIcon,
43
+ key: 'italic',
44
+ label: t('typobar.italic'),
45
+ onClick: editorState.italic,
46
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Italic).keys },
47
+ },
48
+ {
49
+ active: editorState.isUnderline,
50
+ icon: UnderlineIcon,
51
+ key: 'underline',
52
+ label: t('typobar.underline'),
53
+ onClick: editorState.underline,
54
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Underline).keys },
55
+ },
56
+ {
57
+ active: editorState.isStrikethrough,
58
+ icon: StrikethroughIcon,
59
+ key: 'strikethrough',
60
+ label: t('typobar.strikethrough'),
61
+ onClick: editorState.strikethrough,
62
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.Strikethrough).keys },
63
+ },
64
+ {
65
+ type: 'divider',
66
+ },
67
+
68
+ {
69
+ icon: ListIcon,
70
+ key: 'bulletList',
71
+ label: t('typobar.bulletList'),
72
+ onClick: editorState.bulletList,
73
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.BulletList).keys },
74
+ },
75
+ {
76
+ icon: ListOrderedIcon,
77
+ key: 'numberlist',
78
+ label: t('typobar.numberList'),
79
+ onClick: editorState.numberList,
80
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.NumberList).keys },
81
+ },
82
+ {
83
+ icon: ListTodoIcon,
84
+ key: 'tasklist',
85
+ label: t('typobar.taskList'),
86
+ onClick: editorState.checkList,
87
+ },
88
+ {
89
+ type: 'divider',
90
+ },
91
+ {
92
+ active: editorState.isBlockquote,
93
+ icon: MessageSquareQuote,
94
+ key: 'blockquote',
95
+ label: t('typobar.blockquote'),
96
+ onClick: editorState.blockquote,
97
+ },
98
+ {
99
+ type: 'divider',
100
+ },
101
+ {
102
+ icon: SigmaIcon,
103
+ key: 'math',
104
+ label: t('typobar.tex'),
105
+ onClick: editorState.insertMath,
106
+ },
107
+ {
108
+ active: editorState.isCode,
109
+ icon: CodeXmlIcon,
110
+ key: 'code',
111
+ label: t('typobar.code'),
112
+ onClick: editorState.code,
113
+ tooltipProps: { hotkey: getHotkeyById(HotkeyEnum.CodeInline).keys },
114
+ },
115
+ {
116
+ icon: SquareDashedBottomCodeIcon,
117
+ key: 'codeblock',
118
+ label: t('typobar.codeblock'),
119
+ onClick: editorState.codeblock,
120
+ },
121
+ ].filter(Boolean) as ChatInputActionsProps['items'],
122
+ [editorState],
123
+ );
124
+
125
+ return (
126
+ <ChatInputActionBar
127
+ left={<ChatInputActions items={items} />}
128
+ style={{
129
+ background: cssVar.colorFillQuaternary,
130
+ borderTopLeftRadius: 8,
131
+ borderTopRightRadius: 8,
132
+ }}
133
+ />
134
+ );
135
+ });
136
+
137
+ TypoBar.displayName = 'TypoBar';
138
+
139
+ export default TypoBar;
@@ -3,7 +3,11 @@ import { Modal, ModalProps, createRawModal } from '@lobehub/ui';
3
3
  import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
- import { EditorCanvas } from '@/features/EditorCanvas';
6
+ import { useUserStore } from '@/store/user';
7
+ import { labPreferSelectors } from '@/store/user/selectors';
8
+
9
+ import EditorCanvas from './EditorCanvas';
10
+ import TextareCanvas from './TextArea';
7
11
 
8
12
  interface EditorModalProps extends ModalProps {
9
13
  onConfirm?: (value: string) => Promise<void>;
@@ -13,7 +17,8 @@ interface EditorModalProps extends ModalProps {
13
17
  export const EditorModal = memo<EditorModalProps>(({ value, onConfirm, ...rest }) => {
14
18
  const [confirmLoading, setConfirmLoading] = useState(false);
15
19
  const { t } = useTranslation('common');
16
-
20
+ const [v, setV] = useState(value);
21
+ const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
17
22
  const editor = useEditor();
18
23
 
19
24
  return (
@@ -25,12 +30,13 @@ export const EditorModal = memo<EditorModalProps>(({ value, onConfirm, ...rest }
25
30
  okText={t('ok')}
26
31
  onOk={async () => {
27
32
  setConfirmLoading(true);
28
- try {
29
- await onConfirm?.((editor?.getDocument('markdown') as unknown as string) || '');
30
- } catch (e) {
31
- console.error('EditorModal onOk error:', e);
32
- onConfirm?.(value || '');
33
+ let finalValue;
34
+ if (enableRichRender) {
35
+ finalValue = editor?.getDocument('markdown') as unknown as string;
36
+ } else {
37
+ finalValue = v;
33
38
  }
39
+ await onConfirm?.(finalValue || '');
34
40
  setConfirmLoading(false);
35
41
  }}
36
42
  styles={{
@@ -43,7 +49,11 @@ export const EditorModal = memo<EditorModalProps>(({ value, onConfirm, ...rest }
43
49
  width={'min(90vw, 920px)'}
44
50
  {...rest}
45
51
  >
46
- <EditorCanvas editor={editor} editorData={{ content: value }} />
52
+ {enableRichRender ? (
53
+ <EditorCanvas defaultValue={value} editor={editor} />
54
+ ) : (
55
+ <TextareCanvas defaultValue={value} onChange={(v) => setV(v)} value={v} />
56
+ )}
47
57
  </Modal>
48
58
  );
49
59
  });