@@ -2,6 +2,7 @@ import { AppError } from '../../../utils/errors.ts';
22import type { ClickButton } from '../../../core/click-button.ts' ;
33import type { AgentDeviceRuntime , CommandContext } from '../../../runtime-contract.ts' ;
44import { isFillableType } from '../../../utils/snapshot-processing.ts' ;
5+ import type { Point } from '../../../utils/snapshot.ts' ;
56import { requireIntInRange } from '../../../utils/validation.ts' ;
67import { successText } from '../../../utils/success-text.ts' ;
78import { findMistargetedTypeRefToken } from '../../../utils/type-target-warning.ts' ;
@@ -85,6 +86,9 @@ export const fillCommand: RuntimeCommand<FillCommandOptions, FillCommandResult>
8586 options ,
8687) : Promise < FillCommandResult > => {
8788 if ( ! options . text ) throw new AppError ( 'INVALID_ARGS' , 'fill requires text' ) ;
89+ const nativeRefFill = await maybeFillRefTarget ( runtime , options ) ;
90+ if ( nativeRefFill ) return nativeRefFill ;
91+
8892 const resolved = await resolveInteractionTarget ( runtime , options , {
8993 action : 'fill' ,
9094 requireInteractive : true ,
@@ -93,14 +97,15 @@ export const fillCommand: RuntimeCommand<FillCommandOptions, FillCommandResult>
9397 if ( ! runtime . backend . fill ) {
9498 throw new AppError ( 'UNSUPPORTED_OPERATION' , 'fill is not supported by this backend' ) ;
9599 }
100+ const point = requireResolvedPoint ( resolved ) ;
96101 const backendResult = await runtime . backend . fill (
97102 toBackendContext ( runtime , options ) ,
98- resolved . point ,
103+ point ,
99104 options . text ,
100105 { delayMs : options . delayMs } ,
101106 ) ;
102107 const formattedBackendResult = toBackendResult ( backendResult ) ;
103- const nodeType = 'node' in resolved ? ( resolved . node . type ?? '' ) : '' ;
108+ const nodeType = 'node' in resolved ? ( resolved . node ? .type ?? '' ) : '' ;
104109 const warning =
105110 nodeType && ! isFillableType ( nodeType , runtime . backend . platform )
106111 ? `fill target ${ formatTargetForWarning ( resolved ) } resolved to "${ nodeType } ", attempting fill anyway.`
@@ -151,6 +156,9 @@ async function tapCommand(
151156 options : PressCommandOptions ,
152157 action : 'click' | 'press' ,
153158) : Promise < PressCommandResult > {
159+ const nativeRefTap = await maybeTapRefTarget ( runtime , options , action ) ;
160+ if ( nativeRefTap ) return nativeRefTap ;
161+
154162 const resolved = await resolveInteractionTarget ( runtime , options , {
155163 action,
156164 requireInteractive : true ,
@@ -159,25 +167,86 @@ async function tapCommand(
159167 if ( ! runtime . backend . tap ) {
160168 throw new AppError ( 'UNSUPPORTED_OPERATION' , 'tap is not supported by this backend' ) ;
161169 }
162- const backendResult = await runtime . backend . tap (
170+ const point = requireResolvedPoint ( resolved ) ;
171+ const backendResult = await runtime . backend . tap ( toBackendContext ( runtime , options ) , point , {
172+ button : options . button ,
173+ count : options . count ,
174+ intervalMs : options . intervalMs ,
175+ holdMs : options . holdMs ,
176+ jitterPx : options . jitterPx ,
177+ doubleTap : options . doubleTap ,
178+ } ) ;
179+ const formattedBackendResult = toBackendResult ( backendResult ) ;
180+ return {
181+ ...resolved ,
182+ ...( formattedBackendResult ? { backendResult : formattedBackendResult } : { } ) ,
183+ } ;
184+ }
185+
186+ function requireResolvedPoint ( result : { point ?: Point } ) : Point {
187+ if ( ! result . point ) {
188+ throw new AppError ( 'COMMAND_FAILED' , 'Interaction target resolved without coordinates' ) ;
189+ }
190+ return result . point ;
191+ }
192+
193+ async function maybeTapRefTarget (
194+ runtime : AgentDeviceRuntime ,
195+ options : PressCommandOptions ,
196+ action : 'click' | 'press' ,
197+ ) : Promise < PressCommandResult | null > {
198+ if ( action !== 'click' || options . target . kind !== 'ref' || ! runtime . backend . tapTarget ) {
199+ return null ;
200+ }
201+ if ( hasNonDefaultTapOptions ( options ) ) return null ;
202+ const backendResult = await runtime . backend . tapTarget ( toBackendContext ( runtime , options ) , {
203+ kind : 'ref' ,
204+ ref : options . target . ref ,
205+ ...( options . target . fallbackLabel ? { fallbackLabel : options . target . fallbackLabel } : { } ) ,
206+ } ) ;
207+ const formattedBackendResult = toBackendResult ( backendResult ) ;
208+ return {
209+ kind : 'ref' ,
210+ target : { kind : 'ref' , ref : options . target . ref } ,
211+ ...( formattedBackendResult ? { backendResult : formattedBackendResult } : { } ) ,
212+ } ;
213+ }
214+
215+ async function maybeFillRefTarget (
216+ runtime : AgentDeviceRuntime ,
217+ options : FillCommandOptions ,
218+ ) : Promise < FillCommandResult | null > {
219+ if ( options . target . kind !== 'ref' || ! runtime . backend . fillTarget ) return null ;
220+ const backendResult = await runtime . backend . fillTarget (
163221 toBackendContext ( runtime , options ) ,
164- resolved . point ,
165222 {
166- button : options . button ,
167- count : options . count ,
168- intervalMs : options . intervalMs ,
169- holdMs : options . holdMs ,
170- jitterPx : options . jitterPx ,
171- doubleTap : options . doubleTap ,
223+ kind : 'ref' ,
224+ ref : options . target . ref ,
225+ ...( options . target . fallbackLabel ? { fallbackLabel : options . target . fallbackLabel } : { } ) ,
172226 } ,
227+ options . text ,
228+ { delayMs : options . delayMs } ,
173229 ) ;
174230 const formattedBackendResult = toBackendResult ( backendResult ) ;
175231 return {
176- ...resolved ,
232+ kind : 'ref' ,
233+ target : { kind : 'ref' , ref : options . target . ref } ,
234+ text : options . text ,
177235 ...( formattedBackendResult ? { backendResult : formattedBackendResult } : { } ) ,
178236 } ;
179237}
180238
239+ function hasNonDefaultTapOptions ( options : PressCommandOptions ) : boolean {
240+ return Boolean (
241+ options . count !== undefined ||
242+ options . intervalMs !== undefined ||
243+ options . holdMs !== undefined ||
244+ options . jitterPx !== undefined ||
245+ options . doubleTap !== undefined ||
246+ ( options . button !== undefined && options . button !== 'primary' ) ,
247+ ) ;
248+ }
249+
181250function formatTargetForWarning ( result : {
182251 kind : FillCommandResult [ 'kind' ] ;
183252 target ?: ResolvedTarget ;
0 commit comments