feat(doctor): per-source-kind capability breakdown line
Some checks failed
Security / Security check (push) Failing after 2s

Studio's Doctor page _ModulesPanel now shows the source
breakdown directly under the module/capability count:

  Modules: 6 loaded, 10 capabilities
  6 bundle · 3 mcp · 1 system

Reads `CapabilityEntry.sourceKind` (Phase D wire field).
Pre-0.12 hubs return empty sourceKind; the breakdown panel
falls back to grouping by the legacy `kind` string so older
deployments still see a useful count.

Two data-layer additions:
  - `DoctorSnapshot.capabilitiesBySource: Map<String, int>`
  - `_sourceBreakdown(Map<String, int>) -> String` renders
    the line in deterministic alphabetical order.

dart analyze + flutter test green (11 integration tests).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-28 14:41:15 +02:00
parent ceed8aab02
commit 3237c4415a
2 changed files with 45 additions and 6 deletions

View file

@ -656,6 +656,15 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
}
}
/// Format the per-source-kind capability count map as a
/// human-readable line like "6 bundle · 3 mcp · 1 system".
/// Sorted alphabetically for deterministic rendering.
String _sourceBreakdown(Map<String, int> bySource) {
final entries = bySource.entries.toList()
..sort((a, b) => a.key.compareTo(b.key));
return entries.map((e) => '${e.value} ${e.key}').join(' · ');
}
class _ModulesPanel extends StatelessWidget {
final DoctorSnapshot snapshot;
@ -677,14 +686,29 @@ class _ModulesPanel extends StatelessWidget {
color: theme.colorScheme.onSurfaceVariant,
),
const SizedBox(width: FaiSpace.sm),
Text(
l.doctorModulesPanelSummary(
snapshot.moduleCount,
snapshot.capabilityCount,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
l.doctorModulesPanelSummary(
snapshot.moduleCount,
snapshot.capabilityCount,
),
style: theme.textTheme.bodyMedium,
),
if (snapshot.capabilitiesBySource.isNotEmpty) ...[
const SizedBox(height: 2),
Text(
_sourceBreakdown(snapshot.capabilitiesBySource),
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
],
],
),
style: theme.textTheme.bodyMedium,
),
const Spacer(),
FaiPill(
label: snapshot.moduleCount > 0
? l.doctorPillLoaded