notch.monster
DownloadGet it

Permissions

List permissions in manifest.json. Users see them on the install sheet. Sensitive ones are also asked the first time you use them, in a prompt showing your justifications text. Users can turn any of them off at any time in Settings → Plugins, so plan for “no”.

"permissions": ["network", "location"],
"network": { "domains": ["api.open-meteo.com"] },
"justifications": { "location": "Weather where you are." }
Permission API Sensitive
network notch.net.fetch(url, { method, headers, body }) → .status, .ok, .headers, .json(), .text(), .bytes() no
notifications notch.notify(title, body) no
peek notch.peek({ title, detail?, icon? }): the collapsed notch grows into a banner for a few seconds, then shrinks back no
clipboard:write notch.clipboard.writeText(text) no
clipboard:read notch.clipboard.readText() (only while the user is using your widget) yes
location notch.location.get() → { latitude, longitude, accuracyM }, city level (≥ 10 km) yes
location:precise the same, at full accuracy yes
files notch.files.pick({ accept: [".csv"], multiple }) → FileRef[], then notch.files.read(ref) → .text() / .bytes() yes
media:read notch.media.get(), notch.media.onChange(cb) no
media:control notch.media.control("playPause" | "next" | "previous") yes
timer:read notch.timer.get(), notch.timer.onChange(cb) no
timer:control through the timer service yes
tasks:read notch.tasks.list() (today’s tasks) yes
tasks:write notch.tasks.add(title), notch.tasks.complete(id) yes
notes:read / notes:write through the notes service yes
background your tile keeps running while the notch is collapsed, within background.budget no (needs a justification)
audio:play play sound; notch.audio.setPlaying(true) keeps your tile running while collapsed no
alarms notch.alarms.set({ key, at, title, body?, repeatEveryMinutes?, maxRepeats?, startsAt?, completable? }), .cancel(key), .list(), .onChange(fn): the app rings them full screen with a sound no (needs a justification)

Limits

Running in the background

Normally your frames are destroyed when the notch collapses and reloaded when it opens. Two permissions change that for your tile:

While collapsed, visibility is false and the tile isn’t drawn, so requestAnimationFrame doesn’t run: use timers, and do as little as you can. Going over your budget for 10 seconds in a row pauses your plugin until the user resumes it. While the notch is open, every plugin’s limit is 10% of a core.

Checking answers

const ctx = await notch.ready();
if (ctx.permissions.location === "denied") showCityPicker();
notch.on("permissions", (p) => { /* the user changed something */ });

ask means the user hasn’t answered yet. The first call asks them.

Alarms

With alarms you schedule alerts and the app rings them: at at it covers the screen with an alert (your plugin’s name, your title and body) and plays a sound, even while the notch is closed and your plugin isn’t running. The user can Dismiss or Snooze (5, 10 or 15 min; with startsAt, never past it, plus “Until it starts”). With startsAt the alert counts down to it (“Starts 10:00 AM · in 10 min”). With completable: true it also offers Mark done: the alarm becomes done with completed: true, and notch.alarms.onChange tells a running plugin. If nobody answers within a minute it counts as missed and, with repeatEveryMinutes, rings again that much later, up to maxRepeats times (default 3).

await notch.alarms.set({
  key: "standup-2026-10-22",          // yours; setting it again replaces it
  at: Date.parse("2026-10-22T09:50"),
  title: "Standup",
  body: "Room 4",
  startsAt: Date.parse("2026-10-22T10:00"),
  completable: true,
  repeatEveryMinutes: 5,
});
const mine = await notch.alarms.list(); // state: scheduled | ringing | done | missed
notch.alarms.onChange((all) => {
  for (const a of all) if (a.completed) markDone(a.key);
});
await notch.alarms.cancel("standup-2026-10-22");

Alarms ring only on this PC and aren’t synced. An alarm that came due while the app was closed rings when it starts if it’s at most an hour late, and is missed otherwise. Dismissed and missed alarms stay in list() for a week, so you can tell what happened.