合并 AI 设计多会话客户端
整合 Enter 发送与多会话交互,保留服务端持久 Conversation Session,并补齐迁移、回归、Electron E2E 与 canonical 文档。
This commit is contained in:
@@ -376,8 +376,16 @@ describe('Sidebar project initialization flow', () => {
|
||||
const summary = {
|
||||
workspaceId: 'cloud-project',
|
||||
title: '云端概念设计',
|
||||
turnRevision: 0,
|
||||
viewRevision: 0,
|
||||
conversationCount: 1,
|
||||
phase: 'shaping',
|
||||
updatedAt: '2026-07-31T10:00:00Z',
|
||||
};
|
||||
const conversation = {
|
||||
conversationId: 'cloud-conversation',
|
||||
workspaceId: 'cloud-project',
|
||||
title: '主会话',
|
||||
turnRevision: 0,
|
||||
phase: 'shaping',
|
||||
brief: {
|
||||
version: 0,
|
||||
@@ -387,8 +395,21 @@ describe('Sidebar project initialization flow', () => {
|
||||
ready: false,
|
||||
missingDecision: '作品形式',
|
||||
},
|
||||
createdAt: '2026-07-31T10:00:00Z',
|
||||
updatedAt: '2026-07-31T10:00:00Z',
|
||||
messages: [],
|
||||
};
|
||||
const { messages: _messages, ...conversationSummary } = conversation;
|
||||
const secondConversation = {
|
||||
...conversation,
|
||||
conversationId: 'cloud-conversation-two',
|
||||
title: '第二会话',
|
||||
messages: [],
|
||||
createdAt: '2026-07-31T10:05:00Z',
|
||||
updatedAt: '2026-07-31T10:05:00Z',
|
||||
};
|
||||
const { messages: _secondMessages, ...secondConversationSummary } = secondConversation;
|
||||
let hasSecondConversation = false;
|
||||
const bootstrap = {
|
||||
capabilities: {
|
||||
conversation: true,
|
||||
@@ -403,15 +424,51 @@ describe('Sidebar project initialization flow', () => {
|
||||
return { success: true, data: bootstrap };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/cloud-project' && !init?.method) {
|
||||
return { success: true, data: { ...summary, messages: [] } };
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
...summary,
|
||||
conversationCount: hasSecondConversation ? 2 : 1,
|
||||
conversations: hasSecondConversation
|
||||
? [secondConversationSummary, conversationSummary]
|
||||
: [conversationSummary],
|
||||
},
|
||||
};
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/cloud-project/conversations/cloud-conversation') {
|
||||
return { success: true, data: conversation };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/cloud-project-two/conversations/cloud-conversation-two') {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
...conversation,
|
||||
workspaceId: 'cloud-project-two',
|
||||
conversationId: 'cloud-conversation-two',
|
||||
},
|
||||
};
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/cloud-project/tasks') {
|
||||
return { success: true, data: [] };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/cloud-project/conversations'
|
||||
&& init?.method === 'POST') {
|
||||
hasSecondConversation = true;
|
||||
return { success: true, data: secondConversation };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces' && init?.method === 'POST') {
|
||||
return {
|
||||
success: true,
|
||||
data: { ...summary, workspaceId: 'cloud-project-two', title: '角色设定', messages: [] },
|
||||
data: {
|
||||
...summary,
|
||||
workspaceId: 'cloud-project-two',
|
||||
title: '角色设定',
|
||||
conversations: [{
|
||||
...conversationSummary,
|
||||
workspaceId: 'cloud-project-two',
|
||||
conversationId: 'cloud-conversation-two',
|
||||
}],
|
||||
},
|
||||
};
|
||||
}
|
||||
throw new Error(`Unexpected path ${path}`);
|
||||
@@ -428,6 +485,19 @@ describe('Sidebar project initialization flow', () => {
|
||||
expect(screen.queryByText('项目模板')).not.toBeInTheDocument();
|
||||
expect(hostApiFetchMock.mock.calls.some(([path]) => path === '/api/opencode/projects')).toBe(false);
|
||||
expect(screen.queryByText(/添加 Agent/)).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('sidebar-image-conversation-cloud-conversation'))
|
||||
.toHaveAttribute('aria-current', 'page');
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '新建设计会话' }));
|
||||
await waitFor(() => expect(hostApiFetchMock).toHaveBeenCalledWith(
|
||||
'/api/works/image-workspace/workspaces/cloud-project/conversations',
|
||||
expect.objectContaining({ method: 'POST' }),
|
||||
));
|
||||
expect(await screen.findByTestId('sidebar-image-conversation-cloud-conversation-two'))
|
||||
.toHaveAttribute('aria-current', 'page');
|
||||
fireEvent.click(screen.getByTestId('sidebar-image-conversation-cloud-conversation'));
|
||||
await waitFor(() => expect(screen.getByTestId('sidebar-image-conversation-cloud-conversation'))
|
||||
.toHaveAttribute('aria-current', 'page'));
|
||||
|
||||
fireEvent.click(screen.getByTestId('sidebar-create-image-project'));
|
||||
expect(screen.getByRole('dialog', { name: '新建设计项目' })).toBeInTheDocument();
|
||||
@@ -456,8 +526,16 @@ describe('Sidebar project initialization flow', () => {
|
||||
const summary = {
|
||||
workspaceId: 'local-project',
|
||||
title: '概念设计',
|
||||
turnRevision: 0,
|
||||
viewRevision: 0,
|
||||
conversationCount: 1,
|
||||
phase: 'shaping',
|
||||
updatedAt: '2026-07-31T10:00:00Z',
|
||||
};
|
||||
const conversation = {
|
||||
conversationId: 'local-conversation',
|
||||
workspaceId: 'local-project',
|
||||
title: '主会话',
|
||||
turnRevision: 0,
|
||||
phase: 'shaping',
|
||||
brief: {
|
||||
version: 0,
|
||||
@@ -467,8 +545,11 @@ describe('Sidebar project initialization flow', () => {
|
||||
ready: false,
|
||||
missingDecision: '作品形式',
|
||||
},
|
||||
createdAt: '2026-07-31T10:00:00Z',
|
||||
updatedAt: '2026-07-31T10:00:00Z',
|
||||
messages: [],
|
||||
};
|
||||
const { messages: _messages, ...conversationSummary } = conversation;
|
||||
let bootstrap = {
|
||||
capabilities: {
|
||||
conversation: true,
|
||||
@@ -483,7 +564,10 @@ describe('Sidebar project initialization flow', () => {
|
||||
return { success: true, data: bootstrap };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/local-project' && !init?.method) {
|
||||
return { success: true, data: { ...summary, messages: [] } };
|
||||
return { success: true, data: { ...summary, conversations: [conversationSummary] } };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/local-project/conversations/local-conversation') {
|
||||
return { success: true, data: conversation };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/local-project/tasks') {
|
||||
return { success: true, data: [] };
|
||||
@@ -493,11 +577,25 @@ describe('Sidebar project initialization flow', () => {
|
||||
...summary,
|
||||
workspaceId: 'local-project-two',
|
||||
title: '角色设计',
|
||||
messages: [],
|
||||
conversations: [{
|
||||
...conversationSummary,
|
||||
workspaceId: 'local-project-two',
|
||||
conversationId: 'local-conversation-two',
|
||||
}],
|
||||
};
|
||||
bootstrap = { ...bootstrap, workspaces: [created, ...bootstrap.workspaces] };
|
||||
return { success: true, data: created };
|
||||
}
|
||||
if (path === '/api/works/image-workspace/workspaces/local-project-two/conversations/local-conversation-two') {
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
...conversation,
|
||||
workspaceId: 'local-project-two',
|
||||
conversationId: 'local-conversation-two',
|
||||
},
|
||||
};
|
||||
}
|
||||
throw new Error(`Unexpected path ${path}`);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user