Introduce a continuous "loop" setting so a triggered sweep keeps the
cursor moving until the user moves the mouse (or Ctrl+C), instead of
firing a single sweep.
- strategies.ts: add optional `loopPath` to MovementStrategy; give `line`
and `diagonal` infinite loop generators that pick a direction once and
ramp forever (4px/step). Their finite `path` and declared `bounds` are
unchanged, so single-sweep behavior is identical.
- executor.ts: add ExecuteOptions { restore?, bounds?, loop? }. Omitting
options reproduces the original single-sweep contract exactly.
- keeper.ts: in loop mode, run an infinite loopPath once (stopped only by
interruption) or chain a finite path cycle after cycle; force `reflect`
bounds for every pattern and suppress the between-cycle restore, so
line/diagonal bounce edge-to-edge instead of stopping at the first edge.
- config plumbing: new boolean `loop` through config.default.json,
config.ts, configFile.ts, cli.ts (-l/--loop), and move.ts, mirroring
the existing `verbose` precedence.
- docs: README loop-mode section + usage/validation updates; CHANGELOG
Unreleased entry.
- tests: loopPath generators, executor options (bounds override, loop
selection, restore suppression), config/configFile loop plumbing, and
keeper-level loop behavior (ramps far vs. bounded single-sweep, chained
cycles). 79 pass.
The stepCount and stepSize knobs were two controls for one quantity users
actually care about (reach), and the number of steps is an implementation
detail nobody meaningfully tunes. Each pattern has a natural size and
resolution — a jitter is inherently small, an arc a broad curve — so those
now live as constants in each strategy rather than as global config.
- strategies.ts: each pattern defines its own step count and size; MoveContext
drops `config` down to pure geometry (start/width/height/rng), and the
module no longer imports Config at all (dissolving the type-only-import
cycle workaround). line stays byte-for-byte: 250 one-pixel steps.
- executor.ts: executePath takes `config` for pacing (stepDelay); the path
itself needs nothing from it.
- config.ts / cli.ts / move.ts / config.default.json: drop stepCount and
stepSize from the type, seed, validation, resolver, CLI flags (-n, -s),
and help. stepDelay stays as the one pacing lever.
- configFile.ts: tolerate the removed keys instead of rejecting them — every
pre-1.3.0 install seeded stepCount, so a hard "unknown key" failure on
upgrade is avoided. They're ignored with a one-line stderr notice; genuine
unknown keys still error.
The -n/--step-count CLI flag (shipped since 1.0.0) is now an unknown option;
config files degrade gracefully, command lines don't. Stays in the unpushed
1.3.0 release. 64 tests pass.
Turn the hardcoded straight-line sweep into a strategy system behind three
seams so new patterns are easy to add and, for the first time, testable
without nut.js or a real screen:
- src/device.ts: injectable Device seam over nut.js (autoDelayMs lives
here now); the only module that touches the native lib.
- src/strategies.ts: pure per-pattern path generators + registry + lenient
name resolution. Ships line, diagonal, jitter, walk,
arc, figureEight.
- src/executor.ts: single executePath driver owning bounds policy
(abort/clamp/reflect), pacing, interrupt detection, and
restore-on-clean.
keeper.ts's simulateActivity now selects a strategy and delegates to the
executor; the default `line` pattern is byte-for-byte the previous behavior.
New config surface, layered CLI > file > default with strict validation:
- -p/--pattern <name> movement strategy (names matched case/-/_-insensitive)
- -s/--step-size <px> pixels per step; stepCount is now a step *count*
Robustness for the new edge-seeking patterns: interrupt detection compares
against the last commanded (rounded) point with a 2px tolerance, and
clamp/reflect stay a couple pixels off the screen edge, so sub-pixel cursor
placement on scaled/multi-monitor displays isn't misread as user activity.
jitter's radius scales with sweep length so it moves at the default stepSize.
Tests: new suites for strategies, the executor (all bounds policies,
rounding, interrupt, tolerance, pacing), and the keeper loop; config and
configFile suites extended for pattern/stepSize. editor.test.ts moved to
tests/ for consistency. 64 pass.
CliError used to live in cli.ts and was imported by configFile.ts purely
to grab a one-line class — the dependency arrow said 'config-file loader
depends on the CLI parser' when really it just needed a shared error
type.
Moving CliError to its own errors.ts module flattens the graph:
errors.ts (no internal deps)
|
+-- cli.ts
+-- configFile.ts
+-- config.ts (will use it in the next commit)
cli.ts re-exports CliError for any consumer that prefers to keep
importing it from there; the canonical home is now errors.ts.
End users can now set defaults in a config file at:
${XDG_CONFIG_HOME:-$HOME/.config}/move/config.json
CLI flags continue to win when both are set:
CLI flags > config file > built-in defaults
Schema mirrors the CLI flag names and units. Loader is strict: unknown
keys, wrong types, and non-positive numerics are rejected with a clear
message naming the file and key, and the process exits 2.
Changes:
- New src/configFile.ts: existence-aware loader + strict schema
validation. Throws CliError; the entry point converts those to exit 2.
- src/config.ts: ConfigOverrides gains 'verbose'; resolveConfig takes
both file and CLI override layers; new defaultConfigPath() honors
XDG_CONFIG_HOME; new resolveVerbose() layers verbose with the
presence-only-CLI semantics documented.
- src/cli.ts: -C/--config <path> flag. printHelp prints the default
config path and the precedence rule.
- src/move.ts: loads the config file (default XDG path or --config),
passes both override layers into resolveConfig, resolves verbose
separately, exits 2 on any validation failure.
- README: new Configuration section with path, precedence, example,
validation rules, and the verbose CLI-can't-turn-off limitation.
Files table gains src/configFile.ts.