[object Object]

← back to Ticket System

Add centered ticket control popup

2d7b8714c98c9b5eb7f938dc99d8c912b16e15e1 · 2026-09-02 15:03:15 -0700 · Steve Abrams

Files touched

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 →