diff --git a/.gitignore b/.gitignore
index 1b2ae933..a79024a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -227,6 +227,8 @@ __marimo__/
# otherwise record as gitlinks to commits no clone of this one can resolve.
.claude/
-# The macOS app: what pnpm installs, and the schemas tauri-build writes on every build.
+# The macOS app: what pnpm installs, and the schemas and the permissions of its own commands
+# tauri-build writes on every build.
desktop/node_modules/
desktop/src-tauri/gen/
+desktop/src-tauri/permissions/autogenerated/
diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock
index 10e7203b..b485f2df 100644
--- a/desktop/src-tauri/Cargo.lock
+++ b/desktop/src-tauri/Cargo.lock
@@ -1477,10 +1477,14 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
name = "humanize"
version = "0.0.0"
dependencies = [
+ "block2",
"flate2",
"libc",
"log",
+ "objc2",
"objc2-core-foundation",
+ "objc2-foundation",
+ "objc2-user-notifications",
"semver",
"serde",
"serde_json",
@@ -2366,6 +2370,8 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
dependencies = [
+ "bitflags 2.13.2",
+ "block2",
"objc2",
"objc2-foundation",
]
diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml
index 93aedca7..0eb718c8 100644
--- a/desktop/src-tauri/Cargo.toml
+++ b/desktop/src-tauri/Cargo.toml
@@ -27,6 +27,11 @@ libc = "0.2"
log = "0.4"
# The update's alerts: CFUserNotification, which never holds up a quit as a sheet would.
objc2-core-foundation = { version = "0.3", default-features = false, features = ["std", "CFDate", "CFDictionary", "CFString", "CFURL", "CFUserNotification"] }
+# Notifications the page asks for: UNUserNotificationCenter, and a delegate told of a click.
+block2 = { version = "0.6", default-features = false, features = ["std"] }
+objc2 = { version = "0.6", default-features = false, features = ["std"] }
+objc2-foundation = { version = "0.3", default-features = false, features = ["std", "NSError", "NSObject", "NSString"] }
+objc2-user-notifications = { version = "0.3", default-features = false, features = ["std", "bitflags", "block2", "UNNotification", "UNNotificationContent", "UNNotificationRequest", "UNNotificationResponse", "UNNotificationSound", "UNNotificationTrigger", "UNUserNotificationCenter"] }
semver = "1"
serde = { version = "1", features = ["derive"] }
# What generate_context! writes the updater's configuration out with.
diff --git a/desktop/src-tauri/build.rs b/desktop/src-tauri/build.rs
index 261851f6..235af997 100644
--- a/desktop/src-tauri/build.rs
+++ b/desktop/src-tauri/build.rs
@@ -1,3 +1,9 @@
+/// The app's commands, each granted by a capability: `notify` to `hmz web`'s page, the rest to
+/// the app's own.
+const COMMANDS: &[&str] = &["status", "retry", "keep", "update_now", "notify"];
+
fn main() {
- tauri_build::build();
+ let manifest = tauri_build::AppManifest::new().commands(COMMANDS);
+ tauri_build::try_build(tauri_build::Attributes::new().app_manifest(manifest))
+ .expect("failed to run the Tauri build script");
}
diff --git a/desktop/src-tauri/capabilities/default.json b/desktop/src-tauri/capabilities/default.json
index 818e7b8b..02e0605e 100644
--- a/desktop/src-tauri/capabilities/default.json
+++ b/desktop/src-tauri/capabilities/default.json
@@ -1,7 +1,14 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
- "description": "The app's own page: its own commands need no permission, and it is granted only to drag and zoom the window.",
+ "description": "The app's own page: granted its own commands, which say and change how hmz web starts, and to drag and zoom the window.",
"windows": ["main"],
- "permissions": ["core:window:allow-start-dragging", "core:window:allow-internal-toggle-maximize"]
+ "permissions": [
+ "allow-status",
+ "allow-retry",
+ "allow-keep",
+ "allow-update-now",
+ "core:window:allow-start-dragging",
+ "core:window:allow-internal-toggle-maximize"
+ ]
}
diff --git a/desktop/src-tauri/capabilities/page.json b/desktop/src-tauri/capabilities/page.json
index ef5c64cf..14239428 100644
--- a/desktop/src-tauri/capabilities/page.json
+++ b/desktop/src-tauri/capabilities/page.json
@@ -1,8 +1,8 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "page",
- "description": "hmz web's page, on whichever port of this machine the app started it, the window loading no other: granted only to drag and zoom the window by its top row, and none of the app's commands.",
+ "description": "hmz web's page, on whichever port of this machine the app started it, the window loading no other: granted only to post a notification, and to drag and zoom the window by its top row.",
"windows": ["main"],
"remote": { "urls": ["http://127.0.0.1:*"] },
- "permissions": ["core:window:allow-start-dragging", "core:window:allow-internal-toggle-maximize"]
+ "permissions": ["allow-notify", "core:window:allow-start-dragging", "core:window:allow-internal-toggle-maximize"]
}
diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs
index ba491b4b..3fecf5da 100644
--- a/desktop/src-tauri/src/lib.rs
+++ b/desktop/src-tauri/src/lib.rs
@@ -9,6 +9,7 @@ mod child;
mod chrome;
mod install;
mod menu;
+mod notify;
mod server;
mod shell_env;
mod update;
@@ -92,6 +93,8 @@ struct Shell {
page: Mutex