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

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,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.283](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.282...v2.0.0-next.283)
6
+
7
+ <sup>Released on **2026-01-14**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Fix UI issues with tooltip wrapping and dropdown type.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Fix UI issues with tooltip wrapping and dropdown type, closes [#11495](https://github.com/lobehub/lobe-chat/issues/11495) ([9d90eba](https://github.com/lobehub/lobe-chat/commit/9d90eba))
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
+
30
+ ## [Version 2.0.0-next.282](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.281...v2.0.0-next.282)
31
+
32
+ <sup>Released on **2026-01-14**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Update readFile content.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Update readFile content, closes [#11485](https://github.com/lobehub/lobe-chat/issues/11485) ([050499b](https://github.com/lobehub/lobe-chat/commit/050499b))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ## [Version 2.0.0-next.281](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.280...v2.0.0-next.281)
6
56
 
7
57
  <sup>Released on **2026-01-14**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Fix UI issues with tooltip wrapping and dropdown type."
6
+ ]
7
+ },
8
+ "date": "2026-01-14",
9
+ "version": "2.0.0-next.283"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Update readFile content."
15
+ ]
16
+ },
17
+ "date": "2026-01-14",
18
+ "version": "2.0.0-next.282"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "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.283",
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",
@@ -206,7 +206,7 @@
206
206
  "@lobehub/icons": "^4.0.2",
207
207
  "@lobehub/market-sdk": "0.28.1",
208
208
  "@lobehub/tts": "^4.0.2",
209
- "@lobehub/ui": "^4.18.0",
209
+ "@lobehub/ui": "^4.19.0",
210
210
  "@modelcontextprotocol/sdk": "^1.25.1",
211
211
  "@neondatabase/serverless": "^1.0.2",
212
212
  "@next/third-parties": "^16.1.1",
@@ -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
 
@@ -1,13 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { type DropdownMenuCheckboxItem } from '@lobehub/ui';
3
+ import type { DropdownItem } from '@lobehub/ui/es/DropdownMenu/type';
4
4
  import { useMemo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { useGlobalStore } from '@/store/global';
8
8
  import { systemStatusSelectors } from '@/store/global/selectors';
9
9
 
10
- export const useMenu = (): { menuItems: DropdownMenuCheckboxItem[] } => {
10
+ export const useMenu = (): { menuItems: DropdownItem[] } => {
11
11
  const { t } = useTranslation('chat');
12
12
 
13
13
  const [wideScreen, toggleWideScreen] = useGlobalStore((s) => [
@@ -15,14 +15,14 @@ export const useMenu = (): { menuItems: DropdownMenuCheckboxItem[] } => {
15
15
  s.toggleWideScreen,
16
16
  ]);
17
17
 
18
- const menuItems = useMemo<DropdownMenuCheckboxItem[]>(
18
+ const menuItems = useMemo<DropdownItem[]>(
19
19
  () => [
20
20
  {
21
21
  checked: wideScreen,
22
22
  key: 'full-width',
23
23
  label: t('viewMode.fullWidth'),
24
24
  onCheckedChange: toggleWideScreen,
25
- type: 'checkbox',
25
+ type: 'switch',
26
26
  },
27
27
  ],
28
28
  [t, wideScreen, toggleWideScreen],
@@ -74,6 +74,7 @@ const ProviderList = memo(() => {
74
74
  : record.model?.maxDimension
75
75
  ? formatTokenNumber(record.model.maxDimension)
76
76
  : '--',
77
+ showSorterTooltip: false,
77
78
  sorter: (a, b) => {
78
79
  const aValue = a.model?.maxOutput || a.model?.maxDimension || 0;
79
80
  const bValue = b.model?.maxOutput || b.model?.maxDimension || 0;
@@ -81,7 +82,7 @@ const ProviderList = memo(() => {
81
82
  },
82
83
  title: (
83
84
  <Tooltip title={t('models.providerInfo.maxOutputTooltip')}>
84
- {t('models.providerInfo.maxOutput')}
85
+ <span>{t('models.providerInfo.maxOutput')}</span>
85
86
  </Tooltip>
86
87
  ),
87
88
  width: 120,
@@ -95,6 +96,7 @@ const ProviderList = memo(() => {
95
96
  ? '$' + formatPriceByCurrency(inputRate, record.model.pricing?.currency)
96
97
  : '--';
97
98
  },
99
+ showSorterTooltip: false,
98
100
  sorter: (a, b) => {
99
101
  const aRate = getTextInputUnitRate(a.model?.pricing) || 0;
100
102
  const bRate = getTextInputUnitRate(b.model?.pricing) || 0;
@@ -102,7 +104,7 @@ const ProviderList = memo(() => {
102
104
  },
103
105
  title: (
104
106
  <Tooltip title={t('models.providerInfo.inputTooltip')}>
105
- {t('models.providerInfo.input')}
107
+ <span>{t('models.providerInfo.input')}</span>
106
108
  </Tooltip>
107
109
  ),
108
110
  width: 100,
@@ -116,6 +118,7 @@ const ProviderList = memo(() => {
116
118
  ? '$' + formatPriceByCurrency(outputRate, record.model.pricing?.currency)
117
119
  : '--';
118
120
  },
121
+ showSorterTooltip: false,
119
122
  sorter: (a, b) => {
120
123
  const aRate = getTextOutputUnitRate(a.model?.pricing) || 0;
121
124
  const bRate = getTextOutputUnitRate(b.model?.pricing) || 0;
@@ -123,7 +126,7 @@ const ProviderList = memo(() => {
123
126
  },
124
127
  title: (
125
128
  <Tooltip title={t('models.providerInfo.outputTooltip')}>
126
- {t('models.providerInfo.output')}
129
+ <span>{t('models.providerInfo.output')}</span>
127
130
  </Tooltip>
128
131
  ),
129
132
  width: 100,
@@ -72,10 +72,11 @@ const ModelList = memo(() => {
72
72
  key: 'maxOutput',
73
73
  render: (_, record) =>
74
74
  record.maxOutput ? formatTokenNumber(record.maxOutput) : '--',
75
+ showSorterTooltip: false,
75
76
  sorter: (a, b) => (a.maxOutput || 0) - (b.maxOutput || 0),
76
77
  title: (
77
78
  <Tooltip title={t('models.providerInfo.maxOutputTooltip')}>
78
- {t('models.providerInfo.maxOutput')}
79
+ <span>{t('models.providerInfo.maxOutput')}</span>
79
80
  </Tooltip>
80
81
  ),
81
82
  width: 120,
@@ -89,6 +90,7 @@ const ModelList = memo(() => {
89
90
  ? '$' + formatPriceByCurrency(inputRate, record.pricing?.currency)
90
91
  : '--';
91
92
  },
93
+ showSorterTooltip: false,
92
94
  sorter: (a, b) => {
93
95
  const aRate = getTextInputUnitRate(a.pricing) || 0;
94
96
  const bRate = getTextInputUnitRate(b.pricing) || 0;
@@ -96,7 +98,7 @@ const ModelList = memo(() => {
96
98
  },
97
99
  title: (
98
100
  <Tooltip title={t('models.providerInfo.inputTooltip')}>
99
- {t('models.providerInfo.input')}
101
+ <span>{t('models.providerInfo.input')}</span>
100
102
  </Tooltip>
101
103
  ),
102
104
  width: 100,
@@ -110,6 +112,7 @@ const ModelList = memo(() => {
110
112
  ? '$' + formatPriceByCurrency(outputRate, record.pricing?.currency)
111
113
  : '--';
112
114
  },
115
+ showSorterTooltip: false,
113
116
  sorter: (a, b) => {
114
117
  const aRate = getTextOutputUnitRate(a.pricing) || 0;
115
118
  const bRate = getTextOutputUnitRate(b.pricing) || 0;
@@ -117,7 +120,7 @@ const ModelList = memo(() => {
117
120
  },
118
121
  title: (
119
122
  <Tooltip title={t('models.providerInfo.outputTooltip')}>
120
- {t('models.providerInfo.output')}
123
+ <span>{t('models.providerInfo.output')}</span>
121
124
  </Tooltip>
122
125
  ),
123
126
  width: 100,
@@ -70,7 +70,7 @@ const ModelCard = memo<ModelCardProps>(({ pricing, id, provider, displayName })
70
70
  {
71
71
  label: (
72
72
  <Tooltip title={t('messages.modelCard.creditTooltip')}>
73
- {t('messages.modelCard.credit')}
73
+ <span>{t('messages.modelCard.credit')}</span>
74
74
  </Tooltip>
75
75
  ),
76
76
  value: 'credit',
@@ -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
  });
@@ -84,8 +84,7 @@ export const useMenu = (): { menuItems: any[] } => {
84
84
  key: 'full-width',
85
85
  label: t('viewMode.fullWidth', { ns: 'chat' }),
86
86
  onCheckedChange: toggleWideScreen,
87
-
88
- type: 'checkbox' as const,
87
+ type: 'switch' as const,
89
88
  },
90
89
  {
91
90
  type: 'divider' as const,