fix(master): break effect_update_depth_exceeded loop in countdown

Two $effects were reading state they also wrote, turning each into its
own dependency:

- AutoCountdown reset effect: `now = tickStart` read tickStart after
  writing it. Use a local snapshot so neither rune is read post-write.
- MasterPanel auto-call effect: `tickCount++` read tickCount. Drop the
  bump here — AutoCountdown's reset effect already re-baselines on the
  rising edge of `running` and on `duration` change, so re-arms remain
  covered. handleDrawNext keeps its per-draw bump (not in effect scope).
This commit is contained in:
tiennm99 committed 2026-04-30 19:47:22 +07:00
1 parent 1e89137fa6
commit 2b6a8c91f1
2 files changed
+9 -8

No files matched your search

+5 -2
View File
@@ -24,12 +24,15 @@
// Re-baseline whenever the parent bumps tickKey, running flips on, or
// duration changes mid-run (e.g. host moves the speed slider).
// Use a local snapshot so we don't read `tickStart` after writing it —
// that would make this effect depend on its own write and infinite-loop.
$effect(() => {
tickKey; // subscribe
duration; // subscribe — keeps the contract explicit, not parent-coupled
if (running) {
tickStart = performance.now();
now = tickStart;
const t = performance.now();
tickStart = t;
now = t;
}
});
+4 -6
View File
@@ -123,12 +123,10 @@
autoRunning = false;
return;
}
// Re-baseline countdown on every (re-)arm: rising edge of autoRunning,
// speed change, autoCallEnabled toggle. handleDrawNext bumps it again
// per tick so the ring re-fills before the next interval elapses.
// Safe self-write: this effect doesn't read tickCount, so the bump
// can't re-trigger it.
tickCount++;
// No tickCount bump here on purpose: `tickCount++` would read tickCount
// and turn this effect into its own dependency (effect_update_depth_exceeded).
// AutoCountdown's reset effect already re-baselines on `running` rising
// edge and on `duration` change, so re-arms are covered without our help.
const ms = settings.autoCallSpeed * 1000;
const id = setInterval(() => {
if (!state || state.remaining.length === 0) {