Skip to content

feat(desktop): replace sidebar activity strip with port/agent chips and bulk-close hover cards - #5887

Merged
saddlepaddle merged 1 commit into
mainfrom
sidebar-port-bulk-close
Jul 22, 2026
Merged

saddlepaddle merged 1 commit into
mainfrom
sidebar-port-bulk-close

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Jul 22, 2026 •

Copy link
Copy Markdown
Collaborator

What

Replaces the per-workspace sidebar activity strip (the hover-morph row with staggered port badges) with compact chips on a slim activity line under each workspace row, adding a first-class way to bulk-close all ports (and stop all agents) for a workspace.

  • Ports chip: radio tower + count, old count-pill aesthetic (bg-muted/60). Hovering the chip cross-fades the count to an × in place (constant width); clicking kills every port via the existing killPorts and toasts "Closed N ports" (no undo — the processes are gone).
  • Agents chip: avatar of the most-relevant agent (highest STATUS_PRIORITY, newest on ties) + total count; renders only with 2+ running agents (existing noise rule). Clicking the × stops all agents via a new bulk killAgents (Promise.allSettled over the existing terminal.killSession mutation).
  • Hover cards (Radix HoverCard, 150 ms): list every port (label · localhost:port, click-to-open respecting portOpenAction, per-row ×) or agent (icon, label, status, per-row ×), with a plain-styled "Close all ports" / "Stop all agents" footer. Rows are layout-shift-free (visibility swaps, no display toggles).
  • Hover-card mutual exclusion: chips hold a ref-counted suppression on DashboardSidebarHoverProvider while hovered, so the workspace hover card yields to the chip card and returns instantly afterward.

Refactors / removals

  • usePortOpenActions extracted from DashboardSidebarPortBadge and shared with the new hover rows (no behavior change to the bottom-panel badge).
  • useDashboardSidebarWorkspaceRunningAgents + useDashboardSidebarAgentKill moved under the new DashboardSidebarWorkspaceChips; kill hook gains killAgents.
  • Deleted DashboardSidebarWorkspaceDetails (morph strip, DashboardSidebarWorkspaceAgentBadge, DashboardSidebarWorkspaceDetailsAction, stagger constants), the details-expanded @custom-variant in globals.css, and the group/item / data-active scaffolding.
  • Diff-stats ↔ hover-actions swap on the row now uses visibility instead of display, so the row is layout-stable on hover.

Flags

Unchanged: ports chip behind inline-workspace-ports, agents chip behind workspace-agents-row; flag-off still shows the bottom PORTS panel. Store teardown docs updated to point at the chip components.

Testing

  • bun run lint and apps/desktop typecheck clean; no remaining references to the deleted strip/variant.
  • Manually iterated in the dev app across several design rounds (chip legibility, single-avatar rule, no layout shift on row or hover-card rows, hover-card mutual exclusion, toast on bulk close).

https://claude.ai/code/session_01HGmkhuu5WVFn83PhDC7Yoz


Summary by cubic

Replace the sidebar activity strip with compact ports/agents chips under each workspace row. Adds bulk-close actions and hover cards for quick per-item actions; flags remain the same and the bottom PORTS panel is unchanged.

  • New Features

    • Ports chip: radio tower + count; hover swaps count to ×; click closes all ports; hover card lists ports with per-row close and a “Close all ports” footer.
    • Agents chip: shows the most relevant agent + count when 2+ are running; click stops all agents; hover card lists agents with status and per-row stop.
    • Chip hover suppresses the workspace hover card to avoid overlap; bulk actions show success toasts.
  • Refactors

    • Extracted port open logic into usePortOpenActions and reused in the sidebar port badge.
    • Added bulk killAgents to the agent kill hook.
    • Removed the old morph strip and related components/styles, including the details-expanded CSS variant.
    • Made row hover UI layout-stable by switching to visibility (diff stats vs. hover actions) and updated the hover overlay to honor suppression.

Written for commit d196a09. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added workspace sidebar chips showing active ports and running agents.
    • Added hover details for individual ports and agents, including quick open, close, and stop actions.
    • Added controls to close all active ports or stop all running agents with progress feedback and notifications.
  • Bug Fixes

    • Improved sidebar hover behavior to prevent hover cards from closing during chip interactions.
    • Refined visibility behavior for workspace actions and diff statistics during hover and focus.

…nd bulk-close hover cards

Drop the per-workspace morph strip (staggered port badges, unfold
machinery) in favor of compact chips on a slim activity line under each
workspace row:

- Ports chip (radio tower + count) and agents chip (most-relevant agent
  avatar + count, 2+ agents only), styled like the old count pill
- Hovering a chip swaps its count for an x in place; clicking bulk-kills
  every port / stops every agent with a success toast
- Each chip opens a hover card listing items with click-to-open and
  per-item close, plus a close-all footer
- Chip hover suppresses the workspace hover card via a ref-counted hold
  on DashboardSidebarHoverProvider, so the two cards never fight
- Port open logic extracted to usePortOpenActions, shared with the
  bottom-panel port badge; agent kill hook gains a bulk killAgents
- Removes the details-expanded custom variant and the group/item scope

Both experiment flags (inline-workspace-ports, workspace-agents-row)
keep their existing gating; the bottom PORTS panel is unchanged.

Claude-Session: https://claude.ai/code/session_01HGmkhuu5WVFn83PhDC7Yoz
@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The dashboard sidebar replaces the workspace details strip with agent and port chips. New hooks centralize port actions, normalize running-agent data, support bulk agent termination, and suppress parent hover cards while chip hover cards are active.

Changes

Sidebar activity chips

Layer / File(s) Summary
Hover suppression and agent data
apps/desktop/src/renderer/.../DashboardSidebarHoverProvider/*, .../useDashboardSidebarChipHoverSuppression/*, .../useDashboardSidebarWorkspaceRunningAgents/*, .../DashboardSidebarHoverCardOverlay/*
Hover-card suppression is ref-counted and running agents are normalized into ordered sidebar data.
Centralized port opening
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/usePortOpenActions/*, .../DashboardSidebarPortBadge/*
Port-opening decisions and callbacks are provided by usePortOpenActions.
Workspace chip integration and agent controls
apps/desktop/src/renderer/.../DashboardSidebarWorkspaceItem/*, .../DashboardSidebarWorkspaceChips/*, .../DashboardSidebarAgentsChip/*
Expanded workspace rows render activity chips, including agent status, navigation, individual stopping, and bulk stopping.
Port activity chips
apps/desktop/src/renderer/.../DashboardSidebarPortsChip/*
Port chips list active ports, support individual or bulk closure, and provide port-opening actions.
Legacy details cleanup and visibility updates
apps/desktop/src/renderer/globals.css, .../DashboardSidebarWorkspaceDetails/*, .../DashboardSidebarWorkspaceDiffStats/*, apps/desktop/src/renderer/stores/*
Legacy details references and the details-expanded variant are removed, with related visibility and experiment comments updated.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkspaceRow as DashboardSidebarExpandedWorkspaceRow
  participant Chips as DashboardSidebarWorkspaceChips
  participant AgentChip as DashboardSidebarAgentsChip
  participant PortChip as DashboardSidebarPortsChip
  participant KillAgents as useDashboardSidebarAgentKill
  participant PortActions as usePortOpenActions
  WorkspaceRow->>Chips: render workspace activity
  Chips->>AgentChip: pass running agents
  Chips->>PortChip: pass active ports
  AgentChip->>KillAgents: killAgents(terminalIds)
  PortChip->>PortActions: open or close port actions
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, conventional, and accurately summarizes the main sidebar chip refactor and bulk close hover-card changes.
Description check ✅ Passed The description covers the change, rationale, flags, and testing, with only the checklist section missing from the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sidebar-port-bulk-close

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts (1)

73-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

killAgents swallows individual rejection reasons.

Unlike killAgent, which logs console.error("[sidebar-agent] Failed to kill agent:", error) on failure, killAgents only counts rejections for the aggregate toast — the actual error objects from Promise.allSettled are discarded. This makes debugging a partial bulk-kill failure harder (no trace in the console/log).

♻️ Proposed fix to log individual failures
 				const results = await Promise.allSettled(
 					terminalIds.map((terminalId) =>
 						getHostServiceClientByUrl(hostUrl).terminal.killSession.mutate({
 							terminalId,
 							workspaceId,
 						}),
 					),
 				);
-				const failedCount = results.filter(
-					(result) => result.status === "rejected",
-				).length;
+				for (const result of results) {
+					if (result.status === "rejected") {
+						console.error("[sidebar-agent] Failed to kill agent:", result.reason);
+					}
+				}
+				const failedCount = results.filter(
+					(result) => result.status === "rejected",
+				).length;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts`
around lines 73 - 83, Update the bulk failure handling in killAgents to inspect
each rejected result from Promise.allSettled and log its reason with the same
“[sidebar-agent] Failed to kill agent:” context used by killAgent, while
preserving the existing failedCount toast and successful-result count.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/DashboardSidebarAgentsChip.tsx (1)

38-45: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

reduce without an initial value will throw if agents is ever empty.

Currently safe because DashboardSidebarWorkspaceChips only renders this component when agents.length > 1, but that invariant lives in a different file. A one-line initial value removes the coupling risk entirely.

-	const primaryAgent = agents.reduce((best, agent) => {
+	const primaryAgent = agents.reduce((best, agent) => {
 		if (STATUS_PRIORITY[agent.status] !== STATUS_PRIORITY[best.status]) {
 			return STATUS_PRIORITY[agent.status] > STATUS_PRIORITY[best.status]
 				? agent
 				: best;
 		}
 		return agent.startedAt > best.startedAt ? agent : best;
-	});
+	}, agents[0]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/DashboardSidebarAgentsChip.tsx`
around lines 38 - 45, Add an initial value to the agents.reduce call used to
compute primaryAgent in DashboardSidebarAgentsChip, using the existing
empty-agent fallback expected by the component, so the reduction remains safe
when agents is empty while preserving the current priority and startedAt
selection logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx`:
- Around line 116-124: Port-hover actions currently allow clicks to reach the
parent workspace item. In DashboardSidebarPortsChip.tsx at lines 116-124, stop
propagation for the footer “Close all ports” button; in
DashboardSidebarPortHoverRow.tsx at lines 19-46, add the same click-propagation
guard to both hovered-row action buttons so opening or closing ports only
performs the intended action.
- Around line 26-41: Share the port-kill pending state across bulk and per-port
actions by keeping the single useDashboardSidebarPortKill call in
DashboardSidebarPortsChip and passing isPending plus killPort/onClose to each
DashboardSidebarPortHoverRow. In DashboardSidebarPortHoverRow, remove its local
hook usage and consume the parent-provided props so both close paths disable
consistently. Apply this to DashboardSidebarPortsChip.tsx lines 26-41 and
DashboardSidebarPortHoverRow.tsx lines 14-46.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts`:
- Around line 54-94: The useDashboardSidebarAgentKill hook currently keeps
isPending local to each caller, allowing bulk and per-row kill actions to race
on the same terminal IDs. Share in-flight terminal IDs across
DashboardSidebarAgentsChip and DashboardSidebarAgentHoverRow within the
workspace, or disable row actions for IDs included in an active bulk kill, and
ensure killAgents and killAgent consult that shared state before invoking
terminal.killSession.

---

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/DashboardSidebarAgentsChip.tsx`:
- Around line 38-45: Add an initial value to the agents.reduce call used to
compute primaryAgent in DashboardSidebarAgentsChip, using the existing
empty-agent fallback expected by the component, so the reduction remains safe
when agents is empty while preserving the current priority and startedAt
selection logic.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts`:
- Around line 73-83: Update the bulk failure handling in killAgents to inspect
each rejected result from Promise.allSettled and log its reason with the same
“[sidebar-agent] Failed to kill agent:” context used by killAgent, while
preserving the existing failedCount toast and successful-result count.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a1882a32-82d9-4d8b-bce7-46c9e6dfee5e

📥 Commits

Reviewing files that changed from the base of the PR and between 63909c1 and d196a09.

📒 Files selected for processing (35)
  • apps/desktop/src/renderer/globals.css
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHoverCardOverlay/DashboardSidebarHoverCardOverlay.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarPortsList/components/DashboardSidebarPortBadge/DashboardSidebarPortBadge.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/DashboardSidebarExpandedWorkspaceRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/DashboardSidebarWorkspaceChips.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/DashboardSidebarAgentsChip.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/components/DashboardSidebarAgentAvatar/DashboardSidebarAgentAvatar.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/components/DashboardSidebarAgentAvatar/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/components/DashboardSidebarAgentHoverRow/DashboardSidebarAgentHoverRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/components/DashboardSidebarAgentHoverRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/components/DashboardSidebarPortHoverRow/DashboardSidebarPortHoverRow.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/components/DashboardSidebarPortHoverRow/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarChipHoverSuppression/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarChipHoverSuppression/useDashboardSidebarChipHoverSuppression.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarWorkspaceRunningAgents/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarWorkspaceRunningAgents/useDashboardSidebarWorkspaceRunningAgents.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceAgentBadge/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceDetailsAction/DashboardSidebarWorkspaceDetailsAction.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceDetailsAction/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDiffStats/DashboardSidebarWorkspaceDiffStats.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/usePortOpenActions/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/hooks/usePortOpenActions/usePortOpenActions.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/providers/DashboardSidebarHoverProvider/DashboardSidebarHoverProvider.tsx
  • apps/desktop/src/renderer/stores/inline-workspace-ports.ts
  • apps/desktop/src/renderer/stores/workspace-agents-row.ts
💤 Files with no reviewable changes (7)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceDetailsAction/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceAgentBadge/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/components/DashboardSidebarWorkspaceDetailsAction/DashboardSidebarWorkspaceDetailsAction.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/index.ts
  • apps/desktop/src/renderer/globals.css

Comment on lines +26 to +41
export function DashboardSidebarPortsChip({
ports,
}: DashboardSidebarPortsChipProps) {
const { isPending, killPorts } = useDashboardSidebarPortKill();
const { hold, release } = useDashboardSidebarChipHoverSuppression();

const handleCloseAll = async () => {
if (isPending) return;
const results = await killPorts(ports);
const closedCount = results.filter((result) => result.success).length;
if (closedCount > 0) {
toast.success(
closedCount === 1 ? "Closed 1 port" : `Closed ${closedCount} ports`,
);
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bulk "close all" and per-port "close" don't share kill-pending state. usePortKillActions (via useDashboardSidebarPortKill) tracks pendingCount in local useState, so calling it separately in the chip and in each hover row gives each caller its own disjoint isPending. Nothing prevents a user from triggering a per-port close while "close all" is already killing that same port (or vice versa), since neither button is disabled by the other's in-flight state.

  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx#L26-L41: call useDashboardSidebarPortKill() once here and pass isPending/killPort down to DashboardSidebarPortHoverRow as props instead of the row calling the hook itself.
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/components/DashboardSidebarPortHoverRow/DashboardSidebarPortHoverRow.tsx#L14-L46: drop the local useDashboardSidebarPortKill() call, accept isPending/onClose (or killPort) as props from the parent chip instead.
🔧 Proposed fix sketch
 interface DashboardSidebarPortHoverRowProps {
 	port: DashboardSidebarPort;
+	isPending: boolean;
+	onClose: (port: DashboardSidebarPort) => void;
 }

 export function DashboardSidebarPortHoverRow({
 	port,
+	isPending,
+	onClose,
 }: DashboardSidebarPortHoverRowProps) {
-	const { isPending, killPort } = useDashboardSidebarPortKill();
 	const { openPrimary } = usePortOpenActions(port);
 	...
-				onClick={() => {
-					if (isPending) return;
-					void killPort(port);
-				}}
+				onClick={() => {
+					if (isPending) return;
+					onClose(port);
+				}}
-const { isPending, killPorts } = useDashboardSidebarPortKill();
+const { isPending, killPort, killPorts } = useDashboardSidebarPortKill();
 ...
-<DashboardSidebarPortHoverRow key={...} port={port} />
+<DashboardSidebarPortHoverRow
+	key={...}
+	port={port}
+	isPending={isPending}
+	onClose={(p) => void killPort(p)}
+/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function DashboardSidebarPortsChip({
ports,
}: DashboardSidebarPortsChipProps) {
const { isPending, killPorts } = useDashboardSidebarPortKill();
const { hold, release } = useDashboardSidebarChipHoverSuppression();
const handleCloseAll = async () => {
if (isPending) return;
const results = await killPorts(ports);
const closedCount = results.filter((result) => result.success).length;
if (closedCount > 0) {
toast.success(
closedCount === 1 ? "Closed 1 port" : `Closed ${closedCount} ports`,
);
}
};
interface DashboardSidebarPortHoverRowProps {
port: DashboardSidebarPort;
isPending: boolean;
onClose: (port: DashboardSidebarPort) => void;
}
export function DashboardSidebarPortHoverRow({
port,
isPending,
onClose,
}: DashboardSidebarPortHoverRowProps) {
const { openPrimary } = usePortOpenActions(port);
return (
<div className="group/row flex items-center gap-1.5 rounded-sm px-2 py-1 hover:bg-muted">
<button
type="button"
onClick={openPrimary}
disabled={isPending}
className="flex min-w-0 flex-1 items-center gap-1.5 text-left focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
>
<span className="size-1.5 shrink-0 rounded-full bg-green-500" />
{port.label && (
<span className="min-w-0 truncate text-xs">{port.label}</span>
)}
<span className="shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground">
localhost:{port.port}
</span>
</button>
{/* Always in layout, shown via visibility so the row never changes
size on hover. */}
<button
type="button"
onClick={() => {
if (isPending) return;
onClose(port);
}}
disabled={isPending}
aria-label={`Close port ${port.port}`}
className="invisible flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring group-focus-within/row:visible group-hover/row:visible"
>
<LuX className="size-3" strokeWidth={STROKE_WIDTH} />
</button>
Suggested change
export function DashboardSidebarPortsChip({
ports,
}: DashboardSidebarPortsChipProps) {
const { isPending, killPorts } = useDashboardSidebarPortKill();
const { hold, release } = useDashboardSidebarChipHoverSuppression();
const handleCloseAll = async () => {
if (isPending) return;
const results = await killPorts(ports);
const closedCount = results.filter((result) => result.success).length;
if (closedCount > 0) {
toast.success(
closedCount === 1 ? "Closed 1 port" : `Closed ${closedCount} ports`,
);
}
};
export function DashboardSidebarPortsChip({
ports,
}: DashboardSidebarPortsChipProps) {
const { isPending, killPort, killPorts } = useDashboardSidebarPortKill();
const { hold, release } = useDashboardSidebarChipHoverSuppression();
const handleCloseAll = async () => {
if (isPending) return;
const results = await killPorts(ports);
const closedCount = results.filter((result) => result.success).length;
if (closedCount > 0) {
toast.success(
closedCount === 1 ? "Closed 1 port" : `Closed ${closedCount} ports`,
);
}
};
📍 Affects 2 files
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx#L26-L41 (this comment)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/components/DashboardSidebarPortHoverRow/DashboardSidebarPortHoverRow.tsx#L14-L46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx`
around lines 26 - 41, Share the port-kill pending state across bulk and per-port
actions by keeping the single useDashboardSidebarPortKill call in
DashboardSidebarPortsChip and passing isPending plus killPort/onClose to each
DashboardSidebarPortHoverRow. In DashboardSidebarPortHoverRow, remove its local
hook usage and consume the parent-provided props so both close paths disable
consistently. Apply this to DashboardSidebarPortsChip.tsx lines 26-41 and
DashboardSidebarPortHoverRow.tsx lines 14-46.

Comment on lines +116 to +124
<button
type="button"
onClick={() => void handleCloseAll()}
disabled={isPending}
className="flex w-full items-center gap-1.5 rounded-sm px-2 py-1 text-xs hover:bg-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-70"
>
<LuX className="size-3" strokeWidth={STROKE_WIDTH} />
Close all ports
</button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant files and inspect the component hierarchy/events around PortsChip and hover row.
fd -a 'DashboardSidebarPortsChip\.tsx|DashboardSidebarPortHoverRow\.tsx|DashboardSidebarWorkspaceItem\.tsx|HoverCardContent|HoverCard' . | sed 's#^\./##' | head -100

echo '--- PortsChip outline ---'
PortsChip="$(fd 'DashboardSidebarPortsChip\.tsx' . | head -1)"
if [ -n "${PortsChip:-}" ]; then
  wc -l "$PortsChip"
  ast-grep outline "$PortsChip" || true
  echo '--- PortsChip relevant lines ---'
  sed -n '1,180p' "$PortsChip" | nl -ba
fi

echo '--- HoverRow relevant lines ---'
HoverRow="$(fd 'DashboardSidebarPortHoverRow\.tsx' . | head -1)"
if [ -n "${HoverRow:-}" ]; then
  wc -l "$HoverRow"
  sed -n '1,120p' "$HoverRow" | nl -ba
fi

echo '--- Search workspace row click handlers / hover card placement ---'
rg -n "DashboardSidebarWorkspaceItem|DashboardSidebarExpandedWorkspaceRow|HoverCardContent|onMouseEnter|onMouseLeave|onClick|type=\"button\"|Close all ports|open primary|killPort" apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem -S || true

Repository: superset-sh/superset

Length of output: 3347


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Programmatically inspect the relevant source structure without executing repository code.
# This reads the candidate files and builds a small map of hook/handler references.
python3 - <<'PY'
from pathlib import Path
import re

files = [
 Path('apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx'),
 Path('apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/components/DashboardSidebarPortHoverRow/DashboardSidebarPortHoverRow.tsx'),
]
for p in files:
    print(f'=== {p} exists={p.exists()} ===')
    if not p.exists():
        continue
    s = p.read_text()
    for i,line in enumerate(s.splitlines(),1):
        if any(tok in line for tok in ['onClick=', 'onMouseEnter=', 'onMouseLeave=', 'HoverCardContent', 'HoverCardTrigger', 'Close all ports', 'openPrimary', 'killPort', 'stopPropagation']):
            print(f'{i}: {line}')
PY

Repository: superset-sh/superset

Length of output: 1471


🏁 Script executed:

#!/bin/bash
set -euo pipefail

PortsChip="$(fd 'DashboardSidebarPortsChip\.tsx' . | head -1)"
HoverRow="$(fd 'DashboardSidebarPortHoverRow\.tsx' . | head -1)"
WorkspaceItem="$(fd 'DashboardSidebarWorkspaceItem\.tsx' . | head -1)"

echo '--- PortsChip relevant lines ---'
sed -n '28,130p' "$PortsChip"

echo '--- HoverRow relevant lines ---'
sed -n '1,90p' "$HoverRow"

echo '--- Workspace item outline/relevant event handlers ---'
sed -n '1,220p' "$WorkspaceItem"

echo '--- HoverCardContent implementation ---'
echo "$WorkspaceItem" | sed 's#DashboardSidebarWorkspaceItem\.tsx$#../../DashboardSidebarWorkspaceHoverCardContent.tsx#' | xargs -r sed -n '1,180p'

Repository: superset-sh/superset

Length of output: 13394


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- expanded row relevant content ---'
ExpandedRow="$(fd 'DashboardSidebarExpandedWorkspaceRow\.tsx' . | head -1)"
sed -n '1,220p' "$ExpandedRow"

echo '--- hover content implementation ---'
sed -n '1,180p' apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceHoverCardContent/DashboardSidebarWorkspaceHoverCardContent.tsx

echo '--- Overlay implementation ---'
sed -n '1,220p' apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHoverCardOverlay/DashboardSidebarHoverCardOverlay.tsx

echo '--- locate hover card trigger usage ---'
rg -n "DashboardSidebarWorkspaceHoverCardContent|HoverCardContent|HoverCardTrigger|DashboardSidebarWorkspaceItem\(" apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar -S

echo '--- parent click handlers/row clickable behavior ---'
rg -n "onClick|handleClick|createWorkspace|openWorkspace|Navigate|useNavigate|type=\"button\"|DashboardSidebarWorkspaceItemProps" apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarCollapsedWorkspaceButton apps/desktop/src/renderer/hooks -S | head -200

Repository: superset-sh/superset

Length of output: 37717


Add click-propagation guards to port-hover actions. The ports chip trigger intentionally stops clicks from reaching expanded row handlers, but the hover-card actions inside DashboardSidebarPortHoverRow and the footer “Close all ports” button still do not; closing/opening from the hover UI can then also activate the parent workspace item. Stop propagation in both hovered row buttons and the footer “Close all ports” button.

📍 Affects 2 files
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx#L116-L124 (this comment)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/components/DashboardSidebarPortHoverRow/DashboardSidebarPortHoverRow.tsx#L19-L46
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarPortsChip/DashboardSidebarPortsChip.tsx`
around lines 116 - 124, Port-hover actions currently allow clicks to reach the
parent workspace item. In DashboardSidebarPortsChip.tsx at lines 116-124, stop
propagation for the footer “Close all ports” button; in
DashboardSidebarPortHoverRow.tsx at lines 19-46, add the same click-propagation
guard to both hovered-row action buttons so opening or closing ports only
performs the intended action.

Comment on lines +54 to +94
const killAgents = useCallback(
async (terminalIds: string[]): Promise<number> => {
if (terminalIds.length === 0) return 0;
if (!hostUrl) {
toast.error("Failed to stop agents", {
description: "No host is available for this workspace",
});
return 0;
}
setIsPending(true);
try {
const results = await Promise.allSettled(
terminalIds.map((terminalId) =>
getHostServiceClientByUrl(hostUrl).terminal.killSession.mutate({
terminalId,
workspaceId,
}),
),
);
const failedCount = results.filter(
(result) => result.status === "rejected",
).length;
if (failedCount > 0) {
toast.error(
failedCount === 1
? "Failed to stop 1 agent"
: `Failed to stop ${failedCount} agents`,
);
}
return results.length - failedCount;
} finally {
void queryClient.invalidateQueries({
queryKey: ["terminal-agent-bindings", hostUrl, workspaceId],
});
setIsPending(false);
}
},
[hostUrl, workspaceId, queryClient],
);

return { isPending, killAgent, killAgents };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
fd -a 'useDashboardSidebarAgentKill|DashboardSidebarAgentsChip|DashboardSidebarAgentHoverRow' .

echo
echo "== candidate files =="
for f in $(fd 'useDashboardSidebarAgentKill|DashboardSidebarAgentsChip|DashboardSidebarAgentHoverRow' .); do
  echo "--- $f"
  wc -l "$f"
done

echo
echo "== hook outline/read =="
for f in $(fd 'useDashboardSidebarAgentKill' .); do
  echo "--- OUTLINE $f"
  ast-grep outline "$f" || true
  echo "--- CONTENT $f"
  cat -n "$f"
done

echo
echo "== callers =="
rg -n "useDashboardSidebarAgentKill|killAgent|killAgents" . -g '*.ts' -g '*.tsx'

Repository: superset-sh/superset

Length of output: 2829


🏁 Script executed:

#!/bin/bash
set -u

echo "== hook file =="
wc -l apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts
cat -n apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts

echo
echo "== chip file =="
wc -l apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/DashboardSidebarAgentsChip.tsx
cat -n apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/DashboardSidebarAgentsChip.tsx

echo
echo "== hover row file =="
wc -l apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentHoverRow/DashboardSidebarAgentHoverRow.tsx
cat -n apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentHoverRow/DashboardSidebarAgentHoverRow.tsx

echo
echo "== hook imports/usages =="
rg -n "useDashboardSidebarAgentKill|killAgent|killAgents" apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips -g '*.ts' -g '*.tsx'

Repository: superset-sh/superset

Length of output: 15791


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== hover row missing file details =="
ls -la apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/components/DashboardSidebarAgentHoverRow/ || true

echo
echo "== hover row contents (using exact path) =="
cat -n apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip/components/DashboardSidebarAgentHoverRow/DashboardSidebarAgentHoverRow.tsx

echo
echo "== component files with killAgent/killAgents =="
rg -n "useDashboardSidebarAgentKill|killAgent|killAgents" apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/components/DashboardSidebarAgentsChip -g '*.ts' -g '*.tsx'

Repository: superset-sh/superset

Length of output: 6381


Share kill in-flight state between bulk and per-row stop actions.

DashboardSidebarAgentsChip and each DashboardSidebarAgentHoverRow call useDashboardSidebarAgentKill(workspaceId), so they have independent isPending state. Clicking “Stop all agents” while an individual row’s × is clicked can let the per-row action call killSession for an already-disposed session and show a “Failed to kill agent” toast after the bulk action succeeds. Track killing terminal IDs in a shared workspace-scoped store/context (or disable targeted row buttons when a bulk kill includes them) so both actions cannot race on the same sessions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarExpandedWorkspaceRow/components/DashboardSidebarWorkspaceChips/hooks/useDashboardSidebarAgentKill/useDashboardSidebarAgentKill.ts`
around lines 54 - 94, The useDashboardSidebarAgentKill hook currently keeps
isPending local to each caller, allowing bulk and per-row kill actions to race
on the same terminal IDs. Share in-flight terminal IDs across
DashboardSidebarAgentsChip and DashboardSidebarAgentHoverRow within the
workspace, or disable row actions for IDs included in an active bulk kill, and
ensure killAgents and killAgent consult that shared state before invoking
terminal.killSession.

@saddlepaddle
saddlepaddle merged commit 420b2dd into main Jul 22, 2026
11 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

Laurence-NZ added a commit to Laurence-NZ/superset-pwsh that referenced this pull request Jul 23, 2026
…host CLI ParadevAI#5885, Cursor session labels ParadevAI#5882, open-in-IDE v2 ParadevAI#5877, collapsed sidebar DnD ParadevAI#5871, automations FK drop ParadevAI#5878)

# Conflicts:
#	apps/desktop/src/main/lib/agent-setup/notify-hook.test.ts
Laurence-NZ added a commit to Laurence-NZ/superset-pwsh that referenced this pull request Jul 23, 2026
Upstream ParadevAI#5887 rewrote the expanded workspace row's action cluster to
appear on group-focus-within as well as group-hover. The F1 commits-to-pull
(down N) badge cluster only hid on group-hover, so on the active/focused
row the badge and the row actions overlapped in the shared grid cell.
Hide the badge cluster on group-focus-within too, restoring the clean
badge/actions swap.
Laurence-NZ added a commit to Laurence-NZ/superset-pwsh that referenced this pull request Jul 23, 2026
…host CLI ParadevAI#5885, Cursor session labels ParadevAI#5882, open-in-IDE v2 ParadevAI#5877, collapsed sidebar DnD ParadevAI#5871, automations FK drop ParadevAI#5878)

# Conflicts:
#	apps/desktop/src/main/lib/agent-setup/notify-hook.test.ts
Laurence-NZ added a commit to Laurence-NZ/superset-pwsh that referenced this pull request Jul 23, 2026
Upstream ParadevAI#5887 rewrote the expanded workspace row's action cluster to
appear on group-focus-within as well as group-hover. The F1 commits-to-pull
(down N) badge cluster only hid on group-hover, so on the active/focused
row the badge and the row actions overlapped in the shared grid cell.
Hide the badge cluster on group-focus-within too, restoring the clean
badge/actions swap.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant