The canonical clickable-list pattern. Hover to see open PRs, click any row to jump to that PR in the browser.
Requires the GitHub CLI (brew install gh && gh auth login). The command runs in the shell's CWD by default — to pin to a specific repo, add -R owner/name to both gh pr list calls.
yaml
id: gh-prs
interval: 60
order: 30
location: tray
tooltip: "Open pull requests"
click:
shell: "gh pr list --web 2>/dev/null"
command: |
gh pr list --json number,title,url,state,headRefName,author,isDraft \
--limit 10 2>/dev/null \
| jq -c '{
count: length,
prs: [.[] | {
number, title, url, state, draft: .isDraft,
branch: .headRefName,
author: .author.login
}]
}' \
|| echo '{"count":0,"prs":[]}'
view:
hstack:
spacing: 4
children:
- symbol: { name: arrow.triangle.pull, size: 13, color: "#A371F7" }
- if:
when: "${count} > 0"
then:
text:
content: "${count}"
size: 11
weight: semibold
design: rounded
monospacedDigit: true
hover:
vstack:
spacing: 10
alignment: leading
frame: { width: 320 }
children:
- hstack:
children:
- text: { content: "Pull Requests", size: 11, color: secondary, weight: medium }
- spacer: {}
- text: { content: "${count}", size: 11, color: secondary, monospacedDigit: true }
- if:
when: "${count} == 0"
then:
text: { content: "No open PRs.", size: 11, color: secondary }
else:
forEach:
in: "${prs}"
stack: vstack
spacing: 8
alignment: leading
template:
vstack:
alignment: leading
spacing: 2
padding: { vertical: 4, horizontal: 6 }
background: "#FFFFFF11"
cornerRadius: 6
children:
- hstack:
spacing: 6
children:
- if:
when: "${item.draft} == true"
then:
symbol: { name: pencil.circle, size: 11, color: secondary }
else:
symbol: { name: circle.fill, size: 8, color: green }
- text: { content: "#${item.number}", size: 11, design: monospaced, color: secondary }
- text: { content: "${item.title}", size: 12, lineLimit: 1 }
- spacer: {}
- hstack:
spacing: 6
children:
- text: { content: "${item.branch}", size: 10, color: secondary, design: monospaced, lineLimit: 1 }
- spacer: {}
- text: { content: "${item.author}", size: 10, color: secondary }
onTap:
url: "${item.url}"
What this teaches
onTap.urlwith bindings —${item.url}resolves at click time, so each row jumps to its own PR. This is the killer use case for per-node tap actions.- Card-styled list rows — each row gets a subtle background + corner radius via modifiers, so rows feel tappable.
- Conditional badge — the bar chip only shows the count when there are open PRs (
if when: "${count} > 0"). - Empty state — when
count == 0, the popover shows a friendly "No open PRs" message instead of an empty list.
Building on this
- Filter to PRs that need your review: change
gh pr listtogh pr status --json ...and bind tocurrentUser.needsReview. - Show recent issues alongside PRs by adding a second
gh issue listscript and merging via jq. - Surface CI state per PR by extending the jq pipeline with
.statusCheckRollupand rendering a tiny status icon next to each row. - Trigger a system notification when a new PR appears: keep a counter file, diff against the script, fire
osascript -e 'display notification ...'when the count goes up.