Files
cairnquire/apps/macos-sync
2026-06-09 19:07:18 -04:00
..
2026-06-04 08:50:34 -04:00
2026-06-09 19:07:18 -04:00
2026-06-01 10:53:28 -04:00
2026-06-04 08:50:34 -04:00

Cairnquire macOS Sync

A macOS menubar application for syncing and uploading files to a Cairnquire server.

Features

  • Tray Icon: tray.circle.fill as the default menubar icon with upload state animations
  • Drop on Menubar: Drag files directly onto the menubar icon — it bounces, then uploads
  • Upload Progress: Icon switches to tray.circle (outline) while uploading
  • Menubar Popover: Click the icon to see a beautiful status panel with quick actions
  • Auto Sync: Watch a local folder and automatically sync markdown files to the server
  • Drag & Drop Upload: Drop files onto the upload zone to upload and copy the URL to clipboard
  • Quick Upload: Upload files via the menubar menu or popover
  • New Notes: Create notes from clipboard content or a terminal $EDITOR buffer
  • Upload Index: View all uploaded files with timestamps
  • Custom Icons: Choose from 11 different SF Symbols for the menubar icon
  • CLI Tool: Command-line interface for one-off uploads and note creation

Requirements

  • macOS 13.0+
  • Xcode 15.0+ (for building)
  • A running Cairnquire server

Building

Menubar App

cd apps/macos-sync
swift build
swift run CairnquireSync

For local development with Keychain access, sign the SwiftPM executable with a stable Apple Development identity and run the signed binary directly:

cd apps/macos-sync
scripts/sign-dev.sh CairnquireSync
.build/debug/CairnquireSync

The script uses the first valid Apple Development code-signing identity. To choose a specific identity, set CAIRNQUIRE_CODESIGN_IDENTITY to the certificate name or SHA-1 hash shown by security find-identity -v -p codesigning.

Or open in Xcode:

open Package.swift

CLI Tool

cd apps/macos-sync
swift build --product cairnquire-cli
swift run cairnquire-cli --help

You can run the built executable directly after building:

.build/debug/cairnquire-cli help

Setup

  1. Start your Cairnquire server
  2. Create an API token from the web UI at /account/tokens/new
  3. Open the app settings (click the menubar icon → Settings)
  4. Enter your server URL and API token
  5. Optionally configure a sync folder, default upload folder, and New Note behavior

Usage

Menubar App

Left-click the icon to open a beautiful status popover showing:

  • Live sync status with color indicator
  • Last sync time
  • Quick action buttons (Sync, Upload, Note, Drop Zone)
  • Recent uploads preview
  • Settings and server links

Right-click the icon for the traditional menu with all options.

  • Drop on Icon: Drag files directly onto the menubar icon — it bounces, then uploads
  • Sync Now: Manually trigger a sync
  • Upload File: Select files to upload
  • New Note: Create a markdown note from clipboard text or open Terminal.app with $EDITOR, depending on Settings
  • Drop Zone: Open a drag-and-drop upload window
  • View Uploads: See all uploaded files with timestamps
  • Change Icon: Pick from 11 SF Symbols in Settings (default: tray.circle.fill)

CLI Tool

Configure the shared app and CLI settings first. Use the port where your server is actually listening:

cd apps/macos-sync
swift run cairnquire-cli config \
  --server http://localhost:9000 \
  --token dev \
  --folder "$HOME/Documents/Cairnquire" \
  --upload-folder Inbox \
  --new-note editor

Run one bidirectional sync:

swift run cairnquire-cli sync

Start the menubar app to keep watching the configured folder when auto-sync is enabled:

swift run CairnquireSync

Other CLI commands:

# Upload a file
swift run cairnquire-cli upload ~/Documents/report.pdf

# Create a note from clipboard
swift run cairnquire-cli note --clipboard --title="Meeting Notes"

# Create a note from stdin
echo "Hello World" | swift run cairnquire-cli note -

# Create a note from arguments
swift run cairnquire-cli note "This is a note body"

# Show command-specific documentation
swift run cairnquire-cli help upload
swift run cairnquire-cli help sync

Readable uploads (.md, .markdown, .txt, .html, and .htm) are published as rendered documents. If the same inbox filename already exists, the app asks whether to keep both files, replace the existing document, or cancel. If identical attachment bytes were already uploaded, the app asks whether to copy the existing URL or cancel.

When New Note is set to Open Terminal $EDITOR, the menubar action opens a temporary markdown buffer in Terminal.app. The app uploads the saved buffer after the editor exits. It uses $EDITOR, then $VISUAL, and falls back to vi. Configure GUI editor launchers to wait for the buffer to close, such as EDITOR="code --wait".

Configuration

Configuration is stored in:

  • Settings: ~/Library/Preferences/com.cairnquire.sync.plist
  • API Token: macOS Keychain
  • Sync state: ~/Library/Application Support/CairnquireSync/

SwiftPM debug executables are ad-hoc signed. macOS may ask for Keychain access again after rebuilding because the replacement executable does not have a stable Developer ID identity. A packaged, consistently signed app avoids that development-time prompt. Updating settings preserves the existing Keychain item instead of recreating it.

Architecture

Sources/
├── CairnquireSync/           # Shared library
│   ├── API/
│   │   ├── Client.swift      # HTTP client for Cairnquire API
│   │   └── Models.swift      # API response models
│   ├── Sync/
│   │   ├── SyncEngine.swift  # Bidirectional sync engine
│   │   └── FolderWatcher.swift # FSEvents-based folder watcher
│   ├── Upload/
│   │   └── UploadManager.swift # File upload handling
│   ├── Settings/
│   │   └── Config.swift      # Configuration and keychain storage
│   └── UI/
│       ├── MenuBarController.swift  # Main controller
│       ├── StatusPopoverView.swift  # Menubar popover UI
│       ├── SettingsView.swift       # Settings UI
│       ├── DropZoneView.swift       # Drag & drop UI
│       └── UploadsListView.swift    # Upload index UI
├── CairnquireSyncApp/        # Menubar app
│   ├── App.swift             # App entry point
│   └── StatusItemView.swift  # Custom status item view with drag & drop
└── cairnquire-cli/           # CLI tool
    └── main.swift            # CLI entry point

Dev Mode

When running the server locally with CAIRNQUIRE_DEV_MODE=1, you can use the hardcoded API key dev instead of generating a real API token.

Server

CAIRNQUIRE_DEV_MODE=1 mise run server-run

Or set in your config:

{
  "devMode": true
}

macOS App

The app automatically detects localhost servers and defaults to the dev token. No configuration needed when running locally.

CLI

cairnquire-cli config --server http://localhost:8080 --token dev
cairnquire-cli upload ~/Documents/report.pdf

API Gaps

See .project/macos-app-api-gaps.md for documented API endpoints that need server-side implementation.

Readable uploads (.md, .markdown, .txt, .html, and .htm) are published as rendered documents. Other supported files remain attachments. The upload index returns the canonical URL for either kind.