feat: 支持 AI 设计项目多会话交互

需求:用户可在同一设计项目中新建和切换会话,任务列表保持项目级共享。

实现:
- 增加会话选择、新建入口及本地数据迁移
- Conversation 独立消息、Brief、Quote 与流式状态
- 保留项目任务并修复 Task revision 与 ABA 异步竞态
- 保持 Enter 发送、Shift+Enter 换行及 IME 保护
This commit is contained in:
2026-08-06 18:22:15 +08:00
parent 4980894017
commit 03dae62cf3
18 changed files with 1810 additions and 702 deletions

View File

@@ -12,6 +12,7 @@ import {
designAssetDownloadPath,
type DesignAsset,
type DesignAssetSaveResult,
type DesignConversation,
type DesignGenerationTask,
type DesignWorkspace,
type DesignWorkspaceBootstrap,
@@ -49,6 +50,10 @@ export function createImageWorkspaceTurnId(): string {
return createClientId('turn');
}
export function createImageWorkspaceConversationId(): string {
return createClientId('conversation');
}
function getErrorStatus(error: unknown): number {
if (error instanceof AppError && typeof error.details?.status === 'number') {
return error.details.status;
@@ -123,15 +128,42 @@ export function fetchImageWorkspaceProject(workspaceId: string): Promise<DesignW
);
}
export function createImageWorkspaceConversation(
workspaceId: string,
title = '新会话',
clientConversationId = createImageWorkspaceConversationId(),
): Promise<DesignConversation> {
return requestData(
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/conversations`,
{
method: 'POST',
body: JSON.stringify({
clientConversationId,
title: title.trim(),
}),
},
);
}
export function fetchImageWorkspaceConversation(
workspaceId: string,
conversationId: string,
): Promise<DesignConversation> {
return requestData(
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/conversations/${encodeURIComponent(conversationId)}`,
);
}
export function sendImageWorkspaceMessage(
workspaceId: string,
conversationId: string,
expectedTurnRevision: number,
message: string,
clientTurnId = createImageWorkspaceTurnId(),
attachmentAssetIds: string[] = [],
): Promise<DesignWorkspace> {
): Promise<DesignConversation> {
return requestData(
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/messages`,
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/conversations/${encodeURIComponent(conversationId)}/messages`,
{
method: 'POST',
body: JSON.stringify({
@@ -146,12 +178,13 @@ export function sendImageWorkspaceMessage(
export function confirmImageWorkspaceGeneration(
workspaceId: string,
conversationId: string,
expectedTurnRevision: number,
quoteId: string,
clientTurnId = createImageWorkspaceTurnId(),
): Promise<DesignWorkspace> {
): Promise<DesignConversation> {
return requestData(
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/quotes/${encodeURIComponent(quoteId)}/confirm`,
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/conversations/${encodeURIComponent(conversationId)}/quotes/${encodeURIComponent(quoteId)}/confirm`,
{
method: 'POST',
body: JSON.stringify({
@@ -170,10 +203,13 @@ export function fetchImageWorkspaceTasks(
);
}
export async function openImageWorkspaceTaskEvents(workspaceId: string): Promise<EventSource> {
export async function openImageWorkspaceTaskEvents(
workspaceId: string,
conversationId: string,
): Promise<EventSource> {
await ensureHostApiToken();
return createHostEventSource(
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/events`,
`${IMAGE_WORKSPACE_API_PATH}/workspaces/${encodeURIComponent(workspaceId)}/conversations/${encodeURIComponent(conversationId)}/events`,
);
}