All widgets
Timer
Comes with Chopstick
Countdown timer. Pick a length or start it for any length (25 min, 90 seconds, 1h30); it shows the time left on the island and alerts when done.
Already installed? It's in your widget library.






Type it in the notch's search
- Timer for length
- Cancel the timer
What it runs
A widget is a folder of plain files. Here is all of its code.
widget.json
{
"id": "timer",
"name": "Timer",
"icon": "icon.png",
"description": "Countdown timer. Pick a length or start it for any length (25 min, 90 seconds, 1h30); it shows the time left on the island and alerts when done.",
"initial": "running",
"opens_at": "pick",
"show_when": {
"live.running": true
},
"watch": [
{
"script": "state.sh",
"every": 1,
"on": [
"file:end"
],
"as": "live"
}
],
"triggers": [
{
"when": {
"live.finished": true
},
"open": "done"
}
],
"commands": {
"start": {
"phrase": "Timer for {length}",
"description": "Start a countdown timer.",
"inputs": {
"length": {
"type": "duration",
"description": "How long: minutes (25), or with units (90 seconds, 1h30)"
}
},
"do": [
{
"set": {
"minutes": "{{input.length.minutes}}"
}
},
{
"run": "start.sh",
"then": "running"
},
{
"refresh": "live"
}
]
},
"cancel": {
"phrase": "Cancel the timer",
"description": "Cancel the running timer.",
"do": [
{
"run": "clear.sh"
},
{
"refresh": "live"
},
"dismiss"
]
}
},
"states": {
"pick": {
"open": true,
"on_enter": [
{
"run": "running.sh",
"then": "running"
}
],
"compact": {
"leading": {
"symbol": "timer",
"tint": "orange"
}
},
"expanded": {
"size": "compact",
"header": {
"symbol": "timer",
"title": "Timer",
"tint": "orange"
},
"blocks": [
{
"row": [
{
"button": {
"label": "5 min",
"do": [
{
"set": {
"minutes": "5"
}
},
{
"run": "start.sh",
"then": "running"
},
{
"refresh": "live"
}
]
}
},
{
"button": {
"label": "10 min",
"do": [
{
"set": {
"minutes": "10"
}
},
{
"run": "start.sh",
"then": "running"
},
{
"refresh": "live"
}
]
}
},
{
"button": {
"label": "25 min",
"do": [
{
"set": {
"minutes": "25"
}
},
{
"run": "start.sh",
"then": "running"
},
{
"refresh": "live"
}
]
}
}
],
"spacing": "tight"
},
{
"input": {
"placeholder": "Minutes, or 90 s, 1h30",
"do": [
{
"command": "start",
"with": {
"length": "{{item.text}}"
}
}
]
}
}
]
},
"keys": [
{
"key": "esc",
"do": [
"dismiss"
]
}
]
},
"running": {
"compact": {
"leading": {
"symbol": "timer",
"tint": "orange"
},
"trailing": {
"text": "{{live.label}}"
}
},
"expanded": {
"size": "compact",
"header": {
"symbol": "timer",
"title": "Timer",
"tint": "orange"
},
"blocks": [
{
"text": "{{live.label}}",
"style": "title"
},
{
"button": {
"label": "Cancel",
"do": [
{
"run": "clear.sh"
},
{
"refresh": "live"
},
"dismiss"
]
}
}
]
},
"keys": [
{
"key": "esc",
"do": [
"dismiss"
]
}
]
},
"done": {
"task": true,
"open": true,
"compact": {
"leading": {
"symbol": "bell.fill",
"tint": "green"
},
"trailing": {
"text": "Done"
}
},
"expanded": {
"size": "compact",
"header": {
"symbol": "bell.fill",
"title": "Time's up",
"tint": "green"
},
"blocks": [
{
"button": {
"label": "Dismiss",
"do": [
{
"run": "clear.sh"
},
{
"refresh": "live"
},
"dismiss"
]
}
}
]
},
"keys": [
{
"key": "esc",
"do": [
{
"run": "clear.sh"
},
{
"refresh": "live"
},
"dismiss"
]
}
]
}
},
"preview": {
"states": [
"pick",
"running",
"done"
],
"data": {
"live": {
"label": "4:32",
"running": true
}
}
}
}
clear.sh
#!/bin/sh
rm -f "$CHOPSTICK_STORAGE/end"
echo '{"ok":true}'
running.sh
#!/bin/sh # Succeeds while a timer is counting down: opened then, the widget shows it instead of the picker. F="$CHOPSTICK_STORAGE/end" [ -f "$F" ] && [ "$(cat "$F")" -gt "$(date +%s)" ]
start.sh
#!/bin/sh
F="$CHOPSTICK_STORAGE/end"
M="${CHOPSTICK_MINUTES:-5}"
echo $(( $(date +%s) + $(printf '%.0f' "$(echo "$M * 60" | bc -l)") )) > "$F"
echo '{"ok":true}'
state.sh
#!/bin/sh
F="$CHOPSTICK_STORAGE/end"
if [ ! -f "$F" ]; then echo '{"running":false,"finished":false,"label":"","remaining":0}'; exit 0; fi
E=$(cat "$F"); N=$(date +%s); R=$((E-N))
if [ $R -le 0 ]; then echo '{"running":false,"finished":true,"label":"0:00","remaining":0}'; exit 0; fi
printf '{"running":true,"finished":false,"remaining":%d,"label":"%d:%02d"}\n' $R $((R/60)) $((R%60))