(
direction: ScrollDirection,
options?: { amount?: number; pixels?: number; durationMs?: number },
)
| 176 | const DEFAULT_SCROLL_CLICKS = 5; |
| 177 | |
| 178 | export async function scrollLinux( |
| 179 | direction: ScrollDirection, |
| 180 | options?: { amount?: number; pixels?: number; durationMs?: number }, |
| 181 | ): Promise<Record<string, unknown>> { |
| 182 | const provider = resolveLinuxInputProvider(); |
| 183 | if (provider) { |
| 184 | await provider.scroll(direction, options); |
| 185 | return scrollDurationResult(options); |
| 186 | } |
| 187 | |
| 188 | const { tool } = await ensureInputTool(); |
| 189 | |
| 190 | // Translate amount/pixels into a discrete click count. |
| 191 | // xdotool button clicks scroll ~15px each (3 lines × 5px). |
| 192 | // ydotool wheel units are ~40px each. |
| 193 | let scrollCount = DEFAULT_SCROLL_CLICKS; |
| 194 | if (options?.pixels != null) { |
| 195 | scrollCount = |
| 196 | tool === 'xdotool' |
| 197 | ? Math.max(1, Math.round(options.pixels / 15)) |
| 198 | : Math.max(1, Math.round(options.pixels / 40)); |
| 199 | } else if (options?.amount != null) { |
| 200 | // amount is a fraction (0–1+) of the viewport; scale relative to default |
| 201 | scrollCount = Math.max(1, Math.round(DEFAULT_SCROLL_CLICKS * (options.amount / 0.6))); |
| 202 | } |
| 203 | |
| 204 | // xdotool: button 4=up, 5=down, 6=left, 7=right |
| 205 | if (tool === 'xdotool') { |
| 206 | const button = |
| 207 | direction === 'up' ? '4' : direction === 'down' ? '5' : direction === 'left' ? '6' : '7'; |
| 208 | await runPacedScrollSteps(scrollCount, options?.durationMs, async (stepCount) => { |
| 209 | await xdotool('click', '--repeat', String(stepCount), button); |
| 210 | }); |
| 211 | } else { |
| 212 | // ydotool: wheel events use positive/negative values |
| 213 | if (direction === 'up' || direction === 'down') { |
| 214 | await runPacedScrollSteps(scrollCount, options?.durationMs, async (stepCount) => { |
| 215 | const stepValue = direction === 'up' ? String(-stepCount) : String(stepCount); |
| 216 | await ydotool('mousemove', '--wheel', '-y', stepValue); |
| 217 | }); |
| 218 | } else { |
| 219 | await runPacedScrollSteps(scrollCount, options?.durationMs, async (stepCount) => { |
| 220 | const stepValue = direction === 'left' ? String(-stepCount) : String(stepCount); |
| 221 | await ydotool('mousemove', '--wheel', '-x', stepValue); |
| 222 | }); |
| 223 | } |
| 224 | } |
| 225 | return scrollDurationResult(options); |
| 226 | } |
| 227 | |
| 228 | async function runPacedScrollSteps( |
| 229 | totalCount: number, |
no test coverage detected