19 Commits

Author SHA1 Message Date
Iisyourdad f2c5831315 Fix restarted recordings dropping clicks / stopping after one click
Template tests / tests (push) Waiting to run
Template tests / tests (pull_request) Waiting to run
Root cause: warm-before-hide kept the window visible during backend warmup,
and on a restart that warmup could take several seconds (the stream backend
start waits up to 8s). During that visible window, clicks over the app were
skipped by the userIsInApp guard and clicks elsewhere were shot post-click,
so a restarted session looked like it stopped after one click.

- Recording is now 'armed' only after the window is hidden and the buffer is
  primed. A new warmingUp flag makes onOsClick ignore clicks during warmup
  (the window is covering the user's work anyway) instead of mishandling
  them. Cleared on pause/finish.
- armRecording caps the warmup wait (WARMUP_MAX_MS=1500): the window hides
  and the session arms even if the backend start hangs, so it can never sit
  visible for seconds dropping clicks. The backend keeps coming up in the
  background; the first click or two may take the fresh-shot fallback.
- A generation token invalidates an in-flight backend start whose session
  has since finished, so a slow start can't install into a new session or
  leave the starting-guard stuck and block the restart from starting one.

Tests: 4 new behavioral capture tests (warmup ignores clicks; pause/finish
clear it; armRecording warms-then-hides-then-arms; a hung start still arms
within the cap; a stale start is discarded and frees the guard) plus a new
end-to-end self-test scenario (warmup click ignored, first armed click
captured). 152 unit tests + all repo checks pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:17:59 -05:00
Iisyourdad 3d0b753205 Add a 200ms click debounce with extensive behavioral tests
Template tests / tests (push) Waiting to run
Template tests / tests (pull_request) Waiting to run
Per request: clicks of the same button closer together than
capture.clickDebounceMs (default 200ms) now collapse into a single step, so
accidental fast/double clicks don't each become a step. It is a leading-edge
debounce measured from the last *accepted* click, so a run of fast clicks
can't push the next deliberate click out — two clicks spaced beyond the
window (e.g. the reported 400-500ms apart) always register.

Replaces the prior 8ms duplicate-delivery suppression (subsumed by the
window). Configurable; 0 captures every click.

Tests (the point of this change is that it can't silently regress):
- 13 behavioral unit tests in capture.test.js that drive real onOsClick
  calls with controlled timestamps and assert which clicks survive — the
  reported 400/450/500ms cases, sub-window collapse, the 200ms boundary,
  per-button independence, configurability, debounce=0, last-accepted (not
  last-dropped) reference, session reset, and a full onOsClick -> queue ->
  store integration check. No keyword/comment assertions.
- A fourth end-to-end self-test scenario (burst of 40ms clicks collapses to
  1; three 300ms-apart clicks each register => 4 total). The marker/drain
  scenarios set debounce to 0 so they keep stressing the frame pipeline.

147 unit tests + all repo checks pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:02:51 -05:00
Iisyourdad 34cc358902 Never take a post-click screenshot when a pre-click frame exists
Template tests / tests (push) Successful in 1m48s
Template tests / tests (pull_request) Successful in 1m55s
The remaining 'captured slightly after the click' reports came from the
fresh-shot fallback, which grabs the screen when the click is processed
(after it). The previous lead change made that fallback *more* likely: a
frame now had to be >=120ms before the click to qualify, so on machines
where the capture stream can't always keep a frame that old buffered, more
clicks fell through to the post-click shot.

Make the click-lead a two-tier preference instead of a hard gate in
selectFrameForClick:
1. newest frame captured at least leadMs before the click (ideal margin), else
2. newest frame captured before the click at all.
Only when no pre-click frame exists does the caller fresh-shot. leadMs is
threaded through the stream backend to the worker so both selection paths
agree. Verified end to end: frames land ~120-170ms before each click,
markers stay at 0.00%, and the 8-click burst still saves all 8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 08:40:33 -05:00
Iisyourdad 951bba7a21 Fix dropped clicks and late screenshots in fast recording
Template tests / tests (push) Successful in 2m13s
Template tests / tests (pull_request) Successful in 1m48s
Root cause of 'I clicked many times but only got two screenshots':
finishing/pausing a session called backend.stop(), which cancelled every
in-flight frame request to null. Clicks whose PNG had not finished
encoding yet were then dropped — only the first few survived.

Fixes:
- Stream backend now *drains* on stop: it stops accepting new requests but
  keeps the worker alive until frames already selected for queued clicks
  finish encoding. stop({ immediate: true }) keeps the old abandon-now
  behavior for an unhealthy worker.
- Two-stage worker reply: a fast 'frame-selected' ack pins the pairing and
  proves liveness; the slow PNG payload follows. A slow encode (seconds on
  software-rendered hosts) is no longer mistaken for a dead worker, which
  had been forcing the post-click fresh-shot fallback (late screenshots).
- Queued clicks carry their guide id and are stored even if the session
  ends while they wait in the queue.
- The tray gesture that stops a session is discarded by matching its
  recorded screen position, not a time window — a fast workflow click near
  the stop is no longer collateral damage. (Replaces the earlier grace
  window, which dropped whole bursts.)
- A click on a display with no ready stream resolves null so the caller
  fresh-shots the correct monitor instead of returning another screen.
- STEPFORGE_CAPTURE_LOG=1 prints one line per click decision; the
  second-instance handler now surfaces the running window instead of
  exiting silently.
- Self-test gains a fast-burst-then-finish scenario (8/8 saved) and the
  marker/coordinate checks remain at 0.00% offset.

Tests: 133 unit + all repo checks passing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 07:56:31 -05:00
Iisyourdad a0b69f8cc7 Rearchitect click capture: strict click-time frames, off-main-process recorder, exact marker coordinates
Template tests / tests (push) Successful in 1m50s
Implements the architecture change from ai_prompts/prompt3.md:

- New app/click-frames.js: shared timestamped frame ring + strict
  click-to-frame pairing (never a frame whose grab started after the
  click); legacy slack behavior kept behind capture.strictClickFrames=false.
- New stream capture backend (app/stream-backend.js + hidden worker
  window): per-display desktop media streams sampled into ring buffers
  and PNG-encoded entirely off the main process, so click delivery is
  never starved by capture work. Auto-degrades to the legacy in-process
  frame loop when streams cannot start or the worker stops answering.
- Clicks are paired with their frame at event time (eager pairing in
  enqueueClickCapture); only the storing is serialized, so slow encodes
  cannot skew later clicks in a fast burst.
- Linux watcher: restored event-time root coordinates from
  xinput test-xi2 and merge raw/regular twin events structurally.
- Replaced the 40ms time debounce with source-aware duplicate
  suppression: fast legitimate clicks are never dropped.
- New app/coords.js: physical-to-DIP conversion with multi-monitor and
  scale-factor handling; Windows keeps screenToDipPoint.
- STEPFORGE_CLICK_SELFTEST end-to-end hook: 3/3 clicks become steps via
  the stream backend with 0.00% marker offset on this host.
- Tests rewritten/added: strict selection, coords, stream backend,
  Linux coordinate parsing, twin merge, burst clicking (126 passing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:33:31 -05:00
Iisyourdad 5b7b075e91 Fixed clicking location part 5 :(
Template tests / tests (push) Successful in 1m51s
2026-06-11 18:46:56 -05:00
Iisyourdad 6682cdae0f Fixed clicking location part 4
Template tests / tests (push) Successful in 1m52s
2026-06-11 16:57:59 -05:00
Iisyourdad 27439b475d Fixed clicking location part 3
Template tests / tests (push) Successful in 1m50s
2026-06-11 16:33:52 -05:00
Iisyourdad e7b91f8c4c Fixed clicking location part 2
Template tests / tests (push) Successful in 1m48s
2026-06-11 16:12:09 -05:00
Iisyourdad ec9371b43a Undo
Template tests / tests (push) Successful in 1m47s
2026-06-11 15:52:57 -05:00
Iisyourdad 298d7728b8 Fixed an issue where clicking wouldn't line up with screenshot part 5
Template tests / tests (push) Successful in 1m48s
2026-06-11 13:02:37 -05:00
Iisyourdad 3d80c86abf Fixed an issue where clicking wouldn't line up with screenshot part 4
Template tests / tests (push) Successful in 1m48s
2026-06-11 12:45:14 -05:00
Iisyourdad 42affc571d Fixed an issue where clicking wouldn't line up with screenshot part 3
Template tests / tests (push) Successful in 1m54s
2026-06-11 12:38:48 -05:00
Iisyourdad 0ecc1b473f Fixed an issue where clicking wouldn't line up with screenshot part 2
Template tests / tests (push) Successful in 1m56s
2026-06-11 12:23:02 -05:00
Iisyourdad 6d529256bb Fixed an issue where clicking wouldn't line up with screenshot
Template tests / tests (push) Successful in 1m49s
2026-06-11 12:08:37 -05:00
Iisyourdad c352741809 Fixed a bug where a resumed recording wouldn't update the image Part 2
Template tests / tests (push) Successful in 1m51s
2026-06-11 11:57:55 -05:00
Iisyourdad 99376fdeb2 Fix new-capture auto-hide, library capture bar, step delete, and click-capture timing
Template tests / tests (push) Successful in 1m49s
- New Capture sessions now start paused; the window only tucks away once
  the user clicks "Start recording" in the capture bar instead of hiding
  ~1.2s after starting.
- The capture status bar is shown only in the editor view, not over the
  library.
- Fix openModal/confirmDialog resolving as cancelled when an action button
  is clicked, which made the step "Delete" button (and other modal actions)
  silently no-op.
- Click-triggered captures now use the click-time cursor position for the
  marker and arm the capture cache as soon as recording starts, so the
  first click is captured instantly and accurately placed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 09:21:19 -05:00
Iisyourdad 451a831eb6 make click captures instant
Template tests / tests (push) Failing after 15s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:31:19 -05:00
Iisyourdad fe6ce675bd reduce click capture latency
Template tests / tests (push) Failing after 42s
Template tests / tests (pull_request) Failing after 16s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 08:15:50 -05:00