v1.3.3
Added
- First-run quick-suspend pass (
firstRunQuickSuspendinbackground.js). 30s after install, Drowzy suspends tabs that Chrome reports as idle for 10+ minutes viatab.lastAccessed(Chrome 121+; skipped on 120). Without this, a fresh installer waited the full 30-minute timer before any visible effect - the most common reason cited for "didn't notice a memory difference" in uninstall feedback. Conservative threshold (10 min via Chrome's own tracking) avoids surprising a user with a recently-used tab going away. Honors all the usual protections (pinned, audio, whitelist, internal pages). tab.lastAccessedhonored in suspend decisions (shouldSuspendand the final recheck insidesuspendTab). Drowzy now takes the more recent of its own activation timestamp and Chrome'stab.lastAccessed(Chrome 121+) when deciding whether a tab is past the idle threshold. Fixes a class of "tab I just used got suspended" caused by service-worker restarts: when the worker came up cold, Drowzy's in-memory_timestampswere re-seeded toDate.now()for missing tabs, buttab.lastAccessedhad the truer "last focused" timestamp. Feature-detected so 120 still works.- "Keep awake" button on the suspend warning banner (
formcheck.js). The banner used to be a click-to-dismiss strip with no way to actually stop the imminent discard - the only recourse was to whitelist the site after the fact. The banner now has an explicit "Keep awake" button that sends akeepTabAwakemessage to the background; the background bumps the tab's timestamp viatouchTabandsuspendTab's pre-discard recheck reads the bumped timestamp and bails out. New i18n key:keepAwakeBtn. keepTabAwakeaction allowed from content-script senders inbackground.js#onMessage. The unauthorized-content-script guard still blocks every other action, butkeepTabAwakeis whitelisted because the suspend-warning banner runs in page context. Scoped to the sender's own tab id so a content script can't bump arbitrary tabs.- Pre-discard timestamp recheck inside
suspendTab. After the form-check + message round-trips, before callingchrome.tabs.discard, the function now re-reads_timestamps[tabId]andtab.lastAccessedand aborts if either is now within the threshold. Closes the race where the user hits Keep awake during the 2500ms warning window or refocuses the tab via a different code path. - Suspend-timer hint tooltip on the Settings row (
data-i18n-title="suspendTimerHint"inpopup.htmlandsidepanel.html). New users with workflows where they come back to a tab every ~35 min had no hint that 30 was tunable; the tooltip points them at it. New i18n key:suspendTimerHint. - Two new onboarding tip cards addressing the actual top uninstall reasons: "Suspending tabs you still want?" points at the timer + whitelist as fixes for over-aggressive suspends, and "Filling out a form?" points at the
protectFormssetting. Both ship asdata-i18n-htmlso the embedded<strong>survives translation. New i18n keys:onboardingTimerTip,onboardingFormsTip. - Windows-specific keyboard shortcut defaults to avoid Microsoft Office Web access-key collisions:
suspend-currenton Windows is nowAlt+Shift+Z(wasAlt+S). On Windows,Alt+Sis the access key for Send in Outlook Web, Teams, OWA, and most Microsoft 365 surfaces. A user composing an email who hitAlt+Sto send would instead suspend the compose tab - silently, with potential draft loss. macOS/Linux/ChromeOS keepAlt+Ssince those platforms don't have the conflict (Mac Outlook usesCmd+Enter).wake-allon Windows is nowAlt+Shift+W(wasAlt+W).Alt+Wis the access key for the View ribbon in Word, Excel, and PowerPoint Web. Same per-platform override pattern.suspend-othersstays atAlt+Shift+Severywhere - no known conflict.- Existing users who customized their shortcuts via
chrome://extensions/shortcutsare unaffected; only new installs and unmodified bindings pick up the new defaults.
- Dynamic shortcut display in the onboarding page. The
Press <kbd>Alt+S</kbd> ...lines now querychrome.commands.getAll()after i18n localization runs and replace each<kbd>'s text with the user's actual binding - so Windows users seeAlt+Shift+Z/Alt+Shift+Wand any user remap is reflected without re-translating 57 locale files. If a command is unbound, the translated default is left alone. - Forecast stat in the stats strip - adaptive third slot. The third memory slot now adapts based on state to keep the strip at three items (a fourth would feel cramped). When Drowzy has actually freed memory, it shows that ("saved", blue dot,
ramEstimateTooltip). Before any tabs have been suspended but eligible tabs exist, it shows the forecast instead ("available", amber dot,ramForecastTooltip) - e.g.~3.4 GB available. First-session users no longer see " - saved" with no signal anything will happen; they see a real number immediately, addressing the most common uninstall reason ("didn't notice a memory difference"). When neither applies, the slot shows an em-dash with the default "saved" label. Computed ingetStatusas(allTabs - protected - discarded) × MB_PER_TABand exposed aseligibleCount+estimatedMbForecast. statForecastandramForecastTooltipi18n keys inen/messages.jsonfor the new strip item label and tooltip.- Quantified toast feedback for Suspend Others and Wake All. Pre-1.3.3 the buttons flashed
Suspending... → Done → Suspend Othersregardless of whether anything actually happened - a window with zero eligible tabs would still show "Done" and the user would close the popup confused. Now:suspendAllOthersandunsuspendAllboth return their actual count, the popup handlers show"Suspended N tab(s) · ~M MB freed"/"Woke N tab(s)"toasts on success, and"No other tabs to suspend"/"No suspended tabs to wake"toasts when the action would have been a no-op. Removed the now-redundantDonetext-flip and the 400ms timeout since the toast carries the confirmation. New i18n keys:suspendedToast,wokeToast,noOthersToSuspend,noSuspendedToWake. - Smarter current-tab status line. The status under the active tab's domain (e.g.
"Active - won't be suspended") used to be hardcoded regardless of why the tab was protected. Now picks the actual reason in priority order:systemPageCantSuspendforchrome://and other non-http schemes,pinnedWontSuspendwhen the user has Protect Pinned on and the tab is pinned,audioWontSuspendwhen Protect Audio is on and the tab is audible,whitelistedWontSuspendwhen the tab's host matches the whitelist, falling back to the existingactiveWontSuspend. The whitelist button below already updated correctly; only the status text was stale. New i18n keys:pinnedWontSuspend,audioWontSuspend,whitelistedWontSuspend,systemPageCantSuspend. - Onboarding now includes a pin-to-toolbar tip and a one-click link to remap shortcuts. Two of the most common first-hour confusions: "where is the icon?" and "I don't like the default keys." A subtle accent-bordered tip card sits above the CTA with a tip about pinning Drowzy via the puzzle icon, plus a link that opens
chrome://extensions/shortcutsdirectly (<a href="chrome://...">is blocked from regular pages, so the click handler useschrome.tabs.create). New i18n keys:onboardingPinTip,onboardingRemapHint.
Changed
- Strip-stat dot color for the third slot stays blue (
:last-child) by default for "saved", and is overridden to amber (.strip-stat.strip-stat-forecast) when the slot is rendering the forecast instead. JS toggles the class as the slot's mode flips between saved/forecast/empty. - Suspend Others and Wake All buttons disable themselves when there's nothing to act on.
renderStatsStripnow flipsbtn.disabledbased onstatus.eligibleCount/status.suspendedCountand sets atitletooltip explaining why (noOthersToSuspend/noSuspendedToWake). The existing.btn:disabled { opacity: 0.5 }rule already handled the visuals - no CSS change needed. The click handlers also short-circuit onthis.disabledas belt-and-braces. - Whitelist input placeholder updated from
"example.com or site.com/path/*"to"example.com, full URL, or site.com/path/*". The popup'saddFromInputalready stripshttp(s)://andwww.so users can paste full URLs directly, but the old placeholder didn't say so - users would manually trim URLs first. The 56 non-English locale translations ofwhitelistPlaceholderstill say the old wording; semantically equivalent so they stay valid until the next translation pass.
Fixed
- Stale-init of
_timestampscould give every tab a fresh 30-min timer after a service-worker restart. Caught in the pre-publish audit. When the worker came up cold and_timestampswas empty (browser restart, session-storage cleared, or first install), bothinitTimestampsand the lazy-reload branch incheckAndSuspendTabsseeded missing entries withDate.now()- so a tab Chrome itself reported as last-accessed 45 minutes ago still gotlastActive = NOW, and the newtab.lastAccessed-awareshouldSuspendcouldn't help because it took the max of the two (NOW vs. 45m-ago = NOW). Fix: seed missing entries fromtab.lastAccessedwhen Chrome 121+ reports it, falling back toDate.now()only on Chrome 120. Now a service-worker restart recovers the actual idle time instead of resetting it. Same fix applied to the lazy-init incheckAndSuspendTabsand the timer countdown ingetTabListso the popup's "Xm" badge matches the actual eligibility check. - Forecast strip-stat had no visual differentiation from "saved". The 1.3.3 changelog claimed an amber dot for the third slot when rendering the forecast (vs blue for actual saved memory), but neither
popup.cssnorpopup.jsactually toggled the class - both states rendered with the identical blue dot, so a user reading "~3.4 GB available" had no visual cue that this was a forecast and not a number Drowzy had actually freed. Fixed in three places:popup.js#renderStatsStripnow adds/removesstrip-stat-forecaston the slot, andpopup.cssnow has.strip-stat.strip-stat-forecast .strip-stat-label::before(amber dot) and.strip-stat.strip-stat-forecast #statMemoryValue(amber value color) overrides. The blue stays for:last-childby default, so saved still reads as saved. Color transitions added on.strip-stat-valueand.strip-stat-label::beforeso the saved↔forecast flip animates smoothly instead of snapping. - Suspend warning banner ran on hidden tabs - pure theater, since the banner was being painted onto a tab the user definitionally wasn't looking at (it had been idle for 30 minutes).
formcheck.js#showSuspendWarningnow early-returns ifdocument.visibilityState !== 'visible', so the banner only renders when there's a chance of being seen and the "Keep awake" button is reachable. - Form-data check missed several common input types.
hasUnsavedFormDataqueried onlytext/email/url/tel/password/number/no-type/textarea/contenteditable- a search input on a long query, a date picker mid-fill, or atime/week/month/datetime-local/colorfield would all read clean and the tab would get discarded. Query expanded to cover those types. Also added a separate scan forinput[type="checkbox"]andinput[type="radio"]whosecheckeddiffers fromdefaultChecked, since a half-toggled settings page is just as lost as a typed-but-unsaved input. currentTabStatusinitial text was the literal string"Loading..."(viadata-i18n="loading") which would stick on screen ifloadAllthrew before the firstrenderCurrentTabran - the user would see a tab marked "Loading..." indefinitely with no way to know what was wrong. Replaced with an em-dash (matching thecurrentTabDomaininitial state). Same change in bothpopup.htmlandsidepanel.html.- Suspend-warning banner "Click to dismiss" tooltip in
formcheck.jswas hardcoded English; only the banner body text was localized. Wired up via the newclickToDismissi18n key with English fallback for unknown locales.
Translations
- Full localization parity across all 57 locales for all 20 new keys added in this release (16 from the initial 1.3.3 work + 4 from the pre-publish audit pass:
keepAwakeBtn,suspendTimerHint,onboardingTimerTip,onboardingFormsTip). 56 locales × 20 keys = 1120 fresh translations. Every locale now has all 175 keys; no English fallback gaps anywhere in the popup, side panel, onboarding, or content scripts. Same approach as the 1.3.2 parity sweep - AI-authored translations preserving the$COUNT$/$RAM$placeholder structure where applicable and the<strong>wrappers in onboarding tips, with native-speaker review welcomed for any awkward phrasing. ThewhitelistPlaceholderrewording is the one exception held back: existing locale translations of the old wording remain semantically valid so they stay until the next translation pass.
Final QA pass
changelog.htmlbody content rewritten for 1.3.3. The page was still showing the v1.3.2 "Polish & Fixes" cards verbatim, but the dynamic version badge and the "What's new vX" link in Settings would renderv1.3.3- so a user clicking the link would see a header that says 1.3.3 paired with v1.3.2 changes. Replaced with six v1.3.3-specific cards (forecast stat, Windows-friendly shortcuts, quantified suspend/wake toasts, smart current-tab status, friendlier first-run tips, full translation coverage).- Dead
donei18n key removed from all 57 locale files. It was used by the old text-flip pattern on Suspend Others / Wake All (Suspending... → Done → Suspend Others); the toast refactor in this release removed every reference. 57 stale entries dropped. - Three review-prompt button
title=tooltips localized. The visible button text (reviewYes,reviewNo) was already i18n'd, but the hover tooltips on the thumbs-up, thumbs-down, and dismiss buttons inpopup.html/sidepanel.htmlwere hardcoded English. Addeddata-i18n-titlebindings + three new keys (reviewLeaveTitle,reviewReportTitle,reviewDismissTitle) translated across all 56 non-English locales (168 strings). Low-frequency UI (only shown after 50+ suspensions) but rounds out the parity story.
Verifying this build
This release attaches the exact .zip uploaded to the Chrome Web Store. To verify it hasn't been tampered with:
sha256: 07596df748bf1874ea08854b683586bd54a01d49d02263a719ad57ba96978cb1
You can reproduce the build yourself by checking out v1.3.3 and zipping the extension files (everything except .git/, .github/, *.md, docs/).