Async Link is a single native macOS process. Rust owns application lifecycle, Apple Events, LaunchServices integration, browser discovery, settings, placement, and the AppKit interface.
flowchart LR
A["macOS Apple Event"] --> B["URL validation"]
B --> C["Recent-link storage"]
C --> D["FIFO link queue"]
D --> E["Native picker"]
E --> F["Browser or profile selection"]
F --> G["Validated launch request"]
G --> H["Browser application"]
I["Settings window"] --> J["settings.json"]
J --> E
K["LaunchServices and profile files"] --> E
| Area | Owner | Responsibility |
|---|---|---|
| Application lifecycle | native/src/app.rs |
Creates the application, picker, Settings window, status item, and event actions |
| Incoming links | native/src/macos/app_events.rs |
Registers native Apple Event handlers and forwards opened URLs |
| Default browser | native/src/macos/launch_services.rs |
Registers and queries handlers for supported URL schemes |
| Browser discovery | native/src/macos/apps.rs |
Finds registered browser applications and removes duplicate bundle IDs |
| Profile discovery | native/src/macos/profiles.rs |
Reads supported Chromium and Firefox profile metadata |
| Launching | native/src/macos/launcher.rs |
Validates destinations and constructs browser launch arguments |
| Display placement | native/src/placement.rs |
Selects the active display and clamps picker bounds to its visible frame |
| Window behavior | native/src/app.rs |
Configures the titleless picker and Settings window across active and full-screen Spaces |
| Persistence | native/src/settings.rs |
Loads and atomically replaces the global settings file |
- LaunchServices delivers
http,https, orasync-linkevents to the application. - Async Link accepts direct web URLs. Custom-scheme URLs must contain a valid
web URL in their
urlquery parameter. - The destination is stored as the recent link, then it and available opener metadata enter a FIFO queue. The first link becomes active while later links wait without replacing it.
- The picker is placed on the target display, clamped to its visible frame, and brought to the active Space.
- A pointer or keyboard selection creates a validated foreground or background launch request.
- The launcher opens the selected application, adding a profile argument only for a supported browser family.
- A background profile launch uses a short Accessibility focus guard because Chromium can activate itself after LaunchServices requests a background launch.
- Selection or dismissal advances the next queued link.
The picker never displays the destination URL. Its surface contains only the available choices and controls needed to act on or dismiss the link.
Browser applications come from the system handlers registered for web URLs. Applications are deduplicated by bundle ID and sorted into the saved order.
Profile discovery currently supports common Chromium-family profile stores and
Firefox profiles.ini files. A choice key combines the browser bundle ID with
the profile identifier. That stable key is used for aliases, ordering, hidden
choices, and hotkeys.
The picker has two placement modes:
menubarplaces the picker beneath the status item when it is visible. When the item is hidden, it uses the cursor's horizontal position near the top of the active display.floatingplaces the picker near the pointer.
Both modes clamp the complete picker frame to the target display's visible frame, including displays with negative coordinates. AppKit collection behavior allows the picker to appear in the active Space and alongside full-screen Spaces.
Settings live at ~/.async/link/settings.json, outside the application bundle.
Installing a new build does not replace this file. Writes use a temporary file
followed by a rename so a partial write does not leave truncated settings. The
directory and file use user-only permissions. Invalid JSON is left untouched;
an explicit Save or Reset first moves it to a timestamped recovery file.
- Launch destinations are parsed before opening and restricted to supported schemes.
- Browser arguments are constructed as arrays rather than shell commands.
- The picker does not expose a web view, local server, or script bridge.
- The packaged application is ad-hoc signed and verified locally.
- The packaged target is Apple Silicon only.
- Builds are ad-hoc signed and are not notarized.
- There is no update service; installation is an explicit local operation.
- JavaScript and TypeScript matching, rewriting, and routing are not part of the native application.