SDK reference
import { notch, NotchError } from "@notch-monster/plugin-sdk";
import "@notch-monster/plugin-sdk/ui.css"; // optional base look from the theme tokens
Lifecycle
notch.ready(): Promise<Context>
Connects to the host, applies the theme to :root, and resolves the
context. It’s safe to call more than once. Call it before anything else,
except notch.provide (see Services).
interface Context {
apiVersion: number;
pluginId: string;
widgetId: string; // this widget instance
view: "tile" | "expanded" | "service";
size: { cols: number; rows: number; px: { w: number; h: number } };
theme: Record<string, string>; // --notch-* → value
locale: string; // BCP 47, e.g. "en-IN"
permissions: Partial<Record<Permission, "granted" | "denied" | "ask">>;
}
notch.on(event, listener): () => void
Returns a function that unsubscribes.
| Event | Data |
|---|---|
resize |
{ cols, rows, px: { w, h } } |
theme |
new --notch-* values (the SDK applies them for you) |
visibility |
false while scrolled away, in Edit layout, or while the notch is collapsed (a tile kept running by background or audio) |
permissions |
the user changed an answer |
media, timer, alarms |
prefer notch.media.onChange / notch.timer.onChange / notch.alarms.onChange |
service |
{ service, event, data } from a service you use |
Storage
await notch.storage.widget.set("city", "Pune"); // this widget; deleted with it
const city = await notch.storage.widget.get<string>("city"); // null if missing
await notch.storage.plugin.set("cache", { t: Date.now() }); // shared by all your widgets
await notch.storage.plugin.delete("cache"); // → whether it existed
await notch.storage.plugin.keys();
Values are JSON, at most 64 KB each and 5 MB per plugin. Service
frames have no widget, so use storage.plugin there.
Pill
The collapsed notch can show a short status from your plugin (needs
widget.pill).
await notch.pill.set({
text: "31° Sunny", // ≤ 24 characters
icon: "sun", // from PILL_ICONS
progress: 0.4, // optional static ring, 0–1
expiresInMs: 3_600_000, // optional, ≤ 7 days
});
await notch.pill.clear();
Changes go out at most once a second, and the latest one wins. Plugin claims rank below the timer and media, and the newest plugin claim wins.
PILL_ICONS: alert, bell, book, calendar, check, clock, cloud, cloud-rain,
code, coffee, flame, heart, info, mail, message, moon, music, quote,
snowflake, star, sun, trending-down, trending-up, zap.
Stats
Count things for the panel’s Stats view. Declare each metric in the
manifest’s stats, then record values as they happen:
await notch.stats.record("pages_read", 3); // value defaults to 1
The app totals them per day (sum or max) and draws one card per metric:
today, this week, a 7-day chart and a streak. Recording is write-only (you
can’t read stats back), needs no permission beyond declaring the metric,
and is limited to 120 records a minute. Values are numbers from 0 to
1,000,000. Removing the plugin deletes its stats.
Navigation
await notch.openExpanded(); // tile → full panel (needs widget.expanded)
await notch.close(); // expanded → board
await notch.openUrl("https://example.com"); // https only, in the user's browser
Errors
Failed calls reject with NotchError. Its kind is one of invalidInput,
invalidState, notFound, permissionDenied, rateLimited, storage,
internal, unknownMethod or timeout.
try {
await notch.location.get();
} catch (e) {
if (e instanceof NotchError && e.kind === "permissionDenied") showCityPicker();
else throw e;
}
Calls that can wait on the user (a prompt or a picker) time out after 5 minutes. Others time out after 10 seconds.
Also in the SDK
notch.net,notch.notify,notch.peek,notch.clipboard,notch.location,notch.files,notch.media,notch.timer,notch.tasks,notch.audio,notch.alarms: see Permissions.notch.service,notch.provide,notch.emit: see Services.@notch-monster/plugin-sdk/protocolexports the types and constants:PERMISSIONS,PILL_ICONS,SERVICESandTHEME_TOKENS.@notch-monster/plugin-sdk/manifestexportsmanifestError(json), the same check the marketplace runs.