perf: optimize app startup and background lifecycles
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
updateImageWorkspaceGenerationQuote,
|
||||
} from '@/lib/image-workspace';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { setDesktopBackgroundLease } from '@/lib/host-api';
|
||||
import {
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_CODE,
|
||||
type DesignAssistantDeltaEvent,
|
||||
@@ -109,6 +110,18 @@ let projectDeletionGeneration = 0;
|
||||
let generationQuoteUpdateSequence = 0;
|
||||
let reconcileTaskStateOnNextConnect = false;
|
||||
|
||||
function updateBackgroundLease(input: {
|
||||
id: string;
|
||||
kind: string;
|
||||
active: boolean;
|
||||
}): void {
|
||||
try {
|
||||
void Promise.resolve(setDesktopBackgroundLease(input)).catch(() => undefined);
|
||||
} catch {
|
||||
// Renderer-only tests and embedded previews may not expose lifecycle IPC.
|
||||
}
|
||||
}
|
||||
|
||||
function taskRevisionKey(workspaceId: string, taskId: string): string {
|
||||
return `${workspaceId}:${taskId}`;
|
||||
}
|
||||
@@ -376,7 +389,70 @@ function replaceGenerationQuote(
|
||||
}
|
||||
|
||||
export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) => {
|
||||
const pendingAssistantDeltas: DesignAssistantDeltaEvent[] = [];
|
||||
let assistantDeltaFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const applyAssistantDelta = (
|
||||
state: ImageWorkspaceState,
|
||||
delta: DesignAssistantDeltaEvent,
|
||||
): ImageWorkspaceState => {
|
||||
if (state.activeWorkspaceId !== delta.workspaceId
|
||||
|| state.activeConversationId !== delta.conversationId
|
||||
|| (state.conversation?.turnRevision ?? 0) >= delta.turnRevision) {
|
||||
return state;
|
||||
}
|
||||
const pending = state.pendingTurn;
|
||||
if (!pending) {
|
||||
if (delta.chunkIndex !== 0) return state;
|
||||
return {
|
||||
...state,
|
||||
pendingTurn: {
|
||||
workspaceId: delta.workspaceId,
|
||||
conversationId: delta.conversationId,
|
||||
clientTurnId: delta.clientTurnId,
|
||||
turnRevision: delta.turnRevision,
|
||||
userText: null,
|
||||
assistantText: delta.delta,
|
||||
lastChunkIndex: delta.chunkIndex,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (pending.workspaceId !== delta.workspaceId
|
||||
|| pending.conversationId !== delta.conversationId
|
||||
|| pending.clientTurnId !== delta.clientTurnId
|
||||
|| pending.turnRevision !== delta.turnRevision
|
||||
|| delta.chunkIndex !== pending.lastChunkIndex + 1) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
pendingTurn: {
|
||||
...pending,
|
||||
assistantText: `${pending.assistantText}${delta.delta}`,
|
||||
lastChunkIndex: delta.chunkIndex,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const flushAssistantDeltas = () => {
|
||||
assistantDeltaFlushTimer = null;
|
||||
if (pendingAssistantDeltas.length === 0) return;
|
||||
const queued = pendingAssistantDeltas.splice(0, pendingAssistantDeltas.length);
|
||||
set((state) => queued.reduce(applyAssistantDelta, state));
|
||||
};
|
||||
|
||||
const queueAssistantDelta = (delta: DesignAssistantDeltaEvent) => {
|
||||
pendingAssistantDeltas.push(delta);
|
||||
if (assistantDeltaFlushTimer === null) {
|
||||
assistantDeltaFlushTimer = setTimeout(flushAssistantDeltas, 50);
|
||||
}
|
||||
};
|
||||
|
||||
const stopTaskStream = () => {
|
||||
pendingAssistantDeltas.length = 0;
|
||||
if (assistantDeltaFlushTimer !== null) clearTimeout(assistantDeltaFlushTimer);
|
||||
assistantDeltaFlushTimer = null;
|
||||
closeTaskEventSource();
|
||||
set({ taskStreamState: 'idle' });
|
||||
};
|
||||
@@ -414,43 +490,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
if (!delta
|
||||
|| delta.workspaceId !== activeTaskEventWorkspaceId
|
||||
|| delta.conversationId !== activeTaskEventConversationId) return;
|
||||
set((state) => {
|
||||
if (state.activeWorkspaceId !== delta.workspaceId
|
||||
|| state.activeConversationId !== delta.conversationId
|
||||
|| (state.conversation?.turnRevision ?? 0) >= delta.turnRevision) {
|
||||
return state;
|
||||
}
|
||||
const pending = state.pendingTurn;
|
||||
if (!pending) {
|
||||
if (delta.chunkIndex !== 0) return state;
|
||||
return {
|
||||
pendingTurn: {
|
||||
workspaceId: delta.workspaceId,
|
||||
conversationId: delta.conversationId,
|
||||
clientTurnId: delta.clientTurnId,
|
||||
turnRevision: delta.turnRevision,
|
||||
userText: null,
|
||||
assistantText: delta.delta,
|
||||
lastChunkIndex: delta.chunkIndex,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (pending.workspaceId !== delta.workspaceId
|
||||
|| pending.conversationId !== delta.conversationId
|
||||
|| pending.clientTurnId !== delta.clientTurnId
|
||||
|| pending.turnRevision !== delta.turnRevision
|
||||
|| delta.chunkIndex !== pending.lastChunkIndex + 1) {
|
||||
return state;
|
||||
}
|
||||
return {
|
||||
pendingTurn: {
|
||||
...pending,
|
||||
assistantText: `${pending.assistantText}${delta.delta}`,
|
||||
lastChunkIndex: delta.chunkIndex,
|
||||
},
|
||||
};
|
||||
});
|
||||
queueAssistantDelta(delta);
|
||||
});
|
||||
source.addEventListener('design.conversation.snapshot', (event) => {
|
||||
const snapshot = parseConversationSnapshotEvent(event);
|
||||
@@ -579,7 +619,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const message = messageOf(error);
|
||||
if (authenticationRequired(error)) {
|
||||
projectDeletionGeneration += 1;
|
||||
closeTaskEventSource();
|
||||
stopTaskStream();
|
||||
useAuthStore.getState().invalidateSession();
|
||||
set({
|
||||
status: 'auth-required',
|
||||
@@ -735,7 +775,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
)
|
||||
? currentId
|
||||
: bootstrap.workspaces[0]?.workspaceId ?? null;
|
||||
closeTaskEventSource();
|
||||
stopTaskStream();
|
||||
set({
|
||||
status: 'ready',
|
||||
bootstrap,
|
||||
@@ -753,7 +793,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
} catch (error) {
|
||||
const message = handleRequestError(error);
|
||||
if (!authenticationRequired(error)) {
|
||||
closeTaskEventSource();
|
||||
stopTaskStream();
|
||||
set({
|
||||
status: unavailable(error) ? 'unavailable' : 'error',
|
||||
bootstrap: null,
|
||||
@@ -877,7 +917,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
workspaceLoadGeneration += 1;
|
||||
conversationSelectionGeneration += 1;
|
||||
reconcileTaskStateOnNextConnect = false;
|
||||
closeTaskEventSource();
|
||||
stopTaskStream();
|
||||
const nextWorkspaceId = mostRecentlyUpdatedWorkspace(remainingWorkspaces)?.workspaceId
|
||||
?? null;
|
||||
set({
|
||||
@@ -1027,7 +1067,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const shouldReconcile = reconcileTaskStateOnNextConnect;
|
||||
reconcileTaskStateOnNextConnect = false;
|
||||
if (shouldReconcile && get().taskStreamState !== 'connected') {
|
||||
closeTaskEventSource();
|
||||
stopTaskStream();
|
||||
}
|
||||
if (workspaceId && conversationId) startTaskStream(workspaceId, conversationId);
|
||||
if (!workspaceId || !conversationId || !shouldReconcile) return;
|
||||
@@ -1049,6 +1089,8 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const userText = message.trim();
|
||||
const clientTurnId = createImageWorkspaceTurnId();
|
||||
if (!workspace || !conversation) throw new Error('请先选择设计会话');
|
||||
const leaseId = `canvas:${workspace.workspaceId}:${clientTurnId}`;
|
||||
updateBackgroundLease({ id: leaseId, kind: 'canvas-generation', active: true });
|
||||
try {
|
||||
set({
|
||||
pendingTurn: createPendingTurn(conversation, clientTurnId, userText),
|
||||
@@ -1087,6 +1129,8 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
: state.pendingTurn,
|
||||
}));
|
||||
return await recoverRevisionConflict(error);
|
||||
} finally {
|
||||
updateBackgroundLease({ id: leaseId, kind: 'canvas-generation', active: false });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1147,6 +1191,8 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
const conversation = get().conversation;
|
||||
if (!workspace || !conversation) throw new Error('请先选择设计会话');
|
||||
const clientTurnId = createImageWorkspaceTurnId();
|
||||
const leaseId = `canvas:${workspace.workspaceId}:${clientTurnId}`;
|
||||
updateBackgroundLease({ id: leaseId, kind: 'canvas-generation', active: true });
|
||||
const requestWorkspaceLoadGeneration = workspaceLoadGeneration;
|
||||
const requestConversationSelectionGeneration = conversationSelectionGeneration;
|
||||
const isActiveConfirmationWorkspace = (): boolean => (
|
||||
@@ -1247,6 +1293,8 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
: state.pendingTurn,
|
||||
}));
|
||||
return await recoverRevisionConflict(error);
|
||||
} finally {
|
||||
updateBackgroundLease({ id: leaseId, kind: 'canvas-generation', active: false });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1257,7 +1305,7 @@ export const useImageWorkspaceStore = create<ImageWorkspaceState>((set, get) =>
|
||||
projectDeletionGeneration += 1;
|
||||
generationQuoteUpdateSequence += 1;
|
||||
reconcileTaskStateOnNextConnect = false;
|
||||
closeTaskEventSource();
|
||||
stopTaskStream();
|
||||
set({
|
||||
status: 'idle',
|
||||
bootstrap: null,
|
||||
|
||||
Reference in New Issue
Block a user