Repository navigation
Releases
Focus Exe 1.4.0
Added
- Badge-style close button on widget (visible on hover/touch)
- Changelog now loads from GitHub at runtime — no rebuild needed for updates
Changed
- Local builds (
npm run build:local) now create stable folders indist/<browser>/— no separatedist/hot/needed - Page served from
dist/page/(GitHub Pages); extension packages no longer include page assets
Removed
dist/hot/folder and--hotflag- Build-time changelog parsing (was
src/changelog.ts)
Focus Exe 1.3.0
Added
- Bullseye target icon for the focus session counter row
- Single-audio cross-tab lease: brown noise and the metronome are audible from exactly one tab no matter how many are open
src/leadership.ts— sharedchrome.storage.locallease with heartbeat renewal and TTL expirytest/smoke.mjs— shared-store boot helper and 10-check shared-audio smoke suite
Changed
- About & support now opens the source repository on GitHub instead of the product page
build.mjsgenerates a stablepage/changelog.jsonfromCHANGELOG.md; the product page renders the condensed release history at runtime from this JSON instead of hand-typed HTMLsrc/changelog.ts— Keep a Changelog parser emitting a stable JSON schema
Fixed
- Reopening the widget pushes the running session into the audio engine only when this tab holds the shared lease, so a reopened panel is no longer silent but also never layers noise beds or metronome streams with other open tabs
Tests
- 146/146 checks:
test/run.mjsverifies the builtmanifest.jsondeclares the all-URL content script and keeps<all_urls>out ofpermissions(3 manifest checks), plus the three suites (page 31, worker 20, smoke 95).
Focus Exe 1.1.2
Focus Exe 1.1.2 - 2026-09-27
Added
- Changelog on the product page (
#changelog, in the nav): the release history from v1.0.0 condensed into one card per version, linked to the fullCHANGELOG.md. The page test fails if the current version is missing there, so the page can never ship stale. - The release process, written down:
.clinerules/releases.md(the always-on rule - no version bump without a changelog entry, no release push without an annotated tag) and the/releaseskill with the full checklist.
Changed
- Audio keeps playing while the tab is in the background: brown noise and the 60 BPM metronome no longer pause when the tab is hidden. The metronome queues up to 75 s of beats on the audio clock, never schedules a beat in the past, and fades the queued backlog on stop or mode switch. A suspended
AudioContextresumes as soon as the browser allows it, and any page gesture unlocks it. - Reopening the widget restores sound: the running session is pushed into the fresh audio engine, so a reopened panel is no longer silent.
package-lock.jsonversion fields are back in sync withpackage.json.
Tests
- 141/141 checks (page 31, worker 20, smoke 90);
tsc --noEmitclean.
Full history: https://github.com/vallimuthuraja-git/focus-exe/blob/main/CHANGELOG.md
Focus Exe 1.1.1
Fixed
- The mechanical digit scroll on the countdown always runs. Two
prefers-reduced-motiongates in the extension build (one in the render path, one forcinganimation-duration: 1ms) stopped the reel animation whenever the desktop reported reduced motion — which Chrome does when OS animations are off. The digits snapped instead of scrolling, unlike the original app. The roll is now unconditional and matches the original 540 msmechanical-roll; reduced motion still applies to the button, popover and progress-bar transitions.
Verify
Reload the unpacked extension from dist/<browser>, or re-download the zip for your browser below. The product page download links now point at this tag.
Focus Exe 1.1.0
Minor release. The widget's ⓘ button moves into the settings panel and gains a
GitHub link beside it. No timers, audio or stored state changed, and nothing
needs migrating — 1.0.1 installs keep working and their settings are read
untouched.
What changed
The header held a single-purpose ⓘ button that opened the bundled product page.
It is now a row inside the settings panel, and because the panel is built once
per view it is reachable from the compact bar too, which it never was
before:
Settings
Brown Noise [switch]
60 BPM Neuro-Metronome [switch]
-----------------------------
About & support ↗ bundled product page
GitHub ↗ source repository
Each row is a full-width button that mirrors the toggle-row layout, so the whole
strip is one comfortable target rather than a small icon. Clicking a row closes
the panel and asks the service worker to open the tab, since a content script
cannot create tabs. The worker maps the target to a URL it chooses — no URL
is ever taken from the message.
The panel is now titled "Settings" rather than "Audio settings", since it no
longer holds only audio options. Row accessible names begin with the visible
text, so screen readers announce "About & support" rather than something that
merely resembles it (WCAG 2.5.3).
Code layout
The settings panel had grown inside widget.ts; it now has its own files, and
widget.ts keeps only the shell, cards, controls and confirmations:
| File | Contents |
|---|---|
src/ui.ts |
shared shadow-DOM builders (need, icon, button, popButton, replaceIcon) |
src/settings.ts |
the panel: audio toggles plus the About/GitHub rows |
src/settings.css |
the panel styles, lifted out of widget.css |
The panel is driven by data (TOGGLES / LINKS) instead of a hand-built
fragment, and the repository URL is injected from package.json by build.mjs,
keeping it a single source of truth with the product page.
Tests
Suites: 131/131 (page 30, worker 20, smoke 81), up from 110. The new checks
assert the rows exist in both panels, that each sends exactly one correct
message, that the row child order stays copy-then-arrow, and that accessible
names start with the visible label. The worker suite also pins that the repo
link opens the real repository, that foreign senders are ignored, and that an
unknown target opens nothing.
Still to configure
The Support section ships with placeholder handles. Each button safely falls
back to its platform's home page with a small setup note, so nothing 404s — but
donations cannot be received until real handles are filled in.
Downloads
Same six builds, ~34 KB each, unsigned.
| Asset | Install |
|---|---|
focus-exe-1.1.0-chrome.zip |
Unzip → chrome://extensions → Developer mode → Load unpacked |
focus-exe-1.1.0-edge.zip |
Same, via edge://extensions |
focus-exe-1.1.0-brave.zip |
Same, via brave://extensions |
focus-exe-1.1.0-opera.zip |
Same, via opera://extensions |
focus-exe-1.1.0-firefox.zip |
about:debugging#/runtime/this-firefox → Load Temporary Add-on → pick manifest.json |
focus-exe-1.1.0-safari.zip |
xcrun safari-web-extension-converter dist/safari --macos-only --no-open |
Minimums: Chromium 102+, Firefox 115+, Safari 16+.
Focus Exe 1.0.1
Patch release. v1.0.0 had a bug that made the timer unusable after the first dismissal — please read if you installed 1.0.0.
The bug
sanitize() used Number.isFinite(Number(x)) to check whether a stored value was a number. But Number(null) is 0, and Number.isFinite(0) is true, so a null passed the check and became 0.
The extension writes remaining: null whenever it is idle, so the first summon looked fine (sanitize(null) returns the defaults untouched). Every summon after that:
- the clock rendered 0:00 instead of 25:00, and
- pressing Start created a zero-length session that settled instantly — the session counter incremented and a "Focus session complete" notification fired.
A stored running: true, endAt: null record was also settled as a phantom completion, which is exactly the corruption sanitize() exists to reject.
If you installed 1.0.0, update to this release — your session counter may have been incremented by a session that never happened.
The fix
src/state.ts now accepts only a real finite number for endAt and remaining; anything else is treated as absent. clampNumber no longer coerces either, so non-numeric input falls back instead of silently becoming 0.
Why it shipped
The 109 checks that were passing were living outside the repository, and no suite ever seeded a stored idle record. Both are fixed here:
test/is now tracked, withnpm test(pretestbuilds first) running all three suites against the built bundles- a regression case covering all four symptoms, confirmed failing before this fix and passing after
- GitHub Actions now type-checks, tests, packages and deploys the product page on every push to
main
Also in this release
- MIT
LICENSEadded.package.jsondeclared MIT and the product page advertised it, but there was no licence text, so GitHub reported no licence and the repository was legally all-rights-reserved. test/page.mjsderives the release-asset URLs frompackage.jsoninstead of hard-coding the version, so it no longer breaks on every release.
Suites: 110/110 (page 30, worker 17, smoke 63).
Still to configure
The Support section ships with placeholder handles. Each button safely falls back to its platform's home page with a small setup note, so nothing 404s — but donations cannot be received until real handles are filled in.
Downloads
Same six builds, ~33 KB each, unsigned.
| Asset | Install |
|---|---|
focus-exe-1.0.1-chrome.zip |
Unzip → chrome://extensions → Developer mode → Load unpacked |
focus-exe-1.0.1-edge.zip |
Same, via edge://extensions |
focus-exe-1.0.1-brave.zip |
Same, via brave://extensions |
focus-exe-1.0.1-opera.zip |
Same, via opera://extensions |
focus-exe-1.0.1-firefox.zip |
about:debugging#/runtime/this-firefox → Load Temporary Add-on → pick manifest.json |
focus-exe-1.0.1-safari.zip |
xcrun safari-web-extension-converter dist/safari --macos-only --no-open |
Minimums: Chromium 102+, Firefox 115+, Safari 16+.
Focus Exe 1.0.0
First public release. A Manifest V3 focus assistant that runs on any site, on demand, and is torn down when you dismiss it.
What you get
- 25/5 focus and recovery modes with mechanical split-flap digits and a countdown to a deadline
- Brown noise and a 60 BPM neuro-metronome from a Web Audio look-ahead scheduler
- Session counter with a progress bar
- Confirmations on every state change, so a stray click can't end a session
- Minimize/expand, a compact bar, and audio that pauses when the tab is hidden
- Desktop notification when a session or break ends
How to run it
| Action | Result |
|---|---|
| Click the extension icon | Show the widget on the current page (press again to hide) |
Alt+Shift+F |
Same, without leaving the keyboard (rebindable in your browser's shortcut settings) |
| Click anywhere on the page | Widget collapses to its compact bar |
| Click ⓘ in the widget | Open the bundled product page in a new tab |
Downloads
Six builds, one codebase, ~33 KB each.
| Asset | Install |
|---|---|
focus-exe-1.0.0-chrome.zip |
Unzip → chrome://extensions → Developer mode → Load unpacked |
focus-exe-1.0.0-edge.zip |
Same, via edge://extensions |
focus-exe-1.0.0-brave.zip |
Same, via brave://extensions |
focus-exe-1.0.0-opera.zip |
Same, via opera://extensions |
focus-exe-1.0.0-firefox.zip |
about:debugging#/runtime/this-firefox → Load Temporary Add-on → pick manifest.json |
focus-exe-1.0.0-safari.zip |
xcrun safari-web-extension-converter dist/safari --macos-only --no-open |
The Chromium builds are identical; pick whichever browser you use. Minimums: Chromium 102+, Firefox 115+, Safari 16+.
These are unsigned self-hosted packages. Loading unpacked takes a minute and needs no developer account. Firefox temporary add-ons are removed on restart — sign with
web-ext signfor a permanent install. Safari needs the Xcode converter because it ships through the App Store.
Privacy
Not a tracking product, and the permission list is the proof:
- Permissions are only
activeTab,scripting,storage,notifications— nohost_permissions, no blanket<all_urls>, and no access to any page until you summon the widget - No remote code. No
eval, nonew Function, no CDN, and no network calls at all. The product page is a single self-contained file bundled into every build - The UI lives in a closed shadow root in the extension's isolated world and never touches page JavaScript — nothing is written to
window, so a page can neither read nor drive the widget - Timer state lives in extension storage, not the page's
localStorage, so the site can neither read it nor correlate it - Everything read back from storage is re-validated and clamped, and both the service worker and the content script ignore any message whose
sender.idisn't this extension
Build it yourself
npm install && npm run pack # tsc, build all six, write the zipsRequires Node 18+, Chrome 102+, Firefox 115+ or Safari 16+. npm run verify type-checks and produces a packaged build; npm run dev gives you an unminified build with inline sourcemaps.
What's next
v1 is deliberately small. Likely v2 additions: editable focus/break lengths and deadline in the settings panel, per-tab vs global session stats, a session history chart, custom sound packs, and an optional "auto-open on this site" list.
Found a problem or want to help? Open an issue.