@lobehub/lobehub 2.0.0-next.277 → 2.0.0-next.279
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/.cursor/rules/db-migrations.mdc +1 -1
- package/.cursor/rules/debug-usage.mdc +7 -5
- package/.cursor/rules/desktop-controller-tests.mdc +2 -1
- package/.cursor/rules/desktop-feature-implementation.mdc +9 -5
- package/.cursor/rules/desktop-local-tools-implement.mdc +67 -66
- package/.cursor/rules/desktop-menu-configuration.mdc +21 -9
- package/.cursor/rules/desktop-window-management.mdc +17 -2
- package/.cursor/rules/drizzle-schema-style-guide.mdc +6 -6
- package/.cursor/rules/hotkey.mdc +1 -0
- package/.cursor/rules/i18n.mdc +1 -0
- package/.cursor/rules/project-structure.mdc +16 -3
- package/.cursor/rules/react.mdc +17 -5
- package/.cursor/rules/recent-data-usage.mdc +2 -1
- package/.cursor/rules/testing-guide/testing-guide.mdc +262 -238
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +1 -1
- package/.cursor/rules/zustand-action-patterns.mdc +1 -1
- package/.cursor/rules/zustand-slice-organization.mdc +4 -4
- package/CHANGELOG.md +51 -0
- package/CLAUDE.md +1 -1
- package/GEMINI.md +1 -1
- package/changelog/v1.json +14 -0
- package/docs/development/database-schema.dbml +16 -0
- package/locales/en-US/chat.json +24 -0
- package/locales/zh-CN/chat.json +24 -0
- package/package.json +1 -1
- package/packages/business/const/src/index.ts +3 -0
- package/packages/database/migrations/0069_add_topic_shares_table.sql +22 -0
- package/packages/database/migrations/meta/0069_snapshot.json +9704 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/models/__tests__/topicShare.test.ts +318 -0
- package/packages/database/src/models/topicShare.ts +177 -0
- package/packages/database/src/schemas/topic.ts +44 -2
- package/packages/types/src/conversation.ts +5 -0
- package/packages/types/src/topic/topic.ts +46 -0
- package/src/app/[variants]/(main)/agent/features/Conversation/Header/ShareButton/index.tsx +24 -9
- package/src/app/[variants]/(main)/agent/features/Conversation/ThreadHydration.tsx +2 -1
- package/src/app/[variants]/(main)/agent/features/Portal/_layout/Mobile.tsx +3 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/GroupConfig/GroupMember.tsx +3 -2
- package/src/app/[variants]/(main)/group/features/Conversation/Header/ShareButton/index.tsx +26 -9
- package/src/app/[variants]/(main)/group/features/Conversation/ThreadHydration.tsx +2 -1
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +3 -3
- package/src/app/[variants]/(main)/group/profile/features/MemberProfile/AgentTool.tsx +4 -1
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/AspectRatioSelect/index.tsx +1 -2
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ImageNum.tsx +54 -173
- package/src/app/[variants]/(main)/image/_layout/ConfigPanel/components/ResolutionSelect.tsx +22 -67
- package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +18 -0
- package/src/app/[variants]/router/desktopRouter.config.tsx +18 -0
- package/src/app/[variants]/share/t/[id]/SharedMessageList.tsx +54 -0
- package/src/app/[variants]/share/t/[id]/_layout/index.tsx +170 -0
- package/src/app/[variants]/share/t/[id]/features/Portal/index.tsx +66 -0
- package/src/app/[variants]/share/t/[id]/index.tsx +112 -0
- package/src/app/robots.tsx +1 -1
- package/src/business/client/BusinessMobileRoutes.tsx +1 -1
- package/src/features/Conversation/ChatList/index.tsx +17 -6
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Render/index.tsx +8 -4
- package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +15 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tools.tsx +3 -1
- package/src/features/Conversation/Messages/AssistantGroup/components/ContentBlock.tsx +3 -2
- package/src/features/Conversation/Messages/AssistantGroup/components/GroupItem.tsx +2 -2
- package/src/features/Conversation/Messages/Supervisor/components/ContentBlock.tsx +25 -26
- package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +4 -2
- package/src/features/Conversation/Messages/Tool/Tool/index.tsx +16 -12
- package/src/features/Conversation/Messages/Tool/index.tsx +20 -11
- package/src/features/Conversation/Messages/index.tsx +1 -1
- package/src/features/Conversation/store/slices/data/action.test.ts +42 -0
- package/src/features/Conversation/store/slices/data/action.ts +4 -2
- package/src/features/Portal/GroupThread/Header/index.tsx +2 -2
- package/src/features/Portal/MessageDetail/Body/index.tsx +3 -3
- package/src/features/Portal/components/Header.tsx +3 -3
- package/src/features/ProfileEditor/AgentTool.tsx +50 -19
- package/src/features/SharePopover/index.tsx +215 -0
- package/src/features/SharePopover/style.ts +10 -0
- package/src/hooks/useNavigateToAgent.ts +3 -3
- package/src/libs/next/proxy/define-config.ts +4 -1
- package/src/locales/default/chat.ts +26 -0
- package/src/proxy.ts +1 -0
- package/src/server/routers/lambda/__tests__/message.test.ts +152 -0
- package/src/server/routers/lambda/__tests__/share.test.ts +227 -0
- package/src/server/routers/lambda/__tests__/topic.test.ts +174 -0
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/message.ts +37 -4
- package/src/server/routers/lambda/share.ts +55 -0
- package/src/server/routers/lambda/topic.ts +45 -0
- package/src/services/message/index.ts +1 -0
- package/src/services/topic/index.ts +16 -0
- package/src/store/chat/slices/portal/action.test.ts +0 -41
- package/src/store/chat/slices/portal/action.ts +0 -25
- package/src/store/chat/slices/thread/action.test.ts +10 -6
- package/src/store/chat/slices/thread/action.ts +10 -3
- package/src/app/[variants]/(main)/group/features/Portal/features/Portal.tsx +0 -105
- package/src/app/[variants]/(main)/group/features/Portal/features/PortalPanel.tsx +0 -23
|
@@ -268,45 +268,4 @@ describe('chatDockSlice', () => {
|
|
|
268
268
|
});
|
|
269
269
|
});
|
|
270
270
|
|
|
271
|
-
describe('toggleDock', () => {
|
|
272
|
-
it('should toggle dock state when no argument is provided', () => {
|
|
273
|
-
const { result } = renderHook(() => useChatStore());
|
|
274
|
-
|
|
275
|
-
expect(result.current.showPortal).toBe(false);
|
|
276
|
-
|
|
277
|
-
act(() => {
|
|
278
|
-
result.current.togglePortal();
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
expect(result.current.showPortal).toBe(true);
|
|
282
|
-
|
|
283
|
-
act(() => {
|
|
284
|
-
result.current.togglePortal();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
expect(result.current.showPortal).toBe(false);
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
it('should set dock state to the provided value', () => {
|
|
291
|
-
const { result } = renderHook(() => useChatStore());
|
|
292
|
-
|
|
293
|
-
act(() => {
|
|
294
|
-
result.current.togglePortal(true);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
expect(result.current.showPortal).toBe(true);
|
|
298
|
-
|
|
299
|
-
act(() => {
|
|
300
|
-
result.current.togglePortal(false);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
expect(result.current.showPortal).toBe(false);
|
|
304
|
-
|
|
305
|
-
act(() => {
|
|
306
|
-
result.current.togglePortal(true);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
expect(result.current.showPortal).toBe(true);
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
271
|
});
|
|
@@ -28,7 +28,6 @@ export interface ChatPortalAction {
|
|
|
28
28
|
pushPortalView: (view: PortalViewData) => void;
|
|
29
29
|
replacePortalView: (view: PortalViewData) => void;
|
|
30
30
|
toggleNotebook: (open?: boolean) => void;
|
|
31
|
-
togglePortal: (open?: boolean) => void;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
// Helper to get current view type from stack
|
|
@@ -222,28 +221,4 @@ pushPortalView: (view) => {
|
|
|
222
221
|
get().closeNotebook();
|
|
223
222
|
}
|
|
224
223
|
},
|
|
225
|
-
|
|
226
|
-
togglePortal: (open) => {
|
|
227
|
-
const nextOpen = open === undefined ? !get().showPortal : open;
|
|
228
|
-
|
|
229
|
-
if (!nextOpen) {
|
|
230
|
-
// When closing, clear the stack
|
|
231
|
-
set({ portalStack: [], showPortal: false }, false, 'togglePortal/close');
|
|
232
|
-
} else {
|
|
233
|
-
// When opening, if stack is empty, push Home view
|
|
234
|
-
const { portalStack } = get();
|
|
235
|
-
if (portalStack.length === 0) {
|
|
236
|
-
set(
|
|
237
|
-
{
|
|
238
|
-
portalStack: [{ type: PortalViewType.Home }],
|
|
239
|
-
showPortal: true,
|
|
240
|
-
},
|
|
241
|
-
false,
|
|
242
|
-
'togglePortal/openHome',
|
|
243
|
-
);
|
|
244
|
-
} else {
|
|
245
|
-
set({ showPortal: true }, false, 'togglePortal/open');
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
224
|
});
|
|
@@ -141,7 +141,7 @@ describe('thread action', () => {
|
|
|
141
141
|
describe('openThreadCreator', () => {
|
|
142
142
|
it('should set thread creator state and open portal', () => {
|
|
143
143
|
const { result } = renderHook(() => useChatStore());
|
|
144
|
-
const
|
|
144
|
+
const pushPortalViewSpy = vi.spyOn(result.current, 'pushPortalView');
|
|
145
145
|
|
|
146
146
|
act(() => {
|
|
147
147
|
result.current.openThreadCreator('message-id');
|
|
@@ -150,14 +150,14 @@ describe('thread action', () => {
|
|
|
150
150
|
expect(result.current.threadStartMessageId).toBe('message-id');
|
|
151
151
|
expect(result.current.portalThreadId).toBeUndefined();
|
|
152
152
|
expect(result.current.startToForkThread).toBe(true);
|
|
153
|
-
expect(
|
|
153
|
+
expect(pushPortalViewSpy).toHaveBeenCalledWith({ type: 'thread', startMessageId: 'message-id' });
|
|
154
154
|
});
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
describe('openThreadInPortal', () => {
|
|
158
158
|
it('should set portal thread state and open portal', () => {
|
|
159
159
|
const { result } = renderHook(() => useChatStore());
|
|
160
|
-
const
|
|
160
|
+
const pushPortalViewSpy = vi.spyOn(result.current, 'pushPortalView');
|
|
161
161
|
|
|
162
162
|
act(() => {
|
|
163
163
|
result.current.openThreadInPortal('thread-id', 'source-message-id');
|
|
@@ -166,7 +166,11 @@ describe('thread action', () => {
|
|
|
166
166
|
expect(result.current.portalThreadId).toBe('thread-id');
|
|
167
167
|
expect(result.current.threadStartMessageId).toBe('source-message-id');
|
|
168
168
|
expect(result.current.startToForkThread).toBe(false);
|
|
169
|
-
expect(
|
|
169
|
+
expect(pushPortalViewSpy).toHaveBeenCalledWith({
|
|
170
|
+
type: 'thread',
|
|
171
|
+
threadId: 'thread-id',
|
|
172
|
+
startMessageId: 'source-message-id',
|
|
173
|
+
});
|
|
170
174
|
});
|
|
171
175
|
});
|
|
172
176
|
|
|
@@ -182,7 +186,7 @@ describe('thread action', () => {
|
|
|
182
186
|
});
|
|
183
187
|
});
|
|
184
188
|
|
|
185
|
-
const
|
|
189
|
+
const clearPortalStackSpy = vi.spyOn(result.current, 'clearPortalStack');
|
|
186
190
|
|
|
187
191
|
act(() => {
|
|
188
192
|
result.current.closeThreadPortal();
|
|
@@ -191,7 +195,7 @@ describe('thread action', () => {
|
|
|
191
195
|
expect(result.current.portalThreadId).toBeUndefined();
|
|
192
196
|
expect(result.current.threadStartMessageId).toBeUndefined();
|
|
193
197
|
expect(result.current.startToForkThread).toBeUndefined();
|
|
194
|
-
expect(
|
|
198
|
+
expect(clearPortalStackSpy).toHaveBeenCalled();
|
|
195
199
|
});
|
|
196
200
|
});
|
|
197
201
|
|
|
@@ -19,6 +19,7 @@ import { merge } from '@/utils/merge';
|
|
|
19
19
|
import { setNamespace } from '@/utils/storeDebug';
|
|
20
20
|
|
|
21
21
|
import { displayMessageSelectors } from '../message/selectors';
|
|
22
|
+
import { PortalViewType } from '../portal/initialState';
|
|
22
23
|
import { type ThreadDispatch, threadReducer } from './reducer';
|
|
23
24
|
import { genParentMessages } from './selectors';
|
|
24
25
|
|
|
@@ -88,7 +89,8 @@ export const chatThreadMessage: StateCreator<
|
|
|
88
89
|
false,
|
|
89
90
|
'openThreadCreator',
|
|
90
91
|
);
|
|
91
|
-
|
|
92
|
+
// Push Thread view to portal stack instead of togglePortal
|
|
93
|
+
get().pushPortalView({ type: PortalViewType.Thread, startMessageId: messageId });
|
|
92
94
|
},
|
|
93
95
|
openThreadInPortal: (threadId, sourceMessageId) => {
|
|
94
96
|
set(
|
|
@@ -96,7 +98,12 @@ export const chatThreadMessage: StateCreator<
|
|
|
96
98
|
false,
|
|
97
99
|
'openThreadInPortal',
|
|
98
100
|
);
|
|
99
|
-
|
|
101
|
+
// Push Thread view to portal stack with threadId
|
|
102
|
+
get().pushPortalView({
|
|
103
|
+
type: PortalViewType.Thread,
|
|
104
|
+
threadId,
|
|
105
|
+
startMessageId: sourceMessageId ?? undefined,
|
|
106
|
+
});
|
|
100
107
|
},
|
|
101
108
|
|
|
102
109
|
closeThreadPortal: () => {
|
|
@@ -105,7 +112,7 @@ export const chatThreadMessage: StateCreator<
|
|
|
105
112
|
false,
|
|
106
113
|
'closeThreadPortal',
|
|
107
114
|
);
|
|
108
|
-
get().
|
|
115
|
+
get().clearPortalStack();
|
|
109
116
|
},
|
|
110
117
|
createThread: async ({ message, sourceMessageId, topicId, type }) => {
|
|
111
118
|
set({ isCreatingThread: true }, false, n('creatingThread/start'));
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { DraggablePanel, DraggablePanelContainer, type DraggablePanelProps } from '@lobehub/ui';
|
|
4
|
-
import { Flexbox } from '@lobehub/ui';
|
|
5
|
-
import { createStaticStyles, useResponsive } from 'antd-style';
|
|
6
|
-
import isEqual from 'fast-deep-equal';
|
|
7
|
-
import { Activity, type PropsWithChildren, memo, useState } from 'react';
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
CHAT_PORTAL_MAX_WIDTH,
|
|
11
|
-
CHAT_PORTAL_TOOL_UI_WIDTH,
|
|
12
|
-
CHAT_PORTAL_WIDTH,
|
|
13
|
-
} from '@/const/layoutTokens';
|
|
14
|
-
import { useChatStore } from '@/store/chat';
|
|
15
|
-
import { chatPortalSelectors, portalThreadSelectors } from '@/store/chat/selectors';
|
|
16
|
-
import { useGlobalStore } from '@/store/global';
|
|
17
|
-
import { systemStatusSelectors } from '@/store/global/selectors';
|
|
18
|
-
|
|
19
|
-
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
20
|
-
content: css`
|
|
21
|
-
display: flex;
|
|
22
|
-
flex-direction: column;
|
|
23
|
-
height: 100% !important;
|
|
24
|
-
`,
|
|
25
|
-
drawer: css`
|
|
26
|
-
z-index: 10;
|
|
27
|
-
height: 100%;
|
|
28
|
-
background: ${cssVar.colorBgContainer};
|
|
29
|
-
`,
|
|
30
|
-
panel: css`
|
|
31
|
-
overflow: hidden;
|
|
32
|
-
height: 100%;
|
|
33
|
-
background: ${cssVar.colorBgContainer};
|
|
34
|
-
`,
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
const PortalPanel = memo(({ children }: PropsWithChildren) => {
|
|
38
|
-
const { md = true } = useResponsive();
|
|
39
|
-
|
|
40
|
-
const [showPortal, showToolUI, showArtifactUI, showThread] = useChatStore((s) => [
|
|
41
|
-
chatPortalSelectors.showPortal(s),
|
|
42
|
-
chatPortalSelectors.showPluginUI(s),
|
|
43
|
-
chatPortalSelectors.showArtifactUI(s),
|
|
44
|
-
portalThreadSelectors.showThread(s),
|
|
45
|
-
]);
|
|
46
|
-
|
|
47
|
-
const [portalWidth, updateSystemStatus] = useGlobalStore((s) => [
|
|
48
|
-
systemStatusSelectors.portalWidth(s),
|
|
49
|
-
s.updateSystemStatus,
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
const [tmpWidth, setWidth] = useState(portalWidth);
|
|
53
|
-
if (tmpWidth !== portalWidth) setWidth(portalWidth);
|
|
54
|
-
|
|
55
|
-
const handleSizeChange: DraggablePanelProps['onSizeChange'] = (_, size) => {
|
|
56
|
-
if (!size) return;
|
|
57
|
-
const nextWidth = typeof size.width === 'string' ? Number.parseInt(size.width) : size.width;
|
|
58
|
-
if (!nextWidth) return;
|
|
59
|
-
|
|
60
|
-
if (isEqual(nextWidth, portalWidth)) return;
|
|
61
|
-
setWidth(nextWidth);
|
|
62
|
-
updateSystemStatus({ portalWidth: nextWidth });
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<DraggablePanel
|
|
67
|
-
className={styles.drawer}
|
|
68
|
-
classNames={{
|
|
69
|
-
content: styles.content,
|
|
70
|
-
}}
|
|
71
|
-
defaultSize={{ width: tmpWidth }}
|
|
72
|
-
expand={showPortal}
|
|
73
|
-
maxWidth={CHAT_PORTAL_MAX_WIDTH}
|
|
74
|
-
minWidth={
|
|
75
|
-
(showArtifactUI || showToolUI || showThread) && md
|
|
76
|
-
? CHAT_PORTAL_TOOL_UI_WIDTH
|
|
77
|
-
: CHAT_PORTAL_WIDTH
|
|
78
|
-
}
|
|
79
|
-
mode={md ? 'fixed' : 'float'}
|
|
80
|
-
onSizeChange={handleSizeChange}
|
|
81
|
-
placement={'right'}
|
|
82
|
-
showHandleWhenCollapsed={false}
|
|
83
|
-
showHandleWideArea={false}
|
|
84
|
-
size={{ height: '100%', width: portalWidth }}
|
|
85
|
-
styles={{
|
|
86
|
-
handle: { display: 'none' },
|
|
87
|
-
}}
|
|
88
|
-
>
|
|
89
|
-
<DraggablePanelContainer
|
|
90
|
-
style={{
|
|
91
|
-
flex: 'none',
|
|
92
|
-
height: '100%',
|
|
93
|
-
maxHeight: '100vh',
|
|
94
|
-
minWidth: CHAT_PORTAL_WIDTH,
|
|
95
|
-
}}
|
|
96
|
-
>
|
|
97
|
-
<Activity mode={showPortal ? 'visible' : 'hidden'} name="GroupPortal">
|
|
98
|
-
<Flexbox className={styles.panel}>{children}</Flexbox>
|
|
99
|
-
</Activity>
|
|
100
|
-
</DraggablePanelContainer>
|
|
101
|
-
</DraggablePanel>
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
export default PortalPanel;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Suspense, memo } from 'react';
|
|
2
|
-
|
|
3
|
-
import DesktopLayout from '@/app/[variants]/(main)/group/features/Portal/_layout/Desktop';
|
|
4
|
-
import MobileLayout from '@/app/[variants]/(main)/group/features/Portal/_layout/Mobile';
|
|
5
|
-
import Loading from '@/components/Loading/BrandTextLoading';
|
|
6
|
-
|
|
7
|
-
interface PortalPanelProps {
|
|
8
|
-
mobile?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const PortalPanel = memo<PortalPanelProps>(({ mobile }) => {
|
|
12
|
-
const Layout = mobile ? MobileLayout : DesktopLayout;
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<Suspense fallback={<Loading debugId="PortalPanel" />}>
|
|
16
|
-
<Layout />
|
|
17
|
-
</Suspense>
|
|
18
|
-
);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
PortalPanel.displayName = 'PortalPanel';
|
|
22
|
-
|
|
23
|
-
export default PortalPanel;
|