github Ebullioscopic/Atoll v2.3.3-alpha.4
Atoll 2.3.3-alpha.4

pre-release7 hours ago

Atoll 2.3.3-alpha.4

Channel: alpha
Build: 20260720005
Date: 2026-09-07

Added

  • Pin lyrics under the closed notch: a pin button in the Lyrics panel keeps the line being sung visible after the notch closes, so a song can be followed without holding the pointer over the notch. The strip is drawn as an overlay on padding rather than as a row in the notch stack, so it never widens the panel or shifts the music row. Off by default, and inert unless lyrics are enabled.

  • Per-app volume and mute: a mixer in the notch header gives every app currently playing audio its own volume slider and mute button, with levels remembered per app. Built on the CoreAudio process taps already used for the waveform, and a tap is only created for an app that is actually muted or turned down. Off by default; needs audio capture permission.

  • Keep the Mac awake from the notch: a cup-and-saucer button in the notch header holds an IOKit power assertion — indefinitely, or for 15 minutes, 30 minutes, 1, 2 or 4 hours — with ⌘⇧K to toggle it when shortcuts are enabled, and a setting for whether the display is kept on too. The assertion is owned by the Atoll process, so it is released automatically if Atoll exits.

  • NetEase Cloud Music lyrics: when LRCLIB has nothing for the playing track, lyrics are now looked up in NetEase Cloud Music's own catalogue, which is where most Chinese-language releases live and where LRCLIB's coverage runs out. The player publishes no song id, so the track is found again by title and artist and confirmed by duration, which keeps a remix or a live cut from scrolling its timings past the studio recording. LRCLIB is still asked first, and what NetEase serves is shown as-is. (#713)

  • Antigravity token usage and cost: the Antigravity card now shows Today / Week token counts and an estimated API-equivalent cost, read from the desktop client's local language server (GetAllCascadeTrajectories + per-reply modelUsage counters, with GetUserStatus translating the server's placeholder model ids into public ones for pricing). The card's Session / Weekly gauges also show their reset countdown again.

  • Cache-aware cost estimates: the pricing table now carries cache-read and cache-write rates, and the Claude and Codex log parsers keep cached prompt tokens apart from uncached ones, so cache hits are priced at the provider's cache rate instead of the full prompt rate.

  • A way to find extensions: Extensions settings now opens with a link to the Marketplace at getatoll.app/marketplace. The rest of that page manages extensions that are already installed, which is no help to anyone who has none — nothing said they existed as a thing to go and get, or where.

  • Favourite the current song in Cider: the Favorite Song control now works when Cider is the media source. macOS Now Playing carries no favourite command, so Atoll asks Cider's own local API instead -- switch it on under Settings > Connectivity > Manage External Application Access in Cider, and paste the token into Atoll's media settings (or leave it empty if you turn Cider's authentication off). Playback needs none of this and is unchanged.

  • Favorite the playing song from the notch: the heart control now works with Apple Music and YouTube Music as well as Spotify, and under the Now Playing source it follows whichever of them is actually playing. Apple Music goes through Music.app's own scripting interface and YouTube Music through the desktop app's API server, so neither needs a subscription or developer credentials. Sources that cannot favourite show the control disabled rather than hiding it. TIDAL is a halfway case: it reports whether the playing track is in your collection, so the heart shows the real answer, but it will not accept a change from outside the app, so it does not take a click. (#599)

  • TIDAL shuffle and repeat actually work: TIDAL registers no Media Remote command for either, so both buttons moved in the notch while the app carried on playing as before. They now go through TIDAL's Playback menu, which both reports the current state and accepts a change -- and because each repeat item sets its own mode, repeat no longer has to cycle toward the one you asked for.

  • Always show volume control: the lock screen panel can keep a volume slider under the playback controls, instead of leaving volume behind the output button. Off by default and named after the iOS setting it copies — a permanent bar across a panel that is mostly artwork is a preference, so it is asked for rather than assumed. The volume keys move it while the Mac is locked, where the notch has nowhere to draw.

  • Show download speed: an option in Downloads settings puts the current rate beside the download indicator, measured from how fast the files in your Downloads folder are growing. Read from blocks on disk rather than the file's stated length, since a browser that reserves the whole file up front reports its final size from the first moment. Off by default.

  • Fingerprint lock screen activity: choose the lock, an animated fingerprint scan, or both from Lock Screen settings. The fingerprint uses an attributed Lottie animation and completes its green scan while the Mac unlocks. (#774)

  • TIDAL can now be selected as the music source. Atoll follows the TIDAL desktop app's macOS Now Playing session, so unrelated videos no longer replace its metadata or controls, and the real-time waveform captures TIDAL's separate player process. (#764)

  • Choose how the sung line is marked: a Highlight setting beside Show lyrics picks between Sweep, where the highlight travels across the line in time with the singing, and Solid, where the whole line lights at once as it did before.

  • Lyrics follow the music: the current line in the side lyrics panel and the lock screen music player is now lit by a highlight that sweeps across it in time with playback, instead of the whole line changing colour at once. Stretches of a track with no words are shown as music notes rather than as a gap — LRC marks where singing stops with a bare timestamp, so the break is read from the lyrics themselves rather than guessed, and only breaks of five seconds or more are marked so breaths between lines do not flicker.

  • Lyrics beside the lock screen player: tapping the artwork opens the expanded player, which now carries the whole song in a column of its own — scrolling itself to the line being sung, sweeping that line in time, and marking instrumental stretches with notes. Expanded also used to collapse itself five seconds after opening, which is not long enough to read a song by; it waits 30 seconds now, and does not start counting at all while the pointer is on it.

  • Opera downloads are noticed too. Opera is Chromium underneath but names its half-finished files .opdownload rather than .crdownload, so nothing it downloaded ever raised a live activity.

  • Firefox downloads: download detection now notices Firefox downloads as well as Chromium and Safari ones. Firefox writes its in-progress file as .part, which was not among the extensions watched, so its downloads produced no live activity at all. It also creates the destination file up front as an empty placeholder, which the old completion test read as a cancellation — so a Firefox download that did finish would have had its activity yanked away instead of showing the completion animation. Completion is now decided by whether the destination holds data that was written after the temporary file appeared — the same question for every browser, and the one that tells a finished download from a cancellation over a file that was already there.

  • A "Save History Across Restarts" setting for the clipboard manager. With it off, history is kept in memory for the session only: nothing is written to disk — including copied images, which were previously written to disk on every copy regardless — and history already saved is erased. Pinned items are kept either way (#743, closes #727).

  • Spotify "Like Song" media control: save or remove the current track from your Liked Songs directly from the notch, lock screen, and minimalist player, using the official Spotify Web API (OAuth 2.0 PKCE). Add the control to any media slot in settings. (#579)

  • Show the current Claude subscription plan (e.g. Max 5x) as a badge next to the Claude card title in the LLM Usage view (#684).

  • AntiGravity Usage Tracking: Track how much of Antigravity usage is left in the LLM Usage Monitor tab (both Gemini and Claude models)

  • Shelf item removal: Hovering a Shelf item now reveals a × button that removes just that item, with a VoiceOver-accessible "Remove from Shelf" action that works without hovering (#461).

  • Draggable clipboard tab: A .notchTab clipboard display mode shows clipboard history as a card grid inside the notch whose text, image, and single-file entries can be dragged straight out to Finder or other apps (drag = copy), with a hover × to delete a single item (#698).

  • Shelf marquee selection: Dragging on empty space in the Shelf now draws a rubber-band rectangle that selects every item it touches, matching Finder. Holding Shift unions the marquee with the existing selection instead of replacing it (#682).

  • Shelf drag-out move toggle: A new "Allow moving files when dragging out" setting (off by default) keeps drag-out copy-only. Offering a move operation previously let the receiving app relocate the original file out from under the user when the destination was on the same volume (#682).

  • Screen Recording HUD: Added a recording live activity with optional native stop controls and configurable hover presentation.

  • Lyrics on the side: Added ability to show lyrics of the current song when calendar is disabled (#741)

  • New API provider: monitor multiple New API accounts with balance, usage (today/week), RPM, TPM, and request counts in the LLM Usage tab (#752)

  • Secure API key storage in macOS Keychain; account metadata in UserDefaults

  • Per-account error isolation — one account's failure doesn't block others

  • Settings: add/edit/delete accounts, toggle provider, validation with inline errors

Changed

  • The bundled pricing table covers the current Claude 4.x / 5, GPT-5.x / codex and Gemini 3.x families (72 models) instead of four retired ids, and the daily pricing workflow tracks those families by prefix rather than a hand-maintained list. The remote table fetched at launch is merged into the bundled one rather than replacing it, so a lagging remote copy can no longer hide bundled prices.

  • Model ids from usage logs are also tried with openai/ and google/ prefixes, with dashed minor versions converted to dotted ones (claude-fable-5-1claude-fable-5.1), without an Antigravity tier suffix (-high, -low, …) and with a -preview suffix, so ids that exist in the table under a slightly different spelling are priced.

  • Codex usage records are tagged with the model named by the session's turn_context instead of the placeholder codex, so Codex cost can be estimated.

  • The lock screen temperature gauge shows the day's range: with coloured gauges on, the arc is now a gradient running from the day's low to its high with a dot marking the current reading along it, rather than a single-colour fill. The colour scale it draws from was also being read in Celsius whatever the display unit, so every Fahrenheit reading above freezing came out the hottest colour -- readings are converted before they are coloured now, and the scale is continuous rather than five steps, so a range inside one step is still visibly a gradient.

  • Lock Screen settings are split into General and Widgets: the tab held fourteen sections, ten of them configuring one widget each, so the settings you came for when you are not adjusting a particular widget were buried among the per-widget ones. Settings search still opens any of them directly -- it switches to the right segment first.

  • Segmented pickers throughout settings match the clipboard panel: one rounded selection that slides between options on a spring, instead of the stock flat rectangle that snaps. It keeps the system accent colour, so it still follows System Settings > Appearance.

  • Lock screen glass has one home: the Lock Screen Glass section is now only on the Lock Screen tab. Appearance carried a second copy of the same material and glass-mode pickers, with its own variant sliders whose caption admitted they mirrored the other tab — two places to change one setting, and neither obviously the real one. The live glass preview that only existed on the Appearance copy moved over with it, so nothing is lost in the move.

  • The TIDAL source picker now uses the official TIDAL diamond logo instead of a generic waveform symbol. (#782)

  • Lock screen live activity timings now follow the selected icons: the fingerprint remains visible through its scan, while the open lock gets its own brief confirmation beat before the island contracts. (#774)

  • The LIVE indicator shown in place of a progress bar on streams now follows Apple's treatment: two thin rules meeting the word in the middle, softening as they run away from it. It was a 10pt capsule with a fill, a centre shade, a stroke and three blend modes. Shadowed text was dropped over the top of all that. The result read as a progress bar someone had written on, and a stream has no progress to draw. The notch, the lock screen player and the floating window all draw the same component, so all three change together.

  • Redesigned the clipboard panel: search replaces the separate title bar, tabs became a sliding segmented control, rows are cards with a tinted type badge (a real thumbnail for images) and hover actions in a reserved-width container so nothing shifts under the cursor (#743).

  • Lock screen and notch music controls now follow Apple's sizing and animation: the hover/press highlight fills a circular target roughly 2.2x the glyph instead of hugging it, play/pause shares that highlight with the rest of the row, and the track title and artist scale up to Apple's lock screen proportions. Pressing next/previous marches the chevrons in the direction of travel rather than sliding the whole button (#742).

  • Improved the Dutch localization by adding missing translations, corrected terminology, and wording aligned with Apple's Dutch macOS conventions.

  • Refreshing the LLM Usage card now skips session logs whose last write predates the seven-day window instead of re-reading the whole log history, and counts a repeated record when the copy inside the window would previously have been suppressed by a copy outside it (#691).

  • Completed the Simplified Chinese (zh-Hans) localization, filling in all remaining untranslated strings.

  • The separate-tab clipboard now uses the same card grid (two columns) with drag-out and per-item delete, replacing the single-column list (#698).

Fixed

  • Reading the Claude quota no longer logs Claude Code out: when the Claude Code access token expired, Atoll used the shared refresh token to get a new pair, kept the pair in memory, and left the old one in Keychain. Refresh tokens are single-use, so from then on the Keychain copy was dead: the next Atoll launch could not refresh it and showed "Quota unavailable", and Claude Code itself could not refresh it either and dropped to logged out. Atoll now writes the refreshed pair back to where it read it (the credentials file or the Keychain item), keeping every field Claude Code stores there, so both keep working. It also waits until the token has actually expired before refreshing, and re-reads the source first in case Claude Code already did.

  • New OpenAI models are priced again: the Codex card showed est. n/a for any day spent on gpt-6-astra, because the GPT branch of the nightly pricing update's filter matched a literal gpt-5 (gpt-4.1, o3, o4 and codex had branches of their own), so the first GPT-6 model fell through. That branch now takes the whole gpt- family, so the next release in the line is priced the day OpenRouter lists it, and the bundled table already carries GPT-6 Astra.

  • Lyrics with timings are preferred over lyrics without: timings were worth three points in the match ranking against twenty available elsewhere, so a result with no timings but an exactly matching album beat one that had them and agreed slightly less precisely. The song then showed as plain text with no line ever lit, even though timed lyrics existed. Whether the words can be followed at all is now decided before anything else, and the rest of the ranking only separates results that are alike in it.

  • Lyrics with no timings are shown as lines, not as one block: when a track only has plain lyrics available, the whole body was kept as a single lyric line. The panel then treated the entire song as the line being sung, sweeping the highlight across all of it at once and straight through the middle of words. Plain lyrics are now split into their own lines and marked as untimed, so they are simply readable and nothing pretends to know where in them you are.

  • The Claude usage card never showed its resets in … countdown: resets_at from the usage endpoint carries fractional seconds, which the default ISO 8601 parser rejects (#810).

  • The LLM Usage tab was taller than every other tab and left blank space inside each provider card. A fixed 188 pt card height plus a 220 pt minimum tab height were replaced by cards that stretch to the height the panel already offers, which keeps the cards equal and centred at the tab's normal size.

  • The Antigravity card's title wrapped onto two lines beside the pool picker; it now stays on one line and scales down when needed.

  • Antigravity language-server discovery never succeeded on machines with several hundred processes: the ps listing was read only after waiting for ps to exit, so once the output exceeded the 64 KB pipe buffer the two deadlocked until the 2 s timeout killed ps, leaving only the head of the list. The pipe is now drained first and ps prints only pid + command line. Discovery also accepts the desktop client's --flag value argument style and asks lsof for the port when the server was started with --https_server_port 0.

  • The Antigravity pool rows showed the remaining fraction formatted as a dollar amount (US$0.98); those rows are replaced by the token usage rows above.

  • Fixed CPU temperature reporting on M5-series Macs by using the M5 SMC sensor keys instead of the M4 key set (#585).

  • Live activities step around the menu bar: a live activity draws into the strip of menu bar beside the notch, which is where the frontmost app's own menus live, so a timer or download could sit on top of them — the Help menu especially, being the one nearest the notch. macOS offers no way to tell it that part of that strip is spoken for, so Atoll measures where the menus actually end and slides the activity clear, moving back once there is room again (#793).

  • The custom OSD no longer sticks on screen: switching away from Custom OSD while one of its windows was visible left the overlay there for good. Nothing watched the setting, and the only thing that would ever have hidden that window was its own two-second timer, which the switch outran. Turning the OSD off -- or turning off volume, brightness or keyboard backlight individually -- now dismisses what is on screen.

  • The lock screen positioning preview can move a widget as far as the setting allows: it clamped each widget's whole stand-in box inside the mock screen, and the music box is drawn at roughly twice the panel's real proportions -- so the music widget could move only about 10pt down, nowhere near the 160pt the setting itself permits. The widget's centre is what stays on screen now.

  • A stray divider above the lock screen width sliders drew as a short vertical tick with an empty row around it, just above the separator the form already draws.

  • The Controls tab no longer overflows the settings window: its HUD variant cards are a fixed 110pt each, which inside a grouped form became a hard minimum width for the whole pane and pushed everything sideways. They fit to the available width now, and scroll horizontally when there is not enough.

  • The control palette's swipe indicator no longer overlaps the settings beneath it.

  • The lock screen positioning footer and reminder tooltip described the wrong controls, and the battery indicator toggle did not update the rows that depend on it until the tab was reopened.

  • Restored the notch's curved top corners in both standard and Minimalistic music UI while retaining the top-edge anti-gap fill, and synchronized the lock and fingerprint indicators through one shared scan state. (#782)

  • The lock screen Dynamic Island now completes one clean unlock contraction instead of disappearing early or showing a second island that closes immediately afterward. (#774)

  • Lock screen volume slider sat on the panel's edge: the collapsed panel reserved half a point less height than its content needed, so the volume capsule was clipped by the bottom border while the transport row above it kept about 33pt of air. The reserved height is now measured rather than derived, and the capsule has around 20pt of room beneath it.

  • Volume slider stuck at the bottom: a volume the system would not report was being shown as a volume of zero, so on outputs that publish no readable per-channel level the lock screen and notch sliders sat empty and the speaker glyph showed muted. The level is now also read through the virtual main volume that macOS's own volume UI uses, and a read that fails holds the last known level instead of reporting silence. Setting the volume takes the same fallback.

  • The keyboard backlight HUD and OSD toggles come back when the external display app is quit. They were greyed out by the integration setting alone, but the keys are only handed over while that setting is on and the provider is actually running — so quitting it returned the keys to Atoll and left the toggles disabled over a setting that had started working again.

  • Lyrics for untagged tracks: An untagged file playing as "Track 7" by "Unknown artist" no longer shows a stranger's lyrics. lrclib holds many different songs filed under exactly that placeholder title and artist, so the search returned a perfect-scoring match that was somebody else's song. Metadata that names no particular track is now recognised as such and skipped, and a result that agrees with neither the title nor the artist is no longer accepted as the best of a bad set. Search results are also matched with the same diacritic folding used to build the query, so lrclib's "Beyoncé" no longer fails to match the "Beyonce" that was searched for. A karaoke, instrumental, remix or sped-up version is also no longer accepted in place of the track playing, since its words are right but its timings are not.

  • A swipe across the notch skips one track instead of sometimes several. A trackpad delivers a flick as a run of touch events followed by a run of momentum ones, and the gesture was ended between the two — so the momentum tail started accumulating again from zero against the same threshold the touch had just crossed, and fired the skip a second time. Momentum is the end of a flick the user has already let go of, so it no longer counts as new input. The same double-firing applied to the open and close gestures.

  • Native OSD suppression no longer costs a subprocess per check: the watcher that keeps macOS's own volume/brightness HUD suspended asked pgrep for OSDUIHelper for as long as the app ran — a fork, an exec, a pipe and a reap, measured at 67 ms of wall time and ~5 ms of CPU each. The interval adapts: once a second at rest, but 150 ms for the first few checks after the HUD appears, so bursts ran closer to seven times a second. The same question is now answered directly by the kernel in 0.6 ms. In a 15-second sample of an idle app the work fell from 482 samples to 21, and it had been the single largest consumer of CPU in the app.

  • The notch header's glyphs are sized to look the same, rather than to be the same number. Equal point size is equal cap height, which is not equal optical size: measured at 15pt, gearshape covers 289pt² of ink against web.camera's 208 — 39% more — and list.clipboard stands 19pt tall against timer's 16. Each symbol now has a point size solved so its ink height lands on 16pt, and they share a box wide enough not to clip the tall ones.

  • The notch header's buttons no longer crowd each other. Each is a 30pt target, but they sat 4pt apart — close enough to read as one run of buttons — and each glyph carried a default .padding() inside its 30pt capsule, which squeezed it by however much SwiftUI felt like and left the icons visibly unequal. The gap is 8pt and every glyph is set at one size and weight.

  • The clipboard manager's header icon is list.clipboard rather than doc.on.clipboard. The latter is the paste glyph — two sheets over a board — which says "copy this" rather than "the things you have copied". The settings cog is gearshape, the shape macOS itself draws, instead of the older gear.

  • The lyrics and calendar toggles no longer lock each other out. Each was disabled while the other was on, with the lyrics one labelled "Show lyrics on the side (requires calendar off)" — yet the notch's own lyrics button was never gated and the player has always had a layout for both, so the combination was reachable from the notch and refused in Settings. Both toggles are free now, and each says what happens when they are on together: lyrics sit on one line under the artist name while the calendar holds the rest of the notch, and take a panel of their own beside the player when it is off.

  • Several lock screen and media settings now say what they actually control. The lock screen widget layout picker sat under "Weather Widget" labelled "Layout" while restyling the entire status capsule — it also changes how the battery indicator (configured in its own section further down) is drawn, and silently greys out the location and sunrise rows. Skip button behaviour was headed "Floating window panel skip behaviour" and hidden behind the floating-window toggle, yet governs the notch, lock screen and floating players alike — turning the floating window off left it in force but unreachable. The "Change Media Output" toggle also decides whether the lock screen panel has a volume control at all, which its description did not mention.

  • The lock screen reminder could be switched on, and its chip colour chosen, while the reminder live activity that produces it was off — evaluateCurrentState() returns before the defer that publishes the lock screen snapshot, so nothing appeared and nothing said why. The same controls in Live Activities settings were already gated on it; the Lock Screen tab now matches, and explains the dependency.

  • The lock screen widget layout picker has moved out of the Weather Widget section to Appearance, beside Widget appearance. It never controlled the weather alone: snapshot.widgetStyle shapes the focus row, the next-event row, the location segment and the battery gauge as well.

  • Temperature now starts in whatever unit macOS itself displays, instead of always starting in Celsius. Read via UnitTemperature(forLocale:), since the Temperature control in Language & Region is set independently of the measurement system — a US-region Mac can be set to Celsius and a metric one to Fahrenheit, and inferring the unit from a formatted measurement follows the measurement system and gets both of those backwards. Only the initial value is affected; a unit already chosen is left alone.

  • Volume and brightness step sizes now explain why they are greyed out when an external display app owns those keys, instead of appearing broken — and they grey out only while that app actually owns them. Ownership transfers only while DDC integration is on and the selected provider is running, but the controls were gated on the settings alone, so quitting BetterDisplay or Lunar left them disabled even though Atoll had resumed handling the keys. The volume steppers were gated on the external volume listener by itself, which does not even imply the integration is on.

  • The source app badge on the lock screen artwork is sized against the artwork rather than the panel's state. It was a flat 34pt on 60pt of collapsed artwork — over half its width — and pushed a further 12pt clear of the corner, so it read as a second icon parked beside the album instead of a mark on it. It is about a third of the artwork now and straddles the corner, with the shadow scaled to match — and it is no longer cut off there. The artwork clips itself to its own corner radius and its background is already a rounded rect of the same radius, so the clip wrapped around both did nothing except trim whatever overhung, the badge included.

  • Atoll no longer draws its own volume, brightness or keyboard-backlight HUD while the Mac is locked. None of its styles can be seen there — the inline one lives in the notch, the window-based ones sit below the lock screen shield — so each keypress built an indicator nobody could see, and the re-suppression that came with it silenced the native HUD again on every press (#745).

  • Muting from the lock screen now empties the volume capsule and slashes its speaker glyph. Muting leaves the stored level where it was and the capsule read that level directly, so it stayed full and un-slashed while the Mac was silent (#745).

  • The mute button in the notch's output picker no longer has its speaker waves running into the edge of its circular chip. The glyph is about half the chip now, which is roughly where Apple puts one.

  • The lock screen music player no longer stays gone if something hides it while the screen is still locked. Hiding the panel clears its content view and nothing brought it back until the next lock; the lock-state poll now checks that a panel which should be on screen still is, and re-presents it if not.

  • The lock screen music player sits well below the clock now, far enough down to clear the login avatar rather than sitting level with it, and its volume capsule is thinner (10pt collapsed, 13pt expanded). The panel also no longer leaves a band of dead space along its bottom edge: the volume row reserved 72/88pt — sized for the old boxed slider with its icon and percentage label — for a row that now draws just a 14pt gap and a capsule, and content is top-aligned, so the surplus showed up under the capsule. It reserves 40/44pt: the row measures 27, and the panel's fixed content already runs 12pt over the height it is given, so the row has to carry that too.

  • Both lock screen sliders stay grey until you reach for them, the way Apple's do. The volume bar rests desaturated at 0.55 opacity and the seek bar at 0.82 — brighter on purpose, since a transport slider faded as far as the volume bar reads as disabled — and both come up to full colour on hover and full strength on drag.

  • The lock screen seek bar sat below the centre line of the times either side of it, and swelled upward only when dragged. It was bottom-anchored inside a frame sized for its dragging height; it centres in that frame now, which fixes the alignment and makes it grow into the space above and below equally.

  • The lock screen player's play/pause button no longer matches its neighbours in size. Sizing the secondary controls to match Apple left this one where it was, closing the gap between them from 1.7x to 1.35x, so the row read as five buttons of one size instead of a primary control with four around it.

  • Hairline above the lock screen notch: a sliver of wallpaper showed between the notch and the top of the screen. safeAreaInsets.top is fractional on notched hardware (33.5pt), and AppKit rounds a window's frame to whole points while SwiftUI lays the content out at the size it was handed — so a 33.5pt request became a 34pt window holding 33.5pt of content, and because the content view is anchored bottom-left the leftover half point sat along the top edge. The notch is now sized in whole points, and the content view follows the window's actual frame rather than the requested one.

  • Lock screen lock icon: replaced the bundled Lottie drawing with the system lock.fill symbol. The drawn one is noticeably lighter in the stroke and reads as a different icon beside everything else macOS puts on the lock screen; the open-to-closed change now uses a symbol .replace transition.

  • The lock screen music player's default width disagreed with itself — the stored default was 350 while "reset to default" set 420, which was also the slider's maximum. Both are now 390, slightly wider than the old starting width, and the slider goes to 460 so the panel can still be widened.

  • Empty AirPlay picker: With merged AirPlay output active and no devices found, opening the lock screen output picker rendered nothing — the AirPlay branch required a non-empty device list while the route-selector branch stands down whenever merged output is on, so neither appeared and the section's own "No AirPlay outputs available" message was unreachable.

  • Volume can be adjusted from the lock screen again. The slider now sits under the transport controls in the lock screen music panel, styled after the iOS one, rather than being hidden behind the output-device button. macOS's own HUD is unsuppressed for the whole time the Mac is locked — this app's HUD styles all draw where the lock screen cannot show them, so the volume keys previously gave no feedback at all there. That holds even with the music panel up, where the panel's capsule and the native HUD both end up showing volume: suppression is not per-channel, so withholding the native HUD for the panel's sake left the brightness and keyboard-backlight keys with no indicator from either side, and silently dead keys are the worse failure (#745). The panel also no longer reserves the old boxed slider's height under the capsule (#745). Suppressing the native HUD no longer restarts OSDUIHelper when one is already running: launchctl kickstart -k killed the suspended helper and started a live replacement, and every moment between the two drew the native HUD. That was harmless while suppression ran once at startup, but this change re-runs it on every lock-state change, so the native HUD came back on the home screen after each unlock. A helper that is already suspended is now frozen in place instead (#745).

  • The remaining-time label no longer prints an impossible time such as -1732919508:00:54. Senders do not always report a usable duration — a live stream has none to give, and some hand back a sentinel or an epoch timestamp instead — and the label subtracted the position from whatever arrived and formatted the result as hours. Only a finite, positive duration no longer than a day is treated as usable now; zero, negative, non-finite and longer-than-a-day values are all treated as unreported, and both labels show --:-- rather than a formatted impossibility. The slider itself is inert when the duration is unusable, rather than being built from 0 ... duration — that traps outright when the upper bound is below the lower one, so a negative duration crashed before any formatting guard could run. The formatter also no longer converts a non-finite or out-of-range value to Int, which traps as well.

  • The battery with its percentage inside is drawn the way Apple draws one now: a light body, the charge as a coloured fill running from the left, the figure in dark type centred over the whole body rather than over the fill so it does not shift as the charge changes, and a detached terminal. It was a thin battery.0 outline with a small inner bar and the figure competing with the outline for the same few points. The outlined drawing is still what you get with the percentage off.

  • "Show battery percentage inside icon" now works with Minimalistic UI off. The setting was disabled unless minimalistic mode was on, and the standard notch's battery never passed it through — BatteryView has drawn the number inside all along, but only the minimalist pill ever asked it to. The toggle is gated on the notch showing a battery at all now, rather than on which of the two styles is drawn, and the separate percentage beside the icon stands down when the number is inside it so it is not shown twice.

  • Notch output-picker volume slider: the volume slider in the notch's output picker could not be dragged. Changing volume from it raised the notch's own volume HUD, which put a second slider on screen and swapped out the content the popover was anchored to, tearing the popover down mid-drag. Volume changes made from Atoll's own controls now suppress the HUD, the way Bluetooth route changes already did. Closing one output picker no longer closes the notch while another is still open: the media output and AirPlay pickers in both players shared a single flag and each assigned it directly, so whichever ran last won. The picker also no longer requires the pointer to be over it to keep the notch open — the popover is a separate window, so moving into it read as leaving the notch and let the auto-close timer shut both.

  • Lyrics no longer drift out of sync until the track is paused and played again. Senders are not obliged to keep publishing a position: Spotify anchors once when a track starts and then says nothing for the rest of it — measured as an elapsed time of 0 paired with a timestamp 141 seconds old, on a track that had been playing for exactly that long. Extrapolating from that stale anchor is correct while the music runs, because wall-clock and playback time advance together, but they stop agreeing the moment playback stops: a pause the sender did not follow with a fresh position left the anchor where it was, so resuming counted the paused time as played and every pause pushed the estimate further ahead. The position is now re-anchored on the transition itself — frozen where it had got to when playback stops, and restarted from there when it resumes.

  • Synced lyrics no longer sit frozen on the first line for a whole track. The position they are read from is clamped to the song duration, which is zero until the duration arrives — and stays zero for streams that never report one — so it answered zero however far playback had actually got, leaving no line current and the sweep at nothing. Elapsed time is used when there is no usable duration.

  • A lyric line no longer appears up to a third of a second after it is sung. The sync loop polled on a fixed 300ms interval; it now sleeps until the next line's own timestamp, bounded above so seeks and pauses stay responsive and below so lines sharing a timestamp do not spin the loop.

  • The lyric line clears when none is current, instead of leaving the previous one — or, during a track's intro, the first line of the song — on screen. The text was written only when the line index changed, and lyrics arrive with the index already at -1, so nothing ever overwrote what was there.

  • The lyrics highlight sweeps word by word, in reading order, wherever the lyrics are laid out as a list — the side panel and the expanded lock screen column. (The collapsed lock screen player shows one line at a time and still sweeps it as a block, which for a single line is the same thing.) A gradient over a block of text sweeps it as one rectangle, so a line that wrapped lit its second row level with its first rather than after it. Each word now takes its own slice of the line's progress, sized by word length. This is still estimated from line timings rather than real per-word timestamps, which lrclib does not carry — it just estimates in reading order.

  • Lyrics and the progress bar no longer drift behind the music until the track is paused and played again. Senders that publish a position once and then repeat it were still being trusted to say when that position was true: Spotify keeps republishing the exact instant it paused — four reads six seconds apart returned the same 40.342s with its timestamp 235, 241, 247 and 254 seconds old and still climbing. The pair is honest, and harmless while paused since a stopped track is never extrapolated, but on resume the anchor still pointed to before the pause and the whole stopped interval was counted as played. The earlier fix for this only applied when the sender sent no position at all, which is not what Spotify does — it sends one, just not a current one. A position is now taken as a reading of the present only if the timestamp beside it is, and the anchor is re-stamped at the moment playback resumes either way.

  • The lyrics list scrolls back to the start when a new track begins, instead of staying parked where the previous one left it. Which songs it happened to work on was down to a coincidence: a new track resets the current line to -1, and the scroll was guarded on that index having a row of its own — which only tracks opening with an intro long enough to be marked with notes ever have. Rebuilding the rows for a new track never scrolled at all, its handler being attached outside the scroll reader and so having nothing to scroll with.

  • The expanded lock screen player's spacing. Its artwork was a fixed square in a panel whose height grows as the volume and output rows appear, so a band of dead space opened above and below it; it is the flexible column now, taking the smaller of the height it can fill and the width left after the transport row and the lyrics column are served. The panel was also growing by a fixed reserve per row on top of a height the middle column already had room for, and the column pinned its title to the top and its transport to the bottom — so the surplus split itself into the two gaps either side of the progress bar, and left the volume capsule against the bottom edge. It measures what the column needs and grows only by the shortfall now, spacing its blocks evenly and centring them. The output device row was separately being cut in half by the panel's bottom edge, its reserve having counted the section's own padding but not the gap above it.

  • Play/pause on the lock screen read as lagging the click: the glyph swap rode whatever animation happened to be ambient when playback state changed, rather than a named one.

  • Lyrics timing: Synced lyrics no longer run ahead of the music. The Now Playing adapter was streaming its playback anchor as an ISO-8601 string truncated to whole seconds, so every position estimate was biased forward by however much sub-second precision that discarded (448 ms in a sampled reading, varying per update). The adapter now streams microsecond timestamps, a fresh position is no longer paired with the previous update's anchor, and the LRC parser handles millisecond timestamps, single-digit fractions, repeated-chorus lines carrying several timestamps, and the [offset:] tag.

  • Inline lyrics crowding: With lyrics and the calendar both on, the notch player's transport controls were squeezed against the progress slider. The player lays its text and slider out inside a greedy GeometryReader, so the gap above the controls is that reader's leftover space and a third text line takes its height straight out of it. The open notch now grows by one line when the home tab renders inline lyrics in the standard player. Minimalistic UI is unaffected: its player shows lyrics whenever they are enabled, regardless of the calendar, and sizes itself for them separately.

  • Short text scrolling in the marquee: MarqueeText draws two copies of the text 20pt apart and recovered one copy's width by halving the measured loop without subtracting the gap, so every string measured 10pt wider than it is and any text landing within 10pt of its column was mistaken for text that overflows — a short artist name would scroll while the song title, measured a different way, did not. Each scroll cycle also travelled 10pt too far.

  • The Downloads settings tab says what download detection actually does and which browsers it covers, rather than naming a file extension. The "Download indicator style" heading was also drawn in hardcoded white, which is invisible against a light Settings window, and stayed fully lit while its buttons were disabled.

  • A clipboard panel position saved before the redesign no longer opens with part of the panel off screen. The panel grew from 320x400 to 380x480, and a saved frame was accepted whenever it merely intersected the visible frame — so a panel that had sat flush with the right edge now hung about 60pt over it. The saved frame has to be contained now, and is nudged back inside when it is not, rather than the placement being thrown away (#743).

  • Copying the same clipboard row twice in quick succession no longer clears the checkmark while the second copy is still being confirmed. The confirmation scheduled an unconditional reset 1.5s out, so the first press's timer fired over the second; the pending reset is cancelled before the next is scheduled (#743).

  • Media key interception is no longer disabled for the rest of the session when it cannot start. MediaKeyInterceptor attempted CGEvent.tapCreate exactly once at startup, and that call fails outright until Accessibility is granted; since granting it does not relaunch the app, a prompt answered late left the volume and brightness keys reaching macOS untouched until the app was restarted — or until a HUD style was switched in Settings and switched back, which retried the tap as a side effect. The tap is now retried until it installs: every 2s for the first minute, then every 15s, giving up after an hour.

  • On macOS 26 the native volume HUD is drawn by Control Center rather than OSDUIHelper, so the SIGSTOP suppression in SystemOSDManager no longer has any effect on it there and interception is what keeps it hidden. Measured on macOS 26.6: with the tap installed and swallowing the volume keys, the Control Center panel did not appear in 10 of 10 trials; without interception it appeared in all 10; writing the volume through CoreAudio does not summon it either. Behaviour on earlier macOS versions, and for the brightness HUD, was not measured.

  • Fixed hover-to-open silently dying, and the selected Idle Animation never rendering, whenever Idle Animation was enabled and no media was playing. A duplicated else if in the closed-notch view chain had an empty body, and its condition (!coordinator.expandingView.show) is a strict subset of the branch below it (!isCurrentScreenExpansionVisible), so it always won in the idle case and yielded EmptyView. NotchLayout() has no intrinsic size, so that collapsed the notch to its padding and took the .contentShape/.onHover hit area with it — which is why hover appeared to work only while music was playing (#725, closes #540, #624).

  • Fixed provider icons stretching into a flat smear inside menu-style pickers (most visibly the AirDrop icon in the Shelf's Quick Share popover). Rendering a picker's selected row into the NSPopUpButton title drops SwiftUI's frame but keeps .resizable(), so the icon expanded to the button's full width; icons now carry their own point size instead. Also affects the Quick Share picker in Settings and the third-party display app picker.

  • Fixed the screen recording HUD stop button collapsing out from under the pointer while the stop request is being sent.

  • Fixed the screen recording HUD stop button being ignored while hovering when "Open notch on hover" is enabled, because the hover click monitor opened the notch instead of forwarding the click.

  • Fixed stale album artwork appearing during manual track transitions in Apple Music and Spotify by publishing track changes immediately and safely handing off asynchronously fetched artwork.

  • Fixed the LLM Usage card prompting for the login keychain password on every refresh when the Gemini language server is down. The app now tries the language server first (no keychain needed), and otherwise reads the Gemini CLI's token through the security CLI, which is covered by the item's apple-tool: partition grant and never triggers a password prompt.

  • Fixed the timer being clipped behind the notch after the layout changes, and made the boxes in StatsView uniformly sized.

  • Removed the separate floating timer control window; Pause/Stop buttons now render inline inside the notch, vertically centered with the timer countdown (#711).

  • Clearing clipboard favorites now deletes the image files behind them instead of orphaning a PNG of every pinned image in the app's data directory, and the startup cleanup no longer deletes those files while the favorites still reference them (#743).

  • The clipboard settings and shortcut descriptions now show the shortcut you actually have bound, instead of a hard-coded "Cmd+Shift+V" that went stale the moment anyone rebound it (#743).

  • The 3D Bluetooth HUD icon, and its preview tile in Settings, no longer render as an empty box when the bundled animation cannot be decoded. Both call sites decided whether to fall back to the SF Symbol by asking whether a file of that name exists, which is true of a Git LFS pointer stub left by a clone made without git-lfs: the file resolves, the fallback never fires, and the player is handed something undecodable. The movie is now validated before use (#744).

  • The notch volume HUD slider now glides between the discrete steps the volume keys deliver instead of jumping from one to the next (#742).

  • Fixed a launch crash (BUG IN CLIENT OF LIBDISPATCH: trying to lock recursively) that could trap while a Bluetooth audio device was connected. BluetoothAudioManager's initialiser scanned connected devices synchronously, and that scan blocks on Process.waitUntilExit(), which spins the run loop — letting SwiftUI evaluate a view body that reads BluetoothAudioManager.shared and re-enter the initialiser that was still running. The scan now starts on the next main-queue turn instead.

  • Fixed excessive memory usage by streaming LLM usage JSONL files instead of loading them entirely into memory

  • Reduced idle CPU from always-on notch hover polling and OSDUIHelper process checks by backing off when the app is idle (#641).

  • Rich-text clipboard entries now keep their formatting when dragged out of the notch. Rich content is captured as RTF at copy time — including web/HTML copies (browsers, GitHub) that expose only public.html, which is now converted to RTF — and the drag offers that styled RTF with a plain-text fallback. Rich-text editors (TextEdit, Pages, Word) receive the formatting; plain-text targets still get plain text. The exact result depends on what the destination app accepts (#717, closes #712).

  • Fixed a hairline gap at the top of the notch during open animation, and hover-to-open flapping when the pointer sits on the top edge, on physical-notch Macs (#681).

  • Fixed lock-screen widget readability on bright wallpapers by adding a Dark/Light appearance mode for widget text and controls.

  • Fixed Codex Today and Week usage totals remaining at zero when parsing Codex session logs (#664).

  • Recover the Claude quota display after Claude Code rotates its OAuth token, instead of showing "quota unavailable" until the app is restarted (#685).

  • Fixed the Claude quota staying "quota unavailable" on recent Claude Code versions, which store the OAuth token under a per-install hash-suffixed Keychain item (Claude Code-credentials-<hash>) and no longer update the un-suffixed item the app read; the freshest matching item is now used (#699, follow-up to #685).

  • Normalized Claude model IDs when pricing local usage so newer IDs are costed instead of showing US$0.00+, and show an explicit unavailable/partial estimate when a model isn't in the pricing table (#683, #664).

Removed

Don't miss a new Atoll release

NewReleases is sending notifications on new releases.