Skip to content

feat: add +1 min control to give time back to the current round - #518

Open
Cordedmink2 wants to merge 1 commit into
Splode:mainfrom
Cordedmink2:feat/rewind-remaining-time
Open

Cordedmink2 wants to merge 1 commit into
Splode:mainfrom
Cordedmink2:feat/rewind-remaining-time

Conversation

@Cordedmink2

Copy link
Copy Markdown

Why

If you get distracted partway through a focus round, the only options today are to keep going with less time than you meant to spend, or restart the round and lose your progress. This adds a small +1 min control so you can give time back to the current round.

What changes

  • Engine: a new TimerCommand::Rewind { secs } moves the elapsed position backwards, never below 0. The round's total duration, type and running/paused state stay the same. The dial arc and the MM:SS readout are both computed from the same elapsed_secs and total_secs, so they stay in sync.
    • Running: the tick timeline is re-anchored at the last tick boundary, so the countdown continues from the new position with no drift and no burst of catch-up ticks.
    • Paused or suspended: the timer stays paused. Rewinding all the way to 0 puts the engine back to Idle and emits Reset, the same result as Restart Round, so the toggle, tray and UI all agree the round hasn't started.
    • Idle: the command is ignored.
  • Small robustness fix: when the engine is Paused and receives Start, it now resumes instead of silently dropping the command. The controller sends Start whenever the shared elapsed position is 0, which can happen if a running round is rewound to 0:00 and then paused.
  • Plumbing: a new timer_rewind IPC command and timer:rewound Tauri event. The tray arc redraws immediately. There is also an additive rewound WebSocket broadcast ({ "type": "rewound", "payload": { "elapsed_secs", "total_secs" } }), so overlays don't drift. Existing message formats are unchanged.
  • UI: a "+1 min" text button sits inside the dial, just below the readout.
    • It has a tooltip, an accessible label and a minimum 24px target, and works with Tab and Enter/Space.
    • It is disabled when nothing has elapsed and hidden in compact mode, so the dial-only layout is unchanged.
    • No other controls moved.
  • Strings for all 8 locales.

Relationship to #517

#517 also proposes extending the current round (+5) as part of a larger redesign: a distraction counter, sub-minute breaks and a control layout change. This PR is deliberately narrower. It adds one control, leaves the layout alone and doesn't change any settings. If you'd rather take #517's approach, I'm happy to close this.

Verification

  • cargo test: 96 passed, 0 failed. This includes 7 new event-synchronised engine tests:

    • rewind while running
    • clamp at 0 while running
    • rewind while paused, then resume
    • paused rewind to 0 → Idle
    • idle no-op
    • zero-second no-op
    • Start while paused resumes

    The timer tests passed 15 times in a row. I also checked the tests themselves: removing the timeline re-anchor makes 2 of them fail.

  • cargo clippy -- -D warnings: clean.

  • npm run check: 0 errors, 0 warnings. prettier --check is clean on the changed frontend files.

  • The real Svelte main window was driven in headless Chromium against a mocked Tauri IPC that mirrors the engine's semantics (15/15 checks):

    • The button is disabled when idle.
    • Clicking it sends timer_rewind { secs: 60 }.
    • The readout and the arc's stroke-dashoffset stay consistent after rewinding while running, rewinding while paused, and on later ticks.
    • It clamps at the full duration.
    • Keyboard activation works.
    • The button is hidden in compact mode.
    • No console errors.

    I checked the layout at 300×400, 360×478 and 500×600: the button doesn't overlap the readout and stays inside the ring.

  • Builds and tests ran on Linux (aarch64, Rust stable in a Debian bookworm container). I haven't run it natively on macOS or Windows; the changes are platform-neutral.

Lets the user rewind the remaining time (e.g. after being distracted)
without restarting the round.

- New `TimerCommand::Rewind { secs }` moves the engine's elapsed position
  backwards (clamped at 0). Total duration, round type and running/paused
  state are unchanged, so the dial arc and the MM:SS readout stay derived
  from the same elapsed/total pair.
  - Running: the tick timeline is re-anchored at the last tick boundary so
    counting continues from the new position without drift.
  - Paused/suspended: stays paused; rewinding all the way to 0 returns the
    engine to Idle and emits Reset (same outcome as Restart Round).
  - Idle: ignored.
- `Start` received while Paused now resumes instead of being dropped (the
  controller sends Start when the shared elapsed position is 0).
- New `timer_rewind` IPC command, `timer:rewound` Tauri event and additive
  `rewound` WebSocket broadcast; tray arc redraws immediately.
- UI: a "+1 min" button inside the dial below the readout, with tooltip,
  accessible label, 24px target, keyboard activation, and disabled state
  when there is nothing to give back. Hidden in compact mode.
- Strings added for all 8 locales.
- 7 new engine tests (event-synchronised) plus a WebSocket serialisation test.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant