feat: add stop all controls#2768
Conversation
Greptile SummaryThis PR adds "stop all" controls for preview servers at two levels: a workspace-scoped "Stop All Servers" badge in the task toolbar (collapsing multiple pills when more than one server is running) and a global
Confidence Score: 4/5The feature is well-structured and tested; the main concern is that the settings-page bulk-stop swallows errors silently after the user confirms the dialog. After the user confirms "Stop preview servers", the settings card fires rpc.previewServers.stopAll() with void and no .catch(). If the IPC call fails, the modal has already closed and no toast or other feedback is shown — the servers remain running but the user receives no indication of failure. The command palette entry for the same action handles this correctly with a destructive toast on error, so the pattern is established; it just wasn't carried through to the settings card. apps/emdash-desktop/src/renderer/features/settings/components/PreviewServersSettingsCard.tsx — the onSuccess callback in the bulk-stop confirmation dialog needs error handling.
|
| Filename | Overview |
|---|---|
| apps/emdash-desktop/src/renderer/features/settings/components/PreviewServersSettingsCard.tsx | New settings card showing all running preview servers with per-row and bulk-stop actions; error from the bulk-stop RPC call is silently swallowed after the confirmation dialog closes. |
| apps/emdash-desktop/src/renderer/features/tasks/components/preview-servers/preview-servers-badge.tsx | New multi-server badge with nested sub-menus per server and a one-click "Stop All Servers" that fires without confirmation, inconsistent with the settings-page equivalent. |
| apps/emdash-desktop/src/main/core/preview-servers/preview-server-service.ts | Adds listAll() and stopAll() to the service, mirroring the existing stopForWorkspace pattern cleanly. |
| apps/emdash-desktop/src/main/core/preview-servers/controller.ts | Exposes three new RPC endpoints (listAll, stopForWorkspace, stopAll) wiring directly to the service methods. |
| apps/emdash-desktop/src/renderer/features/tasks/stores/preview-server-store.ts | Adds stopAll() which calls stopForWorkspace RPC then clears local state; straightforward and covered by the new test. |
| apps/emdash-desktop/src/renderer/features/tasks/components/preview-servers/preview-server-menu-items.tsx | Extracts the per-server dropdown items from the pill into a reusable component; purely mechanical refactor with no logic changes. |
| apps/emdash-desktop/src/renderer/lib/commands/app-commands.ts | Registers app.stopAllPreviewServers command with proper .catch() error handling that surfaces a destructive toast. |
| apps/emdash-desktop/src/renderer/features/tasks/components/preview-servers/preview-server-format.ts | Refactors status-class helper and adds previewServersSummaryStatusKind for computing aggregate badge state across multiple servers. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User
participant SettingsCard as PreviewServersSettingsCard
participant Badge as PreviewServersBadge
participant Store as PreviewServerStore
participant RPC as IPC / RPC
participant Service as PreviewServerService
participant Events as previewServerEventChannel
User->>SettingsCard: Click Stop all then confirm dialog
SettingsCard->>RPC: rpc.previewServers.stopAll()
RPC->>Service: stopAll()
Service->>Service: stop(id) x N via Promise.all
Service-->>Events: emit remove x N
Events-->>SettingsCard: update server list
User->>Badge: Click Stop All Servers no dialog
Badge->>Store: previews.stopAll()
Store->>RPC: rpc.previewServers.stopForWorkspace(projectId, workspaceId)
RPC->>Service: stopForWorkspace(projectId, workspaceId)
Service-->>Events: emit remove x N
Store->>Store: serversResource.setValue(empty Map)
Note over SettingsCard,Events: Settings card keeps list live via event subscription and initial listAll() fetch
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User
participant SettingsCard as PreviewServersSettingsCard
participant Badge as PreviewServersBadge
participant Store as PreviewServerStore
participant RPC as IPC / RPC
participant Service as PreviewServerService
participant Events as previewServerEventChannel
User->>SettingsCard: Click Stop all then confirm dialog
SettingsCard->>RPC: rpc.previewServers.stopAll()
RPC->>Service: stopAll()
Service->>Service: stop(id) x N via Promise.all
Service-->>Events: emit remove x N
Events-->>SettingsCard: update server list
User->>Badge: Click Stop All Servers no dialog
Badge->>Store: previews.stopAll()
Store->>RPC: rpc.previewServers.stopForWorkspace(projectId, workspaceId)
RPC->>Service: stopForWorkspace(projectId, workspaceId)
Service-->>Events: emit remove x N
Store->>Store: serversResource.setValue(empty Map)
Note over SettingsCard,Events: Settings card keeps list live via event subscription and initial listAll() fetch
Comments Outside Diff (1)
-
apps/emdash-desktop/src/renderer/features/tasks/components/preview-servers/preview-servers-badge.tsx, line 720-723 (link)No confirmation before stopping all workspace servers
"Stop All Servers" executes immediately without a confirmation dialog, while the equivalent action in
PreviewServersSettingsCardrequires the user to confirm first. The badge can be clicked accidentally, silently stopping every server visible in the current workspace with no undo path.Prompt To Fix With AI
This is a comment left during a code review. Path: apps/emdash-desktop/src/renderer/features/tasks/components/preview-servers/preview-servers-badge.tsx Line: 720-723 Comment: **No confirmation before stopping all workspace servers** "Stop All Servers" executes immediately without a confirmation dialog, while the equivalent action in `PreviewServersSettingsCard` requires the user to confirm first. The badge can be clicked accidentally, silently stopping every server visible in the current workspace with no undo path. How can I resolve this? If you propose a fix, please make it concise.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
apps/emdash-desktop/src/renderer/features/settings/components/PreviewServersSettingsCard.tsx:175-177
**Silent error swallowed after user confirmation**
`rpc.previewServers.stopAll()` is fired with `void` after the user confirms the destructive dialog, so any RPC failure is silently dropped. The modal closes and the servers remain running, but the user receives no feedback. Compare with the command palette implementation in `app-commands.ts`, which handles the same call with `.catch()` and surfaces a destructive toast on failure.
### Issue 2 of 2
apps/emdash-desktop/src/renderer/features/tasks/components/preview-servers/preview-servers-badge.tsx:720-723
**No confirmation before stopping all workspace servers**
"Stop All Servers" executes immediately without a confirmation dialog, while the equivalent action in `PreviewServersSettingsCard` requires the user to confirm first. The badge can be clicked accidentally, silently stopping every server visible in the current workspace with no undo path.
Reviews (1): Last reviewed commit: "fix(preview): confirm stopping all serve..." | Re-trigger Greptile
|
I'm not able to watch the recording:) |
Description
adds the ability to see and stop preview servers beyond a single pill.
Screenshot/Recording (if applicable)
https://cap.link/0xmcmymtrcdwfkb
Checklist
messages and, when possible, the PR title