feat(studio): de-jargonize, reveal-in-OS, copyable errors, today carousel, AppBar alignment, F∆I window title (v0.34.0)
Sweeping pass against six user reports collected this session. 1. "Capabilities ist nicht deutsch, Chain auch nicht." The DE locale still leaked English vocabulary. Replaced "Capabilities" → "Fähigkeiten" and "Chain" / "Hash-Chain" → "Kette" / "Hash-Kette" everywhere — store search hint, recommended-source body, federated toast, doctor summary chain row, modules panel summary, MCP / n8n hints, the approvals history blurb. Wire-level identifiers (`chain.reset`) stay as code. 2. "Bei Fehlern unten muss man die auch ins clipboard kopieren können." New `FaiErrorBox` widget: selectable monospace block with a small copy-to-clipboard icon button that flips to a checkmark for two seconds after click. Applied to the Doctor update banner output and the Settings channel toast — the two places long stderr / stdout lands. 3. "Öffnen bei Log kann es nicht öffnen. Audit-DB auch nicht. PID auch nicht." Cause: `SystemActions.openInOs` shells out to `open` / `xdg-open` on file paths the OS has no default handler for (SQLite DB, PID file, log without an .ext that binds). New `revealInOs` uses `open -R` on macOS, `explorer /select,` on Windows, and the parent directory via `xdg-open` on Linux. Doctor's path rows carry an `isDirectory` flag that routes through the new `openOrReveal` so files reveal in Finder / Explorer instead of failing silently. 4. "Oben im Store könnte man diesen Redaktionshinweis auch so bauen, dass man mit pfeil nach rechts links auch weitere anzeigen kann." The Today-Hero became a carousel. Curated fallback list grew from one entry to four (public sources, the sandbox-by-default permission story, the hash-chained audit story, the air-gap-ready single-binary pitch). Hero gets prev / next chevrons plus a dot indicator when the current snapshot has more than one slide. Operator-accepted stories stay single — the carousel collapses when there's only one to show. 5. "Ich fände es schöner wenn rechts und links im Store die Abstände konsistent sind, das Reload-Symbol rechts ist zu weit rechts und Store links auch nicht bündig." AppBar now has `titleSpacing: FaiSpace.xl` so the title's left edge sits flush with the body's left padding (24 dp), and the trailing `SizedBox` after the reload icon shrunk so the icon's outer edge meets the right edge of the rightmost grid card. 6. "Oben der Titel zeigt fai_studio an, das sollte F∆I Studio sein." The OS window title was the pubspec-derived "fai_studio". Macos/Linux/Windows runners now hard-code "F∆I Studio" (with the U+2206 triangle escape so the C++ source stays ASCII). macOS bundle name and display name lifted out of the PRODUCT_NAME variable for the same reason. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
22a851b7e2
commit
3b5fb027c3
17 changed files with 454 additions and 119 deletions
|
|
@ -334,13 +334,17 @@ class _DaemonPathsPanel extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
final entries = <(String, String, IconData)>[
|
||||
(l.doctorPathLog, paths.logPath, Icons.description_outlined),
|
||||
(l.doctorPathConfig, paths.configPath, Icons.settings_outlined),
|
||||
(l.doctorPathDb, paths.dbPath, Icons.storage_outlined),
|
||||
(l.doctorPathModules, paths.modulesDir, Icons.extension_outlined),
|
||||
(l.doctorPathFlows, paths.flowsDir, Icons.account_tree_outlined),
|
||||
(l.doctorPathPid, paths.pidPath, Icons.fingerprint),
|
||||
// Per-row layout: (label, path, icon, isDirectory). The
|
||||
// isDirectory bit drives `openOrReveal` so binary files
|
||||
// (the SQLite audit DB, the PID file) reveal in Finder /
|
||||
// Explorer instead of failing the OS "open" handler.
|
||||
final entries = <(String, String, IconData, bool)>[
|
||||
(l.doctorPathLog, paths.logPath, Icons.description_outlined, false),
|
||||
(l.doctorPathConfig, paths.configPath, Icons.settings_outlined, false),
|
||||
(l.doctorPathDb, paths.dbPath, Icons.storage_outlined, false),
|
||||
(l.doctorPathModules, paths.modulesDir, Icons.extension_outlined, true),
|
||||
(l.doctorPathFlows, paths.flowsDir, Icons.account_tree_outlined, true),
|
||||
(l.doctorPathPid, paths.pidPath, Icons.fingerprint, false),
|
||||
].where((e) => e.$2.isNotEmpty).toList();
|
||||
|
||||
if (entries.isEmpty) {
|
||||
|
|
@ -356,7 +360,13 @@ class _DaemonPathsPanel extends StatelessWidget {
|
|||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
for (final e in entries) _PathRow(label: e.$1, path: e.$2, icon: e.$3),
|
||||
for (final e in entries)
|
||||
_PathRow(
|
||||
label: e.$1,
|
||||
path: e.$2,
|
||||
icon: e.$3,
|
||||
isDirectory: e.$4,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
@ -367,8 +377,14 @@ class _PathRow extends StatelessWidget {
|
|||
final String label;
|
||||
final String path;
|
||||
final IconData icon;
|
||||
final bool isDirectory;
|
||||
|
||||
const _PathRow({required this.label, required this.path, required this.icon});
|
||||
const _PathRow({
|
||||
required this.label,
|
||||
required this.path,
|
||||
required this.icon,
|
||||
required this.isDirectory,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -400,7 +416,10 @@ class _PathRow extends StatelessWidget {
|
|||
const SizedBox(width: FaiSpace.sm),
|
||||
OutlinedButton.icon(
|
||||
onPressed: () async {
|
||||
final r = await SystemActions.openInOs(path);
|
||||
final r = await SystemActions.openOrReveal(
|
||||
path,
|
||||
isDirectory: isDirectory,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
if (!r.ok) {
|
||||
final l = AppLocalizations.of(context)!;
|
||||
|
|
@ -409,7 +428,10 @@ class _PathRow extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.open_in_new, size: 14),
|
||||
icon: Icon(
|
||||
isDirectory ? Icons.folder_open : Icons.open_in_new,
|
||||
size: 14,
|
||||
),
|
||||
label: Text(AppLocalizations.of(context)!.buttonOpen),
|
||||
style: OutlinedButton.styleFrom(
|
||||
visualDensity: VisualDensity.compact,
|
||||
|
|
@ -895,22 +917,7 @@ class _UpdateBannerState extends State<_UpdateBanner> {
|
|||
),
|
||||
if (_applyOutput != null) ...[
|
||||
const SizedBox(height: FaiSpace.md),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(FaiSpace.sm),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(FaiRadius.sm),
|
||||
border: Border.all(color: theme.colorScheme.outlineVariant),
|
||||
),
|
||||
child: SelectableText(
|
||||
_applyOutput!,
|
||||
style: FaiTheme.mono(
|
||||
size: 11,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
FaiErrorBox(text: _applyOutput!, maxHeight: 240),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue