Building widgets

Mac Text Select

Open widgets from a native icon menu beside selected text.

Choose Add → Mac Text Select → Add Mac Text Select in Preen. Allow Accessibility access, then add widgets to the menu using the plus buttons. Use the arrows to change their order. This destination follows selections across your Mac’s displays.

You can also select its bird in Preen and click a widget to add it, or choose Add to Text Select from a widget’s context menu. Publishing through the CLI or MCP adds widgets to your library; assign them to Text Select in Preen. Right-click the device’s bird for Widgets and settings…, Pause, or removal.

Finish selecting text with the mouse, Shift-selection, or Command-A. A native SwiftUI bubble shows the assigned widget icons, anchored to the start of the selected text and kept inside the screen edges. Hover an icon to reveal its widget, preferably above the menu, or below when space is limited. Move into the widget to interact. Escape, clicking elsewhere, typing in the source app, scrolling that app, or changing apps dismisses the menu. After entering the menu, moving away also dismisses it.

Apps must expose selected text through macOS Accessibility. Preen reads native text selections and browser text-marker selections on the focused control or its ancestors, including Firefox-style web documents. When selection start bounds are unavailable, Preen uses the mouse-down position for mouse selections, or the pointer position for keyboard selections. Secure text fields and selections larger than 64 KiB are skipped. Preen does not copy text to the clipboard to discover the selection.

Read the current selection

Existing widgets can render unchanged. Selection-aware widgets use the existing read-only surface context:

PREEN.onSurface(function(surface) {
  if (surface.kind !== 'mac-text-select' || !surface.selection) return;
  // Treat selected text as text, never as HTML.
  document.getElementById('result').textContent = surface.selection.text;
});

surface.selection contains id (a temporary session identifier), text, and appBundleId. The surface also supplies width, height, mode: "expanded", visible: true, and targetID: "local.mac.text-select". Design for a compact 420 × 280-point viewport that may shrink on small screens. The host handles the menu icons and positioning.

Selected text is provided only to the opened widget’s local rendering session. It is not written to the shared feed, persisted by the host, or forwarded to phones. Widgets retain Preen’s existing network sandbox. Actions and database mutations require a recent native click or keypress inside the widget; merely hovering an icon cannot execute them. Registered actions still require the usual approval for the rendered widget code. Text replacement is not part of this first version.

The repository includes tools/examples/text-selection-widget.html, a local word/character counter that demonstrates the context without any actions.