@lobehub/lobehub 2.0.0-next.330 → 2.0.0-next.331
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/src/app/[variants]/(main)/group/profile/features/GroupProfile/index.tsx +12 -6
- package/src/app/[variants]/(main)/group/profile/features/MemberProfile/index.tsx +13 -7
- package/src/features/EditorCanvas/EditorCanvas.tsx +10 -2
- package/src/features/EditorCanvas/EditorDataMode.tsx +27 -18
- package/src/store/agentGroup/action.ts +13 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.331](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.330...v2.0.0-next.331)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-21**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Slove the agent group editor not focus in editdata area.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Slove the agent group editor not focus in editdata area, closes [#11677](https://github.com/lobehub/lobe-chat/issues/11677) ([9ac84e6](https://github.com/lobehub/lobe-chat/commit/9ac84e6))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.330](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.329...v2.0.0-next.330)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-21**</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.331",
|
|
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",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { Button, Flexbox } from '@lobehub/ui';
|
|
4
4
|
import { Divider } from 'antd';
|
|
5
5
|
import { PlayIcon } from 'lucide-react';
|
|
6
|
-
import { memo, useCallback } from 'react';
|
|
6
|
+
import { memo, useCallback, useMemo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import urlJoin from 'url-join';
|
|
9
9
|
|
|
@@ -43,6 +43,15 @@ const GroupProfile = memo(() => {
|
|
|
43
43
|
handleContentChange(saveContent);
|
|
44
44
|
}, [handleContentChange, saveContent]);
|
|
45
45
|
|
|
46
|
+
// Stabilize editorData object reference to prevent unnecessary re-renders
|
|
47
|
+
const editorData = useMemo(
|
|
48
|
+
() => ({
|
|
49
|
+
content: currentGroup?.content ?? undefined,
|
|
50
|
+
editorData: currentGroup?.editorData,
|
|
51
|
+
}),
|
|
52
|
+
[currentGroup?.content, currentGroup?.editorData],
|
|
53
|
+
);
|
|
54
|
+
|
|
46
55
|
return (
|
|
47
56
|
<>
|
|
48
57
|
<Flexbox
|
|
@@ -83,11 +92,8 @@ const GroupProfile = memo(() => {
|
|
|
83
92
|
{/* Group Content Editor */}
|
|
84
93
|
<EditorCanvas
|
|
85
94
|
editor={editor}
|
|
86
|
-
editorData={
|
|
87
|
-
|
|
88
|
-
editorData: currentGroup?.editorData,
|
|
89
|
-
}}
|
|
90
|
-
key={groupId}
|
|
95
|
+
editorData={editorData}
|
|
96
|
+
entityId={groupId}
|
|
91
97
|
onContentChange={onContentChange}
|
|
92
98
|
placeholder={t('group.profile.contentPlaceholder', { ns: 'chat' })}
|
|
93
99
|
/>
|
|
@@ -34,8 +34,8 @@ const MemberProfile = memo(() => {
|
|
|
34
34
|
const updateAgentConfigById = useAgentStore((s) => s.updateAgentConfigById);
|
|
35
35
|
|
|
36
36
|
const groupId = useAgentGroupStore(agentGroupSelectors.activeGroupId);
|
|
37
|
-
const currentGroup = useAgentGroupStore(agentGroupSelectors.currentGroup);
|
|
38
|
-
const currentGroupAgents = useAgentGroupStore(agentGroupSelectors.currentGroupAgents);
|
|
37
|
+
const currentGroup = useAgentGroupStore(agentGroupSelectors.currentGroup, isEqual);
|
|
38
|
+
const currentGroupAgents = useAgentGroupStore(agentGroupSelectors.currentGroupAgents, isEqual);
|
|
39
39
|
const router = useQueryRoute();
|
|
40
40
|
|
|
41
41
|
// Check if the current agent is the supervisor
|
|
@@ -47,6 +47,15 @@ const MemberProfile = memo(() => {
|
|
|
47
47
|
return agent ? !agent.isSupervisor && !agent.virtual : false;
|
|
48
48
|
}, [currentGroupAgents, agentId]);
|
|
49
49
|
|
|
50
|
+
// Stabilize editorData object reference to prevent unnecessary re-renders
|
|
51
|
+
const editorData = useMemo(
|
|
52
|
+
() => ({
|
|
53
|
+
content: config?.systemRole,
|
|
54
|
+
editorData: config?.editorData,
|
|
55
|
+
}),
|
|
56
|
+
[config?.systemRole, config?.editorData],
|
|
57
|
+
);
|
|
58
|
+
|
|
50
59
|
// Wrap updateAgentConfigById for saving editor content
|
|
51
60
|
const updateContent = useCallback(
|
|
52
61
|
async (payload: { content: string; editorData: Record<string, any> }) => {
|
|
@@ -136,11 +145,8 @@ const MemberProfile = memo(() => {
|
|
|
136
145
|
{/* Main Content: Prompt Editor */}
|
|
137
146
|
<EditorCanvas
|
|
138
147
|
editor={editor}
|
|
139
|
-
editorData={
|
|
140
|
-
|
|
141
|
-
editorData: config?.editorData,
|
|
142
|
-
}}
|
|
143
|
-
key={agentId}
|
|
148
|
+
editorData={editorData}
|
|
149
|
+
entityId={agentId}
|
|
144
150
|
onContentChange={onContentChange}
|
|
145
151
|
placeholder={
|
|
146
152
|
isSupervisor
|
|
@@ -38,6 +38,14 @@ export interface EditorCanvasProps {
|
|
|
38
38
|
editorData?: unknown;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Entity ID (e.g., agentId, groupId) to track which entity is being edited.
|
|
43
|
+
* When entityId changes, editor content will be reloaded.
|
|
44
|
+
* When entityId stays the same, editorData changes won't trigger reload.
|
|
45
|
+
* This prevents focus loss during auto-save and optimistic updates.
|
|
46
|
+
*/
|
|
47
|
+
entityId?: string;
|
|
48
|
+
|
|
41
49
|
/**
|
|
42
50
|
* Extra plugins to prepend to BASE_PLUGINS (e.g., ReactLiteXmlPlugin)
|
|
43
51
|
*/
|
|
@@ -113,7 +121,7 @@ export interface EditorCanvasWithEditorProps extends EditorCanvasProps {
|
|
|
113
121
|
* - AutoSave hint display (documentId mode)
|
|
114
122
|
*/
|
|
115
123
|
export const EditorCanvas = memo<EditorCanvasWithEditorProps>(
|
|
116
|
-
({ editor, documentId, editorData, ...props }) => {
|
|
124
|
+
({ editor, documentId, editorData, entityId, ...props }) => {
|
|
117
125
|
// documentId mode - fetch and render with loading/error states
|
|
118
126
|
if (documentId) {
|
|
119
127
|
return (
|
|
@@ -127,7 +135,7 @@ export const EditorCanvas = memo<EditorCanvasWithEditorProps>(
|
|
|
127
135
|
if (editorData) {
|
|
128
136
|
return (
|
|
129
137
|
<EditorErrorBoundary>
|
|
130
|
-
<EditorDataMode editor={editor} editorData={editorData} {...props} />
|
|
138
|
+
<EditorDataMode editor={editor} editorData={editorData} entityId={entityId} {...props} />
|
|
131
139
|
</EditorErrorBoundary>
|
|
132
140
|
);
|
|
133
141
|
}
|
|
@@ -10,6 +10,7 @@ import InternalEditor from './InternalEditor';
|
|
|
10
10
|
export interface EditorDataModeProps extends EditorCanvasProps {
|
|
11
11
|
editor: IEditor | undefined;
|
|
12
12
|
editorData: NonNullable<EditorCanvasProps['editorData']>;
|
|
13
|
+
entityId?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
const loadEditorContent = (
|
|
@@ -35,47 +36,55 @@ const loadEditorContent = (
|
|
|
35
36
|
* EditorCanvas with editorData mode - uses provided data directly
|
|
36
37
|
*/
|
|
37
38
|
const EditorDataMode = memo<EditorDataModeProps>(
|
|
38
|
-
({ editor, editorData, onContentChange, onInit, style, ...editorProps }) => {
|
|
39
|
+
({ editor, editorData, entityId, onContentChange, onInit, style, ...editorProps }) => {
|
|
39
40
|
const { t } = useTranslation('file');
|
|
40
41
|
const isEditorReadyRef = useRef(false);
|
|
41
|
-
// Track
|
|
42
|
-
const
|
|
42
|
+
// Track the current entityId to detect entity changes
|
|
43
|
+
const currentEntityIdRef = useRef<string | undefined>(undefined);
|
|
43
44
|
|
|
44
|
-
// Check if
|
|
45
|
-
|
|
45
|
+
// Check if we're editing a different entity
|
|
46
|
+
// When entityId is undefined, always consider it as "changed" (backward compatibility)
|
|
47
|
+
// When entityId is provided, check if it actually changed
|
|
48
|
+
const isEntityChanged = entityId === undefined || currentEntityIdRef.current !== entityId;
|
|
46
49
|
|
|
47
50
|
const handleInit = useCallback(
|
|
48
51
|
(editorInstance: IEditor) => {
|
|
49
52
|
isEditorReadyRef.current = true;
|
|
50
53
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
loadedContentRef.current = editorData.content;
|
|
56
|
-
}
|
|
57
|
-
} catch (err) {
|
|
58
|
-
console.error('[EditorCanvas] Failed to load content:', err);
|
|
54
|
+
// Always load content on init
|
|
55
|
+
try {
|
|
56
|
+
if (isEntityChanged && loadEditorContent(editorInstance, editorData)) {
|
|
57
|
+
currentEntityIdRef.current = entityId;
|
|
59
58
|
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
console.error('[EditorCanvas] Failed to load content:', err);
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
onInit?.(editorInstance);
|
|
63
64
|
},
|
|
64
|
-
[editorData,
|
|
65
|
+
[editorData, entityId, onInit],
|
|
65
66
|
);
|
|
66
67
|
|
|
67
|
-
// Load content when
|
|
68
|
+
// Load content only when entityId changes (switching to a different entity)
|
|
69
|
+
// Ignore editorData changes for the same entity to prevent focus loss during auto-save
|
|
68
70
|
useEffect(() => {
|
|
69
|
-
if (!editor || !isEditorReadyRef.current
|
|
71
|
+
if (!editor || !isEditorReadyRef.current) return;
|
|
70
72
|
|
|
73
|
+
// Only reload if entityId changed (switching entities)
|
|
74
|
+
if (!isEntityChanged) {
|
|
75
|
+
// Same entity - don't reload, user is still editing
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Different entity - load new content
|
|
71
80
|
try {
|
|
72
81
|
if (loadEditorContent(editor, editorData)) {
|
|
73
|
-
|
|
82
|
+
currentEntityIdRef.current = entityId;
|
|
74
83
|
}
|
|
75
84
|
} catch (err) {
|
|
76
85
|
console.error('[EditorCanvas] Failed to load content:', err);
|
|
77
86
|
}
|
|
78
|
-
}, [editor,
|
|
87
|
+
}, [editor, entityId, isEntityChanged, editorData]);
|
|
79
88
|
|
|
80
89
|
if (!editor) return null;
|
|
81
90
|
|
|
@@ -214,10 +214,21 @@ const chatGroupInternalSlice: StateCreator<
|
|
|
214
214
|
);
|
|
215
215
|
|
|
216
216
|
// Sync group agents to agentStore for builtin agent resolution (e.g., supervisor slug)
|
|
217
|
+
// Use smart merge: only overwrite if server data is newer to prevent race conditions
|
|
217
218
|
const agentStore = getAgentStoreState();
|
|
218
219
|
for (const agent of groupDetail.agents) {
|
|
219
|
-
|
|
220
|
-
|
|
220
|
+
const currentAgentInStore = agentStore.agentMap[agent.id];
|
|
221
|
+
|
|
222
|
+
// Only overwrite if:
|
|
223
|
+
// 1. Agent doesn't exist in store
|
|
224
|
+
// 2. Server data is newer than store data (based on updatedAt)
|
|
225
|
+
if (
|
|
226
|
+
!currentAgentInStore ||
|
|
227
|
+
new Date(agent.updatedAt) > new Date(currentAgentInStore.updatedAt || 0)
|
|
228
|
+
) {
|
|
229
|
+
// AgentGroupMember extends AgentItem which shares fields with LobeAgentConfig
|
|
230
|
+
agentStore.internal_dispatchAgentMap(agent.id, agent as any);
|
|
231
|
+
}
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
// Set activeAgentId to supervisor for correct model resolution in sendMessage
|