[object Object]

← back to Ticket System

Ticket desktop bar: drag-to-resize the bar edge with a visible grip handle

03487e804c10984a518a88cb085ecaec55556084 · 2026-09-03 11:48:20 -0700 · Steve Abrams

The bar is now resizable by DRAGGING its edge (the menu-only resize wasn't
discoverable — 'won't let me adjust'):
- Left dock: drag the RIGHT edge; Right dock: drag the LEFT edge (width 180–700).
- Top bar: drag the BOTTOM edge (height 28–300).
- A blue grip handle marks the draggable edge; the whole edge is a 12px grab zone.
Edge-drag is checked before slide/reorder so grabbing the edge always resizes.
Verified live via cliclick: left dock 300→481px.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 03487e804c10984a518a88cb085ecaec55556084
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 3 11:48:20 2026 -0700

    Ticket desktop bar: drag-to-resize the bar edge with a visible grip handle
    
    The bar is now resizable by DRAGGING its edge (the menu-only resize wasn't
    discoverable — 'won't let me adjust'):
    - Left dock: drag the RIGHT edge; Right dock: drag the LEFT edge (width 180–700).
    - Top bar: drag the BOTTOM edge (height 28–300).
    - A blue grip handle marks the draggable edge; the whole edge is a 12px grab zone.
    Edge-drag is checked before slide/reorder so grabbing the edge always resizes.
    Verified live via cliclick: left dock 300→481px.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 desktop-bar/TicketBar.swift | 57 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/desktop-bar/TicketBar.swift b/desktop-bar/TicketBar.swift
index 553e7d39..100d0263 100644
--- a/desktop-bar/TicketBar.swift
+++ b/desktop-bar/TicketBar.swift
@@ -99,6 +99,10 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
   private var dragStartPanelY: CGFloat?
   private var barSlid = false
   private var didDrag = false
+  private var resizing = false
+  private var resizeStartMouse = CGPoint.zero
+  private var resizeStartSize: CGFloat = 0
+  private var gripView: NSView!
 
   func applicationDidFinishLaunching(_ notification: Notification) {
     NSApp.setActivationPolicy(.accessory)
@@ -198,6 +202,13 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
       hamburger.centerXAnchor.constraint(equalTo: container.centerXAnchor),
       hamburger.centerYAnchor.constraint(equalTo: container.centerYAnchor)
     ])
+    // Visible resize grip at the draggable edge (positioned per-dock in applyPosition).
+    gripView = NSView(frame: .zero)
+    gripView.wantsLayer = true
+    gripView.layer?.backgroundColor = NSColor(calibratedRed: 0.30, green: 0.60, blue: 1.0, alpha: 0.9).cgColor
+    gripView.layer?.cornerRadius = 2
+    gripView.toolTip = "Drag to resize the bar"
+    container.addSubview(gripView)
     panel.contentView = container
     installDragMonitor()
     applyTheme()
@@ -205,6 +216,27 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
     panel.orderFrontRegardless()
   }
 
+  // The draggable resize edge for the current dock, in window (bottom-left) coords.
+  private func inResizeZone(_ loc: CGPoint) -> Bool {
+    let f = panel.frame
+    switch barPosition {
+    case "left":  return loc.x >= f.width - 12
+    case "right": return loc.x <= 12
+    case "top":   return loc.y <= 9
+    default:      return false
+    }
+  }
+  private func positionGrip() {
+    guard gripView != nil else { return }
+    let f = panel.frame
+    switch barPosition {
+    case "left":  gripView.isHidden = false; gripView.frame = NSRect(x: f.width - 6, y: f.height/2 - 60, width: 5, height: 120)
+    case "right": gripView.isHidden = false; gripView.frame = NSRect(x: 1, y: f.height/2 - 60, width: 5, height: 120)
+    case "top":   gripView.isHidden = false; gripView.frame = NSRect(x: f.width/2 - 60, y: 1, width: 120, height: 4)
+    default:      gripView.isHidden = true
+    }
+  }
+
   private static let positionOrder = ["top", "left", "right", "off"]
   @objc private func cyclePosition() {
     let i = (BarController.positionOrder.firstIndex(of: barPosition) ?? 0) + 1
@@ -243,6 +275,7 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
         stack.removeFromSuperview()
       }
       hamburger.isHidden = false
+      gripView?.isHidden = true
       let w: CGFloat = 46, h: CGFloat = 32
       panel.setFrame(NSRect(x: v.minX + 8, y: v.maxY - h, width: w, height: h), display: true)
       panel.orderFrontRegardless()
@@ -277,6 +310,7 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
       panel.setFrame(NSRect(x: v.minX, y: v.maxY - barHeight - offset, width: v.width, height: barHeight), display: true)
     }
     posButton.title = ["top": "▔", "left": "▏", "right": "▕"][barPosition] ?? "▔"
+    positionGrip()
     panel.orderFrontRegardless()
   }
 
@@ -318,6 +352,12 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
       case .leftMouseDown:
         self.didDrag = false
         self.barSlid = false
+        // Grab the resize edge? (right edge of left dock, left edge of right dock, bottom of top bar)
+        self.resizing = self.inResizeZone(event.locationInWindow)
+        if self.resizing {
+          self.resizeStartMouse = NSEvent.mouseLocation
+          self.resizeStartSize = (self.barPosition == "top") ? self.topBarHeight() : self.vBarWidth()
+        }
         self.draggingKey = self.tiles.first(where: {
           $0.bounds.contains($0.convert(event.locationInWindow, from: nil))
         })?.key
@@ -325,6 +365,22 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
         self.dragStartMouseY = NSEvent.mouseLocation.y   // global screen Y
         self.dragStartPanelY = self.panel.frame.origin.y
       case .leftMouseDragged:
+        // Edge-drag RESIZE takes priority over slide/reorder.
+        if self.resizing {
+          self.didDrag = true
+          let m = NSEvent.mouseLocation
+          switch self.barPosition {
+          case "left":
+            UserDefaults.standard.set(Double(min(700, max(180, self.resizeStartSize + (m.x - self.resizeStartMouse.x)))), forKey: "ticketBarVWidth")
+          case "right":
+            UserDefaults.standard.set(Double(min(700, max(180, self.resizeStartSize + (self.resizeStartMouse.x - m.x)))), forKey: "ticketBarVWidth")
+          case "top":
+            UserDefaults.standard.set(Double(min(300, max(28, self.resizeStartSize + (self.resizeStartMouse.y - m.y)))), forKey: "ticketBarHeight")
+          default: break
+          }
+          self.applyPosition()
+          return event
+        }
         let dy = NSEvent.mouseLocation.y - (self.dragStartMouseY ?? NSEvent.mouseLocation.y)
         let dx = event.locationInWindow.x - (self.dragStartX ?? event.locationInWindow.x)
         // Vertical drag → SLIDE the whole bar up/down (Top mode only). Horizontal drag on a tile → reorder.
@@ -338,6 +394,7 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
         }
       case .leftMouseUp:
         if self.barSlid { self.persistBarOffset() }
+        self.resizing = false
         self.draggingKey = nil
         self.dragStartX = nil
         self.dragStartMouseY = nil

← 737fe045 record TK-11188 bounded monitoring proof  ·  back to Ticket System  ·  desktop-bar: add ◉ terminal-dots dropdown (multi-select + fo 02eb728b →