Follows up on #266, which was closed when Finicky was pure Swift and a port wasn't realistic. The v4 rewrite changes that equation.
The pitch
I'd like to contribute Windows support for Finicky. There's nothing like it on Windows — no rule-based browser router, period. Users are stuck manually switching default browsers or clicking through browser picker dialogs.
~87% of the v4 codebase is already cross-platform (Go + Svelte + TypeScript). Only the Objective-C platform layer (~930 lines across 8 files) needs Windows equivalents.
I've done a detailed analysis of every macOS API call in the codebase and mapped each to its Windows equivalent:
| Component |
macOS (current) |
Windows equivalent |
Effort |
| App event loop |
NSApplication |
getlantern/systray Go lib |
Low |
| URL receiving |
Apple Events (kAEGetURL) |
Registry protocol handler + named pipe IPC |
Medium |
| System tray |
NSStatusItem |
getlantern/systray |
Low |
| Default browser get/set |
NSWorkspace Launch Services |
Registry UrlAssociations + ms-settings:defaultapps |
Low |
| Browser discovery |
NSWorkspace.URLsForApplicationsToOpenURL |
Registry StartMenuInternet scan |
Low |
| Browser launch |
exec.Command("open", ...) |
Direct .exe invocation |
Low |
| Config window |
WKWebView + NSWindow |
webview/webview Go lib (wraps WebView2) |
Low |
| JS↔native bridge |
WKScriptMessageHandler |
webview.Bind() |
Low |
| Modifier keys |
NSEvent.modifierFlags |
GetAsyncKeyState() |
Low |
| Battery/power |
IOPowerSources |
GetSystemPowerStatus() |
Low |
| Running app check |
NSWorkspace.runningApplications |
CreateToolhelp32Snapshot |
Low |
| Window title |
Accessibility API |
GetWindowText() |
Low |
| Home/cache dirs |
NSHomeDirectory() |
Go stdlib os.UserHomeDir() |
None |
| Single instance |
NSRunningApplication terminate |
Named mutex (CreateMutex) |
Low |
14 of 18 API mappings are drop-in replacements. Only URL receiving needs architectural work — on macOS the OS pushes URLs to a running app via Apple Events; on Windows the OS launches a new process with the URL as argv[1], so the port needs a named pipe to forward URLs to the running instance. This is a well-established pattern (VS Code, Slack, and Spotify all do it).
Proposed approach
Zero changes to existing macOS code. The approach uses Go build tags:
- Rename 5 existing files with
_darwin.go suffix (adds //go:build darwin constraint)
- Split 3 files that mix portable logic with cgo calls (e.g.,
launcher.go — profile parsing is portable, the open command is not)
- Create 7 new
_windows.go files (~620 lines total)
- Extend
browsers.json with Windows exe names and AppData config paths
- Add 3 Go dependencies:
webview/webview, getlantern/systray, golang.org/x/sys/windows
The Svelte UI, TypeScript config engine, Go rule engine, resolver, shorturl expander, config watcher, and JS VM — all compile and run as-is on Windows with zero modifications. The .finicky.js config format stays identical, so users can share configs across platforms.
Files untouched: config/, logger/, resolver/, rules/, shorturl/, version/, assets/, packages/ (the entire Svelte UI).
The one architectural difference
On macOS:
User clicks link → OS routes to running Finicky via Apple Events → HandleURL()
On Windows:
User clicks link → OS launches finicky.exe with URL as argv[1]
→ Check for existing instance via named mutex
→ If exists: send URL over named pipe to running instance, exit
→ If not: become main instance, start pipe server, process URL
What I'm asking
Would you accept a PR that adds Windows support via this approach? Specifically:
- Are you open to the build-tag file structure (existing macOS code stays untouched, Windows files live alongside)?
- Any concerns about the new dependencies (
webview/webview, getlantern/systray, golang.org/x/sys/windows)?
- Would you want Windows CI (GitHub Actions) added to the repo, or should that live in a fork?
If you'd prefer this as a community fork rather than upstream, that's completely fine — I'd maintain config format compatibility either way. Just want to check before doing the work.
Happy to share the full file-by-file analysis if helpful.
Follows up on #266, which was closed when Finicky was pure Swift and a port wasn't realistic. The v4 rewrite changes that equation.
The pitch
I'd like to contribute Windows support for Finicky. There's nothing like it on Windows — no rule-based browser router, period. Users are stuck manually switching default browsers or clicking through browser picker dialogs.
~87% of the v4 codebase is already cross-platform (Go + Svelte + TypeScript). Only the Objective-C platform layer (~930 lines across 8 files) needs Windows equivalents.
I've done a detailed analysis of every macOS API call in the codebase and mapped each to its Windows equivalent:
NSApplicationgetlantern/systrayGo libkAEGetURL)NSStatusItemgetlantern/systrayNSWorkspaceLaunch ServicesUrlAssociations+ms-settings:defaultappsNSWorkspace.URLsForApplicationsToOpenURLStartMenuInternetscanexec.Command("open", ...).exeinvocationWKWebView+NSWindowwebview/webviewGo lib (wraps WebView2)WKScriptMessageHandlerwebview.Bind()NSEvent.modifierFlagsGetAsyncKeyState()IOPowerSourcesGetSystemPowerStatus()NSWorkspace.runningApplicationsCreateToolhelp32SnapshotGetWindowText()NSHomeDirectory()os.UserHomeDir()NSRunningApplicationterminateCreateMutex)14 of 18 API mappings are drop-in replacements. Only URL receiving needs architectural work — on macOS the OS pushes URLs to a running app via Apple Events; on Windows the OS launches a new process with the URL as
argv[1], so the port needs a named pipe to forward URLs to the running instance. This is a well-established pattern (VS Code, Slack, and Spotify all do it).Proposed approach
Zero changes to existing macOS code. The approach uses Go build tags:
_darwin.gosuffix (adds//go:build darwinconstraint)launcher.go— profile parsing is portable, theopencommand is not)_windows.gofiles (~620 lines total)browsers.jsonwith Windows exe names and AppData config pathswebview/webview,getlantern/systray,golang.org/x/sys/windowsThe Svelte UI, TypeScript config engine, Go rule engine, resolver, shorturl expander, config watcher, and JS VM — all compile and run as-is on Windows with zero modifications. The
.finicky.jsconfig format stays identical, so users can share configs across platforms.Files untouched:
config/,logger/,resolver/,rules/,shorturl/,version/,assets/,packages/(the entire Svelte UI).The one architectural difference
On macOS:
On Windows:
What I'm asking
Would you accept a PR that adds Windows support via this approach? Specifically:
webview/webview,getlantern/systray,golang.org/x/sys/windows)?If you'd prefer this as a community fork rather than upstream, that's completely fine — I'd maintain config format compatibility either way. Just want to check before doing the work.
Happy to share the full file-by-file analysis if helpful.