From 18a6e8106cb969a7392a8c4ad5235fb3ef473c41 Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Wed, 8 Apr 2026 18:18:37 -0400 Subject: [PATCH 1/2] feat(desktop): add hover tooltips to menu items and icon buttons Dropdown items in the agents screen gave no hint about what they did, making the interface opaque for new users and reducing accessibility for screen reader users relying on title text. Advanced dialog fields also had no descriptions, leaving users to guess what Relay URL, ACP command, MCP command, and turn timeout actually control. Radix DropdownMenuItem doesn't support Radix Tooltip natively (portal layering conflicts), so title attributes are used on all dropdown items as a lightweight, reliable fallback that works without any Radix portal workaround. Tooltip components were already in place for icon-only buttons in these sections. --- .../agents/ui/CreateAgentDialogSections.tsx | 19 ++++++++++++++++ .../features/agents/ui/ManagedAgentRow.tsx | 22 ++++++++++++++++++- .../features/agents/ui/PersonasSection.tsx | 5 +++++ .../src/features/agents/ui/TeamsSection.tsx | 5 +++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx b/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx index f0af718dd..dfa657b15 100644 --- a/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx +++ b/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx @@ -144,6 +144,10 @@ export function CreateAgentRuntimeFields({ placeholder="Leave blank to use the desktop relay" value={relayUrl} /> +

+ WebSocket URL of the relay this agent connects to. Leave blank to + use the built-in desktop relay. +

@@ -155,6 +159,10 @@ export function CreateAgentRuntimeFields({ onChange={(event) => onAcpCommandChange(event.target.value)} value={acpCommand} /> +

+ The `sprout-acp` binary path or alias used to launch the ACP harness + process. +

@@ -171,6 +179,10 @@ export function CreateAgentRuntimeFields({ onChange={(event) => onAgentCommandChange(event.target.value)} value={agentCommand} /> +

+ Full path or shell command for the agent binary when no known ACP + runtime was detected. +

) : null} @@ -199,6 +211,10 @@ export function CreateAgentRuntimeFields({ onChange={(event) => onMcpCommandChange(event.target.value)} value={mcpCommand} /> +

+ Command the ACP harness uses to start the MCP tool server for this + agent. +

@@ -211,6 +227,9 @@ export function CreateAgentRuntimeFields({ placeholder="300" value={turnTimeoutSeconds} /> +

+ Seconds before an agent turn is cancelled. Defaults to 300. +

diff --git a/desktop/src/features/agents/ui/ManagedAgentRow.tsx b/desktop/src/features/agents/ui/ManagedAgentRow.tsx index 372414a45..6bbd42e85 100644 --- a/desktop/src/features/agents/ui/ManagedAgentRow.tsx +++ b/desktop/src/features/agents/ui/ManagedAgentRow.tsx @@ -319,6 +319,11 @@ function AgentActionsMenu({ onStart(agent.pubkey)} + title={ + isActive + ? "Push a new deployment to the provider" + : "Deploy this agent to the provider" + } > {isActive ? "Redeploy" : "Deploy"} @@ -326,6 +331,7 @@ function AgentActionsMenu({ onStop(agent.pubkey)} + title="Stop the provider deployment and free its resources" > Shutdown @@ -335,6 +341,7 @@ function AgentActionsMenu({ onStop(agent.pubkey)} + title="Stop the running ACP harness process" > Stop @@ -343,6 +350,7 @@ function AgentActionsMenu({ onStart(agent.pubkey)} + title="Launch the local ACP harness process for this agent" > Spawn @@ -352,6 +360,7 @@ function AgentActionsMenu({ onAddToChannel(agent)} + title="Invite this agent to a channel so it can participate in conversations" > Add to channel @@ -360,6 +369,7 @@ function AgentActionsMenu({ onMintToken(agent.pubkey, agent.name)} + title="Generate a bearer token this agent uses to authenticate with the relay" > Mint token @@ -367,13 +377,17 @@ function AgentActionsMenu({ navigator.clipboard.writeText(agent.pubkey)} + title="Copy the agent's public key to the clipboard" > Copy pubkey {agent.backend.type === "local" ? ( - onOpenLogs(agent.pubkey)}> + onOpenLogs(agent.pubkey)} + title="Show the ACP harness stdout/stderr log inline" + > View logs @@ -385,6 +399,11 @@ function AgentActionsMenu({ onClick={() => onToggleStartOnAppLaunch(agent.pubkey, !agent.startOnAppLaunch) } + title={ + agent.startOnAppLaunch + ? "Stop launching this agent automatically when the desktop app starts" + : "Launch this agent automatically every time the desktop app starts" + } > {agent.startOnAppLaunch @@ -399,6 +418,7 @@ function AgentActionsMenu({ className="text-destructive focus:text-destructive" disabled={isActionPending} onClick={() => onDelete(agent.pubkey)} + title="Permanently remove this agent profile from the desktop app" > Delete diff --git a/desktop/src/features/agents/ui/PersonasSection.tsx b/desktop/src/features/agents/ui/PersonasSection.tsx index f4123cef8..53034790c 100644 --- a/desktop/src/features/agents/ui/PersonasSection.tsx +++ b/desktop/src/features/agents/ui/PersonasSection.tsx @@ -153,6 +153,7 @@ export function PersonasSection({ onEdit(persona)} + title="Edit this persona's name, avatar, and system prompt" > Edit @@ -161,6 +162,7 @@ export function PersonasSection({ onDuplicate(persona)} + title="Create a copy of this persona you can customize independently" > Duplicate @@ -168,6 +170,7 @@ export function PersonasSection({ onExport(persona)} + title="Save this persona as a .persona.json file you can share or back up" > Export @@ -177,6 +180,7 @@ export function PersonasSection({ className="text-destructive focus:text-destructive" disabled={isPending} onClick={() => onDeactivate(persona)} + title="Remove this built-in persona from your agent library" > Remove from My Agents @@ -186,6 +190,7 @@ export function PersonasSection({ className="text-destructive focus:text-destructive" disabled={isPending} onClick={() => onDelete(persona)} + title="Permanently delete this persona" > Delete diff --git a/desktop/src/features/agents/ui/TeamsSection.tsx b/desktop/src/features/agents/ui/TeamsSection.tsx index 91d27fccb..dfcab5030 100644 --- a/desktop/src/features/agents/ui/TeamsSection.tsx +++ b/desktop/src/features/agents/ui/TeamsSection.tsx @@ -200,6 +200,7 @@ export function TeamsSection({ onAddToChannel(team)} + title="Add all personas in this team to a channel at once" > Deploy to channel @@ -208,6 +209,7 @@ export function TeamsSection({ onEdit(team)} + title="Edit this team's name, description, and persona membership" > Edit @@ -215,6 +217,7 @@ export function TeamsSection({ onDuplicate(team)} + title="Create a copy of this team you can customize independently" > Duplicate @@ -222,6 +225,7 @@ export function TeamsSection({ onExport(team)} + title="Save this team as a .team.json file you can share or back up" > Export @@ -231,6 +235,7 @@ export function TeamsSection({ className="text-destructive focus:text-destructive" disabled={isPending} onClick={() => onDelete(team)} + title="Permanently delete this team" > Delete From 860d965a6f4e040dfed13a6abbb2aa8e48c8662b Mon Sep 17 00:00:00 2001 From: Will Pfleger Date: Wed, 8 Apr 2026 19:05:10 -0400 Subject: [PATCH 2/2] fix(desktop): replace broken title tooltips with inline subtitles HTML title attributes don't render in Tauri's WKWebView, making dropdown tooltips invisible. Switch to two-line menu items with muted subtitle text. Also add aria-describedby links for helper text and strip literal backtick characters from JSX strings. --- .../agents/ui/CreateAgentDialogSections.tsx | 59 +++++++--- .../features/agents/ui/ManagedAgentRow.tsx | 104 ++++++++++++------ .../features/agents/ui/PersonasSection.tsx | 43 ++++++-- .../src/features/agents/ui/TeamsSection.tsx | 43 ++++++-- 4 files changed, 181 insertions(+), 68 deletions(-) diff --git a/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx b/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx index dfa657b15..f066b0834 100644 --- a/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx +++ b/desktop/src/features/agents/ui/CreateAgentDialogSections.tsx @@ -22,6 +22,7 @@ export function CreateAgentBasicsFields({ Agent name -

+

Used as the local label and synced to the agent profile display name when the relay accepts the create-time auth.

@@ -139,12 +140,16 @@ export function CreateAgentRuntimeFields({ Relay URL onRelayUrlChange(event.target.value)} placeholder="Leave blank to use the desktop relay" value={relayUrl} /> -

+

WebSocket URL of the relay this agent connects to. Leave blank to use the built-in desktop relay.

@@ -155,12 +160,16 @@ export function CreateAgentRuntimeFields({ ACP command onAcpCommandChange(event.target.value)} value={acpCommand} /> -

- The `sprout-acp` binary path or alias used to launch the ACP harness +

+ The sprout-acp binary path or alias used to launch the ACP harness process.

@@ -175,11 +184,15 @@ export function CreateAgentRuntimeFields({ Custom agent runtime command onAgentCommandChange(event.target.value)} value={agentCommand} /> -

+

Full path or shell command for the agent binary when no known ACP runtime was detected.

@@ -192,13 +205,17 @@ export function CreateAgentRuntimeFields({ Agent runtime args onAgentArgsChange(event.target.value)} placeholder="Comma-separated" value={agentArgs} /> -

- `sprout-acp` splits args on commas, matching the testing guide. +

+ sprout-acp splits args on commas, matching the testing guide.

@@ -207,11 +224,15 @@ export function CreateAgentRuntimeFields({ MCP command onMcpCommandChange(event.target.value)} value={mcpCommand} /> -

+

Command the ACP harness uses to start the MCP tool server for this agent.

@@ -222,12 +243,13 @@ export function CreateAgentRuntimeFields({ Turn timeout onTurnTimeoutChange(event.target.value)} placeholder="300" value={turnTimeoutSeconds} /> -

+

Seconds before an agent turn is cancelled. Defaults to 300.

@@ -237,6 +259,7 @@ export function CreateAgentRuntimeFields({ Parallelism -

- Number of ACP worker subprocesses. `sprout-acp` allows 1-32. +

+ Number of ACP worker subprocesses. sprout-acp allows 1-32.

@@ -259,15 +285,18 @@ export function CreateAgentRuntimeFields({ System prompt override