Skip to content

feat: add a native macOS build with new Preferences and notifications - #1

Open
gahingwoo wants to merge 1 commit into
nikableh:mainfrom
gahingwoo:macos-support
Open

feat: add a native macOS build with new Preferences and notifications#1
gahingwoo wants to merge 1 commit into
nikableh:mainfrom
gahingwoo:macos-support

Conversation

@gahingwoo

@gahingwoo gahingwoo commented Aug 2, 2026

Copy link
Copy Markdown

Koshi now builds and runs as a standalone .app on macOS with no
Homebrew dependency at runtime. build-aux/macos/bundle.sh compiles the
release binary, relinks every dylib it needs with dylibbundler, and
bundles the gdk-pixbuf loaders, the GIO TLS module, the Adwaita and
hicolor icon themes, and compiled GSettings schemas alongside it. A
matching build-macos job in the release workflow packages the result
as a .dmg and attaches it to the GitHub release next to the Flatpak;
it also installs adwaita-icon-theme explicitly, since neither gtk4 nor
libadwaita depends on it and a Homebrew prefix that has never had
another GTK app built on it (a fresh CI runner, mainly) won't have it.

On macOS the window buttons now match every other platform instead of
showing native traffic lights: GtkHeaderBar and GtkWindowControls both
default to platform-native controls there since GTK 4.18, so the main
window and every dialog force use-native-controls off and move the
buttons to the right. AdwDialog also hardcodes its own close button to
the start side with no public way to reposition it, so dialogs get a
plain close button of their own on the end side instead. Keyboard
shortcuts move from a hardcoded to (plus an
explicit fallback on macOS, where 's resolution to Cmd
has proven unreliable in practice), so Cmd+F and friends work as
expected instead of only ever answering to a literal Ctrl press.

New replies notify through UNUserNotificationCenter on macOS, since
there is no session D-Bus for the existing XDG portal path to use.
Authorization is requested once at startup rather than on the first
notification, because addNotificationRequest fails outright while
authorization is still undecided and asking any later would lose a
fresh install's first notification to that race. A warning row in
Preferences points the user at System Settings if they end up denying
it.

Preferences also picks up a few things asked for directly: adjustable
text size for message bodies and, separately, for the whole interface
(the interface one scales through gtk-xft-dpi, the same knob GNOME's
own large-text accessibility setting uses, so it reaches menus and
dialogs too); a system/light/dark theme switch; and, in the thread
view, per-message toggles to fold away quoted text and diff hunks
without losing the row's place in the list. Folding a line only
shrinks the row once the tag covers its trailing newline, not just its
glyphs, and diff-folding now hides a hunk's unchanged context lines
along with the colored ones - leaving them out stranded isolated
fragments of code with blank gaps around them instead of closing the
hunk.

Thread parsing now drops a message repeated under the same Message-ID:
lore mirrors a cross-posted message once per list it was sent to, so a
thread spanning more than one list could come back from t.mbox.gz with
the same message twice, showing as a genuine duplicate in the thread
view and, since the watcher's digest pass goes through the same
parser, as a duplicate notification too.

The profile popover gained an editor for git send-email identities.
Creating, editing, or deleting one still writes straight to the user's
global git config, matching how profile.rs already treats git as the
only account store Koshi keeps.

Finally, a new Sent page (mirroring Favorites/Subscriptions) logs every
reply once git send-email has actually confirmed delivery - subject,
recipients, when, and, for a reply to a thread Koshi had open, enough
to reopen that thread the same way a favorite does.

@gahingwoo
gahingwoo force-pushed the macos-support branch 2 times, most recently from 5fc498c to 0e2a2d1 Compare August 2, 2026 10:47
Koshi now builds and runs as a standalone .app on macOS with no
Homebrew dependency at runtime. build-aux/macos/bundle.sh compiles the
release binary, relinks every dylib it needs with dylibbundler, and
bundles the gdk-pixbuf loaders, the GIO TLS module, the Adwaita and
hicolor icon themes, and compiled GSettings schemas alongside it. A
matching build-macos job in the release workflow packages the result
as a .dmg and attaches it to the GitHub release next to the Flatpak;
it also installs adwaita-icon-theme explicitly, since neither gtk4 nor
libadwaita depends on it and a Homebrew prefix that has never had
another GTK app built on it (a fresh CI runner, mainly) won't have it.

On macOS the window buttons now match every other platform instead of
showing native traffic lights: GtkHeaderBar and GtkWindowControls both
default to platform-native controls there since GTK 4.18, so the main
window and every dialog force use-native-controls off and move the
buttons to the right. AdwDialog also hardcodes its own close button to
the start side with no public way to reposition it, so dialogs get a
plain close button of their own on the end side instead. Keyboard
shortcuts move from a hardcoded <Control> to <Primary> (plus an
explicit <Meta> fallback on macOS, where <Primary>'s resolution to Cmd
has proven unreliable in practice), so Cmd+F and friends work as
expected instead of only ever answering to a literal Ctrl press.

New replies notify through UNUserNotificationCenter on macOS, since
there is no session D-Bus for the existing XDG portal path to use.
Authorization is requested once at startup rather than on the first
notification, because addNotificationRequest fails outright while
authorization is still undecided and asking any later would lose a
fresh install's first notification to that race. A warning row in
Preferences points the user at System Settings if they end up denying
it.

Preferences also picks up a few things asked for directly: adjustable
text size for message bodies and, separately, for the whole interface
(the interface one scales through gtk-xft-dpi, the same knob GNOME's
own large-text accessibility setting uses, so it reaches menus and
dialogs too); a system/light/dark theme switch; and, in the thread
view, per-message toggles to fold away quoted text and diff hunks
without losing the row's place in the list. Folding a line only
shrinks the row once the tag covers its trailing newline, not just its
glyphs, and diff-folding now hides a hunk's unchanged context lines
along with the colored ones - leaving them out stranded isolated
fragments of code with blank gaps around them instead of closing the
hunk.

Thread parsing now drops a message repeated under the same Message-ID:
lore mirrors a cross-posted message once per list it was sent to, so a
thread spanning more than one list could come back from t.mbox.gz with
the same message twice, showing as a genuine duplicate in the thread
view and, since the watcher's digest pass goes through the same
parser, as a duplicate notification too.

The profile popover gained an editor for git send-email identities.
Creating, editing, or deleting one still writes straight to the user's
global git config, matching how profile.rs already treats git as the
only account store Koshi keeps.

Finally, a new Sent page (mirroring Favorites/Subscriptions) logs every
reply once git send-email has actually confirmed delivery - subject,
recipients, when, and, for a reply to a thread Koshi had open, enough
to reopen that thread the same way a favorite does.
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