Extension SDK
Extension Examples
Three complete, production-quality extensions — Pomodoro timer, AI usage rings, and WhatsApp Web bridge.
Extension Examples
Three complete extensions ship with SuperIsland as references.
1. Pomodoro Timer
A focus timer with compact countdown and quick controls.
Capabilities: compact, expanded, fullExpanded, backgroundRefresh, settings, notifications
manifest.json
{
"id": "com.workview.pomodoro",
"name": "Pomodoro Timer",
"version": "1.0.0",
"main": "index.js",
"permissions": ["notifications", "storage"],
"capabilities": ["compact", "expanded", "fullExpanded", "backgroundRefresh", "settings"],
"refreshInterval": 1.0,
"activationTriggers": ["manual"]
}index.js
const WORK_DURATION = () => (DynamicIsland.settings.get("workMinutes") ?? 25) * 60;
const BREAK_DURATION = () => (DynamicIsland.settings.get("breakMinutes") ?? 5) * 60;
let remaining = WORK_DURATION();
let running = false;
let isBreak = false;
function formatTime(s) {
const m = Math.floor(s / 60).toString().padStart(2, "0");
const sec = (s % 60).toString().padStart(2, "0");
return `${m}:${sec}`;
}
DynamicIsland.registerModule({
compact() {
const color = isBreak ? "#22c55e" : "#a855f7";
return View.hstack([
View.icon(isBreak ? "leaf.fill" : "timer", { size: 11, color }),
View.text(formatTime(remaining), {
style: "caption",
color: "#e8e8e8",
bold: true,
}),
View.when(running,
View.animate(View.icon("waveform", { size: 8, color: "#888" }), "pulse")
),
], { spacing: 4 });
},
expanded() {
const label = isBreak ? "Break" : "Focus";
const color = isBreak ? "#22c55e" : "#a855f7";
const total = isBreak ? BREAK_DURATION() : WORK_DURATION();
const progress = 1 - remaining / total;
return View.vstack([
View.hstack([
View.text(label, { style: "caption", color }),
View.spacer(),
View.text(formatTime(remaining), { style: "headline", bold: true }),
]),
View.progress(progress, { color }),
View.hstack([
View.button(
View.text(running ? "Pause" : "Start", { style: "caption", color }),
"toggle"
),
View.spacer(),
View.button(
View.text("Reset", { style: "caption", color: "#888" }),
"reset"
),
]),
], { spacing: 8 });
},
onActivate() {
remaining = DynamicIsland.store.get("remaining") ?? WORK_DURATION();
running = DynamicIsland.store.get("running") ?? false;
isBreak = DynamicIsland.store.get("isBreak") ?? false;
},
onAction(id) {
if (id === "toggle") {
running = !running;
DynamicIsland.store.set("running", running);
DynamicIsland.playFeedback("selection");
}
if (id === "reset") {
running = false;
isBreak = false;
remaining = WORK_DURATION();
DynamicIsland.store.set("running", false);
DynamicIsland.store.set("remaining", remaining);
DynamicIsland.playFeedback("warning");
}
},
});
setInterval(() => {
if (!running) return;
remaining = Math.max(0, remaining - 1);
DynamicIsland.store.set("remaining", remaining);
if (remaining === 0) {
isBreak = !isBreak;
remaining = isBreak ? BREAK_DURATION() : WORK_DURATION();
running = false;
DynamicIsland.notifications.send({
title: isBreak ? "Break time! 🌿" : "Back to work! 🎯",
body: isBreak
? `Take a ${BREAK_DURATION() / 60}m break`
: `Start a ${WORK_DURATION() / 60}m focus session`,
sound: true,
});
DynamicIsland.playFeedback("success");
}
}, 1000);2. AI Usage Rings
Displays Codex + Claude usage/availability with circular ring indicators.
Capabilities: compact, expanded, fullExpanded, minimalCompact, backgroundRefresh
async function fetchUsage() {
// Try local summary files first
const codex = await DynamicIsland.http.fetch(
"file://" + DynamicIsland.store.get("homeDir") + "/.codex/usage-summary.json"
);
const claude = await DynamicIsland.http.fetch(
"file://" + DynamicIsland.store.get("homeDir") + "/.claude/usage-summary.json"
);
return {
codex: codex.data,
claude: claude.data,
};
}
DynamicIsland.registerModule({
async compact() {
const usage = await fetchUsage();
const codexPct = usage.codex?.remaining ?? 0;
const claudePct = usage.claude?.remaining ?? 0;
return View.hstack([
View.circularProgress(codexPct, {
total: 100,
lineWidth: 2,
color: codexPct > 30 ? "#22c55e" : codexPct > 10 ? "#f59e0b" : "#ef4444",
}),
View.circularProgress(claudePct, {
total: 100,
lineWidth: 2,
color: claudePct > 30 ? "#a855f7" : claudePct > 10 ? "#f59e0b" : "#ef4444",
}),
], { spacing: 6 });
},
});3. WhatsApp Web
Pairs with WhatsApp Web via QR code and routes incoming messages to the island notification feed.
Capabilities: compact, expanded, fullExpanded, notificationFeed, backgroundRefresh
DynamicIsland.registerModule({
async compact() {
const state = await DynamicIsland.system.getWhatsAppWeb(0);
const status = state?.status ?? "disconnected";
return View.hstack([
View.icon("message.fill", {
size: 12,
color: status === "connected" ? "#22c55e" : "#888",
}),
View.text(status === "connected" ? "WhatsApp" : "Disconnected", {
style: "caption",
color: status === "connected" ? "#e8e8e8" : "#555",
}),
], { spacing: 4 });
},
async expanded() {
const messages = await DynamicIsland.system.getWhatsAppWeb(3);
if (!messages || messages.length === 0) {
return View.vstack([
View.text("No recent messages", { style: "caption", color: "#555" }),
View.button(View.text("Connect", { style: "caption", color: "#a855f7" }), "connect"),
]);
}
return View.vstack(
messages.map((msg) =>
View.hstack([
View.text(msg.sender, { style: "caption", bold: true }),
View.spacer(),
View.text(msg.body.slice(0, 30), { style: "caption2", color: "#888" }),
])
),
{ spacing: 4 }
);
},
onAction(id) {
if (id === "connect") {
DynamicIsland.system.startWhatsAppWeb();
}
},
});