Skip to content

feat(container-indicator): add Apple container status panel plugin - #483

Merged
ggbond268 merged 2 commits into
mactools-app:mainfrom
AndrewDi:feat/container-indicator-plugin
Oct 15, 2026
Merged

ggbond268 merged 2 commits into
mactools-app:mainfrom
AndrewDi:feat/container-indicator-plugin

Conversation

@AndrewDi

@AndrewDi AndrewDi commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Problem and result

People using Apple's container CLI had to switch to Terminal to check the service, list containers and virtual machines, or start, stop, and restart them. The new Apple Container Status plugin keeps that in one menu bar panel card: service-level start/stop, and per-row status, image, CPU, memory, and storage with start, stop, and restart controls. When the service is not running the card reports 服务未启动 instead of an empty list.

Changes

  • New Plugins/ContainerIndicator/ plugin: container-indicator, panel capability widget only (no settings page, no actions), minHostVersion 2.0.1, pluginKitVersion 7.
  • Card view lists containers and machines directly — no detail chevron. Header line 1 is icon + title, line 2 is status plus the service start/stop buttons, enabled from the parsed service state and hidden when the CLI is missing.
  • Data comes from container CLI probes: service status, list for container CPU/memory, image list --format json for image storage, machine list for machine CPU/memory/disk, and machine inspect for the machine image. Failures keep the last known snapshot and render as an explicit unavailable state.
  • Restart runs stop → start through perform(sequences:), stopping at the first failure.
  • Row copy keeps all three lines on one shared size so hierarchy comes from primary/secondary color. Image references shorten to their last path segment only when the column is too narrow, with the full reference on hover help.
  • Localization: 21 resource keys across the 12 supported locales, display name and search synonyms in plugin.json (Apple Container状态, old 容器状态 kept as a synonym).
  • Docs: both READMEs' monitoring row now mentions managing Apple containers and virtual machines; changes/unreleased/container-indicator-plugin.md added; website plugin data regenerated.

Verification

  • make test TEST_FILTER=ContainerIndicatorPluginTests — 15/15 pass, covering command probe parsing, restart sequence, image/storage resolution, byte formatting, image label shortening, and card-only panel scope.
  • make script-tests — 229 checks OK (includes minimum-host inventory and changelog rules).
  • make validate-changelog — OK.
  • Localization audit — 76 catalogs / 6187 keys / 6822 references consistent.
  • Manual check in the running Debug app against live container v1.5.0 data (2 containers, 2 machines).

UI evidence

New panel card surface. Verified manually in the running Debug build in light and dark appearance, including service start/stop, container and machine rows, and hover help on image names. Screenshots of the open panel popover follow in a comment — the card is only visible while the menu bar panel is open.

Checklist

  • Core behavior and relevant risks are verified using existing tests, focused additions, or manual checks; results or limitations are recorded.
  • UI evidence is attached where applicable; shared components, themes, localization, and accessibility are respected. (Manual light/dark verification done; screenshots to follow in a comment.)
  • User-facing documentation and changelog fragments are updated when required.
  • Plugin metadata, runtime contracts, action policies, and minimum-host compatibility agree.
  • Licensing and third-party notices are complete; no credentials, local configuration, or unrelated generated files are included.

Adds the Container Indicator plugin: a panel-only widget card that lists
Apple container service containers and virtual machines with status,
image, CPU, memory, and storage details plus start, stop, and restart
controls, alongside service-level start and stop buttons. The empty state
reports an unstarted service instead of an empty list.

Container rows read CPU and memory from list and storage from image list;
machine rows read CPU, memory, and disk from machine list and their image
from machine inspect. An image reference shortens to its last path segment
only when the column is too narrow, and hover help still reveals the full
path. All three row lines share one text size, so hierarchy comes from
primary and secondary colors.

Localization covers the 12 supported locales, focused plugin tests cover
the command probe, lifecycle sequences, formatting, and card-only panel
scope, and both READMEs' monitoring rows and the generated website plugin
data include the new plugin.
@AndrewDi

Copy link
Copy Markdown
Contributor Author

@ggbond268 PTAL

@ggbond268

Copy link
Copy Markdown
Collaborator

Thanks for adding this! I reviewed 11361866 and confirmed a few cases worth fixing before merging:

  1. Failed queries look like empty lists. When a container or machine list command fails, the previous entries are cleared and the error is discarded. Please keep the last successful data, mark it as unavailable or stale, and show the failure instead of “None.”

  2. Refreshes can overlap and publish outdated results. I reproduced an older “running” result overwriting a newer “stopped” state. Cancelled checks also continued launching further queries. Please track the refresh task, prevent overlap, and discard outdated results, including checks started before a start/stop operation. When the panel hides or the plugin deactivates, cancellation should also reach the running status command.

  3. Operation errors are not visible in the card. Start/stop failures are saved in lastErrorMessage, but the widget never renders it, and the host does not display it automatically. Please show this message in the card and keep it visible after the follow-up refresh.

The existing 15 tests and 229 script checks pass. I verified the refresh issues with controlled command responses and the missing error message with light/dark rendering. Focused coverage for failed reads and refresh ordering/cancellation would help protect these fixes.

Happy to take another look after the update. Thanks!

…reshes

Addresses the three review findings on the panel card.

Failed container and machine list reads no longer look like empty lists.
Each list now starts from the previous snapshot's entries, and a failed
read records why instead of discarding it. The card keeps showing the
last successful entries under a warning line naming the reason, and only
falls back to the empty message when the list is genuinely empty. A failed
image list leaves the storage sizes already on screen rather than clearing
them.

Refreshes no longer overlap or publish out of order. One refresh task is
tracked per controller and carries a generation number; a newer request
cancels the older one and every publish checks that it is still current, so
a stale "running" result cannot overwrite a newer "stopped" state. Starting
or stopping a service invalidates any check that began before the operation
for the same reason.

Cancellation now reaches the CLI instead of only dropping its result. The
command runner wraps the child process in a task cancellation handler, so
hiding the panel or deactivating the plugin terminates the running
`container` process group and fails the pending call with CancellationError.

Operation and check failures are rendered in the card. The host does not
display `lastErrorMessage`, so the widget now shows it directly and keeps it
visible across the follow-up refresh.

Three focused tests cover a failed list read keeping its previous entries,
a superseded refresh discarding its results, and hiding the panel cancelling
the running status command. Localization adds the three read-failure keys
across all 12 locales.
@AndrewDi

Copy link
Copy Markdown
Contributor Author

Thanks for the careful review — all three are fixed in 1d63b6a2.

1. Failed queries looked like empty lists

Each list now starts from the previous snapshot's entries instead of [], and a failed read records why in ContainerSnapshot.containersFailure / machinesFailure. The card renders that under the section header as a warning line, and only falls back to the empty message when the list is genuinely empty — so a failed read is never presented as 暂无.

A failed image list likewise leaves the storage sizes already on screen instead of clearing them, and a failed system status keeps the last known entries with the check error above them. Only an authoritative stopped/unregistered status still clears the lists, since that really means "nothing is running".

2. Refreshes could overlap and publish outdated results

ContainerIndicatorController now tracks a single refreshTask plus a refreshGeneration. refresh() cancels the previous task, and every publish goes through publish(_:generation:), which drops anything that is no longer current — so an older "running" result can no longer overwrite a newer "stopped" one.

perform(sequences:) invalidates the in-flight check before running, so a check started before a start/stop can never win. stopMonitoring() (called on hide and on deactivate) also cancels it, and cancellation now reaches the CLI: ContainerCommandRunner.run wraps the child in withTaskCancellationHandler, terminating the process group (SIGTERM, then SIGKILL after a deadline) and failing the call with CancellationError rather than only discarding its result.

3. Operation errors were invisible in the card

The card renders state.lastErrorMessage directly under the header as a critical-styled line. It survives the follow-up refresh, because operationErrorMessage is only cleared when the next operation starts.

Coverage

Three focused tests were added (18/18 total):

  • testFailedListReadKeepsPreviousEntriesAndReportsWhy
  • testNewerRefreshDiscardsResultsFromAnOlderOne
  • testHidingThePanelCancelsTheRunningStatusCommand

The probe gained a hold/release gate so these interleave deterministically instead of depending on timing — the superseded refresh is held mid-command while a newer one publishes, then released to prove its result is discarded.

make script-tests passes (229 checks; localization audit 76 catalogs, 6190 keys, 6825 references), and make build is clean.

@ggbond268
ggbond268 merged commit 07b1de2 into mactools-app:main Oct 15, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants