macOS port: full parity third platform (Windows/Linux/macOS from one codebase)#2
Merged
Conversation
Plan for adding macOS as a third supported target alongside Windows and Linux from the same codebase: split the `not(windows)` cfg gates into explicit linux/macos arms (GTK is Linux-only), stand up a native window layer, and fill in Mac feature backends. Phased checklist in docs/macos-port.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add macOS as a third supported target from the same codebase: - Cargo.toml: regate the gtk / gtk-layer-shell deps from cfg(not(windows)) to cfg(target_os = "linux") so they aren't pulled in on macOS (which is also "not windows"). tauri-build auto-added the macos-private-api feature. - lib.rs: move every gtk/layer-shell/COSMIC cfg(not(windows)) block to cfg(target_os = "linux"); resize_palette's no-op arm is now cfg(not(target_os = "linux")) so Windows and macOS both keep params used. - tauri.conf.json: enable app.macOSPrivateApi so the transparent palette window actually renders transparent on macOS. No frontend changes: IS_LINUX (userAgent) is false on macOS, so the palette already uses the native setSize path. cargo build is clean and the app launches without panic (file index scans, transparent window confirmed). Feature backends (launcher, screenshot, audio, system, process, paste), tray, window positioning, and packaging remain — see docs/macos-port.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Core UX parity for macOS: - shortcuts.rs: default global hotkey is Cmd+Shift+Space on macOS (Ctrl+Space is the input-source switcher, Cmd+Space is Spotlight). - Cargo.toml + lib.rs: apply window-vibrancy (NSVisualEffectMaterial HudWindow + 12px radius) to the palette in a cfg(target_os = "macos") setup arm; best-effort. - lib.rs: set_activation_policy(Accessory) so the app is a background agent (no Dock icon / Cmd-Tab entry), like Raycast/Spotlight. - lib.rs: enable the tray icon on macOS (setup_tray gate widened to windows|macos) so there's an entry point + Quit without a Dock icon. Builds clean; app launches, toggles the palette via single-instance, and survives show/hide with no panic. Deferred to 2b: tauri-nspanel (non-activating panel), cursor-monitor positioning, macOS set_window_transparency fallback. See docs/macos-port.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lback - lib.rs: add a macOS position_on_cursor_monitor built on Tauri's cross-platform cursor_position/monitor_from_point/work_area APIs (the Win32 path is untouched, so no Windows regression). The palette now opens centered on the display under the cursor, top at ~20% of the work area. Both show paths (toggle/show) call it on windows|macos. - src/lib/tauri.ts: route macOS through the Linux-style CSS-opacity path in setWindowTransparency instead of the Windows-only native invoke (which errored on mac). The transparency setting now works on macOS. Builds clean (cargo + tsc); launches and toggles twice with no panic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ResultsList: replace scrollIntoView({block:'nearest'}) with deterministic
scrollTop math. WKWebView (macOS) recenters on 'nearest', making the
selection jump to the middle when paging past the bottom of a long list;
the manual computation behaves identically on Chromium (Windows/Linux)
and WebKit (macOS) and only scrolls the minimum needed.
- Palette: add borderRadius:12 to the container on macOS/Linux (Windows
rounds the native frame via DWM). Matches the 12px native vibrancy radius
on macOS, so the corners are actually rounded.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CSS-opacity fallback faded the webview onto the opaque native vibrancy layer, so the transparency slider produced a blurred-wallpaper patch rather than genuine see-through. Implement set_window_transparency natively on macOS: set NSWindow.alphaValue (via objc2 msg_send on the main thread), the direct analogue of the Windows LWA_ALPHA path. This makes the whole window (vibrancy + content) translucent, revealing the desktop behind. Frontend: macOS now uses the native invoke path again (only Linux keeps the CSS-opacity fallback). Adds objc2 as a macOS dep, pinned to window-vibrancy's 0.5 so only one copy builds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sparency) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every stubbed/Linux-only backend now has a macOS arm: - launcher: scan ~/Applications, /Applications, /System/Applications for .app bundles (no descent into bundles; user apps shadow system ones); launch via `open` (LaunchServices activates running instances) - screenshot: `screencapture -x -R<cursor monitor rect>` (points from Tauri monitor APIs; PNG arrives at native Retina pixels). Overlay reuses the Windows physical positioning; its NSWindow is raised to screen-saver level in setup so it covers the menu bar without constrainFrameRect clamping, and click-away cancel now applies on macOS. Clipboard copy uses the arboard arm (wl-copy stays Linux-only) - paste to previous: NSWorkspace frontmost-app pid captured on palette show; paste reactivates it (main thread) then posts ⌘V CGEvents at the HID tap, with an actionable error when Accessibility isn't granted - audio: osascript get/set volume + mute on the default output, exposed as a single "System Output" pseudo-device - system: pmset lock/sleep; graceful shutdown/restart/logout via System Events; Finder empty-trash; Hibernate errors as not-a-macOS-concept - process: sysinfo list + libc SIGKILL (new macOS-only deps, with core-graphics for CGEvent) - scripts: .command runs via `open` (Terminal); fallback opener is `open` instead of xdg-open; gio/.desktop gated to Linux Adds macOS smoke tests (app scan, volume round-trip, process list) — cargo test green. crypto.rs verified portable (plaintext passthrough, nothing Linux-specific). tauri-nspanel deliberately not adopted: explicit reactivation makes it unnecessary for paste. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WKWebView re-fires mouseenter/mousemove when rows re-rank under a
stationary cursor, so every keystroke (which resets selection to the top)
let the row under the idle mouse yank the selection back — typically to
the middle of the list, since the palette opens near the cursor. Chromium
doesn't synthesize these events, which is why Windows behaved.
Apply the movement guard ResultsGrid already had (ignore events whose
coordinates match the last ones) as a container-level mousemove handler in
ResultsList and ActionPanel, and remove the per-row/per-cell onMouseEnter
handlers that bypassed it. ResultRow's separate hover tint went with it:
real hovers now select the row instantly, so the tint only ever showed on
synthetic events.
Also extract the Phase 2 minimal-scroll fix into lib/scroll.ts and apply
it to ActionPanel and ResultsGrid, which still used
scrollIntoView({block:'nearest'}) and had the same WKWebView recentering
jump when paging.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add macOS .app bundle release via scripts/release.cjs (cross-platform). - Add src-tauri/icons/icon.icns and CFBundleURLTypes for commandeer:// deep links. - Implement macOS file-search icons via NSWorkspace.iconForFile:. - Implement macOS system stats (CPU/RAM) using sysinfo. - Enable user-configured screenshot hotkey on macOS (no default to avoid conflicts). - Clean up platform cfg gates in fs.rs (explicit linux/macos instead of not(windows)). - Add CI matrix (ubuntu/windows/macos) for frontend type-check and Rust build/tests. - Update CLAUDE.md and docs/macos-port.md with macOS status and dev notes.
Reconciles the overnight Linux-parity push with the macOS port: - Keep macOS backends (screencapture capture, ⌘V paste, sysinfo process list, cursor-monitor overlay positioning) alongside the new Linux code (four-CLI screenshot fallback chain, X11 support, /proc process list, SIGTERM→SIGKILL, wl-copy/xclip + arboard clipboard fallback). - Re-gate upstream's #[cfg(not(windows))] modules to target_os="linux" where they pull in gtk/gio or shell out to Linux tools (desktop, linux_icons, system::linux, audio::linux, capture_screen_to, desktop_dir_entries) so the macOS build doesn't compile them. - paste_to_previous: adopt the new Result<bool> "was the keystroke delivered" contract; macOS returns true after ⌘V. - setup_tray: non-fatal on macOS/Linux per upstream; palette CSS radius merged (macOS 12px to match vibrancy, Linux 8px). - Keep scripts/release.cjs for npm run release (handles the .app bundle) over upstream's inline bun script; merge CLAUDE.md macOS notes with the new Shipping changes section and architecture text. Verified on macOS: tsc + vite build clean, cargo check clean (one pre-existing warning), 16/16 cargo tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…System fixes, template tray icon Gaps found comparing the finished port against the Windows/Linux arms: - @search was broken on macOS (capture_location was Windows-only and the home fallback Linux-only, so it always threw). When the palette opens over Finder the front window's folder is resolved via AppleScript on a worker thread (only queried when Finder was frontmost, so the one-time Automation prompt can't fire over unrelated apps); otherwise @search falls back to the home folder like Linux. - Clipboard history was plaintext at rest on macOS while Windows (DPAPI) and Linux (ChaCha20 + Secret Service) encrypted. The ChaCha20-Poly1305 arm now covers macOS with a 0600 key file next to the db, plus the same one-time plaintext re-encryption migration (verified live: pre-existing rows re-encrypted on startup, new copies encrypted as captured). Keychain deliberately NOT used: its ACLs bind to the code signature, so an ad-hoc-signed binary re-prompts on every rebuild — and the prompt fires inside setup and blocks launch (observed via process sample stuck in SecKeychainFindGenericPassword). - System folder: "Empty Recycle Bin" is now "Empty Trash" on macOS, and Hibernate (backend rejects it as not a macOS concept) is not listed. - Tray uses a monochrome >_ template glyph on macOS (18pt @2x, alpha-only, icon_as_template) instead of the colored app icon, which macOS would render as an opaque square; Windows/Linux keep the app icon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_bf870673
First real run of the Phase 4 workflow: glib-sys failed on a bare ubuntu-latest because Tauri links system GTK/WebKit. Also pull in gtk-layer-shell (palette overlay surface) and D-Bus headers (keyring/Secret Service). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_bf870673
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the complete macOS port into main, finishing the port plan's core idea: one repo, one branch, no fork — macOS as a third
#[cfg]arm alongside Windows and Linux, with every barenot(windows)gate that touched platform-specific behavior split into explicitlinux/macosgates.What's in here (phases 1–4 + parity pass)
macOSPrivateApifor window transparency.Cmd+Shift+Spacedefault hotkey, HudWindow vibrancy + rounded corners, Accessory activation policy (no Dock icon), tray with a monochrome>_template glyph, cursor-monitor positioning, nativeNSWindow.alphaValuetransparency.open), screenshot (screencapture -Rof the cursor monitor, overlay at screen-saver level), paste-to-previous (NSWorkspace capture → reactivate → ⌘V CGEvents, with a pointed Accessibility error), volume (osascript), system actions (pmset / System Events), processes (sysinfo + SIGKILL), file/app icons (NSWorkspace.iconForFile:),.commandscripts, system stats (sysinfo CPU/mem).releasescript (.exe/ binary /.app),.icns+CFBundleURLTypesdeep links, CI matrix (this PR is its first run), CLAUDE.md platform docs.Windows and Linux arms were deliberately left untouched throughout; the diff to their behavior should be zero. CI on this PR is the cross-platform proof — verified green on macOS locally (cargo test 18/18, live app: palette toggle, clipboard migration re-encrypting real rows, Finder @search fallback under a pending Automation prompt).
Remaining on-device-only items (permission grants that can't be scripted) are tracked in docs/macos-port.md.
🤖 Generated with Claude Code