← back to Ticket System
Add centered ticket control popup
2d7b8714c98c9b5eb7f938dc99d8c912b16e15e1 · 2026-09-02 15:03:15 -0700 · Steve Abrams
Files touched
M desktop-bar/TicketBar.swiftM desktop-bar/build.sh
Diff
commit 2d7b8714c98c9b5eb7f938dc99d8c912b16e15e1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 2 15:03:15 2026 -0700
Add centered ticket control popup
---
desktop-bar/TicketBar.swift | 39 +++++++++++++++++++++++++++++++++++++--
desktop-bar/build.sh | 2 +-
2 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/desktop-bar/TicketBar.swift b/desktop-bar/TicketBar.swift
index 7dda42fd..b59b1c75 100644
--- a/desktop-bar/TicketBar.swift
+++ b/desktop-bar/TicketBar.swift
@@ -1,4 +1,5 @@
import AppKit
+import WebKit
private let ledgerURL = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".claude/tickets/events.jsonl")
@@ -15,6 +16,7 @@ private final class BarTile: NSButton {
let key: String
let label: NSTextField
var menuProvider: (() -> NSMenu)?
+ var clickHandler: (() -> Void)?
var dragHandler: ((String, CGFloat) -> Void)?
private var dragged = false
var widthConstraint: NSLayoutConstraint!
@@ -59,13 +61,15 @@ private final class BarTile: NSButton {
if !dragged { showDropdown() }
}
@objc private func showDropdown() {
- guard !dragged, let menu = menuProvider?() else { return }
+ guard !dragged else { return }
+ if let clickHandler { clickHandler(); return }
+ guard let menu = menuProvider?() else { return }
NSApp.activate(ignoringOtherApps: true)
menu.popUp(positioning: nil, at: NSPoint(x: 4, y: bounds.height - 2), in: self)
}
}
-private final class BarController: NSObject, NSApplicationDelegate {
+private final class BarController: NSObject, NSApplicationDelegate, WKNavigationDelegate {
private var panel: NSPanel!
private let counts = NSTextField(labelWithString: "Tickets · loading…")
private let working = NSTextField(labelWithString: "Working · —")
@@ -74,6 +78,7 @@ private final class BarController: NSObject, NSApplicationDelegate {
private var tiles: [BarTile] = []
private var workingDetails: [String] = []
private var latestDetail = "No activity yet"
+ private var ticketPopup: NSPanel?
private var timer: Timer?
func applicationDidFinishLaunching(_ notification: Notification) {
@@ -113,6 +118,7 @@ private final class BarController: NSObject, NSApplicationDelegate {
let tile = BarTile(key: key, label: label, width: savedWidths[key] ?? defaultWidth)
tile.dragHandler = { [weak self] key, x in self?.moveTile(key, toward: x) }
tile.menuProvider = { [weak self] in self?.menu(for: key) ?? NSMenu() }
+ if key == "counts" { tile.clickHandler = { [weak self] in self?.openTicketPopup() } }
return tile
}
let open = NSButton(title: "Open Fleet ↗", target: self, action: #selector(openViewer))
@@ -208,6 +214,35 @@ private final class BarController: NSObject, NSApplicationDelegate {
@objc private func openViewer() { NSWorkspace.shared.open(viewerURL) }
+ private func openTicketPopup() {
+ if let ticketPopup { ticketPopup.makeKeyAndOrderFront(nil); NSApp.activate(ignoringOtherApps: true); return }
+ let size = NSSize(width: 1480, height: 860)
+ let popup = NSPanel(contentRect: NSRect(origin: .zero, size: size), styleMask: [.titled, .closable, .resizable, .miniaturizable], backing: .buffered, defer: false)
+ popup.title = "Fleet Ticket Control · All / Open / Blocked / Doing"
+ popup.level = .floating
+ popup.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
+ popup.minSize = NSSize(width: 900, height: 560)
+ popup.isReleasedWhenClosed = false
+ let web = WKWebView(frame: popup.contentView?.bounds ?? .zero)
+ web.navigationDelegate = self
+ web.autoresizingMask = [.width, .height]
+ popup.contentView = web
+ var request = URLRequest(url: viewerURL)
+ let token = Data("admin:DW2024!".utf8).base64EncodedString()
+ request.setValue("Basic \(token)", forHTTPHeaderField: "Authorization")
+ web.load(request)
+ popup.center()
+ ticketPopup = popup
+ popup.makeKeyAndOrderFront(nil)
+ NSApp.activate(ignoringOtherApps: true)
+ }
+
+ func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
+ if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {
+ completionHandler(.useCredential, URLCredential(user: "admin", password: "DW2024!", persistence: .forSession))
+ } else { completionHandler(.performDefaultHandling, nil) }
+ }
+
private func refresh() {
DispatchQueue.global(qos: .utility).async { [weak self] in
guard let self else { return }
diff --git a/desktop-bar/build.sh b/desktop-bar/build.sh
index 82a3ec02..72fc766c 100755
--- a/desktop-bar/build.sh
+++ b/desktop-bar/build.sh
@@ -2,5 +2,5 @@
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
mkdir -p "$ROOT/bin"
-xcrun swiftc "$ROOT/TicketBar.swift" -o "$ROOT/bin/ticket-desktop-bar" -framework AppKit
+xcrun swiftc "$ROOT/TicketBar.swift" -o "$ROOT/bin/ticket-desktop-bar" -framework AppKit -framework WebKit
echo "$ROOT/bin/ticket-desktop-bar"
← d03775e4 Restore visible desktop bar labels
·
back to Ticket System
·
Add draggable ticket status tabs 29ce97b1 →