Open the complete working comparison in a desktop workspace tab while preserving inline sidebar diffs when the tab is closed. Working and commit comparisons share one diff panel, and workspace layout is the sole owner of live tab persistence and identity. Refs #1520
106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
import { create } from "zustand";
|
|
import type { ComposerAttachment } from "@/attachments/types";
|
|
import type { AgentProvider } from "@getpaseo/protocol/agent-types";
|
|
import type { WorkspaceDraftTabSetup } from "@/workspace-tabs/model";
|
|
|
|
export interface PendingWorkspaceDraftSubmission {
|
|
serverId: string;
|
|
workspaceId: string;
|
|
draftId: string;
|
|
text: string;
|
|
attachments: ComposerAttachment[];
|
|
cwd: string;
|
|
provider: AgentProvider;
|
|
clientMessageId: string;
|
|
timestamp: number;
|
|
modeId?: string;
|
|
model?: string;
|
|
thinkingOptionId?: string;
|
|
featureValues?: Record<string, unknown>;
|
|
allowEmptyText?: boolean;
|
|
}
|
|
|
|
export interface PendingWorkspaceDraftSetup {
|
|
setup: WorkspaceDraftTabSetup;
|
|
sourceDirectory?: string | null;
|
|
}
|
|
|
|
interface WorkspaceDraftSubmissionState {
|
|
pendingByDraftId: Record<string, PendingWorkspaceDraftSubmission>;
|
|
setupByDraftId: Record<string, PendingWorkspaceDraftSetup>;
|
|
setPending: (submission: PendingWorkspaceDraftSubmission) => void;
|
|
setDraftSetup: (input: {
|
|
draftId: string;
|
|
setup: WorkspaceDraftTabSetup;
|
|
sourceDirectory?: string | null;
|
|
}) => void;
|
|
clearDraftSetup: (input: { draftId: string }) => void;
|
|
consumePending: (input: {
|
|
serverId: string;
|
|
workspaceId: string;
|
|
draftId: string;
|
|
}) => PendingWorkspaceDraftSubmission | null;
|
|
}
|
|
|
|
function matchesPendingSubmission(
|
|
pending: PendingWorkspaceDraftSubmission | null | undefined,
|
|
input: { serverId: string; workspaceId: string; draftId: string },
|
|
): pending is PendingWorkspaceDraftSubmission {
|
|
return (
|
|
pending?.serverId === input.serverId &&
|
|
pending.workspaceId === input.workspaceId &&
|
|
pending.draftId === input.draftId
|
|
);
|
|
}
|
|
|
|
function normalizeDraftId(draftId: string): string {
|
|
return draftId.trim();
|
|
}
|
|
|
|
export const useWorkspaceDraftSubmissionStore = create<WorkspaceDraftSubmissionState>(
|
|
(set, get) => ({
|
|
pendingByDraftId: {},
|
|
setupByDraftId: {},
|
|
setPending: (submission) =>
|
|
set((state) => ({
|
|
pendingByDraftId: {
|
|
...state.pendingByDraftId,
|
|
[submission.draftId]: submission,
|
|
},
|
|
})),
|
|
setDraftSetup: ({ draftId, setup, sourceDirectory }) => {
|
|
const normalizedDraftId = normalizeDraftId(draftId);
|
|
if (!normalizedDraftId) return;
|
|
set((state) => ({
|
|
setupByDraftId: {
|
|
...state.setupByDraftId,
|
|
[normalizedDraftId]: { setup, sourceDirectory: sourceDirectory ?? null },
|
|
},
|
|
}));
|
|
},
|
|
clearDraftSetup: ({ draftId }) => {
|
|
const normalizedDraftId = normalizeDraftId(draftId);
|
|
if (!normalizedDraftId) return;
|
|
set((state) => {
|
|
if (!state.setupByDraftId[normalizedDraftId]) return state;
|
|
const { [normalizedDraftId]: _removed, ...setupByDraftId } = state.setupByDraftId;
|
|
return { setupByDraftId };
|
|
});
|
|
},
|
|
consumePending: (input) => {
|
|
const pending = get().pendingByDraftId[input.draftId];
|
|
if (!matchesPendingSubmission(pending, input)) {
|
|
return null;
|
|
}
|
|
set((state) => {
|
|
if (!matchesPendingSubmission(state.pendingByDraftId[input.draftId], input)) {
|
|
return state;
|
|
}
|
|
const { [input.draftId]: _removed, ...rest } = state.pendingByDraftId;
|
|
return { pendingByDraftId: rest };
|
|
});
|
|
return pending;
|
|
},
|
|
}),
|
|
);
|