Skip to content

Upgrade Native SDK 0.8.0 -> 0.8.4, add track-change notifications - #31

Merged
perminder-klair merged 7 commits into
mainfrom
sdk-084-notifications
Aug 12, 2026
Merged

Upgrade Native SDK 0.8.0 -> 0.8.4, add track-change notifications#31
perminder-klair merged 7 commits into
mainfrom
sdk-084-notifications

Conversation

@perminder-klair

Copy link
Copy Markdown
Collaborator

Moves the SDK pin from 0.8.0 to 0.8.4, and builds on the one thing in those four releases this app can actually use: desktop notifications.

What 0.8.4 gives us

fx.showNotification is new, and it's wired on all three platforms we ship to (show_notification_fn is non-null in the macOS, Windows and Linux hosts). It takes { title, subtitle, body }, bounded at 128 / 128 / 1024 bytes.

That makes it the closest thing to a Now Playing surface the SDK offers, so the player now posts a toast when the track changes. The rule is background-only and on-air-only:

pub fn shouldNotifyTrack(self: *const Model) bool {
    return self.notify_track and !self.app_active and self.live_now() and self.has_track();
}

Each clause is there for a reason. notify_track is the opt-in. !app_active means no toast while you're looking at the player, because the LIVE stage already names the track and a toast every three minutes would just repeat it. live_now() keeps it quiet while tuned out, buffering or failed, since this is a player and not a station ticker. has_track() catches the first fill: got_np sets track_changed when the title flips from "" on the very first fetch, and that's a launch artifact, not a song change.

Off by default. An existing settings.json has no notifyTrack key, so nobody's update silently starts producing OS toasts. The switch is a new NOTIFICATIONS row in the back panel.

Why the decision is a pure predicate

fx.showNotification is inert and unrecorded under fake execution and session replay. There's no notificationState() to assert on the way windowActionState() exists for window verbs, so a test can never observe the call. Putting every gate in shouldNotifyTrack is what makes the feature testable at all; the effect call itself is one line with no branching.

Also new, and skipped

Audio capture landed too (startAudioCapture / stopAudioCapture / feedAudioCapture, a system_audio permission, a mic icon). That's microphone input. A radio player has no use for it, so it's untouched.

The four new markup attributes (images on <markdown>, submit-on-enter, max_width, on_drag) aren't adopted either. No existing view was waiting on any of them.

Checked, not assumed

Surface 0.8.0 to 0.8.4
Effects pub fn list additive only: showNotification, the audio-capture verbs, three feed* test helpers
PlatformFeature enum, *_fn platform services identical
src/platform/linux/gtk_host.c changed by about 80 lines, but entirely outside the patched hunks (see below)
app.zon manifest one new permission, system_audio
Automation protocol 0x096c8aa4730c11ec, unchanged, so existing native automate calls keep working
minimum_zig_version 0.16.0, unchanged
Trace default still .events, so -Dtrace=off stays mandatory

Still no audio output-device API and still no OS media-controls surface (no MPRIS, MPNowPlayingInfoCenter or SystemMediaTransportControls) anywhere in the 0.8.4 tree. Both grepped for rather than carried forward. Upstream native#156, the fractional-scale bug the local patch works around, is still open.

The HiDPI patch needed no edit

I expected to regenerate it, because gtk_host.c moved. It turns out every change falls outside the eight patched hunks: applying the old patch to a pristine 0.8.4 tarball landed at zero offset with no rejects, and the regenerated diff came back byte-identical to the committed one (same md5). So only patch_sdk_version moved.

All seven scale-reporting sites in the stock tree still read an integer API, four gtk_widget_get_scale_factor() and three gdk_surface_get_scale_factor(), with zero uses of the fractional gdk_surface_get_scale(). The patch is still load bearing.

Both version pins moved together, which is the thing that broke the 0.6.0 upgrade: patch_sdk_version in scripts/apply-sdk-patches.sh and native-sdk-version in .github/actions/setup-native/action.yml.

Verification

  • native test passes 106/106, up from 104/104 by the two new tests. The SDK upgrade itself changed nothing under src/: 104/104 before and after, with the app untouched.
  • native check clean, with the same pre-existing view_unbound warnings as before.
  • scripts/apply-sdk-patches.sh still idempotent (already applied, 1/1), scripts/check-release-flags.sh passes, and scripts/set-close-policy.sh still round-trips hide/quit after the app.zon permissions edit.
  • Live run on this fractional-scale Linux display: gpu_scale=1.6666666, not the integer 2.
  • The panel row was driven through automation: the switch reads value=0 on a fresh model, widget-action toggle flips it to value=1, and "notifyTrack":true lands in settings.json. Screenshot checked by eye.

One thing is not verified: an actual OS notification appearing. It needs the app to lose focus and then a real track change, and there's no automation verb to deactivate the app, so testing it would have meant moving windows around on my desktop. The effect is also unobservable under fake execution, so no test reaches it either. The predicate is tested gate by gate and the wiring compiles and validates, but the final hop from fx.showNotification to a visible toast is worth a manual check before this merges: turn the switch on, click away, wait for a song change.

Docs

docs/sdk-notes.md gets a 0.8.4 section covering the notification semantics, the audio-capture verbs we skip, and the patch finding. The re-checked-at claims in CLAUDE.md, README.md and docs/sdk-audio-device-request.md move forward, and the media-controls gotcha in CLAUDE.md is revised, since it is no longer flatly true that there is no OS-facing now-playing surface at all.

The HiDPI patch needed no edit: gtk_host.c changed by ~80 lines but every
change falls outside the eight patched hunks, and regenerating against the
pristine 0.8.4 tarball produced a byte-identical file. Only the two version
pins move. Verified with gpu_scale=1.6666666 on the fractional display.

104/104 tests pass before and after with nothing under src/ changed --
Effects is additive, PlatformFeature is unchanged, and the automation
protocol fingerprint is the same.
shouldNotifyTrack carries the whole decision as a pure predicate because
fx.showNotification is inert and unrecorded under fake execution, so the
effect call itself can never be observed by a test.

notify_track is in view_unbound only until the back-panel switch binds it.
track_changed is hoisted out of the nowPlaying block so the notification
fires after the show name is filled -- otherwise the toast would pair the
new track with the previous show.
bell is not in the SDK's built-in icon set, so it ships as a registered app
SVG reached from markup as app:bell.
@perminder-klair
perminder-klair merged commit 6bc7688 into main Aug 12, 2026
4 checks passed
@perminder-klair
perminder-klair deleted the sdk-084-notifications branch August 12, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant