From 2ea3011397e3eb93af17b9dc75e45d1d3537d6eb Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 26 Jan 2026 01:17:57 +0100 Subject: [PATCH] feat: add setting to show subagent windows for active tab only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds "Subagents for Active Tab Only" toggle in App Settings → Display: - OFF (default): windows visible regardless of active tab (original behavior) - ON: windows only visible when their parent tab is selected Also improves parent session detection by checking active session first, which fixes connection lines going to wrong tabs when multiple sessions share the same working directory. Co-Authored-By: Claude Opus 4.5 --- src/web/public/app.js | 122 +++++++++++++++++++++++++++++++++++--- src/web/public/index.html | 8 +++ 2 files changed, 122 insertions(+), 8 deletions(-) diff --git a/src/web/public/app.js b/src/web/public/app.js index 09d58ec6..a290c206 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -2036,6 +2036,9 @@ class ClaudemanApp { // Update project insights panel for this session this.renderProjectInsightsPanel(); + // Update subagent window visibility for active session + this.updateSubagentWindowVisibility(); + this.terminal.focus(); this.terminal.scrollToBottom(); } catch (err) { @@ -3644,6 +3647,7 @@ class ClaudemanApp { document.getElementById('appSettingsShowMonitor').checked = settings.showMonitor ?? true; document.getElementById('appSettingsSubagentTracking').checked = settings.subagentTrackingEnabled ?? true; document.getElementById('appSettingsShowSubagents').checked = settings.showSubagents ?? true; + document.getElementById('appSettingsSubagentActiveTabOnly').checked = settings.subagentActiveTabOnly ?? false; document.getElementById('appSettingsShowProjectInsights').checked = settings.showProjectInsights ?? true; // Claude CLI settings const claudeModeSelect = document.getElementById('appSettingsClaudeMode'); @@ -3706,6 +3710,7 @@ class ClaudemanApp { showMonitor: document.getElementById('appSettingsShowMonitor').checked, subagentTrackingEnabled: document.getElementById('appSettingsSubagentTracking').checked, showSubagents: document.getElementById('appSettingsShowSubagents').checked, + subagentActiveTabOnly: document.getElementById('appSettingsSubagentActiveTabOnly').checked, showProjectInsights: document.getElementById('appSettingsShowProjectInsights').checked, // Claude CLI settings claudeMode: document.getElementById('appSettingsClaudeMode').value, @@ -3734,6 +3739,7 @@ class ClaudemanApp { this.applyHeaderVisibilitySettings(); this.applyMonitorVisibility(); this.renderProjectInsightsPanel(); // Re-render to apply visibility setting + this.updateSubagentWindowVisibility(); // Apply subagent window visibility setting // Save to server (includes notification prefs for cross-browser persistence) try { @@ -4999,11 +5005,11 @@ class ClaudemanApp { const agent = this.subagents.get(agentId); if (!agent) return; - // Query each session to find which one owns this subagent - for (const [sessionId, session] of this.sessions) { + // Helper to check a session and set parent if found + const checkSession = async (sessionId, session) => { try { const resp = await fetch(`/api/sessions/${sessionId}/subagents`); - if (!resp.ok) continue; + if (!resp.ok) return false; const result = await resp.json(); const subagents = result.data || result.subagents || []; if (subagents.some(s => s.agentId === agentId)) { @@ -5012,11 +5018,27 @@ class ClaudemanApp { this.subagents.set(agentId, agent); // Update window if already open this.updateSubagentWindowParent(agentId); - return; + // Update visibility based on whether this is the active session + this.updateSubagentWindowVisibility(); + return true; } } catch (err) { - // Ignore errors, continue checking other sessions + // Ignore errors } + return false; + }; + + // First, check the active session (most likely parent for new agents) + if (this.activeSessionId && this.sessions.has(this.activeSessionId)) { + const found = await checkSession(this.activeSessionId, this.sessions.get(this.activeSessionId)); + if (found) return; + } + + // Then check other sessions + for (const [sessionId, session] of this.sessions) { + if (sessionId === this.activeSessionId) continue; // Already checked + const found = await checkSession(sessionId, session); + if (found) return; } } @@ -5056,7 +5078,7 @@ class ClaudemanApp { svg.innerHTML = ''; for (const [agentId, windowInfo] of this.subagentWindows) { - if (windowInfo.minimized) continue; + if (windowInfo.minimized || windowInfo.hidden) continue; const agent = this.subagents.get(agentId); if (!agent?.parentSessionId) continue; @@ -5086,12 +5108,66 @@ class ClaudemanApp { } } + /** + * Show/hide subagent windows based on active session. + * Behavior controlled by "Subagents for Active Tab Only" setting. + */ + updateSubagentWindowVisibility() { + const settings = this.loadAppSettingsFromStorage(); + const activeTabOnly = settings.subagentActiveTabOnly ?? false; + + for (const [agentId, windowInfo] of this.subagentWindows) { + const agent = this.subagents.get(agentId); + + // Determine visibility based on setting + let shouldShow; + if (activeTabOnly) { + // Show if: no parent known yet, or parent matches active session + const hasKnownParent = agent?.parentSessionId; + shouldShow = !hasKnownParent || agent.parentSessionId === this.activeSessionId; + } else { + // Show all windows (original behavior) + shouldShow = true; + } + + if (shouldShow) { + // Show window (unless it was minimized by user) + if (!windowInfo.minimized) { + windowInfo.element.style.display = 'flex'; + } + windowInfo.hidden = false; + } else { + // Hide window (but don't close it) + windowInfo.element.style.display = 'none'; + windowInfo.hidden = true; + } + } + // Update connection lines after visibility changes + this.updateConnectionLines(); + } + // ========== Subagent Floating Windows ========== openSubagentWindow(agentId) { // If window already exists, focus it if (this.subagentWindows.has(agentId)) { const existing = this.subagentWindows.get(agentId); + const agent = this.subagents.get(agentId); + const settings = this.loadAppSettingsFromStorage(); + const activeTabOnly = settings.subagentActiveTabOnly ?? false; + + // If window is hidden (different tab) and activeTabOnly is enabled, switch to parent tab + if (existing.hidden && agent?.parentSessionId && activeTabOnly) { + this.selectSession(agent.parentSessionId); + return; + } + + // If not activeTabOnly mode, just show the window + if (existing.hidden && !activeTabOnly) { + existing.element.style.display = 'flex'; + existing.hidden = false; + } + existing.element.style.zIndex = ++this.subagentWindowZIndex; if (existing.minimized) { this.restoreSubagentWindow(agentId); @@ -5173,12 +5249,28 @@ class ClaudemanApp { // Make draggable (with connection line update callback) this.makeWindowDraggable(win, win.querySelector('.subagent-window-header')); + // Check if this window should be visible based on settings + const settings = this.loadAppSettingsFromStorage(); + const activeTabOnly = settings.subagentActiveTabOnly ?? false; + let shouldHide = false; + if (activeTabOnly) { + const hasKnownParent = agent.parentSessionId; + const isForActiveSession = !hasKnownParent || agent.parentSessionId === this.activeSessionId; + shouldHide = !isForActiveSession; + } + // Store reference this.subagentWindows.set(agentId, { element: win, minimized: false, + hidden: shouldHide, }); + // Hide window if not for active session + if (shouldHide) { + win.style.display = 'none'; + } + // Render content this.renderSubagentWindowContent(agentId); @@ -5244,8 +5336,22 @@ class ClaudemanApp { restoreSubagentWindow(agentId) { const windowData = this.subagentWindows.get(agentId); if (windowData) { - windowData.element.style.display = 'flex'; - windowData.element.style.zIndex = ++this.subagentWindowZIndex; + const agent = this.subagents.get(agentId); + const settings = this.loadAppSettingsFromStorage(); + const activeTabOnly = settings.subagentActiveTabOnly ?? false; + + // Determine if we should show the window + let shouldShow = true; + if (activeTabOnly) { + // Only restore if the window belongs to the active session (or has no parent) + shouldShow = !agent?.parentSessionId || agent.parentSessionId === this.activeSessionId; + } + + if (shouldShow) { + windowData.element.style.display = 'flex'; + windowData.element.style.zIndex = ++this.subagentWindowZIndex; + windowData.hidden = false; + } windowData.minimized = false; this.updateConnectionLines(); } diff --git a/src/web/public/index.html b/src/web/public/index.html index 46fcf10a..042a3591 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -630,6 +630,14 @@ Show the Subagents panel (independent from Monitor) +
+ + + Only show subagent windows when their parent tab is selected +