From b845f7279046318cab4886e4539f2ef8598cf2cf Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2026 06:48:59 +0000 Subject: [PATCH] Palette: [UX improvement] Add dynamic labels and disabled state tooltips to MenuBar buttons Co-authored-by: acebytes <2820910+acebytes@users.noreply.github.com> --- .jules/palette.md | 3 +++ Sources/Cacheout/Views/MenuBarView.swift | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index f2d658a..15291fa 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -17,3 +17,6 @@ ## 2024-05-25 - Dynamic Labels and Disabled State Tooltips **Learning:** Users can feel confused when a primary button is disabled without explanation or when a long-running action lacks immediate inline text feedback on the button itself. **Action:** In SwiftUI, enhance button accessibility and UX by adding `.help()` tooltips to explain the required state when disabled, and using dynamic labels (e.g., 'Scanning...') to provide immediate feedback during async operations. +## 2024-05-26 - Dynamic Labels and Help Tooltips on MenuBar Actions +**Learning:** Buttons in compact UIs like MenuBars often lack space for inline error messages or status updates. When a primary action (like Scan or Clean) is disabled or processing, users need immediate contextual feedback to understand why. +**Action:** Always provide dynamic labels (e.g., "Scanning...") and use the `.help()` modifier to explain disabled states or ongoing operations, ensuring users aren't left guessing why a button is unresponsive. diff --git a/Sources/Cacheout/Views/MenuBarView.swift b/Sources/Cacheout/Views/MenuBarView.swift index 9084b59..d003f4a 100644 --- a/Sources/Cacheout/Views/MenuBarView.swift +++ b/Sources/Cacheout/Views/MenuBarView.swift @@ -214,11 +214,12 @@ struct MenuBarView: View { Button { Task { await viewModel.scan() } } label: { - Label("Scan", systemImage: "arrow.clockwise") + Label(viewModel.isScanning ? "Scanning..." : "Scan", systemImage: "arrow.clockwise") .font(.caption.weight(.medium)) } .buttonStyle(.bordered) .disabled(viewModel.isScanning) + .help(viewModel.isScanning ? "Scan in progress" : "Scan for caches") if viewModel.isScanning { ProgressView() @@ -241,6 +242,7 @@ struct MenuBarView: View { .buttonStyle(.borderedProminent) .tint(Color(red: 0.85, green: 0.45, blue: 0.1)) // burnt orange — readable white text .disabled(viewModel.totalRecoverable == 0 || viewModel.isCleaning) + .help(viewModel.isCleaning ? "Cleanup in progress" : (viewModel.totalRecoverable == 0 ? "No recoverable caches found" : "Quick clean recoverable items")) // Open main window Button { @@ -282,12 +284,13 @@ struct MenuBarView: View { Button { Task { await viewModel.dockerPrune() } } label: { - Text("Run") + Text(viewModel.isDockerPruning ? "Running..." : "Run") .font(.caption2.weight(.medium)) } .buttonStyle(.bordered) .controlSize(.mini) .disabled(viewModel.isDockerPruning) + .help(viewModel.isDockerPruning ? "Docker prune in progress" : "Run docker system prune") } }