fix(coding): scope metadata updates by project

This commit is contained in:
2026-08-24 09:43:45 +08:00
parent da92eb1957
commit fcd03f9f6d
5 changed files with 228 additions and 12 deletions

View File

@@ -96,6 +96,9 @@ export function CodingChatPanel({
const selectedAgentId = useCodingWorkspaceStore((state) => state.selectedAgentId);
const workspaceLoadState = useCodingWorkspaceStore((state) => state.loadState);
const workspaceError = useCodingWorkspaceStore((state) => state.error);
const conversationErrorsByProjectId = useCodingWorkspaceStore(
(state) => state.conversationErrorsByProjectId,
);
const creatingAgentIds = useCodingWorkspaceStore((state) => state.creatingAgentIds);
const loadWorkspace = useCodingWorkspaceStore((state) => state.load);
const selectAgent = useCodingWorkspaceStore((state) => state.selectAgent);
@@ -149,6 +152,9 @@ export function CodingChatPanel({
&& !conversation.archivedAt
)) ?? null;
const targetConversationId = selectedConversation?.id ?? null;
const conversationMetadataError = activeProject && targetConversationId
? conversationErrorsByProjectId[activeProject.id]?.[targetConversationId] ?? null
: null;
const provisionalDraftKey = activeProject && selectedAgent
? `new:${activeProject.id}:${selectedAgent.id}`
: null;
@@ -340,9 +346,10 @@ export function CodingChatPanel({
const sourceAgentId = selectedAgent.id;
const sourceConversationId = targetConversationId;
const forked = await forkCodingConversation(sourceConversationId, sourceEntryId);
upsertConversation(forked);
primeConversation(createLocalConversationSnapshot(sourceProjectId, forked));
const workspace = codingWorkspaceStore.getState();
if (workspace.activeProjectId !== sourceProjectId) return;
upsertConversation(sourceProjectId, forked);
primeConversation(createLocalConversationSnapshot(sourceProjectId, forked));
const selectedId = codingConversationStore.getState().selectedConversationId;
if (workspace.activeProjectId === sourceProjectId
&& workspace.selectedAgentId === sourceAgentId
@@ -656,10 +663,12 @@ export function CodingChatPanel({
onOpenSettings={onOpenProjectSettings}
/>
{(workspaceError || connectionError) && (
{(workspaceError || conversationMetadataError || connectionError) && (
<div className="mx-4 mt-3 flex min-h-10 items-center gap-2 rounded-xl bg-destructive/5 px-3 py-2 text-xs text-destructive sm:mx-5">
<CircleAlert className="h-4 w-4 shrink-0" aria-hidden="true" />
<p className="min-w-0 flex-1 text-pretty">{workspaceError ?? connectionError}</p>
<p className="min-w-0 flex-1 text-pretty">
{workspaceError ?? conversationMetadataError ?? connectionError}
</p>
{workspaceError && (
<Button
type="button"

View File

@@ -42,6 +42,7 @@ export interface CodingWorkspaceState {
selectedAgentId: string | null;
loadState: 'idle' | 'loading' | 'ready' | 'error';
error: string | null;
conversationErrorsByProjectId: Record<string, Record<string, string>>;
creatingAgentIds: Record<string, true>;
load(): Promise<void>;
selectAgent(agentId: string): void;
@@ -51,7 +52,7 @@ export interface CodingWorkspaceState {
conversationId: string,
patch: { title?: string; archived?: boolean; unread?: boolean },
): Promise<CodingConversationMetadata>;
upsertConversation(conversation: CodingConversationMetadata): void;
upsertConversation(projectId: string, conversation: CodingConversationMetadata): void;
}
function enabledAgent(config: CodingProjectConfig | null, agentId: string | null): CodingProjectAgent | null {
@@ -76,6 +77,21 @@ function newestConversation(
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0] ?? null;
}
function withConversationError(
errorsByProjectId: Record<string, Record<string, string>>,
projectId: string,
conversationId: string,
message: string | null,
): Record<string, Record<string, string>> {
const next = { ...errorsByProjectId };
const projectErrors = { ...(next[projectId] ?? {}) };
if (message) projectErrors[conversationId] = message;
else delete projectErrors[conversationId];
if (Object.keys(projectErrors).length > 0) next[projectId] = projectErrors;
else delete next[projectId];
return next;
}
function defaultDependencies(): CodingWorkspaceDependencies {
return {
listProjects: listCodingProjects,
@@ -103,6 +119,7 @@ export function createCodingWorkspaceStore(
selectedAgentId: null,
loadState: 'idle',
error: null,
conversationErrorsByProjectId: {},
creatingAgentIds: {},
async load() {
@@ -213,18 +230,36 @@ export function createCodingWorkspaceStore(
},
async patchConversation(conversationId, patch) {
set({ error: null });
const sourceProjectId = get().activeProjectId;
if (!sourceProjectId) throw new Error('当前没有可用的项目。');
set((current) => ({
conversationErrorsByProjectId: withConversationError(
current.conversationErrorsByProjectId,
sourceProjectId,
conversationId,
null,
),
}));
try {
const conversation = await deps.patchConversation(conversationId, patch);
get().upsertConversation(conversation);
get().upsertConversation(sourceProjectId, conversation);
return conversation;
} catch (error) {
set({ error: error instanceof Error ? error.message : String(error) });
const message = error instanceof Error ? error.message : String(error);
set((current) => ({
conversationErrorsByProjectId: withConversationError(
current.conversationErrorsByProjectId,
sourceProjectId,
conversationId,
message,
),
}));
throw error;
}
},
upsertConversation(conversation) {
upsertConversation(projectId, conversation) {
if (get().activeProjectId !== projectId) return;
set((current) => ({
conversations: [
conversation,