[object Object]

← back to Ticket System

Ticket desktop bar: restore left-click dropdowns, center tile group, add Move Bar Up/Down vertical placement

5e92781291c9cf3193cfef36f1d86746ed711f71 · 2026-09-03 09:47:26 -0700 · Steve Abrams

- Remove per-tile clickHandler that short-circuited showDropdown() so the big web
  popup opened on every left-click and the dropdown menu was dead code (fixes 'no
  dropdowns'). Left-click now opens the tile dropdown; the control window moved to
  an 'Open Control Window' menu item + the existing 'Open Fleet' button.
- Center the tile group in the full-width bar via a container + required centerX
  (was left-anchored NSStackView with a left edge inset) (fixes 'not centered').
- Add 'Move Bar Up' / 'Move Bar Down' menu controls persisting ticketBarYOffset,
  honored in reposition() with on-screen clamping (fixes 'move up and down
  vertically').

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015XNcHvXXepEQar7Qsb7GGy

Files touched

Diff

commit 5e92781291c9cf3193cfef36f1d86746ed711f71
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 3 09:47:26 2026 -0700

    Ticket desktop bar: restore left-click dropdowns, center tile group, add Move Bar Up/Down vertical placement
    
    - Remove per-tile clickHandler that short-circuited showDropdown() so the big web
      popup opened on every left-click and the dropdown menu was dead code (fixes 'no
      dropdowns'). Left-click now opens the tile dropdown; the control window moved to
      an 'Open Control Window' menu item + the existing 'Open Fleet' button.
    - Center the tile group in the full-width bar via a container + required centerX
      (was left-anchored NSStackView with a left edge inset) (fixes 'not centered').
    - Add 'Move Bar Up' / 'Move Bar Down' menu controls persisting ticketBarYOffset,
      honored in reposition() with on-screen clamping (fixes 'move up and down
      vertically').
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_015XNcHvXXepEQar7Qsb7GGy
---
 desktop-bar/TicketBar.swift | 45 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 40 insertions(+), 5 deletions(-)

diff --git a/desktop-bar/TicketBar.swift b/desktop-bar/TicketBar.swift
index 6827d199..c5b69291 100644
--- a/desktop-bar/TicketBar.swift
+++ b/desktop-bar/TicketBar.swift
@@ -114,7 +114,9 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
       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() }
-      tile.clickHandler = { [weak self] in self?.openTicketPopup() }
+      // No clickHandler: left-click a tile now opens its DROPDOWN menu (details +
+      // layout controls). The web control window is reachable from the menu's
+      // "Open Control Window" item and from the "Open Fleet ↗" button below.
       return tile
     }
     let open = NSButton(title: "Open Fleet ↗", target: self, action: #selector(openViewer))
@@ -124,10 +126,21 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
     stack.orientation = .horizontal
     stack.spacing = 6
     stack.alignment = .centerY
-    stack.edgeInsets = NSEdgeInsets(top: 0, left: 14, bottom: 0, right: 10)
+    stack.distribution = .fill
     for tile in tiles { tile.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) }
     open.setContentHuggingPriority(.required, for: .horizontal)
-    panel.contentView = stack
+
+    // Center the tile group horizontally in the full-width bar (was left-anchored).
+    let container = NSView()
+    stack.translatesAutoresizingMaskIntoConstraints = false
+    container.addSubview(stack)
+    NSLayoutConstraint.activate([
+      stack.centerXAnchor.constraint(equalTo: container.centerXAnchor),
+      stack.centerYAnchor.constraint(equalTo: container.centerYAnchor),
+      stack.leadingAnchor.constraint(greaterThanOrEqualTo: container.leadingAnchor, constant: 14),
+      stack.trailingAnchor.constraint(lessThanOrEqualTo: container.trailingAnchor, constant: -10)
+    ])
+    panel.contentView = container
     installDragMonitor()
     reposition()
     panel.orderFrontRegardless()
@@ -177,6 +190,10 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
 
   private func menu(for key: String) -> NSMenu {
     let menu = NSMenu(title: key)
+    let openItem = NSMenuItem(title: "Open Control Window ↗", action: #selector(openControlFromMenu), keyEquivalent: "")
+    openItem.target = self
+    menu.addItem(openItem)
+    menu.addItem(.separator())
     let details: [String]
     if key == "counts" {
       details = [counts.stringValue.replacingOccurrences(of: "   ", with: "  •  ")]
@@ -191,7 +208,7 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
       menu.addItem(item)
     }
     menu.addItem(.separator())
-    for (title, action) in [("Move Left", #selector(moveLeft(_:))), ("Move Right", #selector(moveRight(_:))), ("Wider", #selector(wider(_:))), ("Narrower", #selector(narrower(_:)))] {
+    for (title, action) in [("Move Left", #selector(moveLeft(_:))), ("Move Right", #selector(moveRight(_:))), ("Move Bar Up", #selector(moveBarUp(_:))), ("Move Bar Down", #selector(moveBarDown(_:))), ("Wider", #selector(wider(_:))), ("Narrower", #selector(narrower(_:)))] {
       let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
       item.target = self
       item.representedObject = key
@@ -200,6 +217,18 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
     return menu
   }
 
+  @objc private func openControlFromMenu() { openTicketPopup() }
+
+  // Vertical bar placement — "ticketBarYOffset" is pixels BELOW the top of the
+  // visible frame (0 = pinned under the menu bar, the original behavior).
+  @objc private func moveBarUp(_ sender: NSMenuItem) { nudgeBar(by: -40) }
+  @objc private func moveBarDown(_ sender: NSMenuItem) { nudgeBar(by: 40) }
+  private func nudgeBar(by delta: CGFloat) {
+    let current = CGFloat(UserDefaults.standard.double(forKey: "ticketBarYOffset"))
+    UserDefaults.standard.set(Double(current + delta), forKey: "ticketBarYOffset")
+    reposition()
+  }
+
   @objc private func moveLeft(_ sender: NSMenuItem) { shift(sender.representedObject as? String, by: -1) }
   @objc private func moveRight(_ sender: NSMenuItem) { shift(sender.representedObject as? String, by: 1) }
   private func shift(_ key: String?, by delta: Int) {
@@ -230,7 +259,13 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
   @objc private func reposition() {
     guard let screen = NSScreen.main ?? NSScreen.screens.first else { return }
     let frame = screen.visibleFrame
-    panel.setFrame(NSRect(x: frame.minX, y: frame.maxY - 38, width: frame.width, height: 38), display: true)
+    let barHeight: CGFloat = 38
+    let requested = CGFloat(UserDefaults.standard.double(forKey: "ticketBarYOffset")) // px below the top
+    let maxOffset = max(0, frame.height - barHeight)
+    let offset = min(max(0, requested), maxOffset)
+    if offset != requested { UserDefaults.standard.set(Double(offset), forKey: "ticketBarYOffset") }
+    let y = frame.maxY - barHeight - offset
+    panel.setFrame(NSRect(x: frame.minX, y: y, width: frame.width, height: barHeight), display: true)
   }
 
   @objc private func openViewer() { NSWorkspace.shared.open(viewerURL) }

← 56550c41 preserve backup canary monitoring evidence  ·  back to Ticket System  ·  preserve gated canary classifier evidence f9ba422f →