[object Object]

← back to Ticket System

desktop-bar: add screen-1 PASTE beacon panel that mirrors any orange tab dot

e23cf51b7edaecd906e84f31573ddd2240f3aace · 2026-09-13 23:35:30 -0700 · Steve Abrams

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

Files touched

Diff

commit e23cf51b7edaecd906e84f31573ddd2240f3aace
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sun Sep 13 23:35:30 2026 -0700

    desktop-bar: add screen-1 PASTE beacon panel that mirrors any orange tab dot
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01Q3N4rPMPvxXqQPbpQfHorw
---
 desktop-bar/TicketBar.swift | 113 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 111 insertions(+), 2 deletions(-)

diff --git a/desktop-bar/TicketBar.swift b/desktop-bar/TicketBar.swift
index e73da74a..8871ca6f 100644
--- a/desktop-bar/TicketBar.swift
+++ b/desktop-bar/TicketBar.swift
@@ -68,6 +68,13 @@ private final class BarTile: NSButton {
 
 private final class BarController: NSObject, NSApplicationDelegate, NSWindowDelegate, WKNavigationDelegate {
   private var panel: NSPanel!
+  // Separate, additive "PASTE beacon" pinned to screen 1 (TK-11693). Shows whenever
+  // any terminal anywhere raises an ORANGE (paste-waiting) dot, so a paste on a
+  // non-primary monitor still surfaces on screen 1. Never touches the main bar.
+  private var beaconPanel: NSPanel!
+  private let beaconLabel = NSTextField(labelWithString: "")
+  private var beaconVisible = false
+  private var beaconCount = -1
   private let openLabel = NSTextField(labelWithString: "OPEN …")
   private let blockedLabel = NSTextField(labelWithString: "BLOCKED …")
   private let doingLabel = NSTextField(labelWithString: "DOING …")
@@ -112,9 +119,11 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
   func applicationDidFinishLaunching(_ notification: Notification) {
     NSApp.setActivationPolicy(.accessory)
     buildPanel()
+    buildBeacon()
     refresh()
     refreshSystem()
-    timer = Timer.scheduledTimer(withTimeInterval: 4, repeats: true) { [weak self] _ in self?.refresh() }
+    refreshBeacon()
+    timer = Timer.scheduledTimer(withTimeInterval: 4, repeats: true) { [weak self] _ in self?.refresh(); self?.refreshBeacon() }
     // TK-11519: system health was only sampled at launch, so CPU went stale immediately.
     // 15s is deliberate — every probe below is instant (sysctl/pgrep) or capped at 2s.
     systemTimer = Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { [weak self] _ in self?.refreshSystem() }
@@ -576,7 +585,107 @@ private final class BarController: NSObject, NSApplicationDelegate, NSWindowDele
     return view
   }
 
-  @objc private func reposition() { applyPosition() }
+  @objc private func reposition() { applyPosition(); refreshBeacon() }
+
+  // ── PASTE beacon (screen 1) ─────────────────────────────────────────────────
+  // A dedicated, borderless, non-activating panel PINNED to screen 1 that appears
+  // whenever any terminal (across ALL monitors) is flagged with an ORANGE paste dot.
+  // Purely additive — it never modifies the main bar's panel, dock, or tiles.
+  private struct OrangeDot { let tty: String; let label: String }
+
+  // Scan ~/.claude/tab-dots and ~/.codex/tab-dots for *.dot files whose content
+  // starts with the engine's orange emoji 🟠. Strips the leading emoji (and an
+  // immediate secondary marker like 🛑) for a clean label. Missing dirs / unreadable
+  // files are skipped — never crashes.
+  private func scanOrangeDots() -> [OrangeDot] {
+    let home = FileManager.default.homeDirectoryForCurrentUser
+    let dirs = [home.appendingPathComponent(".claude/tab-dots"),
+                home.appendingPathComponent(".codex/tab-dots")]
+    let fm = FileManager.default
+    var found: [OrangeDot] = []
+    for dir in dirs {
+      guard let items = try? fm.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil) else { continue }
+      for url in items where url.pathExtension == "dot" {
+        guard let raw = try? String(contentsOf: url, encoding: .utf8) else { continue }
+        let content = raw.trimmingCharacters(in: .whitespacesAndNewlines)
+        guard content.hasPrefix("🟠") else { continue }
+        var label = String(content.dropFirst()).trimmingCharacters(in: .whitespacesAndNewlines)
+        if label.hasPrefix("🛑") { label = String(label.dropFirst()).trimmingCharacters(in: .whitespacesAndNewlines) }
+        let tty = url.deletingPathExtension().lastPathComponent
+        found.append(OrangeDot(tty: tty, label: label.isEmpty ? tty : label))
+      }
+    }
+    return found.sorted { $0.tty < $1.tty }
+  }
+
+  private func buildBeacon() {
+    let p = NSPanel(contentRect: NSRect(x: 0, y: 0, width: 560, height: 40),
+                    styleMask: [.borderless, .nonactivatingPanel], backing: .buffered, defer: false)
+    p.level = .statusBar
+    p.collectionBehavior = [.canJoinAllSpaces, .stationary, .fullScreenAuxiliary]
+    p.isOpaque = false
+    p.backgroundColor = .clear
+    p.hasShadow = true
+    p.hidesOnDeactivate = false
+    p.ignoresMouseEvents = true            // click-through for now
+    p.isFloatingPanel = true
+    let container = NSView()
+    container.wantsLayer = true
+    container.layer?.cornerRadius = 8
+    // Engine's exact orange: RGB 255,140,0.
+    container.layer?.backgroundColor = NSColor(calibratedRed: 255/255.0, green: 140/255.0, blue: 0/255.0, alpha: 1).cgColor
+    beaconLabel.font = .systemFont(ofSize: 13, weight: .heavy)
+    beaconLabel.textColor = .black
+    beaconLabel.alignment = .center
+    beaconLabel.maximumNumberOfLines = 0
+    beaconLabel.lineBreakMode = .byTruncatingTail
+    beaconLabel.translatesAutoresizingMaskIntoConstraints = false
+    container.addSubview(beaconLabel)
+    NSLayoutConstraint.activate([
+      beaconLabel.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 14),
+      beaconLabel.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -14),
+      beaconLabel.topAnchor.constraint(equalTo: container.topAnchor, constant: 8),
+      beaconLabel.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -8)
+    ])
+    p.contentView = container
+    beaconPanel = p
+    p.orderOut(nil)
+  }
+
+  private func refreshBeacon() {
+    guard beaconPanel != nil else { return }
+    let dots = scanOrangeDots()
+    if dots.isEmpty {
+      if beaconVisible { NSLog("TicketBar beacon HIDE (0 orange dots)"); beaconVisible = false }
+      beaconPanel.orderOut(nil)
+      beaconCount = 0
+      return
+    }
+    let cap = 5
+    var lines = dots.prefix(cap).map { "🟠 PASTE: \($0.label)  [\($0.tty)]" }
+    if dots.count > cap { lines.append("…and \(dots.count - cap) more") }
+    beaconLabel.stringValue = lines.joined(separator: "\n")
+    positionBeacon(lineCount: lines.count)
+    beaconPanel.orderFrontRegardless()
+    if !beaconVisible { NSLog("TicketBar beacon SHOW (%d orange dot(s))", dots.count); beaconVisible = true }
+    // Also log any change in count so add/remove of an orange dot is observable
+    // even while the beacon is already showing (transition-only logs otherwise miss it).
+    if dots.count != beaconCount { NSLog("TicketBar beacon COUNT %d", dots.count); beaconCount = dots.count }
+  }
+
+  // Pin to screen 1 = NSScreen.screens.first (the (0,0)/menu-bar screen), top-center
+  // just under the menu bar. Zero screens → no-op (never crash).
+  private func positionBeacon(lineCount: Int) {
+    guard let screen = NSScreen.screens.first else { return }
+    let full = screen.frame
+    let visible = screen.visibleFrame
+    let width: CGFloat = 620
+    let lineH: CGFloat = 20
+    let height = CGFloat(max(1, lineCount)) * lineH + 16
+    let x = full.midX - width / 2
+    let y = visible.maxY - height - 6      // just under the menu bar
+    beaconPanel.setFrame(NSRect(x: x, y: y, width: width, height: height), display: true)
+  }
 
   @objc private func openViewer() { NSWorkspace.shared.open(viewerURL) }
 

← ff5f20c0 auto-data-snapshot: 2026-09-13T23:18:39 (1 data files) — con  ·  back to Ticket System  ·  beacon: filter dead-tty ghost orange dots so screen-1 PASTE ae3c0a34 →