sketchybar: surface meetingbar calendar failures
When sketchybar runs as an AeroSpace child rather than under launchd,
EventKit denies calendar access and the plugin rendered a blank label
indistinguishable from having no meetings.
Assisted-by: Claude Opus 5 via Claude Code
diff --git a/.config/sketchybar/meetingbar.swift b/.config/sketchybar/meetingbar.swift
index 25fae198..7aee288f 100755
--- a/.config/sketchybar/meetingbar.swift
+++ b/.config/sketchybar/meetingbar.swift
@@ -53,15 +53,20 @@ class MeetingBar {
case .authorized, .fullAccess, .writeOnly:
self.init(calendarTitle: calendarTitle, eventStore: eventStore)
case .denied:
- throw MeetingBarError(message: "Calendar access denied")
+ throw MeetingBarError(
+ message: "Calendar access previously denied for this process. "
+ + "Grant it in System Settings > Privacy & Security > Calendars.")
case .restricted:
- throw MeetingBarError(message: "Calendar access restricted")
+ throw MeetingBarError(message: "Calendar access restricted by policy")
case .notDetermined:
let semaphore = DispatchSemaphore(value: 0)
var accessGranted = false
+ var requestError: Error?
+
eventStore.requestFullAccessToEvents { granted, error in
accessGranted = granted
+ requestError = error
semaphore.signal()
}
@@ -70,7 +75,9 @@ class MeetingBar {
if accessGranted {
self.init(calendarTitle: calendarTitle, eventStore: eventStore)
} else {
- throw MeetingBarError(message: "Calendar access denied")
+ let detail = requestError.map { ": \($0.localizedDescription)" } ?? ""
+ throw MeetingBarError(
+ message: "Calendar access request was not granted\(detail)")
}
@unknown default:
throw MeetingBarError(message: "Unknown authorization status")
diff --git a/.config/sketchybar/plugins/meetingbar.sh b/.config/sketchybar/plugins/meetingbar.sh
index 697a1efd..c236d5fd 100755
--- a/.config/sketchybar/plugins/meetingbar.sh
+++ b/.config/sketchybar/plugins/meetingbar.sh
@@ -1,8 +1,15 @@
#!/bin/sh
CALENDAR=
-JSON=$("$CONFIG_DIR/meetingbar.swift" "$CALENDAR")
+
+# On failure the swift script prints nothing, so jq emits no output and the
+# `//` default never fires — leaving a blank label that looks like a working
+# plugin with no meetings. Surface it instead; stderr lands in sketchybar's log.
+if ! JSON=$("$CONFIG_DIR/meetingbar.swift" "$CALENDAR"); then
+ sketchybar --set "$NAME" label="calendar unavailable"
+ exit 1
+fi
DISPLAY=$(echo "$JSON" | jq -r '.primary.display // "No meetings"')
-sketchybar --set "$NAME" label="$DISPLAY"
+sketchybar --set "$NAME" label="${DISPLAY:-No meetings}"
diff --git a/.config/sketchybar/plugins/meetingbar_click.sh b/.config/sketchybar/plugins/meetingbar_click.sh
index 52ff76f2..c3a20063 100755
--- a/.config/sketchybar/plugins/meetingbar_click.sh
+++ b/.config/sketchybar/plugins/meetingbar_click.sh
@@ -11,7 +11,15 @@ if [ "$BUTTON" = "right" ]; then
exit 0
fi
- JSON=$("$CONFIG_DIR/meetingbar.swift" "$CALENDAR")
+ if ! JSON=$("$CONFIG_DIR/meetingbar.swift" "$CALENDAR"); then
+ sketchybar --remove '/meetingbar\.popup\./' 2>/dev/null
+ sketchybar --add item meetingbar.popup.0 popup.meetingbar \
+ --set meetingbar.popup.0 \
+ icon.drawing=off \
+ label="Calendar unavailable — check sketchybar's log" \
+ --set meetingbar popup.drawing=on
+ exit 1
+ fi
PINNED_ID=""
if [ -f "$PIN_FILE" ]; then
@@ -63,7 +71,7 @@ if [ "$BUTTON" = "right" ]; then
sketchybar --set meetingbar popup.drawing=on
else
# Left click: open the displayed event's meeting URL.
- JSON=$("$CONFIG_DIR/meetingbar.swift" "$CALENDAR")
+ JSON=$("$CONFIG_DIR/meetingbar.swift" "$CALENDAR") || exit 1
URL=$(echo "$JSON" | jq -r '.primary.url // empty')
if [ -n "$URL" ]; then