Add inline subtitles to menu items and field descriptions (#276)

This commit is contained in:
Will Pfleger
2026-04-09 21:15:40 -04:00
committed by GitHub
parent b71f50400c
commit a791566c91
4 changed files with 193 additions and 31 deletions
@@ -22,6 +22,7 @@ export function CreateAgentBasicsFields({
Agent name
</label>
<Input
aria-describedby="help-agent-name"
autoCapitalize="none"
autoCorrect="off"
data-testid="agent-name-input"
@@ -31,7 +32,7 @@ export function CreateAgentBasicsFields({
spellCheck={false}
value={name}
/>
<p className="text-xs text-muted-foreground">
<p className="text-xs text-muted-foreground" id="help-agent-name">
Used as the local label and synced to the agent profile display name
when the relay accepts the create-time auth.
</p>
@@ -139,11 +140,19 @@ export function CreateAgentRuntimeFields({
Relay URL
</label>
<Input
aria-describedby="help-agent-relay-url"
id="agent-relay-url"
onChange={(event) => onRelayUrlChange(event.target.value)}
placeholder="Leave blank to use the desktop relay"
value={relayUrl}
/>
<p
className="text-xs text-muted-foreground"
id="help-agent-relay-url"
>
WebSocket URL of the relay this agent connects to. Leave blank to
use the built-in desktop relay.
</p>
</div>
<div className="space-y-1.5">
@@ -151,10 +160,18 @@ export function CreateAgentRuntimeFields({
ACP command
</label>
<Input
aria-describedby="help-agent-acp-command"
id="agent-acp-command"
onChange={(event) => onAcpCommandChange(event.target.value)}
value={acpCommand}
/>
<p
className="text-xs text-muted-foreground"
id="help-agent-acp-command"
>
The sprout-acp binary path or alias used to launch the ACP harness
process.
</p>
</div>
</div>
@@ -167,10 +184,18 @@ export function CreateAgentRuntimeFields({
Custom agent runtime command
</label>
<Input
aria-describedby="help-agent-runtime-command"
id="agent-runtime-command"
onChange={(event) => onAgentCommandChange(event.target.value)}
value={agentCommand}
/>
<p
className="text-xs text-muted-foreground"
id="help-agent-runtime-command"
>
Full path or shell command for the agent binary when no known ACP
runtime was detected.
</p>
</div>
) : null}
@@ -180,13 +205,17 @@ export function CreateAgentRuntimeFields({
Agent runtime args
</label>
<Input
aria-describedby="help-agent-runtime-args"
id="agent-runtime-args"
onChange={(event) => onAgentArgsChange(event.target.value)}
placeholder="Comma-separated"
value={agentArgs}
/>
<p className="text-xs text-muted-foreground">
`sprout-acp` splits args on commas, matching the testing guide.
<p
className="text-xs text-muted-foreground"
id="help-agent-runtime-args"
>
sprout-acp splits args on commas, matching the testing guide.
</p>
</div>
@@ -195,10 +224,18 @@ export function CreateAgentRuntimeFields({
MCP command
</label>
<Input
aria-describedby="help-agent-mcp-command"
id="agent-mcp-command"
onChange={(event) => onMcpCommandChange(event.target.value)}
value={mcpCommand}
/>
<p
className="text-xs text-muted-foreground"
id="help-agent-mcp-command"
>
Command the ACP harness uses to start the MCP tool server for this
agent.
</p>
</div>
<div className="space-y-1.5">
@@ -206,11 +243,15 @@ export function CreateAgentRuntimeFields({
Turn timeout
</label>
<Input
aria-describedby="help-agent-timeout"
id="agent-timeout"
onChange={(event) => onTurnTimeoutChange(event.target.value)}
placeholder="300"
value={turnTimeoutSeconds}
/>
<p className="text-xs text-muted-foreground" id="help-agent-timeout">
Seconds before an agent turn is cancelled. Defaults to 300.
</p>
</div>
<div className="space-y-1.5">
@@ -218,6 +259,7 @@ export function CreateAgentRuntimeFields({
Parallelism
</label>
<Input
aria-describedby="help-agent-parallelism"
data-testid="agent-parallelism-input"
id="agent-parallelism"
inputMode="numeric"
@@ -229,8 +271,11 @@ export function CreateAgentRuntimeFields({
type="number"
value={parallelism}
/>
<p className="text-xs text-muted-foreground">
Number of ACP worker subprocesses. `sprout-acp` allows 1-32.
<p
className="text-xs text-muted-foreground"
id="help-agent-parallelism"
>
Number of ACP worker subprocesses. sprout-acp allows 1-32.
</p>
</div>
</div>
@@ -240,15 +285,18 @@ export function CreateAgentRuntimeFields({
System prompt override
</label>
<Textarea
aria-describedby="help-agent-system-prompt"
data-testid="agent-system-prompt-input"
id="agent-system-prompt"
onChange={(event) => onSystemPromptChange(event.target.value)}
placeholder="Leave blank to send no ACP system prompt"
value={systemPrompt}
/>
<p className="text-xs text-muted-foreground">
Blank means no override. `sprout-acp` will not add a `[System]`
prompt.
<p
className="text-xs text-muted-foreground"
id="help-agent-system-prompt"
>
Blank means no override. sprout-acp will not add a [System] prompt.
</p>
</div>
</>
@@ -302,7 +350,7 @@ export function CreateAgentOptionToggles({
<p className="mt-1 text-sm text-foreground/70">
{prereqs !== null && !isMintSupported
? `Unavailable until ${prereqs.admin.command} is installed.`
: "Use `sprout-admin` to create a bearer token for this agent."}
: "Use sprout-admin to create a bearer token for this agent."}
</p>
</button>
@@ -321,14 +321,26 @@ function AgentActionsMenu({
onClick={() => onStart(agent.pubkey)}
>
<Play className="h-4 w-4" />
{isActive ? "Redeploy" : "Deploy"}
<div className="flex flex-col">
<span>{isActive ? "Redeploy" : "Deploy"}</span>
<span className="text-xs font-normal text-muted-foreground">
{isActive
? "Push a new deployment to the provider"
: "Deploy this agent to the provider"}
</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem
disabled={isActionPending}
onClick={() => onStop(agent.pubkey)}
>
<Square className="h-4 w-4" />
Shutdown
<div className="flex flex-col">
<span>Shutdown</span>
<span className="text-xs font-normal text-muted-foreground">
Stop the provider deployment and free its resources
</span>
</div>
</DropdownMenuItem>
</>
) : isActive ? (
@@ -337,7 +349,12 @@ function AgentActionsMenu({
onClick={() => onStop(agent.pubkey)}
>
<Square className="h-4 w-4" />
Stop
<div className="flex flex-col">
<span>Stop</span>
<span className="text-xs font-normal text-muted-foreground">
Stop the running ACP harness process
</span>
</div>
</DropdownMenuItem>
) : (
<DropdownMenuItem
@@ -345,7 +362,12 @@ function AgentActionsMenu({
onClick={() => onStart(agent.pubkey)}
>
<Play className="h-4 w-4" />
Spawn
<div className="flex flex-col">
<span>Spawn</span>
<span className="text-xs font-normal text-muted-foreground">
Launch the local ACP harness process for this agent
</span>
</div>
</DropdownMenuItem>
)}
@@ -354,7 +376,13 @@ function AgentActionsMenu({
onClick={() => onAddToChannel(agent)}
>
<UserPlus className="h-4 w-4" />
Add to channel
<div className="flex flex-col">
<span>Add to channel</span>
<span className="text-xs font-normal text-muted-foreground">
Invite this agent to a channel so it can participate in
conversations
</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem
@@ -362,20 +390,36 @@ function AgentActionsMenu({
onClick={() => onMintToken(agent.pubkey, agent.name)}
>
<KeyRound className="h-4 w-4" />
Mint token
<div className="flex flex-col">
<span>Mint token</span>
<span className="text-xs font-normal text-muted-foreground">
Generate a bearer token this agent uses to authenticate with the
relay
</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(agent.pubkey)}
>
<Clipboard className="h-4 w-4" />
Copy pubkey
<div className="flex flex-col">
<span>Copy pubkey</span>
<span className="text-xs font-normal text-muted-foreground">
Copy the agent&apos;s public key to the clipboard
</span>
</div>
</DropdownMenuItem>
{agent.backend.type === "local" ? (
<DropdownMenuItem onClick={() => onOpenLogs(agent.pubkey)}>
<FileText className="h-4 w-4" />
View logs
<div className="flex flex-col">
<span>View logs</span>
<span className="text-xs font-normal text-muted-foreground">
Show the ACP harness stdout/stderr log inline
</span>
</div>
</DropdownMenuItem>
) : null}
@@ -387,9 +431,18 @@ function AgentActionsMenu({
}
>
<Power className="h-4 w-4" />
{agent.startOnAppLaunch
? "Disable auto-start"
: "Enable auto-start"}
<div className="flex flex-col">
<span>
{agent.startOnAppLaunch
? "Disable auto-start"
: "Enable auto-start"}
</span>
<span className="text-xs font-normal text-muted-foreground">
{agent.startOnAppLaunch
? "Stop launching this agent automatically when the desktop app starts"
: "Launch this agent automatically every time the desktop app starts"}
</span>
</div>
</DropdownMenuItem>
) : null}
@@ -401,7 +454,12 @@ function AgentActionsMenu({
onClick={() => onDelete(agent.pubkey)}
>
<Trash2 className="h-4 w-4" />
Delete
<div className="flex flex-col">
<span>Delete</span>
<span className="text-xs font-normal text-muted-foreground">
Permanently remove this agent profile from the desktop app
</span>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -155,7 +155,13 @@ export function PersonasSection({
onClick={() => onEdit(persona)}
>
<Pencil className="h-4 w-4" />
Edit
<div className="flex flex-col">
<span>Edit</span>
<span className="text-xs font-normal text-muted-foreground">
Edit this persona&apos;s name, avatar, and system
prompt
</span>
</div>
</DropdownMenuItem>
) : null}
<DropdownMenuItem
@@ -163,14 +169,26 @@ export function PersonasSection({
onClick={() => onDuplicate(persona)}
>
<CopyPlus className="h-4 w-4" />
Duplicate
<div className="flex flex-col">
<span>Duplicate</span>
<span className="text-xs font-normal text-muted-foreground">
Create a copy of this persona you can customize
independently
</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem
disabled={isPending}
onClick={() => onExport(persona)}
>
<Download className="h-4 w-4" />
Export
<div className="flex flex-col">
<span>Export</span>
<span className="text-xs font-normal text-muted-foreground">
Save this persona as a .persona.json file you can
share or back up
</span>
</div>
</DropdownMenuItem>
{persona.isBuiltIn ? (
<DropdownMenuItem
@@ -179,7 +197,12 @@ export function PersonasSection({
onClick={() => onDeactivate(persona)}
>
<Trash2 className="h-4 w-4" />
Remove from My Agents
<div className="flex flex-col">
<span>Remove from My Agents</span>
<span className="text-xs font-normal text-muted-foreground">
Remove this built-in persona from your agent library
</span>
</div>
</DropdownMenuItem>
) : (
<DropdownMenuItem
@@ -188,7 +211,12 @@ export function PersonasSection({
onClick={() => onDelete(persona)}
>
<Trash2 className="h-4 w-4" />
Delete
<div className="flex flex-col">
<span>Delete</span>
<span className="text-xs font-normal text-muted-foreground">
Permanently delete this persona
</span>
</div>
</DropdownMenuItem>
)}
</DropdownMenuContent>
@@ -202,7 +202,12 @@ export function TeamsSection({
onClick={() => onAddToChannel(team)}
>
<Rocket className="h-4 w-4" />
Deploy to channel
<div className="flex flex-col">
<span>Deploy to channel</span>
<span className="text-xs font-normal text-muted-foreground">
Add all personas in this team to a channel at once
</span>
</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
@@ -210,21 +215,39 @@ export function TeamsSection({
onClick={() => onEdit(team)}
>
<Pencil className="h-4 w-4" />
Edit
<div className="flex flex-col">
<span>Edit</span>
<span className="text-xs font-normal text-muted-foreground">
Edit this team&apos;s name, description, and persona
membership
</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem
disabled={isPending || hasMissingPersonas}
onClick={() => onDuplicate(team)}
>
<CopyPlus className="h-4 w-4" />
Duplicate
<div className="flex flex-col">
<span>Duplicate</span>
<span className="text-xs font-normal text-muted-foreground">
Create a copy of this team you can customize
independently
</span>
</div>
</DropdownMenuItem>
<DropdownMenuItem
disabled={isPending || hasMissingPersonas}
onClick={() => onExport(team)}
>
<Download className="h-4 w-4" />
Export
<div className="flex flex-col">
<span>Export</span>
<span className="text-xs font-normal text-muted-foreground">
Save this team as a .team.json file you can share or
back up
</span>
</div>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
@@ -233,7 +256,12 @@ export function TeamsSection({
onClick={() => onDelete(team)}
>
<Trash2 className="h-4 w-4" />
Delete
<div className="flex flex-col">
<span>Delete</span>
<span className="text-xs font-normal text-muted-foreground">
Permanently delete this team
</span>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>