Refine mobile emulator agent setup layout and step blocking (#4923)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jinjing 2026-06-08 19:29:11 -07:00 committed by GitHub
parent edadb1d7b7
commit 94770b7acd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 141 additions and 124 deletions

View File

@ -17,6 +17,7 @@ import {
useInstalledAgentSkill
} from '@/hooks/useInstalledAgentSkills'
import { useMountedRef } from '@/hooks/useMountedRef'
import { cn } from '@/lib/utils'
import { Button } from '../ui/button'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
import {
@ -184,6 +185,8 @@ export function BrowserUseSetup({
const showStep2 = matchesSettingsSearch(searchQuery, [BROWSER_USE_PANE_SEARCH_ENTRIES[1]])
const showStep3 = matchesSettingsSearch(searchQuery, [BROWSER_USE_PANE_SEARCH_ENTRIES[2]])
const completedCount = [cliEnabled, skillDetected, cookiesImported].filter(Boolean).length
const step2Blocked = !cliEnabled && !skillDetected
const step3Blocked = !cookiesImported && (!cliEnabled || !skillDetected)
const sourceLabel = defaultProfile?.source
? `${BROWSER_FAMILY_LABELS[defaultProfile.source.browserFamily] ?? defaultProfile.source.browserFamily}${defaultProfile.source.profileName ? ` (${defaultProfile.source.profileName})` : ''}`
@ -300,16 +303,17 @@ export function BrowserUseSetup({
title="Install Browser Use Skill"
description="Install the Browser Use skill so agents can operate Orca's browser."
keywords={BROWSER_USE_PANE_SEARCH_ENTRIES[1].keywords}
className={`rounded-xl border border-border/60 bg-card/50 p-4 ${
cliEnabled ? '' : 'opacity-60'
}`}
className={cn(
'rounded-xl border border-border/60 bg-card/50 p-4',
step2Blocked && 'opacity-60'
)}
>
<BrowserUseSkillStep
command={ORCA_CLI_SKILL_INSTALL_COMMAND}
skillDetected={skillDetected}
skillLoading={skillLoading}
skillError={skillError}
disabled={!cliEnabled}
disabled={step2Blocked}
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
onBeforeOpenTerminal={async () => {
useAppStore.getState().recordFeatureInteraction('agent-browser-setup')
@ -327,9 +331,10 @@ export function BrowserUseSetup({
title="Import Browser Cookies"
description="Import cookies from Chrome, Edge, or other browsers so agents can reuse your logins."
keywords={BROWSER_USE_PANE_SEARCH_ENTRIES[2].keywords}
className={`rounded-xl border border-border/60 bg-card/50 p-4 ${
cliEnabled && skillDetected ? '' : 'opacity-60'
}`}
className={cn(
'rounded-xl border border-border/60 bg-card/50 p-4',
step3Blocked && 'opacity-60'
)}
>
<div className="flex items-start gap-3">
<StepBadge

View File

@ -16,6 +16,7 @@ import {
useInstalledAgentSkill
} from '@/hooks/useInstalledAgentSkills'
import { useMountedRef } from '@/hooks/useMountedRef'
import { cn } from '@/lib/utils'
import { AgentSkillSetupPanel } from './AgentSkillSetupPanel'
import { StepBadge } from './BrowserUseStepBadge'
import { MobileEmulatorExamples } from './MobileEmulatorExamples'
@ -79,6 +80,7 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element {
const cliEnabled = isOrcaCliAvailableOnPath(cliInstallStatus)
const cliSupported = cliInstallStatus?.supported ?? false
const completedCount = [cliEnabled, cliSkillInstalled].filter(Boolean).length
const step2Blocked = !cliEnabled && !cliSkillInstalled
const handleEnableCli = async (): Promise<void> => {
setCliBusy(true)
@ -97,124 +99,115 @@ export function MobileEmulatorAgentControlRow(): React.JSX.Element {
}
return (
<div className="space-y-3 py-3">
<div className="rounded-2xl border border-border/60 bg-card/30 p-4">
<div className="flex items-center justify-between gap-3">
<div className="space-y-0.5">
<p className="text-sm font-semibold">Agent Mobile Emulator Control</p>
<div className="rounded-2xl border border-border/60 bg-card/30 p-4">
<div className="flex items-center justify-between gap-3">
<div className="space-y-0.5">
<p className="text-sm font-semibold">Agent Mobile Emulator Control</p>
<p className="text-xs text-muted-foreground">
Let coding agents control the active mobile emulator with Orca CLI commands.
</p>
</div>
<span
className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${
completedCount === 2
? 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'
: 'bg-muted text-muted-foreground'
}`}
>
{completedCount}/2
</span>
</div>
<div className="mt-3 divide-y divide-border/40">
<div className="flex items-start gap-3 py-3">
<StepBadge index={1} state={cliEnabled ? 'done' : cliBusy ? 'in-progress' : 'pending'} />
<div className="min-w-0 flex-1 space-y-1">
<p className="text-sm font-medium">Enable Orca CLI</p>
<p className="text-xs text-muted-foreground">
Let coding agents control the active mobile emulator with Orca CLI commands.
Registers the Orca CLI command so agents can control the active emulator from their
shell.
</p>
{cliInstallStatus?.commandPath && cliEnabled ? (
<p className="text-[11px] text-muted-foreground">
Installed at{' '}
<code className="rounded bg-muted px-1 py-0.5">{cliInstallStatus.commandPath}</code>
</p>
) : null}
{!cliEnabled && cliInstallStatus?.detail ? (
<p className="text-[11px] text-muted-foreground">{cliInstallStatus.detail}</p>
) : null}
</div>
<span
className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${
completedCount === 2
? 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400'
: 'bg-muted text-muted-foreground'
}`}
>
{completedCount}/2
</span>
<TooltipProvider delayDuration={250}>
<Tooltip>
<TooltipTrigger asChild>
<span>
<Button
type="button"
size="sm"
variant={cliEnabled ? 'outline' : 'default'}
disabled={cliLoading || cliBusy || !cliSupported || cliEnabled}
onClick={() => void handleEnableCli()}
>
{cliLoading ? <Loader2 className="size-3.5 animate-spin" /> : null}
{getCliActionLabel(cliInstallStatus, cliBusy)}
</Button>
</span>
</TooltipTrigger>
{!cliSupported && !cliLoading && cliInstallStatus?.detail ? (
<TooltipContent side="left" sideOffset={6}>
{cliInstallStatus.detail}
</TooltipContent>
) : null}
</Tooltip>
</TooltipProvider>
</div>
<div className="mt-3 space-y-3">
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
<div className="flex items-start gap-3">
<StepBadge
index={1}
state={cliEnabled ? 'done' : cliBusy ? 'in-progress' : 'pending'}
/>
<div className="min-w-0 flex-1 space-y-1">
<p className="text-sm font-medium">Enable Orca CLI</p>
<p className="text-xs text-muted-foreground">
Registers the Orca CLI command so agents can control the active emulator from
their shell.
</p>
{cliInstallStatus?.commandPath && cliEnabled ? (
<p className="text-[11px] text-muted-foreground">
Installed at{' '}
<code className="rounded bg-muted px-1 py-0.5">
{cliInstallStatus.commandPath}
</code>
</p>
) : null}
{!cliEnabled && cliInstallStatus?.detail ? (
<p className="text-[11px] text-muted-foreground">{cliInstallStatus.detail}</p>
) : null}
</div>
<TooltipProvider delayDuration={250}>
<Tooltip>
<TooltipTrigger asChild>
<span>
<Button
type="button"
size="sm"
variant={cliEnabled ? 'outline' : 'default'}
disabled={cliLoading || cliBusy || !cliSupported || cliEnabled}
onClick={() => void handleEnableCli()}
>
{cliLoading ? <Loader2 className="size-3.5 animate-spin" /> : null}
{getCliActionLabel(cliInstallStatus, cliBusy)}
</Button>
</span>
</TooltipTrigger>
{!cliSupported && !cliLoading && cliInstallStatus?.detail ? (
<TooltipContent side="left" sideOffset={6}>
{cliInstallStatus.detail}
</TooltipContent>
) : null}
</Tooltip>
</TooltipProvider>
</div>
</div>
<div className={cliEnabled ? '' : 'opacity-60'}>
<AgentSkillSetupPanel
variant="inline"
title="Orca CLI skill"
description="Enables agents to use Orca CLI commands, including mobile emulator control."
command={ORCA_CLI_SKILL_INSTALL_COMMAND}
terminalTitle="Orca CLI skill setup"
terminalAriaLabel="Orca CLI skill install terminal"
terminalWorktreeId="settings-mobile-emulator-orca-cli-skill-terminal"
installed={cliSkillInstalled}
loading={cliSkillLoading}
error={cliSkillError}
installDisabled={!cliEnabled}
leading={<StepBadge index={2} state={cliSkillInstalled ? 'done' : 'pending'} />}
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
onBeforeOpenTerminal={async () => {
await ensureOrcaCliAvailableForAgentSkillTerminal({
onStatusChange: setCliInstallStatus
})
}}
onRecheck={refreshCliSkill}
/>
</div>
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
<div className="flex items-center gap-2">
<Import className="size-3.5 text-muted-foreground" />
<p className="text-sm font-medium">Common emulator commands</p>
</div>
<p className="mt-1 text-xs text-muted-foreground">
Commands target the active emulator for the current worktree. Coordinates are
normalized from 0..1.
</p>
<div className="mt-3 grid gap-1.5 [@media(min-width:520px)]:grid-cols-2">
{EMULATOR_CLI_COMMANDS.map((command) => (
<code
key={command}
className="block break-all rounded-md border border-border/60 bg-background/60 px-2 py-1 font-mono text-[11px] leading-snug text-foreground"
>
{command}
</code>
))}
</div>
</div>
<MobileEmulatorExamples />
<div className={cn('py-3', step2Blocked && 'opacity-60')}>
<AgentSkillSetupPanel
variant="inline"
title="Orca CLI skill"
description="Enables agents to use Orca CLI commands, including mobile emulator control."
command={ORCA_CLI_SKILL_INSTALL_COMMAND}
terminalTitle="Orca CLI skill setup"
terminalAriaLabel="Orca CLI skill install terminal"
terminalWorktreeId="settings-mobile-emulator-orca-cli-skill-terminal"
installed={cliSkillInstalled}
loading={cliSkillLoading}
error={cliSkillError}
installDisabled={step2Blocked}
leading={<StepBadge index={2} state={cliSkillInstalled ? 'done' : 'pending'} />}
preInstallNotice={AGENT_SKILL_CLI_PREREQUISITE_NOTICE}
onBeforeOpenTerminal={async () => {
await ensureOrcaCliAvailableForAgentSkillTerminal({
onStatusChange: setCliInstallStatus
})
}}
onRecheck={refreshCliSkill}
/>
</div>
<div className="py-3">
<div className="flex items-center gap-2">
<Import className="size-3.5 text-muted-foreground" />
<p className="text-sm font-medium">Common emulator commands</p>
</div>
<p className="mt-1 text-xs text-muted-foreground">
Commands target the active emulator for the current worktree. Coordinates are normalized
from 0..1.
</p>
<div className="mt-3 grid gap-1.5 [@media(min-width:520px)]:grid-cols-2">
{EMULATOR_CLI_COMMANDS.map((command) => (
<code
key={command}
className="block break-all rounded-md border border-border/60 bg-background/60 px-2 py-1 font-mono text-[11px] leading-snug text-foreground"
>
{command}
</code>
))}
</div>
</div>
<MobileEmulatorExamples variant="inline" />
</div>
</div>
)

View File

@ -1,5 +1,6 @@
import { Copy, Sparkles } from 'lucide-react'
import { toast } from 'sonner'
import { cn } from '@/lib/utils'
import { Button } from '../ui/button'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'
@ -18,9 +19,19 @@ async function copyPrompt(prompt: string): Promise<void> {
}
}
export function MobileEmulatorExamples(): React.JSX.Element {
type MobileEmulatorExamplesProps = {
variant?: 'card' | 'inline'
}
export function MobileEmulatorExamples({
variant = 'card'
}: MobileEmulatorExamplesProps): React.JSX.Element {
return (
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
<div
className={cn(
variant === 'card' ? 'rounded-xl border border-border/60 bg-card/50 p-4' : 'py-3'
)}
>
<div className="flex items-center gap-2">
<Sparkles className="size-3.5 text-muted-foreground" />
<p className="text-sm font-medium">Try it example prompts</p>
@ -33,7 +44,7 @@ export function MobileEmulatorExamples(): React.JSX.Element {
{EMULATOR_EXAMPLE_PROMPTS.map((prompt) => (
<li
key={prompt}
className="flex items-start gap-2 rounded-lg border border-border/50 bg-background/60 px-3 py-2"
className="flex items-start gap-2 rounded-lg border border-border bg-background px-3 py-2"
>
<p className="flex-1 text-[11px] leading-relaxed text-foreground/90">
&ldquo;{prompt}&rdquo;

View File

@ -211,9 +211,17 @@ export function MobileEmulatorSettingsPane({
</Select>
}
/>
{enabled ? <MobileEmulatorAgentControlRow /> : null}
</SearchableSetting>
{enabled ? (
<SearchableSetting
title="Agent Mobile Emulator Control"
description="Let coding agents control the active mobile emulator with Orca CLI commands."
keywords={MOBILE_EMULATOR_SEARCH_ENTRIES[3]?.keywords}
>
<MobileEmulatorAgentControlRow />
</SearchableSetting>
) : null}
</div>
)
}