Repository navigation
Let a Doppler drag clear of the markers draw a new curve - #341
Merged
Merged
Conversation
An analyst reported that Doppler mode sometimes stops responding: a click & drag on the gram does nothing, and switching mode and back does not help. The cause is that placement was only offered while *no* marker existed. Once any one did, a press anywhere but on a marker resolved to nothing, so every drag was silently ignored. Three ways in, none of them a misuse of the tool: - a curve already on the gram, when the analyst drags to draw another; - a curve restored from storage, possibly outside the zoomed view; - an invisible lone f+, left behind when a press was released before any movement: `completeMarkerPlacement` bailed on the missing f- and kept the seeded f+, nothing drew (only a complete curve renders), and `hasPersistableAnnotations` counted it as an annotation, so it was saved at once and came back on reload. Now: - `resolveDopplerDrag` looks for a marker under the pointer first and otherwise starts a placement, whatever the marker state. There is one curve per gram: a drag clear of the markers replaces it. The replaced curve rides on the drag target, so Escape, a mode switch or a moveless release puts it back rather than leaving nothing. - A press released without moving places nothing. - `isCompleteCurve` in `utils/doppler.js` is the one rule for "there is a curve"; the renderer, `hasPersistentFeatures` and storage all read it, so a lone marker can neither draw, persist nor block a placement. Two hardenings for a related way every mode can go dead, found while tracing the pointer path but not reproduced: a region-zoom or wheel-pan drag whose release is lost with the pointer still on the gram. Such a drag blocked every press and right-click, and a mode switch could not clear it because `_cancelAllDrags` only reached the modes' handlers. A mode switch now ends with the engine's `cancelActiveDrag`, and a window `blur` cancels whichever drag is running. Tests: the doppler spec covers replacement, the moveless press with and without an existing curve, Escape restoring the old curve, and a partial stored curve no longer blocking placement; the region-zoom spec covers recovery after a blur and after a mode switch; the storage spec's doppler persistence test now drags a curve instead of clicking twice, which only ever "placed" the orphan f+; the hotspot spec's clear-press test asserts a placement rather than nothing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsjY9pBg5RAmyqbThX4Uae
Contributor
PR PreviewPreview deployment is ready! View Preview Last updated: 2026-09-18T13:06:53.893Z |
CI's coverage ratchet caught what the local run had not: `snapshotCurve` and `replacedCurveOf` in utils/doppler.js had no unit test, so the uncovered-line count rose from 409 to 413. They are pure, so the unit lane pins them: a curve is complete only with all three markers, a snapshot copies a complete curve and is null for anything less, and the replaced curve rides on the place target alone. Also lowers the `instance.state` reach-in baseline the merge and `restoreCurve` brought down, as the ratchet asks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RsjY9pBg5RAmyqbThX4Uae
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
An analyst reported that Doppler mode sometimes stops responding: a click & drag on the gram does nothing, switching mode and back does not help, and a refresh sometimes does. Reproduced against
debug.htmland confirmed in the published 0.3.0 bundle.resolveDopplerDragoffered a placement only while no marker existed. Once any one did, a press anywhere but on a marker resolved tonull, so every drag was silently ignored. Three ways in, none a misuse of the tool:completeMarkerPlacementbailed on the missing f− and kept it, nothing drew (only a complete curve renders), andhasPersistableAnnotationscounted it as an annotation, so it was saved at once and restored on reload.Fix
resolveDopplerDraglooks for a marker under the pointer first and otherwise starts a placement. A drag clear of the markers replaces the curve; the replaced curve rides on the drag target, so Escape, a mode switch or a moveless release puts it back.isCompleteCurveinutils/doppler.jsis the one rule for "there is a curve"; the renderer,hasPersistentFeaturesand storage all read it, so a lone marker can neither draw, persist nor block a placement.Hardening (not reproduced)
Tracing the pointer path found one way every mode can go dead, which would also fit the Cross Cursor incident: a region-zoom or wheel-pan drag whose release is lost with the pointer still over the gram.
hasActiveDragthen drops every press and right-click, and_cancelAllDragscould not reach those two handlers, so only a reload cleared it. A mode switch now ends with the engine'scancelActiveDrag, and a windowblurcancels whichever drag is running.Tests
doppler-mode.spec.js: replacement, moveless press with and without a curve, Escape restoring the old curve, a partial stored curve no longer blocking placement.region-zoom.spec.js: recovery after a blur and after a mode switch mid-selection.storage.spec.js: the doppler persistence test now drags a curve; it used to click twice, which only ever "placed" the orphan f+.doppler-hotspot.spec.js: a press clear of every marker now asserts a placement rather than nothing.storage-validation.test.js: the storage predicate follows what renders.The branch carries a merge of
main(the control-panel rebuild and v0.3.0); the one conflict was the Doppler guidance text, resolved into the new{ trigger, outcome }form. On the merged headyarn typecheck,yarn lint,yarn hygiene,yarn test:unitand the full Playwright suite (535 tests) pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01RsjY9pBg5RAmyqbThX4Uae