Merge branch 'main' into dev
Surgical merge of 74 commits from main into dev. Key features ported: - Provider visibility gating (appVersion filtering for Pi/Copilot) - Pi agent provider + Copilot re-enabled - Provider-declared features system (Codex fast mode) - Workspace dedup by worktree root (adapted to integer IDs) - Bulk close archiving fix for stored agents - Agent creation timeout increase to 60s - Audio/voice crash fixes (external buffer copies) - Multi-host setup fix - Reload agent tab action Dev architecture preserved: SQLite storage, service proxy, workspace setup dialog, hover cards, removed launcher tabs.
This commit is contained in:
@@ -8,7 +8,7 @@ import { MenuHeader } from "@/components/headers/menu-header";
|
||||
import { useOpenProjectPicker } from "@/hooks/use-open-project-picker";
|
||||
import { usePanelStore } from "@/stores/panel-store";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { isCompactFormFactor } from "@/constants/layout";
|
||||
import { isCompactFormFactor, HEADER_INNER_HEIGHT, HEADER_INNER_HEIGHT_MOBILE, HEADER_TOP_PADDING_MOBILE } from "@/constants/layout";
|
||||
import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
|
||||
|
||||
export function OpenProjectScreen({ serverId }: { serverId: string }) {
|
||||
@@ -59,11 +59,15 @@ const styles = StyleSheet.create((theme) => ({
|
||||
},
|
||||
content: {
|
||||
position: "relative",
|
||||
flexGrow: 1,
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: 0,
|
||||
padding: theme.spacing[6],
|
||||
paddingBottom: {
|
||||
xs: HEADER_INNER_HEIGHT_MOBILE + HEADER_TOP_PADDING_MOBILE + theme.spacing[6],
|
||||
md: HEADER_INNER_HEIGHT + theme.spacing[6],
|
||||
},
|
||||
},
|
||||
logo: {
|
||||
marginBottom: theme.spacing[8],
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
Stethoscope,
|
||||
Info,
|
||||
Shield,
|
||||
Puzzle,
|
||||
} from "lucide-react-native";
|
||||
import { useAppSettings, type AppSettings } from "@/hooks/use-settings";
|
||||
import type { HostProfile, HostConnection } from "@/types/host-connection";
|
||||
@@ -50,6 +51,7 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { AdaptiveModalSheet, AdaptiveTextInput } from "@/components/adaptive-modal-sheet";
|
||||
import { DesktopPermissionsSection } from "@/desktop/components/desktop-permissions-section";
|
||||
import { IntegrationsSection } from "@/desktop/components/integrations-section";
|
||||
import { LocalDaemonSection } from "@/desktop/components/desktop-updates-section";
|
||||
import { isElectronRuntime } from "@/desktop/host";
|
||||
import { useDesktopAppUpdater } from "@/desktop/updates/use-desktop-app-updater";
|
||||
@@ -69,6 +71,7 @@ type SettingsSectionId =
|
||||
| "hosts"
|
||||
| "appearance"
|
||||
| "shortcuts"
|
||||
| "integrations"
|
||||
| "diagnostics"
|
||||
| "about"
|
||||
| "permissions"
|
||||
@@ -85,17 +88,21 @@ function getSettingsSections(context: { isDesktopApp: boolean }): SettingsSectio
|
||||
{ id: "hosts", label: "Hosts", icon: Server },
|
||||
{ id: "appearance", label: "Appearance", icon: Palette },
|
||||
{ id: "shortcuts", label: "Shortcuts", icon: Keyboard },
|
||||
{ id: "diagnostics", label: "Diagnostics", icon: Stethoscope },
|
||||
{ id: "about", label: "About", icon: Info },
|
||||
{ id: "permissions", label: "Permissions", icon: Shield },
|
||||
];
|
||||
|
||||
if (context.isDesktopApp) {
|
||||
sections.push(
|
||||
{ id: "permissions", label: "Permissions", icon: Shield },
|
||||
{ id: "integrations", label: "Integrations", icon: Puzzle },
|
||||
{ id: "daemon", label: "Daemon", icon: Settings },
|
||||
);
|
||||
}
|
||||
|
||||
sections.push(
|
||||
{ id: "diagnostics", label: "Diagnostics", icon: Stethoscope },
|
||||
{ id: "about", label: "About", icon: Info },
|
||||
);
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
@@ -177,13 +184,10 @@ interface HostsSectionProps {
|
||||
routeServerId: string;
|
||||
theme: ReturnType<typeof useUnistyles>["theme"];
|
||||
handleEditDaemon: (profile: HostProfile) => void;
|
||||
setAddConnectionTargetServerId: (id: string | null) => void;
|
||||
setPendingEditReopenServerId: (id: string | null) => void;
|
||||
setIsAddHostMethodVisible: (visible: boolean) => void;
|
||||
isAddHostMethodVisible: boolean;
|
||||
isDirectHostVisible: boolean;
|
||||
isPasteLinkVisible: boolean;
|
||||
addConnectionTargetServerId: string | null;
|
||||
closeAddConnectionFlow: () => void;
|
||||
goBackToAddConnectionMethods: () => void;
|
||||
setIsDirectHostVisible: (visible: boolean) => void;
|
||||
@@ -203,7 +207,6 @@ interface HostsSectionProps {
|
||||
handleSaveEditDaemon: (label: string) => Promise<void>;
|
||||
handleRemoveConnection: (serverId: string, connectionId: string) => Promise<void>;
|
||||
handleRemoveDaemon: (profile: HostProfile) => void;
|
||||
handleAddConnectionFromModal: () => void;
|
||||
restartConfirmationMessage: string;
|
||||
waitForCondition: (
|
||||
predicate: () => boolean,
|
||||
@@ -243,8 +246,6 @@ function HostsSection(props: HostsSectionProps) {
|
||||
style={styles.addButton}
|
||||
textStyle={styles.addButtonText}
|
||||
onPress={() => {
|
||||
props.setAddConnectionTargetServerId(null);
|
||||
props.setPendingEditReopenServerId(null);
|
||||
props.setIsAddHostMethodVisible(true);
|
||||
}}
|
||||
>
|
||||
@@ -264,22 +265,17 @@ function HostsSection(props: HostsSectionProps) {
|
||||
props.setIsPasteLinkVisible(true);
|
||||
}}
|
||||
onScanQr={() => {
|
||||
const targetServerId = props.addConnectionTargetServerId;
|
||||
const source = targetServerId ? "editHost" : "settings";
|
||||
const sourceServerId = props.routeServerId || targetServerId || undefined;
|
||||
const sourceServerId = props.routeServerId || undefined;
|
||||
props.closeAddConnectionFlow();
|
||||
router.push({
|
||||
pathname: "/pair-scan",
|
||||
params: targetServerId
|
||||
? { source, targetServerId, sourceServerId }
|
||||
: { source, sourceServerId },
|
||||
params: { source: "settings", sourceServerId },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<AddHostModal
|
||||
visible={props.isDirectHostVisible}
|
||||
targetServerId={props.addConnectionTargetServerId ?? undefined}
|
||||
onClose={props.closeAddConnectionFlow}
|
||||
onCancel={props.goBackToAddConnectionMethods}
|
||||
onSaved={({ serverId, hostname, isNewHost }) => {
|
||||
@@ -291,7 +287,6 @@ function HostsSection(props: HostsSectionProps) {
|
||||
|
||||
<PairLinkModal
|
||||
visible={props.isPasteLinkVisible}
|
||||
targetServerId={props.addConnectionTargetServerId ?? undefined}
|
||||
onClose={props.closeAddConnectionFlow}
|
||||
onCancel={props.goBackToAddConnectionMethods}
|
||||
onSaved={({ serverId, hostname, isNewHost }) => {
|
||||
@@ -371,7 +366,6 @@ function HostsSection(props: HostsSectionProps) {
|
||||
onSave={(label) => void props.handleSaveEditDaemon(label)}
|
||||
onRemoveConnection={props.handleRemoveConnection}
|
||||
onRemoveHost={props.handleRemoveDaemon}
|
||||
onAddConnection={props.handleAddConnectionFromModal}
|
||||
restartConfirmationMessage={props.restartConfirmationMessage}
|
||||
waitForCondition={props.waitForCondition}
|
||||
isScreenMountedRef={props.isMountedRef}
|
||||
@@ -520,6 +514,8 @@ function SettingsSectionContent({
|
||||
return <DiagnosticsSection {...diagnosticsProps} />;
|
||||
case "about":
|
||||
return <AboutSection {...aboutProps} />;
|
||||
case "integrations":
|
||||
return isDesktopApp ? <IntegrationsSection /> : null;
|
||||
case "permissions":
|
||||
return isDesktopApp ? <DesktopPermissionsSection /> : null;
|
||||
case "daemon":
|
||||
@@ -570,31 +566,35 @@ function SettingsDesktopLayout({ sections, sectionContentProps }: SettingsLayout
|
||||
{sections.map((section) => {
|
||||
const isSelected = section.id === selectedSectionId;
|
||||
const IconComponent = section.icon;
|
||||
const showSeparator =
|
||||
section.id === "integrations" || section.id === "diagnostics";
|
||||
return (
|
||||
<Pressable
|
||||
key={section.id}
|
||||
style={[
|
||||
desktopStyles.sidebarItem,
|
||||
isSelected && { backgroundColor: theme.colors.surface2 },
|
||||
]}
|
||||
onPress={() => setSelectedSectionId(section.id)}
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ selected: isSelected }}
|
||||
>
|
||||
<IconComponent
|
||||
size={theme.iconSize.md}
|
||||
color={isSelected ? theme.colors.foreground : theme.colors.foregroundMuted}
|
||||
/>
|
||||
<Text
|
||||
<View key={section.id}>
|
||||
{showSeparator ? <View style={desktopStyles.sidebarSeparator} /> : null}
|
||||
<Pressable
|
||||
style={[
|
||||
desktopStyles.sidebarLabel,
|
||||
isSelected && { color: theme.colors.foreground },
|
||||
desktopStyles.sidebarItem,
|
||||
isSelected && { backgroundColor: theme.colors.surface2 },
|
||||
]}
|
||||
numberOfLines={1}
|
||||
onPress={() => setSelectedSectionId(section.id)}
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ selected: isSelected }}
|
||||
>
|
||||
{section.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<IconComponent
|
||||
size={theme.iconSize.md}
|
||||
color={isSelected ? theme.colors.foreground : theme.colors.foregroundMuted}
|
||||
/>
|
||||
<Text
|
||||
style={[
|
||||
desktopStyles.sidebarLabel,
|
||||
isSelected && { color: theme.colors.foreground },
|
||||
]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{section.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
@@ -728,10 +728,6 @@ export default function SettingsScreen() {
|
||||
const [isAddHostMethodVisible, setIsAddHostMethodVisible] = useState(false);
|
||||
const [isDirectHostVisible, setIsDirectHostVisible] = useState(false);
|
||||
const [isPasteLinkVisible, setIsPasteLinkVisible] = useState(false);
|
||||
const [addConnectionTargetServerId, setAddConnectionTargetServerId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [pendingEditReopenServerId, setPendingEditReopenServerId] = useState<string | null>(null);
|
||||
const [pendingNameHost, setPendingNameHost] = useState<{
|
||||
serverId: string;
|
||||
hostname: string | null;
|
||||
@@ -810,7 +806,6 @@ export default function SettingsScreen() {
|
||||
setIsAddHostMethodVisible(false);
|
||||
setIsDirectHostVisible(false);
|
||||
setIsPasteLinkVisible(false);
|
||||
setAddConnectionTargetServerId(null);
|
||||
}, []);
|
||||
|
||||
const goBackToAddConnectionMethods = useCallback(() => {
|
||||
@@ -829,24 +824,6 @@ export default function SettingsScreen() {
|
||||
handleEditDaemon(profile);
|
||||
}, [daemons, handleEditDaemon, params.editHost]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingEditReopenServerId) return;
|
||||
if (isAddHostMethodVisible || isDirectHostVisible || isPasteLinkVisible) return;
|
||||
const profile = daemons.find((daemon) => daemon.serverId === pendingEditReopenServerId) ?? null;
|
||||
setPendingEditReopenServerId(null);
|
||||
setAddConnectionTargetServerId(null);
|
||||
if (profile) {
|
||||
handleEditDaemon(profile);
|
||||
}
|
||||
}, [
|
||||
daemons,
|
||||
handleEditDaemon,
|
||||
isAddHostMethodVisible,
|
||||
isDirectHostVisible,
|
||||
isPasteLinkVisible,
|
||||
pendingEditReopenServerId,
|
||||
]);
|
||||
|
||||
const handleSaveEditDaemon = useCallback(
|
||||
async (nextLabelRaw: string) => {
|
||||
if (!editingServerId) return;
|
||||
@@ -884,15 +861,6 @@ export default function SettingsScreen() {
|
||||
setPendingRemoveHost(profile);
|
||||
}, []);
|
||||
|
||||
const handleAddConnectionFromModal = useCallback(() => {
|
||||
if (!editingServerId) return;
|
||||
const serverId = editingServerId;
|
||||
setEditingDaemon(null);
|
||||
setAddConnectionTargetServerId(serverId);
|
||||
setPendingEditReopenServerId(serverId);
|
||||
setIsAddHostMethodVisible(true);
|
||||
}, [editingServerId]);
|
||||
|
||||
const handleThemeChange = useCallback(
|
||||
(newTheme: AppSettings["theme"]) => {
|
||||
void updateSettings({ theme: newTheme });
|
||||
@@ -938,13 +906,10 @@ export default function SettingsScreen() {
|
||||
routeServerId,
|
||||
theme,
|
||||
handleEditDaemon,
|
||||
setAddConnectionTargetServerId,
|
||||
setPendingEditReopenServerId,
|
||||
setIsAddHostMethodVisible,
|
||||
isAddHostMethodVisible,
|
||||
isDirectHostVisible,
|
||||
isPasteLinkVisible,
|
||||
addConnectionTargetServerId,
|
||||
closeAddConnectionFlow,
|
||||
goBackToAddConnectionMethods,
|
||||
setIsDirectHostVisible,
|
||||
@@ -964,7 +929,6 @@ export default function SettingsScreen() {
|
||||
handleSaveEditDaemon,
|
||||
handleRemoveConnection,
|
||||
handleRemoveDaemon,
|
||||
handleAddConnectionFromModal,
|
||||
restartConfirmationMessage: "This will restart the daemon. The app will reconnect automatically.",
|
||||
waitForCondition,
|
||||
isMountedRef,
|
||||
@@ -1029,7 +993,6 @@ interface HostDetailModalProps {
|
||||
onSave: (label: string) => void;
|
||||
onRemoveConnection: (serverId: string, connectionId: string) => Promise<void>;
|
||||
onRemoveHost: (host: HostProfile) => void;
|
||||
onAddConnection: () => void;
|
||||
restartConfirmationMessage: string;
|
||||
waitForCondition: (
|
||||
predicate: () => boolean,
|
||||
@@ -1047,7 +1010,6 @@ function HostDetailModal({
|
||||
onSave,
|
||||
onRemoveConnection,
|
||||
onRemoveHost,
|
||||
onAddConnection,
|
||||
restartConfirmationMessage,
|
||||
waitForCondition,
|
||||
isScreenMountedRef,
|
||||
@@ -1278,15 +1240,6 @@ function HostDetailModal({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="md"
|
||||
style={styles.addButton}
|
||||
textStyle={styles.addButtonText}
|
||||
onPress={onAddConnection}
|
||||
>
|
||||
+ Add connection
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
@@ -1893,6 +1846,12 @@ const desktopStyles = StyleSheet.create((theme) => ({
|
||||
color: theme.colors.foregroundMuted,
|
||||
fontWeight: theme.fontWeight.normal,
|
||||
},
|
||||
sidebarSeparator: {
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: theme.colors.border,
|
||||
marginVertical: theme.spacing[2],
|
||||
marginHorizontal: theme.spacing[3],
|
||||
},
|
||||
contentPane: {
|
||||
flex: 1,
|
||||
},
|
||||
|
||||
@@ -192,8 +192,8 @@ export function KeyboardShortcutsSection() {
|
||||
|
||||
return (
|
||||
<View style={settingsStyles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={settingsStyles.sectionTitle}>Shortcuts</Text>
|
||||
<View style={settingsStyles.sectionHeader}>
|
||||
<Text style={settingsStyles.sectionHeaderTitle}>Shortcuts</Text>
|
||||
{hasOverrides && (
|
||||
<Button variant="ghost" size="sm" onPress={() => void resetAll()}>
|
||||
Reset all
|
||||
@@ -246,11 +246,6 @@ export function KeyboardShortcutsSection() {
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create((theme) => ({
|
||||
sectionHeader: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
},
|
||||
subsectionTitle: {
|
||||
color: theme.colors.foregroundMuted,
|
||||
fontSize: theme.fontSize.xs,
|
||||
|
||||
@@ -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;
|
||||
@@ -95,6 +104,7 @@ export function buildWorkspaceTabMenuEntries(
|
||||
menuTestIDBase,
|
||||
onCopyResumeCommand,
|
||||
onCopyAgentId,
|
||||
onReloadAgent,
|
||||
onCloseTab,
|
||||
onCloseTabsBefore,
|
||||
onCloseTabsAfter,
|
||||
@@ -128,6 +138,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",
|
||||
@@ -195,6 +216,7 @@ export function buildWorkspaceDesktopTabActions(
|
||||
menuTestIDBase: contextMenuTestId,
|
||||
onCopyResumeCommand: input.onCopyResumeCommand,
|
||||
onCopyAgentId: input.onCopyAgentId,
|
||||
onReloadAgent: input.onReloadAgent,
|
||||
onCloseTab: input.onCloseTab,
|
||||
onCloseTabsBefore: input.onCloseTabsToLeft,
|
||||
onCloseTabsAfter: input.onCloseTabsToRight,
|
||||
|
||||
Reference in New Issue
Block a user