MaciWatch plugins are purely declarative manifests: metadata, page components, settings, and HTTP actions live in one JSON file. Import it and it runs — no code required.
Copy the AI prompt below into any AI and add one line describing what you need; or configure and export <id>.json with the online builder. You can also download a Manifest V2 example plugin — including its explicit permission declarations — and tweak its fields.
02
Import into MaciWatch
Crown-click to open the Honeycomb → Plugin Manager → Import at the top right: pick a JSON file or paste the manifest text. Re-importing the same id overwrites and reinstalls.
03
Open it from the Honeycomb
The plugin appears in the Honeycomb — tap to open. The first HTTP action runs automatically on open to fetch data, and you can configure periodic refresh.
AI Prompt
Let AI write your plugin
Don't want to hand-write JSON? Copy the format prompt below into any AI (ChatGPT, Claude, GLM…), describe your need at the end, and it will produce a plugin manifest ready to import into MaciWatch.
Copy the prompt
Paste it into an AI and describe your need
Import the generated JSON into MaciWatch
The prompt is field-aligned with the app's built-in parser — generated manifests can go straight through Plugin Manager → Import → Paste manifest text.
Plugin Builder
Online plugin builder
Configure on the left and watch the Manifest V2 JSON preview update live on the right. Permissions are derived from your actions and remote images; copy or download when done, then import into MaciWatch.
Reference
Manifest format reference
Fields and behavior map one-to-one to MaciWatch's built-in parser; in the examples, · marks the default value when an optional field is omitted.
1. Top-level fields
A plugin is a JSON object holding metadata, pages, actions, initial state, and a refresh policy:
Field
Type
Required
Description
id
String
Yes
Unique identifier. Starts with a letter/digit, only A-Z a-z 0-9 . _ -, ≤ 100 chars; reverse-domain recommended, e.g. com.example.stock
name
String
Yes
Display name in the Honeycomb and page headers
version
Int
Yes
Integer version, starting at 1
icon
String
Yes
SF Symbol name; every reference is validated at import — invalid symbols are rejected
colors
[String]
No
Honeycomb icon gradient, two hex colors; gray when omitted. Supports #RGB / #RRGGBB / #RRGGBBAA
background
Object
No
Page background: solid / gradient / image; defaults to the app background
state
Object
No
Initial state key/values (strings / numbers / booleans); edits persist on this Mac
refreshInterval
Double
No
Auto-refresh in seconds, effective at ≥ 5; no timer by default
loadOnStart
Bool
No
Runs the default refresh action when the plugin opens · defaults to true; set false for manual only
permissions
[Object]
Manifest V2
Explicit capability grants. Each item is { "capability", "scope", "required" }; capabilities are networkRequest, clipboardWrite, openURL, mediaControl, nativePage, remoteImage, and autoRefresh. Host scope accepts an exact domain or a parent domain.
pages
Object
Yes
Page table keyed by page id; main is the entry page, the rest are navigate sub-pages
actions
Object
Yes
Action table keyed by action id, referenced by buttons / trailing controls / auto refresh
2. Pages & headers
Every page (PluginPage) has four parts: a header, a body component array, an optional settings section, and optional bottom buttons. The page renders in a scrolling header / main / extras / bottom layout.
Field
Type
Description
header
Object
title (required), icon (optional), trailing[] (optional, right-side controls)
body
Array
Body components, see next section; may be empty
settings
Array
Settings controls that write back to state on change; optional
bottom
Array
Bottom buttons; optional
fit
Bool
true = fits one screen (FitPage): the center doesn't scroll and content adapts — text auto-shrinks, rings / QR codes fill the center; defaults to false with overflow scrolling + a breathing collapse arrow at the bottom
native
String
Native page marker (currently whitelists music): renders the built-in Music app for the whole page (QR login / grid / player), identical to the native one — the plugin is just an entry point
Header trailing controls trailing come in three kinds:
type
Fields
Behavior
button
icon, action
Tap runs the action; shows a loading state when it is the default refresh action
toggle
bind, iconOn, iconOff
Switch bound to a state key; swaps icons with the value
text
bind
Read-only display of a state value
Layout model
Every plugin page runs on the app's unified watch layout system — a header, a center body, a persistent settings area, and bottom buttons. Two modes come out of the box, and both obey the app's two layout principles: the first screen always fits (nothing clipped or overflowing), and content below the fold is hinted by a breathing arrow in the bottom-right corner.
Rates
14:32
7.16CNY
1 USD → CNY
Fits the screen — no arrow is shown at all
Radar
14:32
42.8k★
More repos below…
neo-idea/MaciWatch
42.8k
More below — a breathing ↓ arrow; tap to jump to the end
Radar
14:32
Scrolled to the end
apple/swift-evolution
3.1k
At the end — the arrow flips ↑ to return to the top
Default scrolling page: header / body / settings / bottom all scroll as one page. When content overflows, the breathing fold arrow appears (hidden while everything fits, ↓ while there is more below, ↑ once you reach the end).
One-screen fit page (fit: true): the center never scrolls — content adapts instead. Text auto-shrinks down to 50% to stay fully visible, and rings / QR codes / images fill the center area.
Sub-pages, never popups: navigate switches pages full-screen and the header gains a back arrow — the app has no alert popups at all. Destructive bottom buttons use double-tap confirm (first tap turns red + shake) instead of a dialog.
Header overflow guard: more than two trailing controls collapse the leading ones into a glass ⋯ menu — put your high-frequency actions last in the trailing array.
3. Body components
body is an array of components distinguished by type. Text comes from either value (a literal supporting {{key}} templates) or bind (reads state; an np. prefix reads the built-in player live) — pick one.
Reads a state array into list rows. onTap: {page, prefix} writes the tapped element's fields to prefix.* (plus _index / _total / posFrac) then navigates; progressKey adds a usage bar per row (progressMax defaults to 100, shown as a percent)
progress
value / bind, label, detail, showPercent, max, thresholds
Progress bar, value 0–1. With label / detail / thresholds it renders in the AI-usage style (label + percent row / tri-color capsule bar / detail row); thresholds: {warning, critical} turns orange / red past the limits
ring
value / bind, label, showPercent, max, thresholds
Large ring in the AI-usage home style: trimmed ring + center percent / label; fills the center on fit pages, fixed size on scrolling pages
stats
items[]
Three-column stat grid (weather-page style): each cell icon + value / bind + caption
sparkline
bind
Mini line chart: reads a numeric array, normalized with a gradient fill
countdown
value / bind, label
Countdown: target supports ISO8601 or unix seconds / millis, self-driving every second
tags
bind or values[]
Pill tags: reads a string array (or literal), auto-wrapping grid
QR code: fills the center on fit pages, fixed size on scrolling pages
spinner
bind
Loading indicator: bind reads a boolean (np.isLoading works out of the box), shown when true; always shown when omitted
divider
—
Divider line
4. Settings
The settings section lives in the extras area; controls read and write state keys directly via bind, changes persist immediately, and HTTP action {{key}} templates always see the latest values.
type
Fields
Description
picker
label, bind, options[]
Single-select dropdown; options are a string array
true = double-tap confirm: the first tap turns it red for 3 s, the second executes — the no-alerts guard against accidental taps
6. Actions
type
Fields
Behavior
http
method · GET, url, headers{}, body, extract{}
HTTPS request (15 s timeout) gated by scoped networkRequest; url / headers / body support {{key}} templates, and extract writes the response into state by JSONPath
set
values{}
Writes several state key/values in place
copy
text
Renders the template, then copies to the clipboard; requires clipboardWrite
open-url
url
Renders the template, then opens the HTTPS URL in the default browser; requires scoped openURL
navigate
page
Navigates to another page in pages; the header gains a back arrow
media
command
Controls the built-in player: toggle (play / pause) · next · prev; works with any loaded music source queue and requires mediaControl
app
provider
Opens a native music source page: local · netease · qq · kugou; requires nativePage. The last three are community-edition capabilities; the App Store build gracefully degrades to a notice
JSONPath: extract values are dot paths with array indexes, e.g. $.rates.CNY, $.list[1].n; unresolvable paths write null for that key.
7. Runtime semantics
Default refresh action: the first http action (otherwise the first action). Runs once when the plugin opens with loadOnStart ≠ false; repeats on a cycle when refreshInterval ≥ 5 seconds.
Template variables: {{key}} is replaced with the current state value (numbers and booleans stringify directly); keys missing from state stay as-is, which makes step-by-step debugging easy.
Player data source np.*: a bind starting with np. bypasses plugin state and reads the built-in player live (np.title, np.artist, np.playlist, np.cover, np.isPlaying, np.isLoading, np.index, np.total, np.currentTime, np.duration, np.progress, np.timeText); read-only — set / extract / settings / list onTap prefixes cannot write to it.
Layout semantics: pages scroll on overflow by default (a breathing collapse arrow at the bottom hints at more content and flips once you reach the end); fit: true forces a one-screen fit with an adaptive center.
State persistence: state is written to local UserDefaults on first change (key plugin.state.<id>) and survives app restarts; Plugin Manager can reset it to the initial values.
Feedback: a glass loading state shows while a request is in flight; failures surface a notice under the header that clears automatically on success.
8. Install & manage
Location: ~/Library/Application Support/MaciWatch/plugins/<id>.json; you can also drop files in manually — Plugin Manager scans the folder automatically.
Importing: Plugin Manager → Import at the top right → pick a JSON file or paste the manifest text; the filename doesn't matter — the manifest's own id decides what lands on disk.
Overwrite & uninstall: re-importing the same id overwrites and reinstalls; Plugin Manager supports deleting and hiding from the Honeycomb (visibility is just a hidden list).
Built-in examples: Exchange Rates, Hitokoto, and the community-build Now Playing entry are seeded on first launch — modify or delete them freely. Every example manifest declares its permissions explicitly.
Community shares: the community examples below import directly; share your plugin JSON as a code snippet — the best ones get collected here.
9. Security & validation
id whitelist: must start with a letter/digit plus A-Z a-z 0-9 . _ -, ≤ 100 chars; path traversal (e.g. ../evil) and symbol-only ids are rejected.
Web protocols only: Manifest V2 network requests, remote images, and open-url destinations require https://; local schemes such as file:// are always rejected.
Explicit permissions: import shows every declared capability before anything runs. Network, remote-image, and open-URL scopes restrict requests to the exact host or a declared parent domain; authorization can be revoked at any time and permission changes invalidate old authorization.
Auto-refresh gate: loading on open or refreshing on a timer requires both networkRequest and autoRefresh.
Icon validation: every SF Symbol reference in the manifest (header, trailing, body, bottom buttons) must exist in the current system symbol set, or the import is rejected.
Data boundary: state stays on this Mac and is never uploaded; requests time out after 15 s, and a failure never affects other plugins.
Examples
Example plugins
Complete Manifest V2 files — built-ins plus community shares. Each one declares the permissions it needs; import as-is, or hand it to an AI to remix.
Built-in examples
Exchange Rates
7.16CNY
1 USD → CNY
Base currencyUSD
Exchange Rates · demo.exchange Built-in
GET request + JSONPath extraction: a picker switches the base currency, a metric shows the rate, and the trailing refresh button and bottom primary button share one action.
View manifest
Hitokoto
The best way out is always through.
— Robert Frost
Hitokoto · demo.hitokoto Built-in
Minimal structure: fit one-screen layout, gradient background + hero text; pulls a quote from the hitokoto API on open, and “Another one” refetches.
View manifest
Now Playing
Native Music
One plugin entry · zero plugin UI logic
Now Playing · demo.music Built-in
The smallest nativePage example: it declares only nativePage: music, so the built-in Music app renders the full native experience. This seed is community-build only and requests no network permission.
View manifest
Shared by the community
Code snippets from the community — import and go, or download and remix into your own version.
Repo Radar
42.8k★
neo-idea/MaciWatch
Tap a repo for details · language:swift
neo-idea/MaciWatchDesk watch for macOS
42.8k
apple/swift-evolutionProposal reviews
3.1k
Repo Radar · demo.radar Community
A full showcase of sub-pages + list taps + threshold rings: pulls a language list from the GitHub Search API, onTap opens a repo detail page, and the pin remembers locally; the help page statically demos progress threshold colors.
View manifest
Visual
23%CPU
61%Memory
3.2MBNetwork
SwiftpluginswatchOSopen source
73天 6:24:09Time to target
Visual Components · demo.visual Community
Every visual component in one manifest: stats grids, sparkline charts, tag pills, a countdown, image, qr, spinner, gradient text, plus a danger button with double-tap confirm.
View manifest
Netease Music
Nightcall
Kavinsky · OutRun
Playback45%
Daily PicksPrivate FM
Netease Music Suite · community.music Community
A conventional music plugin manifest: live np.* playback data, media actions for prev/next, quality settings, and an about page; the main page carries the native: "music" marker so the built-in Music app renders the body natively (community build required).