prole/prole-app/Sources/AppStatusView.swift
chrisfu 0c69f90e71 ProleStatus: app-window default, light splash restored, status bar controls; endpoint config via properties; build + docs
- Default to Application Window mode with full app menu; status bar overlay remains available
- Restore clickable startup Splash Tip (5s) with animated GIF and live counter; use light theme (Aqua)
- Status bar item: monospaced "P" icon; left click shows overlay (status mode) or main window (app mode)
- Overlay: add Maximize button (□) to toggle back to main window; keep click‑through elsewhere
- Global hotkey Cmd+Opt+Shift+P toggles modes; Prole menu item mirrors the same toggle and updates title dynamically
- Application menu (Prole): Show Status Bar / Show Main Window (contextual), Refresh Now, Quit
- Application Window layout: non‑scrolling, vertical 1‑line rows (svc, k3s aggregate, local) with top‑right timestamp; bottom‑left controls (⟳ Refresh, _ Minimize)
- Parameterize endpoints via `prole.properties` (bundled + user override). Replace legacy raspberry with retropie defaults
- ServiceChecker + UI read endpoints from Config loader; tooltips/labels reflect configured hosts/ports
- Build script: generate `Info.plist` with `LSUIElement=false`; bundle resources (`prole-type.gif`, `prole.properties`); ad‑hoc codesign. Universal build supported
- Documentation: rewrite README with technical build/run/config details and operational posture

Files:
- proleStatus/Sources/: AppDelegate.swift, OverlayWindow.swift, StatusView.swift, StatusItemController.swift,
  SplashTipWindowController.swift, MainWindowController.swift, AppStatusView.swift, ServiceChecker.swift, Config.swift
- proleStatus/build.sh
- proleStatus/prole.properties
- proleStatus/README.md

Notes:
- Build verified via `./build.sh build` (arm64). App starts in App Window mode with light theme; menu + hotkey + overlay toggle operate as intended
2025-12-02 19:42:54 -08:00

79 lines
3.1 KiB
Swift

// Timestamp
let now = Date()
timestampLabel.stringValue = timeFormatter.string(from: now)
// SVC
lightSvc.state = c.svcReachable ? .green : .red
labelSvc.stringValue = "svc: \(cfg.svcHost):\(cfg.svcPort) \(c.svcReachable ? "" : "") \(c.svcLatency >= 0 ? "(\(c.svcLatency) ms)" : "")"
labelSvc.toolTip = c.tooltipForSvc()
// K3s aggregate: retropie + pi
let aggCount = (c.raspberryReachable ? 1 : 0) + (c.piReachable ? 1 : 0)
lightK3s.state = aggCount == 2 ? .green : (aggCount == 1 ? .yellow : .red)
labelK3s.stringValue = "k3s: \(cfg.retropieHost) \(c.raspberryReachable ? "" : ""), \(cfg.piHost) \(c.piReachable ? "" : "")"
labelK3s.toolTip = c.tooltipForAggregateK3s()
// Local
lightLocal.state = c.localReachable ? .green : .red
labelLocal.stringValue = "local: \(cfg.localHost):\(cfg.localPort) \(c.localReachable ? "" : "") \(c.localLatency >= 0 ? "(\(c.localLatency) ms)" : "")"
labelLocal.toolTip = c.tooltipForLocal()
// Port forwards
if let m = pfManager {
let running = m.runningCount
let total = m.details.count
lightPF.state = m.allRunning ? .green : .red
labelPF.stringValue = total > 0 ? "kubectl port-forwards: \(running)/\(total) running" : "kubectl port-forwards: none configured"
labelPF.toolTip = "Managed processes: \(running)/\(total) running"
renderPFDetails(m)
} else {
lightPF.state = .red
labelPF.stringValue = "kubectl port-forwards: disabled"
labelPF.toolTip = "To enable, set pf.enabled=true in prole.properties"
clearPFDetails()
}
needsLayout = true
}
// MARK: - PF Details rendering
private func clearPFDetails() {
pfDetailsStack.arrangedSubviews.forEach { v in
pfDetailsStack.removeArrangedSubview(v)
v.removeFromSuperview()
}
}
private func renderPFDetails(_ manager: PortForwardManager) {
clearPFDetails()
let details = manager.details
guard !details.isEmpty else { return }
for d in details {
let dot = TrafficLight()
dot.state = d.running ? .green : .red
let pidText: String
if let pid = d.pid, pid > 0 {
pidText = "PID \(pid)"
} else {
pidText = "PID -"
}
let label = NSTextField(labelWithString: "\(pidText)\(d.running ? "running" : "stopped")\(d.command)")
label.font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .small))
label.textColor = .secondaryLabelColor
label.lineBreakMode = .byTruncatingMiddle
let row = NSStackView(views: [dot, label])
row.orientation = .horizontal
row.alignment = .centerY
row.spacing = 8
row.translatesAutoresizingMaskIntoConstraints = false
pfDetailsStack.addArrangedSubview(row)
}
}
}