Changelog
Every WolfWave release. New features, fixes, and breaking changes. Auto-updating via Sparkle. macOS menu bar app for Apple Music streamers.
Release notes
All notable changes to WolfWave are documented here.
v2.0.0. Unreleased
Security
- Stream Widgets auth token. The overlay WebSocket and widget HTTP server now require a per-install authentication token. Connections without a matching
wolfwave.token.<hex>subprotocol are rejected on the handshake before any playback data is sent. The token is minted on first launch, stored in the macOS Keychain, and auto-injected into the widget URL and servedwidget.htmlso existing OBS browser sources keep working without manual changes. Settings → Stream Widgets exposes the token behind an eye toggle (hidden by default), a Regenerate button, and a free-form edit field for streamers who want to supply their own value; regenerating or editing the token drops every active client until they reconnect with the new credential - Streamer Mode. New tray-menu toggle masks the connected Twitch channel name, overlay/widget URLs, the WebSocket URI, and the auth token across the WolfWave UI so the app is safe to show on camera. Copy/Open buttons next to masked values are disabled so screenshots can't reveal the underlying value via accessibility. UI-only redaction. The WebSocket broadcast payload, Discord Rich Presence, and Twitch chat output are unaffected
Added
- Back up & restore settings. Export your preferences to a plain JSON file in Settings → Advanced, then import them on another Mac or after a reinstall. Accounts and secrets are never part of the backup; on import you choose whether to reconnect Twitch, and declining skips just that account while the rest of your preferences are restored. See Back Up & Restore
- Appearance toggle. Pick Light, Dark, or System in Settings → General. System follows macOS automatically; Light and Dark override it app-wide, menu bar menu included. The picker shows System Settings-style appearance thumbnails. Settings cards now sit on an opaque macOS surface, so they no longer bloom bright in dark mode or glare near-white in light mode
- Discord idle & pause controls. New "When not playing" section in Settings → Discord. Show idle status keeps a "Listening to WolfWave · Idle" marker on your profile when nothing is playing instead of clearing it; Hide track while paused clears the profile when you pause. Both off by default. The Discord preview card now mirrors real behavior, showing the track only while playing or paused and switching to a matching empty/idle state when playback stops, Apple Music is closed, or Discord isn't running, instead of always showing a sample song
- Chat Vote-Skip. Viewers vote to skip the current song with
!voteskip/!vs; chat-tally mode counts unique voters against a minimum threshold, opt-in Twitch Polls mode opens a native poll (Affiliate/Partner). Configurable in Settings → Song Requests → Chat Vote-Skip - Listening History. Opt-in, on-device log of the tracks you play; off by default, turn it on in Settings or onboarding
- Stats & Charts. Top artists, listening time, 7-day trend, and listening-by-hour charts in the new History & Stats section
- Monthly Wrap. A personal "wrapped"-style monthly summary. Save it as a PNG or send it via the native macOS share sheet (Messages, Mail, AirDrop)
!statscommand. Viewers ask for today's top track in chat; replies only while the stream is live- Song Requests. Viewers request songs via
!sr <song>in Twitch chat; plays through Music.app with no focus-steal on the streamer's screen !queue/!myqueue. Show the full queue or a viewer's own requests in chat!skip/!next. Mod-only skip current request!clearqueue. Mod-only wipe the queue (with confirmation)!hold/!resume. Mod-only hold mode; requests buffer without auto-playing so the streamer can curate before releasing- Hold controls. Hold/Resume button in Queue settings view and menu bar toggle
- Music.app closed buffering. Requests save when Music.app is closed, flush on relaunch
- Fallback playlist. Plays a configured Apple Music playlist when the queue empties
- Song Request Queue UI. Now-playing card, position badges, per-requester labels, and action controls
- Per-user limits, subscriber-only mode, command aliases, and enable/disable toggles per command
- Custom aliases for
!song,!last,!stats. Comma-separated alias fields in Settings → Twitch and Settings → History & Stats. Aliases honor the same cooldown and live-gate rules as the canonical command - Hold-queue toggle in Song Request settings. Flip the request queue between Hold and Playing from the Playback card. Previously menu-bar only
- Recreate Reward button. Clears the stored Channel-Point reward ID and forces a fresh "Request a Song" reward on Twitch. Use after deleting the reward manually. The managed reward ID renders below the cost picker (masked under Streamer Mode)
- History retention picker. Keep listening history for Forever / 7 / 30 / 90 / 180 / 365 days. Older entries prune at next launch
- macOS 26 Liquid Glass onboarding. Full wizard rebuild with two new steps (Menu Bar Pointer + OBS Widget) and Liquid Glass materials to match Tahoe's design language
- Apple Music onboarding step. MusicKit authorization during the first-launch wizard with a graceful denied-state recovery path
- OBS Widget onboarding step. Overlay URL preview and HTTP widget toggle wired into onboarding so streamers can copy the overlay link before completing setup
- LAN IP cache. Overlay URL lookups in settings no longer block on every redraw
- Diagnostics + bug report flow. Export Logs, Clear Logs, and a one-click Report a Bug button in Advanced settings; the issue is pre-filled with redacted log context
- Branded About panel. Native SwiftUI About window replaces the default sheet
- Apple Music logo on Music access row. Granted state now shows the Apple Music brand mark
- Widget themes. Six selectable overlay-widget themes (Default, Dark, Light, Glass, Neon, WolfWave) and three layouts (Horizontal, Vertical, Compact)
- Discord presence polish. Friendlier presence buttons, a live presence preview in Discord settings, and cleaner connection-state handling
- Song-change notifications. Opt-in macOS notification on every track change, with track name, artist, and album art; off by default in Settings → General → Notifications
- Skip-vote notifications. Opt-in macOS notifications when a chat skip-vote starts (silent, shows the threshold or open poll) and when it passes (names the skipped track, default sound). Off by default in Settings → Notifications; disabled until Chat Vote-Skip is on. The onboarding Permissions step also lets you choose which alerts to receive
- On-device diagnostics. Opt-in MetricKit diagnostics in Advanced settings with a share card for attaching reports to a bug filing; reports stay on-device
- Discord playlist presence. Discord Rich Presence now shows the current Apple Music playlist alongside the track
- WolfMark branding. New album-art placeholder, branded download page, and refreshed repo README + SECURITY docs
- OBS widget transitions. The overlay slides in with a subtle bounce when a song starts and slides + blurs out calmly when playback stops, with the progress bar draining smoothly to zero. Rapid track skips crossfade the artwork + metadata instead of re-triggering the entrance. No strobing on stream. Pause keeps the card mounted.
Changed
- Music playback via AppleScript + focus preservation. Music.app never steals focus from OBS or streaming tools
- MusicKit used exclusively for search/resolve, not playback
- Settings views decomposed into per-section files for sharper redraws and easier editing
- Onboarding polish. Redundant success rows removed, Apple Music permission copy tightened, stable button + step sizing eliminates UI shift
- OBS step collapsed to a single toggle with the overlay URL promoted to first-class status
- Tightened user-facing wording across onboarding, settings, and the menu bar
- Notifications toggle disables itself once permission is authorized; denied state unified across the wizard
- Onboarding permissions consolidated. Apple Music access, the notification-permission ask, and the per-alert toggles now live in one "Permissions" step
PillButtonnow uses the macOS-standard rounded rectangle (replacesCapsule)- Menu bar preview tray icon renders crisply via
NSImage - Settings window sized to fit cleanly on 720p and 1080p displays
- Music permission row is actionable and self-healing. Tap to re-request or deep-link into System Settings
- Test suite consolidated and expanded (more than 880 tests across 86 files)
- Settings views read from
DSFontandDSSpace. Settings, onboarding, and shared views now reference the generated design tokens instead of hardcoded font sizes and paddings; consistent typography and spacing across every settings tab - Concurrency polish. Remaining
DispatchQueue.main.asyncsites in menu-bar, song-request, and Discord settings switched to structuredTask { @MainActor }for Swift 6 cleanliness - Logger PII regex literals. Patterns moved to compile-checked
#/.../#literals; no moretry! Regex(...)warnings - Liquid Glass onboarding surface. The wizard content panel now renders on a
.glassEffect(.regular)material, matching the glass convention used by settings cards and the now-playing card - Streamer Mode Twitch account masking. The signed-in Twitch account name in Settings is now masked when Streamer Mode is on, matching the existing channel-ID rows and menu bar
- Onboarding step accessibility. VoiceOver now announces the current step by name ("Step 3 of 7: Twitch") via
accessibilityHintandaccessibilityValueon the progress dots - Settings sidebar regrouped by purpose. The sidebar is grouped by what each section is for, and the App group is ordered by how often you reach for it. App Visibility is now a native single-column picker for the menu bar, the Dock, or both, and the General tab lays Startup and Display Mode out side by side
Performance
- Timer wakeup coalescing. Periodic timers (fallback poll, WebSocket progress tick, Discord poll, song-request auto-advance) now carry leeway/tolerance so macOS can batch their wakeups together, cutting idle energy use for an all-day menu bar app. Real-time track changes still arrive via distributed notifications; polling behavior is otherwise unchanged
- Settings page switch latency eliminated. Navigation between sections is now instant
- Font enumeration deferred off-main so Widget Setup never blocks first paint
- Now-Playing Server row renders instantly thanks to the cached LAN IP lookup
Developer
Developer-facing changes. Not visible to end users.
- Unified design system. A single
design-system/tokens.jsonsource feeds a generator that emits four platform outputs (Swift, docs CSS, widget JS, marketing TS); a Turbotokenstask runs as a build prerequisite - Component catalog. One markdown entry per reusable view under
design-system/components/ - DEBUG-only Debug settings tab. Developer tooling (inspectors, service controls, log/event views, UI previews) plus What's New preview controls, available only in DEBUG builds
- Expanded design-token roster ,
font.sizenow exposes 9/15/16/18/24/26/28/36 px slots andspaceaddss0(2 px),s10(32 px),s11(44 px) so every view can reference a token without rounding visual semantics - Sparkle delegate hardening. Opted out of Sparkle's system-profile telemetry (
allowedSystemProfileKeysreturns empty); explicit user consent now documented as the contract forautomaticallyDownloadsUpdates = false - WebSocket security-model doc. Added a
## Security modelblock toWebSocketServerService.swiftdocumenting the loopback-only contract; token-based auth tracked as a follow-up - New unit tests ,
SongBlocklistTests,HistoryFormattingTests,LaunchAtLoginServiceTestscover three previously-untested services - Accessibility labels. Added explicit
accessibilityLabel/Hint/Identifierto permission and About-panel buttons that previously relied only on inferred labels (better VoiceOver clarity) - OBS widget build pipeline. Widget source moved into a new
apps/widget/Tailwind + TypeScript workspace. The bundledwidget.htmlis now a generated artifact with the compiled CSS, design tokens, and JS runtime all inlined. Still a single self-contained file. Xcode auto-rebuilds it via a pre-build Run Script phase; CI rebuilds it before everyxcodebuildinvocation. Manual command:bun run --filter widget build. WebSocket payload contract, widget URL, and theme/layout URL params are unchanged. See OBS Widget Architecture for the pipeline + transition state machine. - Inside-out release signing.
build_release.ymlnow signs nested bundles deepest-first before the app shell, dropping--deep --forcewhich stripped per-bundle entitlements. The pipeline is XPC-ready for any future sandbox helper - Typed NotificationCenter payload helpers.
Core/NotificationPayloads.swiftcentralizes all notification payload post/observe pairs so send and receive sides share the same keys and types, removing ~38 hand-decodeduserInfosites - HelixClient. Shared HTTP wrapper for all Helix API calls, replacing per-service
send()helpers. Maps 401, 429, and structured Helix error payloads uniformly - Preferences typed accessor.
Core/Preferences.swiftcentralizes UserDefaults reads/writes for non-bool keys across AppDelegate and WolfWaveApp - Actor adoption.
SongBlocklistandSkipVoteManagerconverted fromfinal class + NSLockto actors;BotCommandContextmarkednonisolated + Sendable. Combine fully removed from the app target
Fixed
- One recovery card for denied Music access. Settings → General no longer repeats the same "Music access denied" warning in three places. A single recovery card owns the explanation and the Open System Settings action, the Sync Music card hides its duplicate permission row while denied, and the now-playing card shows a calm orange "Paused" instead of a red "Denied". "Try again" shows a spinner and a "still off" hint instead of feeling dead (#240)
- Twitch bot silent after relaunch. EventSub now auto-reconnects to the saved channel on launch after token validation, so chat commands work without reopening the Twitch settings pane. Silent reconnect. No "online" chat ping unless you explicitly click Connect
- Keychain duplicate-item self-heal ,
upsertItemrecovers fromerrSecDuplicateItem(OSStatus -25299) when a legacy entry uses a mismatched accessibility class. Fixes the WebSocket auth token mint error after onboarding - Twitch auth UI layout shifts. The Twitch settings card no longer resizes between "Not signed in", "Authorizing", and "Error" states. Reserved minHeight + symmetric transitions; deleted the unused
TwitchReauthViewso re-auth surfaces inline in the same card - Onboarding step content is now vertically centered in the wizard window. Fixes step views being pinned to the top with empty space below. Reserved extras slot now center-aligns its contents so compact steps (Discord, Preferences) no longer leave dead space below the toggle
- Two-PC stream widget. Overlay WebSocket and widget HTTP server bind to all interfaces (previously loopback only) and
widget.htmlconnects tolocation.hostnameinstead of hardcodedlocalhost. Streamers can openhttp://<lan-ip>:7780from a second computer or phone on the same network and the widget loads (paired with the new auth token so LAN exposure stays gated) - GitHub URL config plumbing ,
GITHUB_REPO_OWNER/GITHUB_REPO_NAMEfromConfig.xcconfignow flow throughInfo.plistso Report a Bug and releases URLs resolve from config - Intel Homebrew cask detection. Intel Macs using Homebrew now correctly disable Sparkle and show the Homebrew update card; the previous matcher missed
/usr/local/Caskroom/ - Native build warnings, SwiftUI layout reentrancy, and duplicate log emission
- Onboarding URL bug, permissions correctness, and layout stability
- Export Logs crash on empty or large logs
- Settings/About show deferred past the AppKit layout pass to silence recursion warnings
- Music access icon adapts to light/dark mode; top scroll fade removed
- PillButton width shift across state changes pinned down
- Apple Music + OBS logos trimmed to glyphs so template rendering tints correctly
- Menu bar pointer arrow anchored to TrayIcon center in onboarding
- Brand icons in menu bar render as templates with refreshed logo SVGs
- Settings window sidebar toggle. Removed the duplicate titlebar toggle, restored the toolbar sidebar toggle, eliminated the floating
>>reveal control - About panel spacer gap removed; app icon logo cutouts preserved via the
evenoddfill-rule - Apple Music control under sandbox. Granted the Apple Events entitlement so the permission prompt actually appears and ScriptingBridge can drive Music.app
- Now-playing survives ScriptingBridge drift. The player-state parser accepts every bridge type Music.app returns instead of silently blanking the card, Discord Rich Presence, and overlay
- Recheck + Sync toggle force a fresh now-playing read so the UI catches up immediately
- Apple Music permission Recheck button visibly responds when tapped
- Stats card layout. Fixed jumping card sizes and added an Apple Music permission gate
- Settings window default size enlarged;
StatusChiptightened so integration rows fit - About panel Check for Updates now invokes Sparkle; Release Notes points at the docs changelog
- Onboarding Preferences step no longer overlaps the nav bar on shorter window heights
- Onboarding step header anchored so icons stop drifting between steps
- Vote-skip window timer race. A timer that outlasted a fast-passing vote could clear a newer session when session cooldown is 0. The timer now captures a session token at spawn and bails if a different session has since opened
Removed
WallpaperBloomBackground. Replaced with native macOS chrome- Redundant onboarding success rows
v1.2.0. April 4, 2026
Added
- Shared UI components. Reusable settings components for a more consistent look
Changed
- Music architecture. Refactored to a pluggable source system, laying groundwork for future music sources beyond Apple Music
- AppDelegate decomposed. Split into focused extensions for maintainability
- Logger. Streamlined format with local time and emoji prefixes
- Widget settings. Compact 2-per-row layout, auto-sizing dropdowns
- Settings UI. Unified headers and test buttons across Discord & Twitch sections
Fixed
- Settings window not appearing when opened from menu bar in menu-bar-only mode
- 4 Xcode build warnings (actor isolation and unreachable code)
- Widget favicon broken reference
v1.1.0. March 31, 2026
Added
- Discord buttons. Rich Presence now shows two clickable buttons: Open in Apple Music (direct track link) and song.link (opens on Spotify, YouTube Music, Tidal, and more)
- Launch at Login. New toggle in Settings → App Visibility. Uses SMAppService, appears in System Settings → General → Login Items
- Custom DMG background. Installer window has a polished dark background with WolfWave brand colors
- Homebrew auto-update. GitHub Actions now automatically opens a pull request on the Homebrew tap on new releases
Fixed
- iTunes Search API URL encoding. Track/artist names with &, +, or = no longer break artwork lookups
- Launch at Login toggle now reverts if SMAppService registration fails
v1.0.2. March 31, 2026
Fixed
- App icon missing in CI-built releases
- Sparkle updater unable to detect new versions (build number now incremented per release)
- Sparkle initialization race condition fixed
v1.0.1. March 30, 2026
Changed
- Dropped Intel (x86_64) support. Apple Silicon only
- Raised minimum macOS version to 26.0 (Tahoe)
- Logger: replaced NSLock with serial DispatchQueue for thread-safe file I/O
- TwitchChatService/DiscordRPCService: documented thread safety patterns
- KeychainService: added error logging for all Keychain operations
- AppConstants: cached GitHub repo resolution
- Migrated TwitchViewModel and OnboardingViewModel to @Observable macro
- WhatsNewView: dynamic version string, native button style, v1.0.1 feature highlights
- Narrowed entitlements file exception path
- Added Twitch user ID redaction to log output
- NotificationCenter observers properly cleaned up on app termination
- Windows (Settings, Onboarding, What's New) properly released on close
- Deferred Sparkle/onboarding init past initial layout to fix layoutSubtreeIfNeeded warning
- Removed duplicate "up to date" alert (Sparkle handles it natively)
- Added VoiceOver accessibility labels across all settings and onboarding views
v1.0.0. March 30, 2026
Added
- Native macOS menu bar app for Apple Music integration
- Real-time now-playing detection via ScriptingBridge
- Twitch chat bot with !song, !currentsong, !nowplaying, !lastsong, !last, !prevsong commands
- Discord Rich Presence with dynamic album art
- OBS stream widget via built-in WebSocket server
- Automatic updates via Sparkle (DMG) or Homebrew
- First-launch onboarding wizard
- macOS Keychain credential storage
- Bot command cooldowns and broadcaster bypass
- Settings UI with NavigationSplitView sidebar
- Diagnostic log export
For the full changelog, see CHANGELOG.md on GitHub.
Security
How WolfWave keeps your Twitch and Discord tokens safe. MacOS Keychain storage, App Sandbox, OAuth Device Code flow, and EdDSA-signed Sparkle updates.
Design System
WolfWave's visual language. Design tokens, reusable SwiftUI components, and brand assets. One source of truth powering the native app, docs, widget, and marketing.