fix(design): separate progress from chat replies
This commit is contained in:
@@ -96,7 +96,9 @@ type ImageWorkspaceState = {
|
||||
let inFlightLoad: Promise<DesignWorkspaceBootstrap | null> | null = null;
|
||||
let activeEventSource: EventSource | null = null;
|
||||
let activeEventWorkspaceId: string | null = null;
|
||||
let eventConnectionGeneration = 0;
|
||||
let selectionGeneration = 0;
|
||||
const assistantStreamChunkIndexes = new Map<string, number>();
|
||||
|
||||
function updateBackgroundLease(tasks: DesignGenerationTask[]): void {
|
||||
const active = tasks.some((task) => task.status === 'queued' || task.status === 'running');
|
||||
@@ -112,6 +114,7 @@ function updateBackgroundLease(tasks: DesignGenerationTask[]): void {
|
||||
}
|
||||
|
||||
function closeEventSource(): void {
|
||||
eventConnectionGeneration += 1;
|
||||
if (activeEventSource) {
|
||||
activeEventSource.onopen = null;
|
||||
activeEventSource.onerror = null;
|
||||
@@ -292,6 +295,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
}));
|
||||
try {
|
||||
const result = await submitImageWorkspaceCommand(command);
|
||||
assistantStreamChunkIndexes.delete(operation.id);
|
||||
set((state) => {
|
||||
const pendingOperations = { ...state.pendingOperations };
|
||||
delete pendingOperations[operation.id];
|
||||
@@ -339,6 +343,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
return { pendingOperations, error: null };
|
||||
});
|
||||
} else if (isDefinitiveCommandFailure(error)) {
|
||||
assistantStreamChunkIndexes.delete(operation.id);
|
||||
set((state) => {
|
||||
const pendingOperations = { ...state.pendingOperations };
|
||||
delete pendingOperations[operation.id];
|
||||
@@ -426,10 +431,12 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const workspace = await createImageWorkspaceProject(title);
|
||||
selectionGeneration += 1;
|
||||
closeEventSource();
|
||||
assistantStreamChunkIndexes.clear();
|
||||
set((state) => ({
|
||||
...withWorkspace(state, workspace),
|
||||
fieldDrafts: {},
|
||||
chatDraft: '',
|
||||
assistantStreams: {},
|
||||
quoteBlockers: [],
|
||||
lastEventId: null,
|
||||
}));
|
||||
@@ -467,6 +474,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
if (deletingActive) {
|
||||
selectionGeneration += 1;
|
||||
closeEventSource();
|
||||
assistantStreamChunkIndexes.clear();
|
||||
}
|
||||
set((state) => ({
|
||||
bootstrap: state.bootstrap ? { ...state.bootstrap, workspaces: remaining } : null,
|
||||
@@ -475,6 +483,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
deletingWorkspaceId: null,
|
||||
fieldDrafts: deletingActive ? {} : state.fieldDrafts,
|
||||
chatDraft: deletingActive ? '' : state.chatDraft,
|
||||
assistantStreams: deletingActive ? {} : state.assistantStreams,
|
||||
error: null,
|
||||
}));
|
||||
if (deletingActive && remaining[0]) {
|
||||
@@ -492,6 +501,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
if (workspaceId === get().activeWorkspaceId && get().workspace) return;
|
||||
selectionGeneration += 1;
|
||||
closeEventSource();
|
||||
assistantStreamChunkIndexes.clear();
|
||||
set({
|
||||
activeWorkspaceId: workspaceId,
|
||||
workspace: null,
|
||||
@@ -499,6 +509,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
lastEventId: null,
|
||||
fieldDrafts: {},
|
||||
chatDraft: '',
|
||||
assistantStreams: {},
|
||||
quoteBlockers: [],
|
||||
error: null,
|
||||
});
|
||||
@@ -520,8 +531,9 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const workspace = get().workspace;
|
||||
if (!workspace) return;
|
||||
const workspaceId = workspace.workspace.workspaceId;
|
||||
if (activeEventSource && activeEventWorkspaceId === workspaceId) return;
|
||||
if (activeEventWorkspaceId === workspaceId) return;
|
||||
closeEventSource();
|
||||
const connectionGeneration = eventConnectionGeneration;
|
||||
activeEventWorkspaceId = workspaceId;
|
||||
set({ eventState: 'connecting' });
|
||||
void openImageWorkspaceEvents(
|
||||
@@ -529,7 +541,9 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
workspace.workspace.sessionId,
|
||||
get().lastEventId ?? undefined,
|
||||
).then((source) => {
|
||||
if (activeEventWorkspaceId !== workspaceId || get().activeWorkspaceId !== workspaceId) {
|
||||
if (connectionGeneration !== eventConnectionGeneration
|
||||
|| activeEventWorkspaceId !== workspaceId
|
||||
|| get().activeWorkspaceId !== workspaceId) {
|
||||
source.close();
|
||||
return;
|
||||
}
|
||||
@@ -543,6 +557,9 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const event = parseWorkspaceEvent(raw);
|
||||
if (!event || get().activeWorkspaceId !== workspaceId) return;
|
||||
if (event.type === 'design.assistant.delta') {
|
||||
const previousChunkIndex = assistantStreamChunkIndexes.get(event.clientOperationId);
|
||||
if (previousChunkIndex !== undefined && event.chunkIndex <= previousChunkIndex) return;
|
||||
assistantStreamChunkIndexes.set(event.clientOperationId, event.chunkIndex);
|
||||
set((state) => ({
|
||||
lastEventId: event.id,
|
||||
assistantStreams: {
|
||||
@@ -592,7 +609,9 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
'design.workspace.updated',
|
||||
]) source.addEventListener(eventType, receive);
|
||||
}).catch((error) => {
|
||||
if (activeEventWorkspaceId === workspaceId) {
|
||||
if (connectionGeneration === eventConnectionGeneration
|
||||
&& activeEventWorkspaceId === workspaceId) {
|
||||
activeEventWorkspaceId = null;
|
||||
set({ eventState: 'degraded', error: messageForError(error) });
|
||||
}
|
||||
});
|
||||
@@ -708,6 +727,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
reset: () => {
|
||||
selectionGeneration += 1;
|
||||
closeEventSource();
|
||||
assistantStreamChunkIndexes.clear();
|
||||
updateBackgroundLease([]);
|
||||
set({
|
||||
status: 'idle',
|
||||
|
||||
Reference in New Issue
Block a user