Skip to content

Commit 4e7a496

Browse files
erancybersecclaude
andcommitted
feat: scheduler fires reliably in background tabs (v1.2.0)
Replace main-thread setInterval with a Web Worker tick so scheduled messages send on time even when the tab is not focused. Browsers throttle hidden-tab timers but not Worker timers. Falls back to setInterval when Workers are unavailable. UI copy updated to reflect that the tab only needs to be open, not active. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0aba44a commit 4e7a496

File tree

2 files changed

+31
-7
lines changed

2 files changed

+31
-7
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project are documented here.
44

55
---
66

7+
## [1.2.0] — 2026-04-10
8+
9+
### Improvements
10+
11+
#### Scheduler — Background Tab Support
12+
- Scheduled messages now fire reliably even when the tab is in the background (not the active/focused tab)
13+
- Replaced the main-thread `setInterval` poller with a **Web Worker**-based tick; browsers throttle main-thread timers in hidden tabs but do not throttle Worker timers
14+
- Falls back to plain `setInterval` in environments that do not support Web Workers
15+
- Updated all UI copy from "The browser tab must remain open" to clarify the tab only needs to be open — it does not need to be the active tab
16+
17+
---
18+
719
## [1.1.0] — 2026-04-10
820

921
### New Features

index.html

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ <h2 class="text-lg font-semibold text-gray-800 mb-1">Compose &amp; Send</h2>
539539
<input id="compose-schedule-dt" type="datetime-local" class="flex-1 border border-gray-300 rounded-lg px-3 py-2 text-sm"/>
540540
<button onclick="scheduleCompose()" class="btn-send text-white font-semibold px-4 py-2 rounded-lg text-sm whitespace-nowrap">Confirm</button>
541541
</div>
542-
<p class="text-xs text-gray-400">The browser tab must remain open at send time.</p>
542+
<p class="text-xs text-gray-400">The browser tab must remain open at send time (it doesn't need to be the active tab).</p>
543543
</div>
544544
</div>
545545
</div>
@@ -1025,7 +1025,7 @@ <h3 class="text-sm font-semibold text-gray-800">Message Sequence</h3>
10251025
<input id="groups-schedule-dt" type="datetime-local" class="flex-1 border border-gray-300 rounded-lg px-3 py-2 text-sm"/>
10261026
<button onclick="scheduleGroupBroadcast()" class="btn-send text-white font-semibold px-4 py-2 rounded-lg text-sm whitespace-nowrap">Confirm</button>
10271027
</div>
1028-
<p class="text-xs text-gray-400">The browser tab must remain open at send time.</p>
1028+
<p class="text-xs text-gray-400">The browser tab must remain open at send time (it doesn't need to be the active tab).</p>
10291029
</div>
10301030
</div>
10311031
</div>
@@ -1222,7 +1222,7 @@ <h3 class="text-sm font-semibold text-red-600">Leave Group</h3>
12221222
<h2 class="text-lg font-semibold text-gray-800">Scheduled Jobs</h2>
12231223
<button onclick="clearDoneJobs()" class="text-xs text-gray-400 hover:text-red-500 transition-colors">Clear completed</button>
12241224
</div>
1225-
<p class="text-sm text-gray-500 mb-5">Messages queued for future delivery. The browser tab must remain open at send time.</p>
1225+
<p class="text-sm text-gray-500 mb-5">Messages queued for future delivery. The browser tab must remain open at send time (it doesn't need to be the active tab).</p>
12261226
<div id="scheduled-jobs-list" class="space-y-3">
12271227
<p class="text-sm text-gray-400 text-center py-12">No scheduled jobs yet.</p>
12281228
</div>
@@ -3104,7 +3104,19 @@ <h3 class="text-sm font-semibold text-gray-800">Privacy Settings</h3>
31043104
}
31053105
}
31063106
check();
3107-
setInterval(check, 30000);
3107+
// Use a Web Worker for the tick so the timer fires reliably even when
3108+
// the tab is in the background (browsers throttle main-thread setInterval
3109+
// in hidden tabs, but Worker timers are not throttled the same way).
3110+
try {
3111+
const workerBlob = new Blob([
3112+
'setInterval(function(){ postMessage("tick"); }, 30000);'
3113+
], { type: 'application/javascript' });
3114+
const worker = new Worker(URL.createObjectURL(workerBlob));
3115+
worker.onmessage = () => check();
3116+
} catch (_) {
3117+
// Worker not supported — fall back to plain setInterval
3118+
setInterval(check, 30000);
3119+
}
31083120
}
31093121

31103122
async function scheduleCompose() {
@@ -3128,7 +3140,7 @@ <h3 class="text-sm font-semibold text-gray-800">Privacy Settings</h3>
31283140
await saveJob(job);
31293141
document.getElementById('compose-schedule-panel').classList.add('hidden');
31303142
renderScheduledJobs();
3131-
alert(`Scheduled! Will send at ${new Date(dt).toLocaleString()}. Keep this tab open.`);
3143+
alert(`Scheduled! Will send at ${new Date(dt).toLocaleString()}. Keep this tab open (it doesn't need to be the active tab).`);
31323144
}
31333145

31343146
async function scheduleGroupBroadcast() {
@@ -3151,7 +3163,7 @@ <h3 class="text-sm font-semibold text-gray-800">Privacy Settings</h3>
31513163
await saveJob(job);
31523164
document.getElementById('groups-schedule-panel').classList.add('hidden');
31533165
renderScheduledJobs();
3154-
alert(`Scheduled! Will send at ${new Date(dt).toLocaleString()}. Keep this tab open.`);
3166+
alert(`Scheduled! Will send at ${new Date(dt).toLocaleString()}. Keep this tab open (it doesn't need to be the active tab).`);
31553167
}
31563168

31573169
async function cancelJob(id) {
@@ -5898,7 +5910,7 @@ <h3 class="text-sm font-semibold text-gray-800">Privacy Settings</h3>
58985910
chatAutoResize(input);
58995911
document.getElementById('chat-schedule-panel').classList.add('hidden');
59005912
const name = chatResolveName(jid);
5901-
alert(`Message scheduled for ${new Date(dt).toLocaleString()} to ${name}. Keep this tab open.`);
5913+
alert(`Message scheduled for ${new Date(dt).toLocaleString()} to ${name}. Keep this tab open (it doesn't need to be the active tab).`);
59025914
}
59035915

59045916
// ── Send media ───────────────────────────────────────────────────────

0 commit comments

Comments
 (0)