fix(doctor): copy button per path row, readable chain count, cased source kinds
Some checks are pending
Security / Security check (push) Waiting to run

- every daemon file row gets a copy-to-clipboard action next to
  view/open so terminal users can grab the raw path
- audit stat subtitle says 'n of m events verified' instead of the
  cryptic 'n/m chain'
- capability source-kind breakdown renders display names
  (Bundle/MCP/System) instead of raw lowercase wire tokens

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-18 00:04:05 +02:00
parent 8d5d1d74bb
commit b52590c7d3

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../data/error_presentation.dart'; import '../data/error_presentation.dart';
import '../data/chain_log.dart'; import '../data/chain_log.dart';
@ -436,6 +437,19 @@ class _PathRow extends StatelessWidget {
), ),
), ),
const SizedBox(width: ChainSpace.sm), const SizedBox(width: ChainSpace.sm),
IconButton(
icon: const Icon(Icons.copy_outlined, size: 14),
tooltip: l.buttonCopy,
visualDensity: VisualDensity.compact,
onPressed: () async {
await Clipboard.setData(ClipboardData(text: path));
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(l.aboutCopiedToast)),
);
},
),
const SizedBox(width: ChainSpace.xs),
if (isLog || isConfig) ...[ if (isLog || isConfig) ...[
OutlinedButton.icon( OutlinedButton.icon(
onPressed: () => isConfig onPressed: () => isConfig
@ -715,12 +729,31 @@ class _DaemonActionsCardState extends State<_DaemonActionsCard> {
} }
/// Format the per-source-kind capability count map as a /// Format the per-source-kind capability count map as a
/// human-readable line like "6 bundle · 3 mcp · 1 system". /// human-readable line like "6 Bundle · 3 MCP · 1 System".
/// Sorted alphabetically for deterministic rendering. /// Sorted alphabetically for deterministic rendering. The hub
/// reports lowercase wire identifiers; render them as display
/// names so no raw English tokens leak into the localized UI.
String _sourceBreakdown(Map<String, int> bySource) { String _sourceBreakdown(Map<String, int> bySource) {
final entries = bySource.entries.toList() final entries = bySource.entries.toList()
..sort((a, b) => a.key.compareTo(b.key)); ..sort((a, b) => a.key.compareTo(b.key));
return entries.map((e) => '${e.value} ${e.key}').join(' · '); return entries
.map((e) => '${e.value} ${_sourceKindLabel(e.key)}')
.join(' · ');
}
String _sourceKindLabel(String kind) {
switch (kind) {
case 'mcp':
return 'MCP';
case 'bundle':
return 'Bundle';
case 'system':
return 'System';
default:
return kind.isEmpty
? kind
: kind[0].toUpperCase() + kind.substring(1);
}
} }
class _ModulesPanel extends StatelessWidget { class _ModulesPanel extends StatelessWidget {