fix: proper tab isolation with updateTabById
- Added updateTabById function to store for updating specific tabs by ID - Capture tab ID at request start (requestTabId) - All streaming updates now go to the captured tab ID, not activeTabId - Switching tabs during streaming no longer pollutes other tabs - Local UI state only updates if still on the same tab - Complete isolation: each tab's history/preview/agent is independent
This commit is contained in:
@@ -418,6 +418,7 @@ export default function AIAssist() {
|
|||||||
addAIAssistTab,
|
addAIAssistTab,
|
||||||
removeAIAssistTab,
|
removeAIAssistTab,
|
||||||
updateActiveTab,
|
updateActiveTab,
|
||||||
|
updateTabById,
|
||||||
selectedProvider,
|
selectedProvider,
|
||||||
selectedModels,
|
selectedModels,
|
||||||
setSelectedModel
|
setSelectedModel
|
||||||
@@ -515,6 +516,10 @@ export default function AIAssist() {
|
|||||||
const finalInput = forcedPrompt || input;
|
const finalInput = forcedPrompt || input;
|
||||||
if (!finalInput.trim() || isProcessing) return;
|
if (!finalInput.trim() || isProcessing) return;
|
||||||
|
|
||||||
|
// CRITICAL: Capture the tab ID at the start of this request
|
||||||
|
const requestTabId = activeTabId;
|
||||||
|
if (!requestTabId) return;
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
setAbortController(controller);
|
setAbortController(controller);
|
||||||
|
|
||||||
@@ -526,7 +531,7 @@ export default function AIAssist() {
|
|||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
};
|
};
|
||||||
const newHistory = [...aiAssistHistory, userMsg];
|
const newHistory = [...aiAssistHistory, userMsg];
|
||||||
updateActiveTab({ history: newHistory });
|
updateTabById(requestTabId, { history: newHistory });
|
||||||
setInput("");
|
setInput("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,9 +545,10 @@ export default function AIAssist() {
|
|||||||
timestamp: new Date()
|
timestamp: new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update history in active tab
|
// Update history in the REQUEST's tab (not current active tab)
|
||||||
const updatedHistory = [...aiAssistHistory, assistantMsg];
|
const startingHistory = [...aiAssistHistory, { role: "user" as const, content: finalInput, timestamp: new Date() }];
|
||||||
updateActiveTab({ history: updatedHistory });
|
const updatedHistory = [...startingHistory, assistantMsg];
|
||||||
|
updateTabById(requestTabId, { history: updatedHistory });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let accumulated = "";
|
let accumulated = "";
|
||||||
@@ -571,22 +577,21 @@ export default function AIAssist() {
|
|||||||
|
|
||||||
if (streamStatus) setStatus(streamStatus);
|
if (streamStatus) setStatus(streamStatus);
|
||||||
|
|
||||||
if (preview && JSON.stringify(preview) !== JSON.stringify(lastParsedPreview)) {
|
// Only update local state if we're still on the same tab
|
||||||
setPreviewData(preview);
|
if (activeTabId === requestTabId) {
|
||||||
lastParsedPreview = preview;
|
if (preview && JSON.stringify(preview) !== JSON.stringify(lastParsedPreview)) {
|
||||||
setShowCanvas(true);
|
setPreviewData(preview);
|
||||||
if (isPreviewRenderable(preview)) setViewMode("preview");
|
lastParsedPreview = preview;
|
||||||
|
setShowCanvas(true);
|
||||||
|
if (isPreviewRenderable(preview)) setViewMode("preview");
|
||||||
|
}
|
||||||
|
|
||||||
// Save preview data to tab
|
if (agent !== currentAgent) {
|
||||||
updateActiveTab({ previewData: preview });
|
setCurrentAgent(agent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (agent !== currentAgent) {
|
// Always update the REQUEST's tab (by ID), not the active tab
|
||||||
setCurrentAgent(agent);
|
|
||||||
updateActiveTab({ currentAgent: agent });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream updates to current tab history
|
|
||||||
const lastMsg = {
|
const lastMsg = {
|
||||||
role: "assistant" as const,
|
role: "assistant" as const,
|
||||||
content: accumulated,
|
content: accumulated,
|
||||||
@@ -595,8 +600,10 @@ export default function AIAssist() {
|
|||||||
timestamp: new Date()
|
timestamp: new Date()
|
||||||
};
|
};
|
||||||
|
|
||||||
updateActiveTab({
|
updateTabById(requestTabId, {
|
||||||
history: [...updatedHistory.slice(0, -1), lastMsg]
|
history: [...updatedHistory.slice(0, -1), lastMsg],
|
||||||
|
previewData: preview || undefined,
|
||||||
|
currentAgent: agent
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
signal: controller.signal
|
signal: controller.signal
|
||||||
@@ -617,7 +624,7 @@ export default function AIAssist() {
|
|||||||
console.error("Assist error:", error);
|
console.error("Assist error:", error);
|
||||||
const message = error instanceof Error ? error.message : "AI Assist failed";
|
const message = error instanceof Error ? error.message : "AI Assist failed";
|
||||||
const errorMsg: AIAssistMessage = { role: "assistant", content: message, timestamp: new Date() };
|
const errorMsg: AIAssistMessage = { role: "assistant", content: message, timestamp: new Date() };
|
||||||
updateActiveTab({ history: [...aiAssistHistory, errorMsg] });
|
updateTabById(requestTabId, { history: [...aiAssistHistory, errorMsg] });
|
||||||
} finally {
|
} finally {
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
setAbortController(null);
|
setAbortController(null);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ interface AppState {
|
|||||||
addAIAssistTab: (agent?: string) => void;
|
addAIAssistTab: (agent?: string) => void;
|
||||||
removeAIAssistTab: (id: string) => void;
|
removeAIAssistTab: (id: string) => void;
|
||||||
updateActiveTab: (updates: Partial<AIAssistTab>) => void;
|
updateActiveTab: (updates: Partial<AIAssistTab>) => void;
|
||||||
|
updateTabById: (tabId: string, updates: Partial<AIAssistTab>) => void;
|
||||||
|
|
||||||
setLanguage: (lang: "en" | "ru" | "he") => void;
|
setLanguage: (lang: "en" | "ru" | "he") => void;
|
||||||
setSelectedProvider: (provider: ModelProvider) => void;
|
setSelectedProvider: (provider: ModelProvider) => void;
|
||||||
@@ -149,6 +150,11 @@ const useStore = create<AppState>((set) => ({
|
|||||||
t.id === state.activeTabId ? { ...t, ...updates } : t
|
t.id === state.activeTabId ? { ...t, ...updates } : t
|
||||||
)
|
)
|
||||||
})),
|
})),
|
||||||
|
updateTabById: (tabId, updates) => set((state) => ({
|
||||||
|
aiAssistTabs: state.aiAssistTabs.map(t =>
|
||||||
|
t.id === tabId ? { ...t, ...updates } : t
|
||||||
|
)
|
||||||
|
})),
|
||||||
|
|
||||||
setLanguage: (lang) => set({ language: lang }),
|
setLanguage: (lang) => set({ language: lang }),
|
||||||
setSelectedProvider: (provider) => set({ selectedProvider: provider }),
|
setSelectedProvider: (provider) => set({ selectedProvider: provider }),
|
||||||
|
|||||||
Reference in New Issue
Block a user