Skip to content

Commit 24f398c

Browse files
authored
refactor: remove scrollintoview command (#400)
1 parent 8e42f2e commit 24f398c

35 files changed

Lines changed: 47 additions & 1257 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ In practice, most work follows the same pattern:
5252
1. Discover the exact app id with `apps` if the package or bundle name is uncertain.
5353
2. `open` a target app or URL.
5454
3. `snapshot -i` to inspect the current screen.
55-
4. `press`, `fill`, `scroll`, `get`, or `wait` using refs or selectors. On iOS and Android, default snapshot text follows the same visible-first contract: refs shown in default output are actionable now, while hidden content is surfaced as scroll/list discovery hints instead of tappable off-screen refs.
55+
4. `press`, `fill`, `scroll`, `get`, or `wait` using refs or selectors. On iOS and Android, default snapshot text follows the same visible-first contract: refs shown in default output are actionable now, while hidden content is surfaced as scroll/list discovery hints instead of tappable off-screen refs. If the target only appears in a hidden-content hint, use `scroll <direction>` and re-snapshot.
5656
Use `rotate <orientation>` when a flow needs a deterministic portrait or landscape state on mobile targets.
5757
5. `diff snapshot` or re-snapshot after UI changes.
5858
6. `close` when the session is finished.

skills/agent-device/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Use this skill as a router with mandatory defaults. Read this file first. For no
1818
- In React Native dev or debug builds, check early for visible warning or error overlays, tooltips, and toasts that can steal focus or intercept taps. If they are not part of the requested behavior, dismiss them and continue. If you saw them, report them in the final summary.
1919
- Do not browse the web or use external sources unless the user explicitly asks.
2020
- Re-snapshot after meaningful UI changes instead of reusing stale refs.
21-
- Treat refs in default snapshot output as actionable-now, not durable identities. If a target is off-screen, use `scrollintoview` or scroll and re-snapshot.
21+
- Treat refs in default snapshot output as actionable-now, not durable identities. If a target appears only in an off-screen summary, use `scroll <direction>` and re-snapshot until the target is visible.
2222
- Prefer `@ref` or selector targeting over raw coordinates.
2323
- Ensure the correct target is pinned and an app session is open before interacting.
2424
- Keep the loop short: `open` -> inspect/act -> verify if needed -> `close`.

skills/agent-device/references/exploration.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Open this file when the app or screen is already running and you need to discove
3838
- `press`
3939
- `fill`
4040
- `type`
41-
- `scrollintoview`
41+
- `scroll`
4242
- `wait`
4343
- `keyboard dismiss` when the keyboard obscures the next target
4444

@@ -115,10 +115,10 @@ App: com.apple.Preferences
115115
## Refs vs selectors
116116

117117
- Use refs for discovery, debugging, and short local loops.
118-
- Use `scrollintoview @ref` when the target is already known from the current snapshot and you want the command to re-snapshot after each swipe until the element reaches the viewport safe band.
119-
- If `scrollintoview @ref` succeeds, prefer the returned `currentRef` for the next action.
118+
- When a target appears only in a visible-first off-screen summary, such as `[off-screen below] ... "Battery"`, use `scroll down` and then `snapshot -i`. For `[off-screen above]`, use `scroll up` and then `snapshot -i`.
119+
- For more than two repeated scroll checks, create a short shell loop instead of issuing each command by hand. Stop when the label appears or the snapshot stops changing.
120120
- Visible-first off-screen summaries are intentionally compact. If you need the full off-screen tree instead of a short summary, retry with `snapshot --raw`.
121-
- Cap long searches with `--max-scrolls <n>` when the list may be unbounded or the target may not exist.
121+
- Cap long searches in the loop when the list may be unbounded or the target may not exist.
122122
- Use selectors for deterministic scripts, assertions, and replay-friendly actions.
123123
- Prefer selector or `@ref` targeting over raw coordinates.
124124
- For tap interactions, `press` is canonical and `click` is an equivalent alias.
@@ -132,11 +132,25 @@ agent-device press 'id="camera_row" || label="Camera" role=button'
132132
agent-device is visible 'id="camera_settings_anchor"'
133133
```
134134

135+
Example loop:
136+
137+
```bash
138+
previous=''
139+
for _ in 1 2 3 4 5 6; do
140+
current="$(agent-device snapshot -i)"
141+
printf '%s\n' "$current"
142+
printf '%s\n' "$current" | grep -q 'Battery' && break
143+
[ "$current" = "$previous" ] && break
144+
previous="$current"
145+
agent-device scroll down 0.5 >/dev/null
146+
done
147+
```
148+
135149
## Interaction fallbacks
136150

137151
When `press @ref` fails:
138152

139-
1. If the error says the ref is off-screen, run `scrollintoview @ref` and reuse the returned `currentRef` or take one fresh snapshot.
153+
1. If the error says the ref is off-screen, use the off-screen summary direction to run `scroll <direction>`, then take a fresh `snapshot -i`.
140154
2. Re-snapshot if the UI may have changed.
141155
3. Retry `press @ref` or a selector-based `press`.
142156
4. If `screenshot --overlay-refs --json` returned a reliable `overlayRefs[].center`, use `agent-device press <x> <y>`.

src/cli/commands/generic.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -176,17 +176,6 @@ export const genericClientCommandHandlers = {
176176
pixels: flags.pixels,
177177
}),
178178
),
179-
[CLIENT_COMMANDS.scrollIntoView]: createGenericClientCommandHandler(
180-
CLIENT_COMMANDS.scrollIntoView,
181-
({ client, positionals, flags }) =>
182-
client.interactions.scrollIntoView({
183-
...buildSelectionOptions(flags),
184-
...(positionals[0]?.startsWith('@')
185-
? { ref: positionals[0], label: positionals.slice(1).join(' ') || undefined }
186-
: { text: positionals.join(' ') }),
187-
maxScrolls: flags.maxScrolls,
188-
}),
189-
),
190179
[CLIENT_COMMANDS.pinch]: createGenericClientCommandHandler(
191180
CLIENT_COMMANDS.pinch,
192181
({ client, positionals, flags }) =>

src/cli/commands/output.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,6 @@ export function writeCommandCliOutput(
130130
const successText = readCommandMessage(data);
131131
if (successText) {
132132
process.stdout.write(`${successText}\n`);
133-
for (const extraLine of readCommandSuccessLines(command, data)) {
134-
process.stdout.write(`${extraLine}\n`);
135-
}
136133
}
137134
return 0;
138135
}
@@ -216,12 +213,3 @@ function writeNetworkCliOutput(data: Record<string, unknown>): void {
216213
}
217214
}
218215
}
219-
220-
function readCommandSuccessLines(command: string, data: Record<string, unknown>): string[] {
221-
if (command !== CLIENT_COMMANDS.scrollIntoView) {
222-
return [];
223-
}
224-
const ref = typeof data.ref === 'string' ? data.ref : '';
225-
const currentRef = typeof data.currentRef === 'string' ? data.currentRef : '';
226-
return currentRef && currentRef !== ref ? [`Current ref: @${currentRef}`] : [];
227-
}

src/client-command-registry.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export const CLIENT_COMMANDS = {
2828
replay: 'replay',
2929
rotate: 'rotate',
3030
scroll: 'scroll',
31-
scrollIntoView: 'scrollintoview',
3231
screenshot: 'screenshot',
3332
settings: 'settings',
3433
snapshot: 'snapshot',

src/client-normalizers.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,6 @@ export function buildFlags(options: InternalRequestOptions): CommandFlags {
283283
clickButton: options.clickButton,
284284
pauseMs: options.pauseMs,
285285
pattern: options.pattern,
286-
maxScrolls: options.maxScrolls,
287286
headless: options.headless,
288287
restart: options.restart,
289288
replayUpdate: options.replayUpdate,

src/client-types.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -527,22 +527,6 @@ export type ScrollOptions = ClientCommandBaseOptions & {
527527
pixels?: number;
528528
};
529529

530-
export type ScrollIntoViewOptions = ClientCommandBaseOptions &
531-
(
532-
| {
533-
text: string;
534-
ref?: never;
535-
label?: never;
536-
}
537-
| {
538-
ref: string;
539-
label?: string;
540-
text?: never;
541-
}
542-
) & {
543-
maxScrolls?: number;
544-
};
545-
546530
export type PinchOptions = ClientCommandBaseOptions & {
547531
scale: number;
548532
x?: number;
@@ -704,7 +688,6 @@ type CommandExecutionOptions = {
704688
clickButton?: 'primary' | 'secondary' | 'middle';
705689
pauseMs?: number;
706690
pattern?: 'one-way' | 'ping-pong';
707-
maxScrolls?: number;
708691
headless?: boolean;
709692
restart?: boolean;
710693
replayUpdate?: boolean;
@@ -802,7 +785,6 @@ export type AgentDeviceClient = {
802785
type: (options: TypeTextOptions) => Promise<CommandRequestResult>;
803786
fill: (options: FillOptions) => Promise<CommandRequestResult>;
804787
scroll: (options: ScrollOptions) => Promise<CommandRequestResult>;
805-
scrollIntoView: (options: ScrollIntoViewOptions) => Promise<CommandRequestResult>;
806788
pinch: (options: PinchOptions) => Promise<CommandRequestResult>;
807789
get: (options: GetOptions) => Promise<CommandRequestResult>;
808790
is: (options: IsOptions) => Promise<CommandRequestResult>;

src/client.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -351,12 +351,6 @@ export function createAgentDeviceClient(
351351
[options.direction, ...optionalNumber(options.amount)],
352352
options,
353353
),
354-
scrollIntoView: async (options) =>
355-
await executeCommandRequest(
356-
CLIENT_COMMANDS.scrollIntoView,
357-
scrollIntoViewPositionals(options),
358-
options,
359-
),
360354
pinch: async (options) =>
361355
await executeCommandRequest(
362356
CLIENT_COMMANDS.pinch,
@@ -458,15 +452,6 @@ function elementPositionals(options: ElementTarget): string[] {
458452
return [options.selector];
459453
}
460454

461-
function scrollIntoViewPositionals(options: {
462-
text?: string;
463-
ref?: string;
464-
label?: string;
465-
}): string[] {
466-
if (options.ref !== undefined) return [options.ref, ...optionalString(options.label)];
467-
return [options.text ?? ''];
468-
}
469-
470455
function stringifyPayload(payload: AppPushOptions['payload']): string {
471456
return typeof payload === 'string' ? payload : JSON.stringify(payload);
472457
}
@@ -615,7 +600,6 @@ export type {
615600
ReplayTestOptions,
616601
RotateCommandOptions,
617602
RotateCommandResult,
618-
ScrollIntoViewOptions,
619603
ScrollOptions,
620604
SessionCloseResult,
621605
SettingsUpdateOptions,

src/core/__tests__/capabilities.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ test('core commands support iOS simulator, iOS device, and Android', () => {
161161
'rotate',
162162
'screenshot',
163163
'scroll',
164-
'scrollintoview',
165164
'snapshot',
166165
'trigger-app-event',
167166
'type',
@@ -200,7 +199,6 @@ test('macOS supports the Apple runner interaction core but excludes mobile-only
200199
'settings',
201200
'screenshot',
202201
'scroll',
203-
'scrollintoview',
204202
'snapshot',
205203
'swipe',
206204
'trigger-app-event',
@@ -310,7 +308,6 @@ test('Linux supports desktop interaction commands and blocks mobile/unsupported
310308
'record',
311309
'reinstall',
312310
'rotate',
313-
'scrollintoview',
314311
'settings',
315312
'trigger-app-event',
316313
],

0 commit comments

Comments
 (0)