From b52590c7d3d400daba1f0ac8cf41141a7118cf2a Mon Sep 17 00:00:00 2001 From: flemming-it Date: Sat, 18 Jul 2026 00:04:05 +0200 Subject: [PATCH] fix(doctor): copy button per path row, readable chain count, cased source kinds - 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 --- lib/pages/doctor.dart | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/pages/doctor.dart b/lib/pages/doctor.dart index 42fda7f..d91bbd9 100644 --- a/lib/pages/doctor.dart +++ b/lib/pages/doctor.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import '../data/error_presentation.dart'; import '../data/chain_log.dart'; @@ -436,6 +437,19 @@ class _PathRow extends StatelessWidget { ), ), 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) ...[ OutlinedButton.icon( onPressed: () => isConfig @@ -715,12 +729,31 @@ 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. +/// human-readable line like "6 Bundle · 3 MCP · 1 System". +/// 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 bySource) { final entries = bySource.entries.toList() ..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 {