feat(app): restore reload action for agent tabs

This commit is contained in:
Mohamed Boudra
2026-04-05 09:21:45 +07:00
parent 7a2d976081
commit c32bc847bf
7 changed files with 148 additions and 3 deletions
@@ -14,6 +14,7 @@ import {
ArrowRightToLine,
Columns2,
Copy,
RotateCw,
Rows2,
SquarePen,
SquareTerminal,
@@ -71,6 +72,7 @@ type WorkspaceDesktopTabsRowProps = {
onCloseTab: (tabId: string) => Promise<void> | void;
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
onCloseTabsToRight: (tabId: string) => Promise<void> | void;
onCloseOtherTabs: (tabId: string) => Promise<void> | void;
@@ -291,11 +293,14 @@ function TabChip({
disabled={entry.disabled}
destructive={entry.destructive}
onSelect={entry.onSelect}
tooltip={entry.tooltip}
leading={(() => {
const iconColor = theme.colors.foregroundMuted;
switch (entry.icon) {
case "copy":
return <Copy size={16} color={iconColor} />;
case "rotate-cw":
return <RotateCw size={16} color={iconColor} />;
case "arrow-left-to-line":
return <ArrowLeftToLine size={16} color={iconColor} />;
case "arrow-right-to-line":
@@ -335,6 +340,7 @@ export function WorkspaceDesktopTabsRow({
onCloseTab,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTabsToLeft,
onCloseTabsToRight,
onCloseOtherTabs,
@@ -457,6 +463,7 @@ export function WorkspaceDesktopTabsRow({
normalizedWorkspaceId={normalizedWorkspaceId}
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onCloseTabsToLeft={onCloseTabsToLeft}
onCloseTabsToRight={onCloseTabsToRight}
onCloseOtherTabs={onCloseOtherTabs}
@@ -585,6 +592,7 @@ function ResolvedDesktopTabChip({
normalizedWorkspaceId,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTabsToLeft,
onCloseTabsToRight,
onCloseOtherTabs,
@@ -608,6 +616,7 @@ function ResolvedDesktopTabChip({
normalizedWorkspaceId: string;
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
onCloseTabsToRight: (tabId: string) => Promise<void> | void;
onCloseOtherTabs: (tabId: string) => Promise<void> | void;
@@ -630,6 +639,7 @@ function ResolvedDesktopTabChip({
tabCount,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTab,
onCloseTabsToLeft,
onCloseTabsToRight,
@@ -644,6 +654,7 @@ function ResolvedDesktopTabChip({
onCloseTabsToRight,
onCopyAgentId,
onCopyResumeCommand,
onReloadAgent,
tabCount,
],
);
@@ -21,6 +21,7 @@ import {
Ellipsis,
EllipsisVertical,
PanelRight,
RotateCw,
SquarePen,
SquareTerminal,
X,
@@ -182,6 +183,7 @@ type MobileWorkspaceTabSwitcherProps = {
onSelectSwitcherTab: (key: string) => void;
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onCloseTab: (tabId: string) => Promise<void> | void;
onCloseTabsAbove: (tabId: string) => Promise<void> | void;
onCloseTabsBelow: (tabId: string) => Promise<void> | void;
@@ -269,6 +271,7 @@ function MobileWorkspaceTabOption({
onPress,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTab,
onCloseTabsAbove,
onCloseTabsBelow,
@@ -284,6 +287,7 @@ function MobileWorkspaceTabOption({
onPress: () => void;
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onCloseTab: (tabId: string) => Promise<void> | void;
onCloseTabsAbove: (tabId: string) => Promise<void> | void;
onCloseTabsBelow: (tabId: string) => Promise<void> | void;
@@ -299,6 +303,7 @@ function MobileWorkspaceTabOption({
menuTestIDBase,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTab,
onCloseTabsBefore: onCloseTabsAbove,
onCloseTabsAfter: onCloseTabsBelow,
@@ -342,11 +347,14 @@ function MobileWorkspaceTabOption({
disabled={entry.disabled}
destructive={entry.destructive}
onSelect={entry.onSelect}
tooltip={entry.tooltip}
leading={(() => {
const iconColor = theme.colors.foregroundMuted;
switch (entry.icon) {
case "copy":
return <Copy size={16} color={iconColor} />;
case "rotate-cw":
return <RotateCw size={16} color={iconColor} />;
case "arrow-left-to-line":
return <ArrowLeftToLine size={16} color={iconColor} />;
case "arrow-right-to-line":
@@ -389,6 +397,7 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
onSelectSwitcherTab,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTab,
onCloseTabsAbove,
onCloseTabsBelow,
@@ -460,6 +469,7 @@ const MobileWorkspaceTabSwitcher = memo(function MobileWorkspaceTabSwitcher({
onPress={onPress}
onCopyResumeCommand={onCopyResumeCommand}
onCopyAgentId={onCopyAgentId}
onReloadAgent={onReloadAgent}
onCloseTab={onCloseTab}
onCloseTabsAbove={onCloseTabsAbove}
onCloseTabsBelow={onCloseTabsBelow}
@@ -1366,6 +1376,23 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
[normalizedServerId, toast],
);
const handleReloadAgent = useCallback(
async (agentId: string) => {
if (!client || !isConnected) {
toast.error("Host is not connected");
return;
}
try {
await client.refreshAgent(agentId);
toast.show("Reloaded agent", { variant: "success" });
} catch (error) {
toast.error(error instanceof Error ? error.message : "Failed to reload agent");
}
},
[client, isConnected, toast],
);
const handleCopyWorkspacePath = useCallback(async () => {
if (!isAbsolutePath(normalizedWorkspaceId)) {
toast.error("Workspace path not available");
@@ -2211,6 +2238,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
onSelectSwitcherTab={handleSelectSwitcherTab}
onCopyResumeCommand={handleCopyResumeCommand}
onCopyAgentId={handleCopyAgentId}
onReloadAgent={handleReloadAgent}
onCloseTab={handleCloseTabById}
onCloseTabsAbove={handleCloseTabsToLeft}
onCloseTabsBelow={handleCloseTabsToRight}
@@ -2231,6 +2259,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
onCloseTab={handleCloseTabById}
onCopyResumeCommand={handleCopyResumeCommand}
onCopyAgentId={handleCopyAgentId}
onReloadAgent={handleReloadAgent}
onCloseTabsToLeft={handleCloseTabsToLeft}
onCloseTabsToRight={handleCloseTabsToRight}
onCloseOtherTabs={handleCloseOtherTabs}
@@ -2267,6 +2296,7 @@ function WorkspaceScreenContent({ serverId, workspaceId }: WorkspaceScreenProps)
onCloseTab={handleCloseTabById}
onCopyResumeCommand={handleCopyResumeCommand}
onCopyAgentId={handleCopyAgentId}
onReloadAgent={handleReloadAgent}
onCloseTabsToLeft={handleCloseTabsToLeftInPane}
onCloseTabsToRight={handleCloseTabsToRightInPane}
onCloseOtherTabs={handleCloseOtherTabsInPane}
@@ -15,6 +15,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
it("uses desktop tab ordering labels for desktop menus", () => {
const onCopyResumeCommand = vi.fn();
const onCopyAgentId = vi.fn();
const onReloadAgent = vi.fn();
const onCloseTab = vi.fn();
const onCloseTabsBefore = vi.fn();
const onCloseTabsAfter = vi.fn();
@@ -28,6 +29,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
menuTestIDBase: "workspace-tab-context-agent_123",
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTab,
onCloseTabsBefore,
onCloseTabsAfter,
@@ -37,6 +39,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
expect(entries.filter((entry) => entry.kind === "item").map((entry) => entry.label)).toEqual([
"Copy resume command",
"Copy agent id",
"Reload agent",
"Close to the left",
"Close to the right",
"Close other tabs",
@@ -53,6 +56,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
menuTestIDBase: "workspace-tab-menu-agent_123",
onCopyResumeCommand: vi.fn(),
onCopyAgentId: vi.fn(),
onReloadAgent: vi.fn(),
onCloseTab: vi.fn(),
onCloseTabsBefore: vi.fn(),
onCloseTabsAfter: vi.fn(),
@@ -62,6 +66,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
expect(entries.filter((entry) => entry.kind === "item").map((entry) => entry.label)).toEqual([
"Copy resume command",
"Copy agent id",
"Reload agent",
"Close tabs above",
"Close tabs below",
"Close other tabs",
@@ -83,6 +88,7 @@ describe("buildWorkspaceTabMenuEntries", () => {
menuTestIDBase: "workspace-tab-menu-draft_123",
onCopyResumeCommand: vi.fn(),
onCopyAgentId: vi.fn(),
onReloadAgent: vi.fn(),
onCloseTab: vi.fn(),
onCloseTabsBefore: vi.fn(),
onCloseTabsAfter: vi.fn(),
@@ -92,6 +98,34 @@ describe("buildWorkspaceTabMenuEntries", () => {
expect(entries.some((entry) => entry.kind === "item" && entry.label === "Copy agent id")).toBe(
false,
);
expect(entries.some((entry) => entry.kind === "item" && entry.label === "Reload agent")).toBe(
false,
);
expect(entries.some((entry) => entry.kind === "separator")).toBe(false);
});
it("adds reload tooltip copy for agent tabs", () => {
const entries = buildWorkspaceTabMenuEntries({
surface: "desktop",
tab: createAgentTab(),
index: 0,
tabCount: 1,
menuTestIDBase: "workspace-tab-context-agent_123",
onCopyResumeCommand: vi.fn(),
onCopyAgentId: vi.fn(),
onReloadAgent: vi.fn(),
onCloseTab: vi.fn(),
onCloseTabsBefore: vi.fn(),
onCloseTabsAfter: vi.fn(),
onCloseOtherTabs: vi.fn(),
});
expect(entries).toContainEqual(
expect.objectContaining({
kind: "item",
key: "reload-agent",
tooltip: "Reload agent to update skills, MCPs or login status.",
}),
);
});
});
@@ -8,8 +8,15 @@ export type WorkspaceTabMenuEntry =
kind: "item";
key: string;
label: string;
icon?: "copy" | "arrow-left-to-line" | "arrow-right-to-line" | "copy-x" | "x";
icon?:
| "copy"
| "rotate-cw"
| "arrow-left-to-line"
| "arrow-right-to-line"
| "copy-x"
| "x";
hint?: string;
tooltip?: string;
disabled?: boolean;
destructive?: boolean;
testID: string;
@@ -28,6 +35,7 @@ interface BuildWorkspaceTabMenuEntriesInput {
menuTestIDBase: string;
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onCloseTab: (tabId: string) => Promise<void> | void;
onCloseTabsBefore: (tabId: string) => Promise<void> | void;
onCloseTabsAfter: (tabId: string) => Promise<void> | void;
@@ -40,6 +48,7 @@ interface BuildWorkspaceDesktopTabActionsInput {
tabCount: number;
onCopyResumeCommand: (agentId: string) => Promise<void> | void;
onCopyAgentId: (agentId: string) => Promise<void> | void;
onReloadAgent: (agentId: string) => Promise<void> | void;
onCloseTab: (tabId: string) => Promise<void> | void;
onCloseTabsToLeft: (tabId: string) => Promise<void> | void;
onCloseTabsToRight: (tabId: string) => Promise<void> | void;
@@ -92,6 +101,7 @@ export function buildWorkspaceTabMenuEntries(
menuTestIDBase,
onCopyResumeCommand,
onCopyAgentId,
onReloadAgent,
onCloseTab,
onCloseTabsBefore,
onCloseTabsAfter,
@@ -125,6 +135,17 @@ export function buildWorkspaceTabMenuEntries(
void onCopyAgentId(agentId);
},
});
entries.push({
kind: "item",
key: "reload-agent",
label: "Reload agent",
icon: "rotate-cw",
tooltip: "Reload agent to update skills, MCPs or login status.",
testID: `${menuTestIDBase}-reload-agent`,
onSelect: () => {
void onReloadAgent(agentId);
},
});
entries.push({
kind: "separator",
key: "copy-separator",
@@ -192,6 +213,7 @@ export function buildWorkspaceDesktopTabActions(
menuTestIDBase: contextMenuTestId,
onCopyResumeCommand: input.onCopyResumeCommand,
onCopyAgentId: input.onCopyAgentId,
onReloadAgent: input.onReloadAgent,
onCloseTab: input.onCloseTab,
onCloseTabsBefore: input.onCloseTabsToLeft,
onCloseTabsAfter: input.onCloseTabsToRight,