feat: move stop button from shortcuts to kebab menu in workspace list (#21518)

## Summary

Moves the stop action from the icon-button shortcuts to the kebab menu
(WorkspaceMoreActions) in the workspaces list view.

## Problem

The stop icon was difficult to recognize without context in the
workspace list view. Users couldn't easily identify what the stop button
did based on the icon alone.

## Solution

- The stop action is not a primary action and doesn't need to be
highlighted in the icon-button view
- Moved the stop action into the kebab (⋮) menu
- The start button remains as a primary action when the workspace is
offline, since starting a workspace is a more common and expected action

## Changes

- `WorkspaceMoreActions`: Added optional `onStop` and `isStopPending`
props to conditionally render a "Stop" menu item
- `WorkspacesTable`: Removed the stop `PrimaryAction` button and instead
passes the stop callback to `WorkspaceMoreActions` when the workspace
can be stopped

## Testing

- TypeScript compiles without errors
- All existing tests pass
- Manually verified that the stop action appears in the kebab menu when
the workspace is running

Fixes #21516

---

Created on behalf of @jacobhqh1

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: Jake Howell <jake@hwll.me>
This commit is contained in:
blinkagent[bot]
2026-01-15 16:19:20 +00:00
committed by GitHub
co-authored by blink-so[bot] Jake Howell
parent 0ebe8e57ad
commit 7fc84ecf0b
2 changed files with 18 additions and 11 deletions
@@ -21,6 +21,7 @@ import {
EllipsisVertical,
HistoryIcon,
SettingsIcon,
SquareIcon,
TrashIcon,
} from "lucide-react";
import { type FC, useEffect, useState } from "react";
@@ -37,11 +38,15 @@ import { WorkspaceDeleteDialog } from "./WorkspaceDeleteDialog";
type WorkspaceMoreActionsProps = {
workspace: Workspace;
disabled: boolean;
onStop?: () => void;
isStopping?: boolean;
};
export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
workspace,
disabled,
onStop,
isStopping,
}) => {
const queryClient = useQueryClient();
@@ -115,6 +120,13 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
</DropdownMenuTrigger>
<DropdownMenuContent id="workspace-options" align="end">
{onStop && (
<DropdownMenuItem onClick={onStop} disabled={isStopping}>
<SquareIcon />
Stop&hellip;
</DropdownMenuItem>
)}
<DropdownMenuItem asChild>
<RouterLink
to={`/@${workspace.owner_name}/${workspace.name}/settings`}
@@ -53,7 +53,6 @@ import {
FileIcon,
PlayIcon,
RefreshCcwIcon,
SquareIcon,
SquareTerminalIcon,
StarIcon,
} from "lucide-react";
@@ -485,16 +484,6 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
</PrimaryAction>
)}
{abilities.actions.includes("stop") && (
<PrimaryAction
onClick={() => setIsStopConfirmOpen(true)}
isLoading={stopWorkspaceMutation.isPending}
label="Stop workspace"
>
<SquareIcon />
</PrimaryAction>
)}
{abilities.actions.includes("updateAndStart") && (
<>
<PrimaryAction
@@ -570,6 +559,12 @@ const WorkspaceActionsCell: FC<WorkspaceActionsCellProps> = ({
<WorkspaceMoreActions
workspace={workspace}
disabled={!abilities.canAcceptJobs}
onStop={
abilities.actions.includes("stop")
? () => setIsStopConfirmOpen(true)
: undefined
}
isStopping={stopWorkspaceMutation.isPending}
/>
</div>