You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SaneClick is a macOS app + Finder Sync extension that adds curated and custom actions to the Finder right-click menu. The host app manages scripts and settings; the extension renders menus and triggers execution.
Non-goals
No cloud sync or analytics for script data.
No background daemon beyond the Finder Sync extension.
No direct modification of Finder state outside the Finder Sync API.
System Context
Host app: SwiftUI app that manages scripts, categories, and updates.
Finder Sync extension: Builds the context menu and triggers script execution.
App Group container: Shared storage and IPC between host + extension.
Sparkle: Update checks via appcast.
No GitHub DMG: DMGs are hosted on Cloudflare R2, not in GitHub.
Architecture Principles
Shared data lives in the App Group container so both targets see the same source of truth.
Extension is read-only on script data; host app owns edits and broadcasts changes.
File-based IPC (with locking) is favored over direct cross-process calls.
Fail safe: if App Group is unavailable, fall back to Application Support.
Core Components
Component
Responsibility
Key Files
ScriptStore
Load/save scripts + categories; notifies extension on change
SaneClick/Services/ScriptStore.swift
ScriptExecutor
Executes bash/AppleScript/Automator workflows
SaneClick/Services/ScriptExecutor.swift
UpdateService
Sparkle updater wrapper
SaneClick/Services/UpdateService.swift
FinderSync
Finder context menu + execution trigger
SaneClickExtension/FinderSync.swift
Script models
Script types, filters, categories
SaneClick/Models/*
Data and Persistence
Scripts: scripts.json in App Group container M78L6FXD48.group.com.saneclick.app. Fallback: ~/Library/Application Support/SaneClick/scripts.json.
Categories: categories.json in the same container (same fallback path).
Execution requests: pending_execution.json + .execution.lock in App Group container.
Backups: ScriptStore writes *.backup.json and *.corrupted.json when needed.
Key Flows
Script Edit -> Extension Menu Refresh
User edits scripts in the host app.
ScriptStore writes scripts.json and posts com.saneclick.scriptsChanged.
Finder Sync extension rebuilds the menu on next menu(for:) call.
Finder Context Menu -> Script Execution
Finder Sync builds the menu from scripts.json and current selection.
User clicks an item; extension writes pending_execution.json.
Extension posts com.saneclick.executeScript via DistributedNotificationCenter.
Host app ScriptExecutor reads request, locks, validates, and executes.
Result is posted to UI via ScriptExecutor.executionCompletedNotification.
State Machines
Finder Menu Rendering
stateDiagram-v2
[*] --> Idle
Idle --> MenuRequested: Finder asks for menu
MenuRequested --> ScriptsLoaded: read scripts.json
ScriptsLoaded --> MenuRendered: filter + build menu
MenuRendered --> Idle
MenuRequested --> EmptyMenu: no scripts or read failure
EmptyMenu --> Idle