Skip to content

Commit 01bc0da

Browse files
committed
Cloud actions: iCloud Drive only, via FileManager ubiquity APIs
`NSFileProviderManager.evictItemWithIdentifier` etc. failed in practice with `NSFileProviderErrorProviderNotFound` ("The application cannot be used right now") — its host-side methods are reserved for the app that *bundles* the FP extension, not third-party callers like Cmdr. There's no public path for non-host apps to evict / download arbitrary File Provider items; Finder uses private XPC. The `FileManager.evictUbiquitousItem(at:)` / `startDownloadingUbiquitousItem(at:)` APIs route through iCloud's separate ubiquity infrastructure and accept any URL inside an iCloud container. Switching to those works for iCloud Drive paths and gives synchronous `Result<(), NSError>` (no completion-handler machinery, no `objc2-file-provider` crate at all). Effect: - `Make available offline` / `Remove download` only show in the context menu for paths under `~/Library/Mobile Documents/com~apple~CloudDocs/`. - For Dropbox / GoogleDrive / OneDrive / Box files the menu items no longer appear — the user has to use the provider's own client (or Finder). That's the honest state with public APIs. - `is_in_cloud_storage` removed (was unused outside the now-deleted fallback path); `is_in_icloud_drive` is the strict gate. - `objc2-file-provider` dropped from `Cargo.toml`. Module-doc comment in `cloud_actions.rs` records the dead-end so future agents don't re-litigate `NSFileProviderManager`.
1 parent 204fb28 commit 01bc0da

6 files changed

Lines changed: 101 additions & 204 deletions

File tree

Cargo.lock

Lines changed: 0 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src-tauri/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,6 @@ objc2-app-kit = { version = "0.3", features = [
157157
"NSApplication", "NSMenu", "NSMenuItem", "NSPasteboard", "NSRunningApplication",
158158
"NSWorkspace", "NSPanel", "NSOpenPanel", "NSSavePanel", "NSWindow", "NSResponder",
159159
] }
160-
objc2-file-provider = { version = "0.3", features = [
161-
"Extension", "NSFileProviderDomain", "NSFileProviderItem", "NSFileProviderError",
162-
] }
163160
block2 = "0.6"
164161
security-framework = "3.2"
165162
# Drive indexing: macOS FSEvents watcher with event IDs and sinceWhen replay

apps/desktop/src-tauri/src/commands/ui.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,16 @@ pub fn show_file_context_menu<R: Runtime>(
7474

7575
#[cfg(target_os = "macos")]
7676
fn build_file_context_info(primary_path: &str, all_paths: &[String]) -> FileContextInfo {
77-
use crate::file_system::cloud_actions::is_in_cloud_storage;
77+
use crate::file_system::cloud_actions::is_in_icloud_drive;
7878
use crate::file_system::open_with::compute_open_with_choices;
7979
use crate::file_system::sync_status::get_sync_statuses;
8080
use std::path::PathBuf;
8181

8282
let path_buf = PathBuf::from(primary_path);
83-
let is_cloud = is_in_cloud_storage(&path_buf);
83+
let is_icloud_drive = is_in_icloud_drive(&path_buf);
8484

8585
// Sync status of the primary path only — drives the cloud-action label.
86-
let sync_status = if is_cloud {
86+
let sync_status = if is_icloud_drive {
8787
let mut statuses = get_sync_statuses(vec![primary_path.to_string()]);
8888
statuses.remove(primary_path).unwrap_or_default()
8989
} else {
@@ -94,7 +94,7 @@ fn build_file_context_info(primary_path: &str, all_paths: &[String]) -> FileCont
9494

9595
FileContextInfo {
9696
sync_status,
97-
is_cloud,
97+
is_icloud_drive,
9898
open_with,
9999
}
100100
}

apps/desktop/src-tauri/src/file_system/CLAUDE.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,19 @@ Submodule docs: [listing/](listing/CLAUDE.md), [write_operations/](write_operati
66

77
## Cloud actions and "Open with" (macOS)
88

9-
- `cloud_actions.rs` — wraps `NSFileProviderManager.evictItem(...)` and
10-
`requestDownloadForItem(...)` so the file context menu can offer "Make available offline" and
11-
"Remove download" for any File-Provider-managed file (iCloud Drive, Dropbox, Google Drive,
12-
OneDrive, Box). Detection is fast (`is_in_cloud_storage` — pure path-prefix check against
13-
`~/Library/Mobile Documents/com~apple~CloudDocs` and `~/Library/CloudStorage/`); the actual
14-
evict/download chain calls async FP APIs synchronously via completion handlers + `mpsc::sync_channel`.
9+
- `cloud_actions.rs` — wraps `FileManager.evictUbiquitousItem(at:)` and
10+
`startDownloadingUbiquitousItem(at:)` so the file context menu can offer "Make available
11+
offline" and "Remove download". **iCloud Drive only.** `NSFileProviderManager`'s host-side
12+
methods looked like the cross-provider API but are reserved for the app that *bundles* the
13+
File Provider extension (Dropbox.app for Dropbox etc.) — third-party apps get
14+
`NSFileProviderErrorProviderNotFound` ("The application cannot be used right now") on the
15+
enumerate / evict / download calls. The `FileManager` ubiquity APIs route through iCloud's
16+
separate code path and accept any URL inside an iCloud container, so we offer the menu
17+
items only for paths under `~/Library/Mobile Documents/com~apple~CloudDocs/`. Module-doc
18+
comment in `cloud_actions.rs` has the full story for future agents.
19+
20+
`is_in_icloud_drive` (strict path-prefix check against
21+
`~/Library/Mobile Documents/com~apple~CloudDocs/`) gates the eviction menu items.
1522
- `open_with.rs``URLsForApplicationsToOpenURL:` for candidate apps, with multi-selection
1623
intersection. Session cache keyed by lowercased extension. Subscribes to
1724
`NSWorkspace.didLaunchApplicationNotification` / `didTerminateApplicationNotification` for

0 commit comments

Comments
 (0)