Toast timeout ring starts at an odd point; action toasts stay 30 s (make 8 s) #539

Closed
opened 2026-09-30 17:28:35 +00:00 by kayg · 8 comments
Owner

Owner report (2026-09-30, screenshot of the "Moved 3 items to the Trash · Undo" toast)

  1. "the thin ring line should start from the top center instead of such a weird place". The timeout ring is an SVG <rect> (AppToaster.svelte, .cal-toast-ring rect, pathLength 100). A rect path starts at its top-left corner after the radius, so the drain starts at an odd point on the upper-left edge. Fix: draw the ring as a <path> that starts at the top centre and runs clockwise around the capsule (rebuilt from the measured box in the existing ResizeObserver). It drains clockwise from the top centre, as an Apple-style countdown does. Keep pathLength 100, the pause-on-hover/focus sync and reduced-motion behaviour.
  2. "it takes too long to dismiss? what's the default?" Today: DEFAULT_DURATION = 30_000 and UNDO_MS = 30_000 (toasts.svelte.ts, set 2026-09-25); confirmations are 4 s. New default: 8 s for toasts with an action (Undo), 4 s for plain confirmations. Hover and keyboard focus still pause the timer. Update the doc comments and DESIGN §28 (ASD-STE100).
    Test: a unit test for the durations; an e2e (toast-ring.mjs) that samples the ring's first dash point at t≈0 and asserts that it is at the top centre (x ≈ width/2, y ≈ 0), and that the toast leaves after ~8 s without hover. Attach screenshots at 0 %, 25 % and 75 % drain.
## Owner report (2026-09-30, screenshot of the "Moved 3 items to the Trash · Undo" toast) 1. "the thin ring line should start from the top center instead of such a weird place". The timeout ring is an SVG `<rect>` (AppToaster.svelte, `.cal-toast-ring rect`, pathLength 100). A rect path starts at its top-left corner after the radius, so the drain starts at an odd point on the upper-left edge. **Fix:** draw the ring as a `<path>` that starts at the top centre and runs clockwise around the capsule (rebuilt from the measured box in the existing ResizeObserver). It drains clockwise from the top centre, as an Apple-style countdown does. Keep pathLength 100, the pause-on-hover/focus sync and reduced-motion behaviour. 2. "it takes too long to dismiss? what's the default?" Today: `DEFAULT_DURATION = 30_000` and `UNDO_MS = 30_000` (toasts.svelte.ts, set 2026-09-25); confirmations are 4 s. **New default:** 8 s for toasts with an action (Undo), 4 s for plain confirmations. Hover and keyboard focus still pause the timer. Update the doc comments and DESIGN §28 (ASD-STE100). **Test:** a unit test for the durations; an e2e (toast-ring.mjs) that samples the ring's first dash point at t≈0 and asserts that it is at the top centre (x ≈ width/2, y ≈ 0), and that the toast leaves after ~8 s without hover. Attach screenshots at 0 %, 25 % and 75 % drain.
Author
Owner

Starting work on branch job/toast-539 from base aa372eef6c (origin/dev). Root cause confirmed: ToastBody.svelte measures the toast box but only updates the SVG viewBox; the ring still uses a rounded rect whose dash starts at the upper-left. The adapter also uses 30 s for non-confirmation toasts, including Undo actions. I’m adding the requested duration and geometry coverage.

Starting work on branch job/toast-539 from base aa372eef6c9312403f59a65d5b90cb3648ff52ed (origin/dev). Root cause confirmed: ToastBody.svelte measures the toast box but only updates the SVG viewBox; the ring still uses a rounded rect whose dash starts at the upper-left. The adapter also uses 30 s for non-confirmation toasts, including Undo actions. I’m adding the requested duration and geometry coverage.
Author
Owner

Duration finding: the new action-toast unit test failed first because the adapter sent 30,000 ms instead of 8,000 ms. The adapter default and shared UNDO_MS are now 8,000 ms; the plain confirmation remains 4,000 ms. Verification: targeted duration test passed; bun run check passed with 0 errors and 0 warnings; bun run test passed (140 files, 915 tests).

Duration finding: the new action-toast unit test failed first because the adapter sent 30,000 ms instead of 8,000 ms. The adapter default and shared UNDO_MS are now 8,000 ms; the plain confirmation remains 4,000 ms. Verification: targeted duration test passed; bun run check passed with 0 errors and 0 warnings; bun run test passed (140 files, 915 tests).
Author
Owner

Ring regression evidence: against the pre-change production bundle, the updated toast-ring probe failed as expected. Playwright timed out after 5,000 ms waiting for the .cal-toast-ring path because the live toast still contained the rounded rect. The implementation now rebuilds a clockwise capsule path from the existing ResizeObserver measured border box; reduced motion still hides the ring and the same Sonner expanded state pauses it.

Ring regression evidence: against the pre-change production bundle, the updated toast-ring probe failed as expected. Playwright timed out after 5,000 ms waiting for the .cal-toast-ring path because the live toast still contained the rounded rect. The implementation now rebuilds a clockwise capsule path from the existing ResizeObserver measured border box; reduced motion still hides the ring and the same Sonner expanded state pauses it.
Author
Owner

The first desktop drain strip now passes at exact CSS offsets 0, 25 and 75. The next browser run timed out on an explicitly timed toast whose DOM had data-expanded=true after the preceding Undo screenshot click; Sonner had paused it because the pointer remained over the toaster. The E2E now moves the pointer away before waiting for expiry.

The first desktop drain strip now passes at exact CSS offsets 0, 25 and 75. The next browser run timed out on an explicitly timed toast whose DOM had data-expanded=true after the preceding Undo screenshot click; Sonner had paused it because the pointer remained over the toaster. The E2E now moves the pointer away before waiting for expiry.
Author
Owner

Production ring evidence

The production e2e passed for the top-centre, clockwise ring path at 390, 820 and 1440 px in Paper and Tokyo Night. It also passed hover and keyboard-focus pause checks. The Undo toast left after 8,585 ms without hover. Drain strips show the 0%, 25% and 75% frames in both themes.

## Production ring evidence The production e2e passed for the top-centre, clockwise ring path at 390, 820 and 1440 px in Paper and Tokyo Night. It also passed hover and keyboard-focus pause checks. The Undo toast left after 8,585 ms without hover. Drain strips show the 0%, 25% and 75% frames in both themes. - [Paper drain strip (0%, 25%, 75%)](https://git.kayg.org/attachments/648fad43-1cdf-4e26-8107-d940d178b697) - [Tokyo Night drain strip (0%, 25%, 75%)](https://git.kayg.org/attachments/2c2e1810-5f1d-4cf6-9b7b-ca5295ae287f) - [390 px, Paper](https://git.kayg.org/attachments/5d2285d2-a2cc-4916-b441-20a481cfa6f6) - [390 px, Tokyo Night](https://git.kayg.org/attachments/4b665904-2da6-4878-b6de-5ecc1ff90fbe) - [820 px, Paper](https://git.kayg.org/attachments/f659924e-fa28-45e3-a1ec-45790cee8b38) - [820 px, Tokyo Night](https://git.kayg.org/attachments/3f34bdfb-19d9-4683-b7e9-6b6b0f7ea212) - [1440 px, Paper](https://git.kayg.org/attachments/3cbafe85-75e9-43e1-82d7-ab3bfb22c32d) - [1440 px, Tokyo Night](https://git.kayg.org/attachments/2d77922b-07f1-4838-8e36-4fa5bdcac858)
Author
Owner

Final report — toast-539

Built

  • Action toasts use an 8 s default; plain confirmations use 4 s. The measured SVG path starts at the capsule's top centre and drains clockwise. Hover and keyboard focus pause the toast timer and ring together.
  • Added unit duration checks, production e2e coverage, and a toast-ring performance profile. The previous comment has the screenshots for 390, 820 and 1440 px in Paper and Tokyo Night, plus 0%, 25% and 75% drain strips.

Files

  • apps/web/src/lib/stores/toasts.svelte.ts, toasts.svelte.test.ts
  • apps/web/src/lib/components/AppToaster.svelte, ToastBody.svelte
  • apps/web/e2e/toast-ring.mjs, docs/DESIGN.md
  • bench/toast-ring-539.mjs, bench/run.sh, bench/record.py, bench/test_record.py
  • docs/perf/BASELINE.md, README.md, baseline.json, and runs/2026-09-30-toast-ring-539-local.{json,md}

Commits

  • f6554fbf2 start toast countdown rings at the capsule top
  • 721123d2a profile toast countdown performance on production UI
  • d4c6dfa2e document toast profile invariants
  • Head: d4c6dfa2e609d9d2acb169be798c6603fd97614c (job/toast-539)

Gates and e2e output

bun run check output, verbatim:

Text sizes and UI shape values use shared role tokens.
UI transitions and animation options use shared motion tokens or documented exceptions.
Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/toast-539/apps/web
Getting Svelte diagnostics...

svelte-check found 0 errors and 0 warnings

bun run test summary, verbatim:

 Test Files  140 passed (140)
      Tests  915 passed (915)
   Start at  21:48:46
   Duration  197.77s (transform 50%, environment 21%, import 15%, tests 9%, setup 4%)

Production e2e, verbatim:

PASS: ring geometry and two-line layout match at all 3 widths and 2 themes; Undo expires after 8443 ms; 0%, 25% and 75% drain screenshots saved in /home/kayg/Developer/calternal-wt/toast-539/artifacts/toast-ring-review.

python3 -m unittest bench.test_record: Ran 7 tests in 0.126s / OK. node --check, bash -n bench/run.sh and git diff --check passed with no output. No Rust crate changed. cargo clean output, verbatim: Removed 7169 files, 4.5GiB total. The web build output was removed.

Performance and gaps

The toast profile is in docs/perf/baseline.json. At 390 px, the 15-sample average was p50/p95 17.0/118.5 ms, 63% browser CPU and 454,299,648 B RSS; the 50-toast burst was 2,339.8 ms, 26.5% CPU and 521,445,376 B peak RSS. At 1440 px, average was 37.6/126.8 ms, 70% CPU and 535,625,728 B RSS; burst was 1,012 ms, 76.5% CPU and 612,655,104 B peak RSS.

The perf VM attempt held /root/perf.lock, but Chromium failed with error: goto: Page crashed while opening Calendar. The successful local sample had load averages 38.09, 36.65 and 36.99. Treat it as an initial, noisy baseline; collect a quiet perf VM profile when its browser run works. The burst is one sample, so its p50 and p95 are the same.

Decisions

The existing adapter treats alerts and dismissible status toasts as the default duration class. That class now uses 8 s. DESIGN §28 specifies 8 s for action toasts and 4 s for plain confirmations; it does not specify alerts or dismissible status toasts. The bench uses a 50-toast burst with 180-character labels to exercise wrapping and ring sizing; the design does not set a burst count.

## Final report — toast-539 ### Built - Action toasts use an 8 s default; plain confirmations use 4 s. The measured SVG path starts at the capsule's top centre and drains clockwise. Hover and keyboard focus pause the toast timer and ring together. - Added unit duration checks, production e2e coverage, and a toast-ring performance profile. The previous comment has the screenshots for 390, 820 and 1440 px in Paper and Tokyo Night, plus 0%, 25% and 75% drain strips. ### Files - `apps/web/src/lib/stores/toasts.svelte.ts`, `toasts.svelte.test.ts` - `apps/web/src/lib/components/AppToaster.svelte`, `ToastBody.svelte` - `apps/web/e2e/toast-ring.mjs`, `docs/DESIGN.md` - `bench/toast-ring-539.mjs`, `bench/run.sh`, `bench/record.py`, `bench/test_record.py` - `docs/perf/BASELINE.md`, `README.md`, `baseline.json`, and `runs/2026-09-30-toast-ring-539-local.{json,md}` ### Commits - `f6554fbf2` start toast countdown rings at the capsule top - `721123d2a` profile toast countdown performance on production UI - `d4c6dfa2e` document toast profile invariants - Head: `d4c6dfa2e609d9d2acb169be798c6603fd97614c` (`job/toast-539`) ### Gates and e2e output `bun run check` output, verbatim: ```text Text sizes and UI shape values use shared role tokens. UI transitions and animation options use shared motion tokens or documented exceptions. Loading svelte-check in workspace: /home/kayg/Developer/calternal-wt/toast-539/apps/web Getting Svelte diagnostics... svelte-check found 0 errors and 0 warnings ``` `bun run test` summary, verbatim: ```text Test Files 140 passed (140) Tests 915 passed (915) Start at 21:48:46 Duration 197.77s (transform 50%, environment 21%, import 15%, tests 9%, setup 4%) ``` Production e2e, verbatim: ```text PASS: ring geometry and two-line layout match at all 3 widths and 2 themes; Undo expires after 8443 ms; 0%, 25% and 75% drain screenshots saved in /home/kayg/Developer/calternal-wt/toast-539/artifacts/toast-ring-review. ``` `python3 -m unittest bench.test_record`: `Ran 7 tests in 0.126s` / `OK`. `node --check`, `bash -n bench/run.sh` and `git diff --check` passed with no output. No Rust crate changed. `cargo clean` output, verbatim: `Removed 7169 files, 4.5GiB total`. The web build output was removed. ### Performance and gaps The toast profile is in `docs/perf/baseline.json`. At 390 px, the 15-sample average was p50/p95 17.0/118.5 ms, 63% browser CPU and 454,299,648 B RSS; the 50-toast burst was 2,339.8 ms, 26.5% CPU and 521,445,376 B peak RSS. At 1440 px, average was 37.6/126.8 ms, 70% CPU and 535,625,728 B RSS; burst was 1,012 ms, 76.5% CPU and 612,655,104 B peak RSS. The perf VM attempt held `/root/perf.lock`, but Chromium failed with `error: goto: Page crashed` while opening Calendar. The successful local sample had load averages 38.09, 36.65 and 36.99. Treat it as an initial, noisy baseline; collect a quiet perf VM profile when its browser run works. The burst is one sample, so its p50 and p95 are the same. ### Decisions The existing adapter treats alerts and dismissible status toasts as the default duration class. That class now uses 8 s. DESIGN §28 specifies 8 s for action toasts and 4 s for plain confirmations; it does not specify alerts or dismissible status toasts. The bench uses a 50-toast burst with 180-character labels to exercise wrapping and ring sizing; the design does not set a burst count.
Author
Owner

Implementation detail for review: the path uses a 0.5 CSS px inset so its 1 px stroke stays centred on the measured capsule edge. It starts at (width / 2, 0.5) and runs clockwise.

Implementation detail for review: the path uses a 0.5 CSS px inset so its 1 px stroke stays centred on the measured capsule edge. It starts at `(width / 2, 0.5)` and runs clockwise.
Author
Owner

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).

Shipped in merge round 4, deployed to calternal.cloud in 1af8ead26 (healthy).
kayg closed this issue 2026-10-01 09:17:45 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#539
No description provided.