@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 +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx +20 -2
- package/packages/observability-otel/src/node.ts +40 -3
- package/src/features/EditorModal/EditorCanvas.tsx +62 -0
- package/src/features/EditorModal/TextArea.tsx +30 -0
- package/src/features/EditorModal/Typobar.tsx +139 -0
- package/src/features/EditorModal/index.tsx +18 -8
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
|
+
[](#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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
});
|