Mac Desktop is experimental and disabled by default. Local opt-in uses the
preen.features.desktopWidget preference in the Mac app’s own defaults domain;
quit and reopen the app after changing it. Saved assignments are preserved.
When enabled, Mac Desktop is a native WidgetKit destination in Preen. It uses the medium desktop size: two units wide and one unit tall. macOS chooses the exact bounds.
Connect the surface
- In Preen, choose Add a display → Mac Desktop → Connect Mac Desktop.
- Choose a library widget, or Add an example counter.
- Right-click the desktop and choose Edit Widgets → Preen → Preen Desktop. Add the medium widget.
Select the Desktop bird and click a library widget to change its source. The bird indicates that the local destination is enabled; it does not confirm that macOS has placed or refreshed the widget. Additional macOS placements show the same assigned source in this first version.
Publish native content
The desktop consumes the nativeSurface field in the assigned widget’s existing
JSON data feed. Use the same authenticated CLI, MCP push_data, or collector
endpoint that supplies the widget’s other data. The Mac hub validates the native
content and mirrors it to the WidgetKit extension through an App Group. The
extension holds no Preen credentials and does not execute the widget’s HTML.
This is a specific counter format, not a general native-code loader: publishing Swift or Kotlin source does not create a renderer. HTML and counter content can share one widget and one producer; supply both views’ data in the same snapshot.
After adding the example counter:
preen data 'Native counter' '{"nativeSurface":{"schemaVersion":1,"type":"counter","title":"Daily progress","subtitle":"One step at a time","value":25,"target":40,"unit":"tasks","style":{"accent":"#B64832"}}}'
Data pushes replace the feed object. Include any other fields your HTML widget needs in the same push. HTML-only widgets remain available on other destinations; the desktop shows a message until their producer supplies native content.
The native content contract is shared with the planned Live Activity surface:
| Field | Meaning |
|---|---|
schemaVersion | Required; 1. |
type | Required; "counter". |
title | Required nonblank text, at most 80 characters. |
subtitle | Optional text, at most 160 characters. |
value | Required finite number. |
target | Optional positive finite number; draws progress. |
unit | Optional text, at most 24 characters. |
style.accent | Optional #RRGGBB. |
style.background | Optional #RRGGBB. |
style.foreground | Optional #RRGGBB. |
The encoded content is capped at 2,048 bytes. Omitted colors use platform defaults; macOS appearance settings may alter the presentation. Reaching or exceeding a target does not end anything. The progress bar clamps visually while the numeric value remains unchanged.
An absent or null nativeSurface clears native content. Malformed or unsupported
content replaces the previous counter with a message. Changing the assignment,
pausing, removing the destination, or deleting its source also clears that
presentation when macOS next refreshes.
Refresh and lifecycle
The hub mirrors the latest feed during its status refresh and coalesces automatic WidgetKit reload requests to at most one per minute. The widget also requests a timeline refresh after 15 minutes. Both are requests: macOS decides when the desktop actually refreshes. The setup preview reflects the hub’s latest read; it does not prove that the desktop has caught up.
When Preen is closed, the extension retains the last shared content. Keep the Mac hub and your producer running for new updates. This destination needs no phone, pairing, cloud relay, or subscription.
The first version displays a counter and opens Preen when clicked. It does not provide JavaScript execution, arbitrary native code, or action buttons. The shared native model/view are ready for a future iOS ActivityKit adapter; Live Activity creation, APNs delivery, and lifecycle APIs are separate work.