Collapse bounds policies to reflect-only; drop abort and clamp
The executor kept every commanded point on-screen via a per-strategy
BoundsPolicy of abort / clamp / reflect. Measured against the real
strategies, the other two earned nothing: abort truncated a sweep at the
first edge (line on a narrow screen ran only 90 of 250 steps), and clamp
could park the cursor against an edge (a monotonic ramp stalled 162 steps
in a row) -- both counter to the program's whole purpose of keeping the
cursor moving. reflect bounces off the edge and keeps going, and is
already what line/diagonal need in loop mode. arc's declared clamp was
provably dead code (it clamps its own endpoint, so no sample ever leaves
the screen).
Collapse to reflect-only:
- strategies.ts: remove the BoundsPolicy type and the `bounds` field from
the interface and all six strategies. Keep the local clamp() helper --
it's arc's endpoint geometry, not an on-screen policy; docstring says so.
- executor.ts: resolveTarget loses its policy parameter and its null
return and just reflects both axes; delete clampInt; SweepOutcome drops
"aborted"; ExecuteOptions drops `bounds`; remove the Out of bounds log.
- keeper.ts: loopOpts is now { restore: false, loop: true } -- the
reflect override added with loop mode is redundant.
- tests: drop the abort-outcome, clamp, and bounds-override tests; simplify
fixed() to take no policy; add a regression test that a monotonic ramp
past an edge never yields two identical points in a row (the guarantee
that motivated removing clamp).
Behavior is unchanged for every pattern at normal cursor positions
(verified: line's normal sweep is byte-identical). The only differences
are at a screen edge, where motion now bounces instead of stopping. No
config keys, flags, or pattern names changed.
Docs updated to match, including in-code comments, the README strategies
table (Bounds column removed) and verbose description, the sequence
diagram (resolveTarget signature + getPosition/width ordering + a loop-mode
note), and a CHANGELOG Changed entry.
This commit is contained in:
+14
-17
@@ -8,8 +8,8 @@
|
||||
* the interesting parts stay testable:
|
||||
* - `device.ts` — the nut.js I/O boundary (injected here).
|
||||
* - `strategies.ts` — pure "where to move" pattern generators.
|
||||
* - `executor.ts` — the "how to move" driver (bounds, timing,
|
||||
* interrupt detection, restore).
|
||||
* - `executor.ts` — the "how to move" driver (on-screen reflection,
|
||||
* timing, interrupt detection, restore).
|
||||
*
|
||||
* `runKeeper` takes an optional `Device` so tests can drive the loop with a
|
||||
* fake; production supplies the nut.js device. Importing this module is
|
||||
@@ -18,9 +18,9 @@
|
||||
* Logging policy:
|
||||
* - The startup banner in `runKeeper` is unconditional so the user always
|
||||
* sees the process is alive.
|
||||
* - Per-sweep / interrupt / bounds lines are gated by `config.verbose`
|
||||
* (see `makeLogger`). Errors stay on `console.error`, raised by the
|
||||
* entry point on unhandled rejection.
|
||||
* - Per-sweep / interrupt lines are gated by `config.verbose` (see
|
||||
* `makeLogger`). Errors stay on `console.error`, raised by the entry
|
||||
* point on unhandled rejection.
|
||||
*/
|
||||
|
||||
import { createNutDevice, type Device, type Point } from "./device.ts";
|
||||
@@ -55,19 +55,16 @@ function makeLogger(verbose: boolean): Logger {
|
||||
* at the CLI / config-file boundary should prevent that from ever happening.
|
||||
*
|
||||
* Single-sweep mode (`config.loop === false`) runs exactly one sweep via
|
||||
* `executePath`, which owns bounds, pacing, interrupt detection, and
|
||||
* restore-on-clean — unchanged from before loop mode existed.
|
||||
* `executePath`, which owns on-screen reflection, pacing, interrupt
|
||||
* detection, and restore-on-clean — unchanged from before loop mode existed.
|
||||
*
|
||||
* Loop mode (`config.loop === true`) keeps the cursor moving until the
|
||||
* user moves the mouse (or Ctrl+C). Two things change for every pattern:
|
||||
* the cursor is never restored between iterations (`restore: false`), and the
|
||||
* bounds policy is forced to `reflect` so edge-seeking paths bounce off the
|
||||
* screen instead of aborting (`line`) or sticking in a corner (`clamp`).
|
||||
* Patterns that define an infinite `loopPath` (`line`, `diagonal`) run it once
|
||||
* and are stopped only by interruption; the rest have their finite `path`
|
||||
* chained, re-read from the cursor's current position each cycle. Per-cycle
|
||||
* event logs are suppressed to avoid unbounded output — one line brackets the
|
||||
* run at each end.
|
||||
* user moves the mouse (or Ctrl+C). The cursor is never restored between
|
||||
* iterations (`restore: false`). Patterns that define an infinite `loopPath`
|
||||
* (`line`, `diagonal`) run it once and are stopped only by interruption; the
|
||||
* rest have their finite `path` chained, re-read from the cursor's current
|
||||
* position each cycle. Per-cycle event logs are suppressed to avoid unbounded
|
||||
* output — one line brackets the run at each end.
|
||||
*/
|
||||
async function simulateActivity(config: Config, log: Logger, device: Device): Promise<void> {
|
||||
const width: number = await device.width();
|
||||
@@ -83,7 +80,7 @@ async function simulateActivity(config: Config, log: Logger, device: Device): Pr
|
||||
|
||||
log.event(`Loop mode (${strategy.name}); repeating until you move the mouse.`);
|
||||
const cycleLog: Logger = { info: log.info, event: (): void => {} };
|
||||
const loopOpts = { restore: false, bounds: "reflect" as const, loop: true };
|
||||
const loopOpts = { restore: false, loop: true };
|
||||
|
||||
let cycles = 0;
|
||||
let outcome: SweepOutcome;
|
||||
|
||||
Reference in New Issue
Block a user