Files
openclaw/apps/macos/Sources/OpenClaw/StatusMenuSummaryCards.swift
Peter Steinberger 64f30c70d7 refactor(macos): automation summaries as hosted cards with clickable job rows (#130502)
* test(macos): add debug menu fixtures and submenu-open capture hooks

* feat(macos): render automation summaries as hosted cards

The Automations/Usage/Devices rows were native items cramming count and
next-run into a fitted title that middle-ellipsized into garbage
('5 …s'). They are now hosted summary cards - title left, compact detail
right, own chevron - which escape the native title budget and pick up
the selection highlight. Automation submenu jobs become hosted rows
(name left, next run right, full name on the accessibility label) that
open the Automations page on click instead of dead disabled text.

* style(macos): swiftformat pass on menu fixture hooks

* test(macos): capture hooks accept CLI arguments

launchctl setenv races open(1), which made screenshot launches flaky;
--debug-open-menu / --debug-menu-fixtures / --debug-probe-rightclick now
work as arguments. The arrow-key submenu navigation never fired inside
the tracking loop and is removed.
2026-08-26 17:11:35 -07:00

92 lines
3.0 KiB
Swift

import Foundation
import SwiftUI
/// Root-menu summary row (Automations, Usage, Devices): title left, compact
/// detail right, own chevron. Hosted cards escape the native title budget,
/// so long details truncate instead of stretching or ellipsizing the title.
@MainActor
struct StatusSummaryCard: View {
let symbolName: String
let title: String
let detail: String?
@Environment(\.menuItemHighlighted) private var isHighlighted
private var palette: MenuItemHighlightColors.Palette {
MenuItemHighlightColors.palette(self.isHighlighted)
}
var body: some View {
HStack(spacing: 10) {
Image(systemName: self.symbolName)
.foregroundStyle(self.palette.secondary)
.frame(width: 16, height: 16)
Text(self.title)
.font(.callout)
.foregroundStyle(self.palette.primary)
.lineLimit(1)
.layoutPriority(1)
Spacer(minLength: 8)
if let detail = self.detail {
Text(detail)
.font(.caption)
.foregroundStyle(self.palette.secondary)
.lineLimit(1)
.truncationMode(.tail)
}
Image(systemName: "chevron.right")
.font(.caption.weight(.semibold))
.foregroundStyle(self.palette.secondary)
}
.padding(.horizontal, 14)
.padding(.vertical, 8)
.frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine)
.accessibilityLabel(self.detail.map { "\(self.title), \($0)" } ?? self.title)
}
}
/// Automations submenu row: job name left, next run right. The full name
/// stays on the accessibility label; middle truncation keeps the suffix.
@MainActor
struct StatusJobRow: View {
let name: String
let nextRun: String?
@Environment(\.menuItemHighlighted) private var isHighlighted
private var palette: MenuItemHighlightColors.Palette {
MenuItemHighlightColors.palette(self.isHighlighted)
}
var body: some View {
HStack(spacing: 10) {
Image(systemName: "clock")
.foregroundStyle(self.palette.secondary)
.frame(width: 16, height: 16)
Text(self.name)
.font(.callout)
.foregroundStyle(self.palette.primary)
.lineLimit(1)
.truncationMode(.middle)
Spacer(minLength: 8)
if let nextRun = self.nextRun {
Text(nextRun)
.font(.caption.monospacedDigit())
.foregroundStyle(self.palette.secondary)
.lineLimit(1)
}
}
.padding(.horizontal, 14)
.padding(.vertical, 7)
.frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine)
.accessibilityLabel(self.nextRun.map { "\(self.name), \($0)" } ?? self.name)
}
}