Skip to content

About

A standalone, robust Swift 6 selection retrieval engine for macOS

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

38 Commits

Folders and files

Repository files navigation

OpenSelection

A standalone, robust, Swift 6 selection retrieval engine for macOS (macOS 14+).

OpenSelection extracts the currently selected text, selection screen bounds, rich representations (HTML, RTF), and source application information from any active macOS application. It handles native Cocoa controls, WebKit web areas, Chromium/Electron webviews, terminal emulators, PDF viewers, and Microsoft Office documents using a multi-tiered, deadline-bounded cascade.

ZERO third-party dependencies — built entirely on Apple system frameworks (AppKit, ApplicationServices, CoreGraphics, os.log) with Swift 6 complete concurrency checking (SWIFT_STRICT_CONCURRENCY: complete).


Features

  • Multi-tiered Strategy Cascade:
    • AXTextControlStrategy: Direct zero-copy accessibility reads for native text fields (AXTextField, AXTextArea).
    • AXWebAreaStrategy: Text marker range inspection and bounded settle-retries for WebKit web content.
    • menuCopy: Non-invasive menu item traversal triggering Edit ▸ Copy via Accessibility.
    • keyboardCopy: Synthetic ⌘C dispatch with pasteboard snapshot archiving and transient marker restoration. Passive monitoring adds menu-shortcut authorization before dispatch.
    • officeScript: AppleScript Object Model retrieval for Microsoft Office (Word, Excel, PowerPoint) avoiding clipboard pollution.
  • Watchdog Deadlines & Concurrency Gating:
    • All accessibility inspects run off the cooperative thread pool on a dedicated concurrent queue.
    • Hard timeouts (axReadTimeout: 0.5s, pasteboardCopyTimeout: 0.25s) ensure slow or hung target apps never block the caller.
    • Concurrency permit gate bounds in-flight inspects and menu presses to prevent thread pool exhaustion.
  • Rich Content Enrichment:
    • Automatically captures rich representations (public.html, public.rtf) for web content and formatted documents.
  • Clipboard Preservation:
    • PasteboardSnapshot captures and restores clipboard items tagged with org.nspasteboard.TransientType and org.nspasteboard.AutoGeneratedType, preventing third-party clipboard managers from logging transient copies.
  • Context & Cursor Gating:
    • Role-based gates prevent firing copies on row selections (e.g. Finder file rows, Mail message lists) or non-text controls (AXButton, AXSlider).
    • Cursor silhouette analysis (CursorClassifier) identifies beam, arrow, and pointing hand cursors to filter non-text contexts.

Installation

Swift Package Manager

In Package.swift:

dependencies: [
    .package(url: "https://github.com/ganeshmshetty/OpenSelection.git", from: "0.1.0")
]

Or in Xcode, add the package dependency pointing to the repository.


Quickstart

Retrieve Plain Text

import OpenSelection

if let text = await OpenSelection.selectedText() {
    print("Selected text: \(text)")
}

Retrieve Full Selection Details

import OpenSelection

if let result = await OpenSelection.current() {
    print("Text: \(result.text)")
    if let bounds = result.bounds {
        print("Screen bounds: \(bounds)")
    }
    if let html = result.html {
        print("HTML: \(html)")
    }
    print("Strategy used: \(result.strategy)")
    print("Source app: \(result.sourceApp?.localizedName ?? "unknown")")
}

Custom Configuration

import OpenSelection

var config = SelectionConfiguration.default
config.axReadTimeout = 0.3
config.pasteboardCopyTimeout = 0.2

let result = await OpenSelection.retrieve(
    configuration: config,
    allowCopyFallback: true
)

Pluggable Logging

import OpenSelection

OpenSelection.logger = { message in
    print("[CustomLog] \(message)")
}

Passive Monitoring

OpenSelectionMonitor rejects mouse gestures whose initial window moved, resized or disappeared before release, and cancels pending reads on a new press or application activation. Its copy fallback uses AutomaticCopyCapture: an enabled Command-C menu shortcut must be available before it invokes Copy. Shortcut matching ignores translated titles and selectors. The AX menu walk runs off the main actor with a 150 ms aggregate deadline (or the configured axReadTimeout, if shorter), per-message timeouts and at most one probe in flight. Cancellation, the frontmost PID and the existing overlay guard are checked before copying. Logs include only the trigger/PID, never selection or clipboard contents.

Applications with their own gesture monitor can use the same capture seam:

let coordinator = SelectionRetrievalCoordinator(copyCapture: { trigger in
    await AutomaticCopyCapture.capture(trigger: trigger)
})

SelectionGestureWindow.at(_:) accepts AppKit screen coordinates and captures a window ID/frame; compare its frame with SelectionGestureWindow.currentFrame(for:) on release. This filters window moves even when the focused editor still exposes an old selection. Missing initial window metadata leaves ordinary selection detection intact. A window moved away and back to exactly its original frame, or a custom control drag, is not classified by this geometry check.

Passive copy fails closed when the shortcut cannot be verified. This does not add a mandatory menu pre-gate to SelectionRetrievalCoordinator, PasteboardCopyEngine or OpenSelection.current(): explicit requests keep their existing behavior for copy-only apps with incomplete AX menus. Clients that replace the monitor's coordinator also take responsibility for its copy policy.


Retrieval Strategies Explained

Strategy Description Best For
.axTextControl Direct AX query (kAXSelectedTextAttribute / kAXValueAttribute) Native Cocoa apps (TextEdit, Notes, Pages, Finder dialogs)
.axWebArea WebKit text marker ranges (AXSelectedTextMarkerRange) with settle retries Safari, Mail web bodies, WebKit webviews
.officeScript AppleScript Object Model document selection extraction Word, Excel, PowerPoint
.menuCopy Locates and presses Edit ▸ Copy via AX menu hierarchy Apps with disabled keystroke taps or custom menus
.keyboardCopy Archives pasteboard, fires ⌘C, captures result, restores pasteboard. The passive monitor additionally authorizes the enabled shortcut. Chromium, Electron (VS Code, Slack), Terminal, Ghostty

Edge-Case Guarantees

  1. Unresponsive App Protection: If an application hangs on an AX request or beachballs, the worker continuation resumes with nil after the deadline (axReadTimeout).
  2. Clipboard Manager Transparency: Any temporary copy made during .keyboardCopy or .menuCopy is immediately restored with transient markers so Raycast, Alfred, Maccy, and Paste don't record ghost entries.
  3. Office Document Clipboard Safety: Blind ⌘C keystrokes are never fired against Microsoft Office apps; selection is retrieved via the native AppleScript object model.
  4. Row-Selection Safety: Select-all (⌘A) gestures on tables, outlines, and lists (Finder files, Mail thread list) are recognized and skipped to prevent copying file lists as plain text.

License

Licensed under the Apache License, Version 2.0. See LICENSE and NOTICE for details.

About

A standalone, robust Swift 6 selection retrieval engine for macOS

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages