You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A tool built without .Status(...) shows no failure anywhere in the tray: restore, start and click errors only reach stderr #29
LastError is the only place the tray reports failures. It holds failed clicks, a failed OnStart, and the restore error re-seeded by #12, and it is shown only through the status line. TrayMenu creates that status line only when status is not null (TrayApp/Menu/TrayMenu.cs:40-45). The tooltip falls back to the display name (TrayApp/Tray/TrayApplication.cs:332).
.Status(...) is optional: it is optional in the README API table, and Constructor_WithNoStatus_LeavesTheStatusLineOut tests that it can be left out. For any tool that leaves it out, every refusal is invisible to a tray user, who usually has no terminal attached. This contradicts the TrayMenu.Activate remarks, which say failures are "shown in the status line for everyone else".
Observed (main a96d122, real tray run; menu and tooltip read over D-Bus)
The test tool had no .Status, an OnStart that throws "device busy", and a toggle setter that throws "no inhibitor available":
nostatus: device busy (stderr only)
--- before click ---
id=1 label='Keep awake' checked=0 enabled=True
id=3 label='Quit nostatus'
nostatus: no inhibitor available (stderr only)
--- after click ---
id=1 label='Keep awake' checked=0 enabled=True <- nothing changed, no reason shown
ToolTip: ['', [], '', '']
Why it matters
The failure-surfacing work from #6 and #12 does nothing for these tools. The user sees a toggle that simply won't tick, and nothing says why.
Suggested fix / acceptance criteria
When LastError is set, show it even if there is no status line. Either of these would do:
Always create a TrayStatusItem, and keep it hidden or empty while there is no error and no status.
Or put "{DisplayName} failed: {LastError}" in the tooltip.
Test: a TrayMenu built without status exposes the message of a failed Run through Items or through the tooltip.
Priority: Medium. For any tool built without .Status(...), a supported configuration, tray users never see start, restore or click failures, so a toggle silently refuses to tick. That is a real usability gap for those tools, but nothing crashes, errors still reach stderr, and tools that set a status are unaffected.
Area / suggested assignment:TrayApp/Menu/TrayMenu.cs:40-45 (the status item, the only consumer of LastError, exists only when status is non-null), :87/:97 (LastError, SeedError), :158-175 (Run records the error); TrayApp/Tray/TrayApplication.cs:332 (tooltip falls back to app.DisplayName).
Notes: The smallest change is to have TrayMenu expose a Tooltip, or an ErrorText => LastError is null ? null : $"{displayName} failed: {LastError}", and have TrayApplication.cs:332 use menu.StatusItem?.Header ?? menu.ErrorText ?? app.DisplayName. A more visible option is to always create the TrayStatusItem with getter () => LastError is null ? status?.Invoke() : ... and hide it plus its separator while the text is null, which needs an IsVisible on TrayMenuItem/NativeMenuItem. Test: new TrayMenu("x", status: null, items) followed by a Run(() => throw ...) exposes "x failed: ..." through Items or the tooltip property, and a successful Run clears it.
Activity
matt-edmondson commentedon Sep 30, 2026
Triage
.Status(...), a supported configuration, tray users never see start, restore or click failures, so a toggle silently refuses to tick. That is a real usability gap for those tools, but nothing crashes, errors still reach stderr, and tools that set a status are unaffected.TrayApp/Menu/TrayMenu.cs:40-45(the status item, the only consumer ofLastError, exists only whenstatusis non-null),:87/:97(LastError,SeedError),:158-175(Runrecords the error);TrayApp/Tray/TrayApplication.cs:332(tooltip falls back toapp.DisplayName).LastError, which is only useful once this is fixed. An OnStop that throws aborts a console run with an unhandled exception (exit 134), and the tray swallows the same error silently #24 is the tray swallowing anOnStoperror silently. A misspelled active icon resource makes the tray vanish (exit 0) the first time the tool turns on; the remembered toggle then makes every later launch exit 1 #28's refresh path also sets the tooltip. No duplicate.TrayMenuexpose aTooltip, or anErrorText => LastError is null ? null : $"{displayName} failed: {LastError}", and haveTrayApplication.cs:332usemenu.StatusItem?.Header ?? menu.ErrorText ?? app.DisplayName. A more visible option is to always create theTrayStatusItemwith getter() => LastError is null ? status?.Invoke() : ...and hide it plus its separator while the text is null, which needs anIsVisibleonTrayMenuItem/NativeMenuItem. Test:new TrayMenu("x", status: null, items)followed by aRun(() => throw ...)exposes"x failed: ..."throughItemsor the tooltip property, and a successfulRunclears it.Generated by Claude Code