Browser pairing, notifications, element hiding; drop the top bar

Pairing decrypts a Chromium browser's cookie store (PBKDF2-HMAC-SHA1
against its Keychain key, then AES-128-CBC) and injects the result into
WKHTTPCookieStore. Only the configured apps' hosts and their sign-in
hosts survive the filter. Browsers are offered most-recently-used first,
since the first entry becomes the default and someone with four
Chromium browsers installed wants the one they actually browse in.

WKWebView defines window.Notification but it does nothing: constructing
one throws no error and shows no banner, so a page believes it notified
you. Measured on the machine as `api=function shim=no` before the shim
was made unconditional; `from page: Odoo - Test notification -> raised`
after.

Anything on a page can be right-clicked away. The rule is re-asserted on
every navigation, because the injected script only carries a snapshot
from when the view was built and a selector added since would otherwise
come back on reload.

The top bar is gone. Navigation lives beside the cog, the nav carries
the traffic lights, and two-finger swipe goes back and forward.

The seed is now the real app list, scoped to exact hosts so a Drive link
inside Gmail switches rather than being swallowed.
This commit is contained in:
2026-09-01 12:17:31 +02:00
parent e369c82774
commit ff4a0c6bc4
44 changed files with 2492 additions and 419 deletions
@@ -179,6 +179,29 @@ and seeding. Cookie decryption is tested against a fixture database with a known
Webview orchestration and bounds sync have no seam that a unit test can reach. They are
verified by running the app and driving it.
## Notifications
WKWebView defines `window.Notification`, but it is inert: constructing one throws nothing
and shows nothing, so a page believes it has notified you and you never hear about it. It
is replaced by a shim that forwards over the same sentinel channel as everything else, and
Rust raises a real macOS notification carrying the app's name.
Verified on the machine: `permission: Granted · direct: raised · page: api=function shim=no`
was the reading that showed the native API existed and the shim had therefore never
installed. The shim now replaces it unconditionally.
Service-worker push is **not** covered — only notifications a page raises while it is open.
## Hiding elements
Anything on a page can be right-clicked and hidden. The injected script owns a stylesheet
of `display: none` rules, re-added when a single-page app rewrites `<head>`. Selectors are
per app and stored in `apps.json`, editable and reversible in Settings.
The selector generator prefers an id, then up to two stable-looking classes per level,
then position — skipping classes that look hashed or numbered, because those change on
every deploy. A selector that stops matching hides nothing; it never hides the wrong thing.
## Out of scope
- Windows and Linux.