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:
flemming-it 2026-05-08 14:48:33 +02:00
parent 22a851b7e2
commit 3b5fb027c3
17 changed files with 454 additions and 119 deletions

View file

@ -28,6 +28,8 @@
"buttonOpen": "Öffnen",
"buttonExplain": "Erklären",
"buttonReadDocs": "Doku öffnen",
"buttonCopy": "In Zwischenablage kopieren",
"buttonCopied": "Kopiert",
"settingsTitle": "Hub-Endpunkt",
"settingsHost": "Host",
@ -62,7 +64,7 @@
"n8nAddTooltip": "n8n-Endpunkt hinzufügen",
"n8nRemoveTooltip": "Endpunkt entfernen",
"addMcpTitle": "MCP-Server hinzufügen",
"addMcpIntro": "Tools, die der Server via `tools/list` anbietet, erscheinen im Store als `mcp.<name>.<tool>`-Capabilities.",
"addMcpIntro": "Tools, die der Server via `tools/list` anbietet, erscheinen im Store als `mcp.<name>.<tool>`-Fähigkeiten.",
"addMcpSuggestionsLabel": "Vorschläge — zum Vorausfüllen klicken",
"addMcpNameLabel": "Name (keine Punkte)",
"addMcpNameHint": "filesystem",
@ -73,7 +75,7 @@
"addMcpNotesLabel": "Notizen (optional)",
"addMcpAddButton": "Hinzufügen + Discovery",
"addN8nTitle": "n8n-Endpunkt hinzufügen",
"addN8nIntro": "Aktive Workflows erscheinen im Store als `n8n.<name>.<workflow_slug>`-Capabilities.",
"addN8nIntro": "Aktive Workflows erscheinen im Store als `n8n.<name>.<workflow_slug>`-Fähigkeiten.",
"addN8nNameLabel": "Name (keine Punkte)",
"addN8nNameHint": "ops",
"addN8nBaseUrlLabel": "Basis-URL (ohne /api/v1)",
@ -102,7 +104,7 @@
"channelsEnableAutostart": "Autostart bei Anmeldung aktivieren",
"channelsDisableAutostart": "Autostart deaktivieren",
"storeSearchHint": "Module suchen — Name, Tagline, Tag, Capability…",
"storeSearchHint": "Module suchen — Name, Tagline, Tag, Fähigkeit…",
"storeAskHint": "Was brauchst du? Versuch's mit „ein Modul, das tabellarische Daten liest\" oder tippe einfach einen Begriff.",
"storeAskSubmit": "Suchen",
"storeAskAi": "KI fragen",
@ -187,7 +189,7 @@
"storeOpenBrowserFailed": "Browser konnte nicht geöffnet werden: {error}",
"@storeOpenBrowserFailed": { "placeholders": { "error": { "type": "String" } } },
"storeSectionTags": "Tags",
"storeSectionRequiredCapabilities": "Erforderliche Capabilities",
"storeSectionRequiredCapabilities": "Erforderliche Fähigkeiten",
"storeSectionRequiredHostServices": "Erforderliche Host-Dienste",
"storeSectionScreenshots": "Screenshots",
"storeSectionDocumentation": "Dokumentation",
@ -221,19 +223,19 @@
"storeProvenanceN8n": "n8n · {provider}",
"@storeProvenanceN8n": { "placeholders": { "provider": { "type": "String" } } },
"storeRecommendedTitle": "Öffentliche Quelle hinzufügen — ein Klick",
"storeRecommendedBody": "Kuratierte MCP-Server über HTTPS erreichbar. Kein Subprozess, kein API-Key, kein Node. Föderierte Capabilities erscheinen sofort im Store.",
"storeRecommendedBody": "Kuratierte MCP-Server über HTTPS erreichbar. Kein Subprozess, kein API-Key, kein Node. Föderierte Fähigkeiten erscheinen sofort im Store.",
"storeRecommendedAddTooltip": "Hinzufügen und entdecken",
"storeRecommendedAdding": "Wird hinzugefügt…",
"storeRecommendedDeepWikiTagline": "GitHub-Repo-Doku-Suche (KI-gestützt)",
"storeRecommendedSemgrepTagline": "Security-Scanning für Code-Schwachstellen",
"storeProviderAddedToast": "{name} hinzugefügt — {n} {n, plural, =1{föderierte Capability} other{föderierte Capabilities}}.",
"storeProviderAddedToast": "{name} hinzugefügt — {n} {n, plural, =1{föderierte Fähigkeit} other{föderierte Fähigkeiten}}.",
"@storeProviderAddedToast": {
"placeholders": {
"name": { "type": "String" },
"n": { "type": "int" }
}
},
"storeProviderAddedZeroToast": "{name} ist eingetragen, aber der Server hat noch keine Capabilities geliefert. Öffne Einstellungen → MCP-Clients, um Discovery erneut zu versuchen oder den Server zu prüfen.",
"storeProviderAddedZeroToast": "{name} ist eingetragen, aber der Server hat noch keine Fähigkeiten geliefert. Öffne Einstellungen → MCP-Clients, um Discovery erneut zu versuchen oder den Server zu prüfen.",
"@storeProviderAddedZeroToast": {
"placeholders": { "name": { "type": "String" } }
},
@ -253,7 +255,7 @@
"auditFilterStep": "Step",
"auditFilterModule": "Modul",
"auditClearLogTooltip": "Protokoll löschen (nur local/dev Kanal)",
"auditClearedToast": "{n} Ereignisse auf Kanal „{channel}\" gelöscht. Chain mit Marker neu geseedet.",
"auditClearedToast": "{n} Ereignisse auf Kanal „{channel}\" gelöscht. Kette mit Marker neu geseedet.",
"@auditClearedToast": {
"placeholders": {
"n": { "type": "int" },
@ -276,7 +278,7 @@
"@auditLiveStatus": { "placeholders": { "n": { "type": "int" } } },
"auditDisconnected": "getrennt · {error}",
"@auditDisconnected": { "placeholders": { "error": { "type": "String" } } },
"auditHashChainVerified": "Hash-Chain verifiziert",
"auditHashChainVerified": "Hash-Kette verifiziert",
"auditDetailHeader": "DETAIL",
"auditAskingFull": "Frage an die konfigurierte System-AI läuft…",
"auditFixLabel": "FIX",
@ -308,7 +310,7 @@
"@modulesRecentActivityHint": { "placeholders": { "n": { "type": "int" } } },
"modulesActivityInstalled": "installiert",
"modulesActivityUninstalled": "deinstalliert",
"modulesCapabilitiesLabel": "Capabilities",
"modulesCapabilitiesLabel": "Fähigkeiten",
"modulesUninstallTitle": "Modul deinstallieren?",
"modulesUninstallBody": "Entfernt {name} v{version} von diesem Hub. Flows, die es verwenden, schlagen beim nächsten Lauf fehl. Ein `module.uninstalled`-Audit-Event wird festgehalten.",
"@modulesUninstallBody": {
@ -329,7 +331,7 @@
"modulesUninstalling": "Wird deinstalliert…",
"moduleSheetFailedToLoad": "Laden fehlgeschlagen: {error}",
"@moduleSheetFailedToLoad": { "placeholders": { "error": { "type": "String" } } },
"moduleSheetCapabilities": "Capabilities",
"moduleSheetCapabilities": "Fähigkeiten",
"moduleSheetPermissions": "Deklarierte Berechtigungen",
"moduleSheetNoPermissions": "(keine — reines Berechnungsmodul)",
@ -381,7 +383,7 @@
"approvalsRejectFailed": "Ablehnung fehlgeschlagen: {error}",
"@approvalsRejectFailed": { "placeholders": { "error": { "type": "String" } } },
"approvalsHistoryEmpty": "Noch kein Verlauf",
"approvalsHistoryEmptyHint": "Entschiedene Freigaben (freigegeben / abgelehnt / abgelaufen) erscheinen hier.\nDas Audit-Log enthält den vollständigen hash-chained Datensatz.",
"approvalsHistoryEmptyHint": "Entschiedene Freigaben (freigegeben / abgelehnt / abgelaufen) erscheinen hier.\nDas Audit-Log enthält den vollständigen hash-verketteten Datensatz.",
"approvalsDialogDecided": "entschieden",
"approvalsDialogCreated": "erstellt",
"approvalsDialogReason": "Begründung",
@ -396,10 +398,10 @@
"doctorSummaryApprovals": "Freigaben",
"doctorSummaryAudit": "Protokoll",
"doctorSummaryServices": "Dienste",
"doctorSummaryCapabilities": "{n} Capabilities",
"doctorSummaryCapabilities": "{n} Fähigkeiten",
"@doctorSummaryCapabilities": { "placeholders": { "n": { "type": "int" } } },
"doctorSummaryPending": "offen",
"doctorSummaryChain": "{verified}/{total} Chain",
"doctorSummaryChain": "{verified}/{total} Kette",
"@doctorSummaryChain": {
"placeholders": {
"verified": { "type": "int" },
@ -407,7 +409,7 @@
}
},
"doctorSummaryDeclared": "deklariert",
"doctorModulesPanelSummary": "{n} Module · {m} Capabilities",
"doctorModulesPanelSummary": "{n} Module · {m} Fähigkeiten",
"@doctorModulesPanelSummary": {
"placeholders": {
"n": { "type": "int" },
@ -428,7 +430,7 @@
"doctorHostServicesSection": "Host-Dienste",
"doctorDaemonFilesSection": "Daemon-Dateien",
"doctorDaemonControlSection": "Daemon-Steuerung",
"doctorChainIntact": "Hash-Chain intakt",
"doctorChainIntact": "Hash-Kette intakt",
"doctorChainIntactDetail": "{n} Ereignisse · prev_event_sha256 lückenlos verifiziert",
"@doctorChainIntactDetail": { "placeholders": { "n": { "type": "int" } } },
"doctorChainTampered": "Manipulation erkannt",
@ -491,10 +493,10 @@
"doctorApplyDone": "Update angewendet. Studio neu starten, um die neue Daemon-Version zu sehen.",
"mcpHeader": "MCP-CLIENTS",
"mcpHint": "Externe MCP-Server bringen ihre Tools als `mcp.<server>.<tool>` Capabilities ein. Einmal konfigurieren, im Store sichtbar.",
"mcpHint": "Externe MCP-Server bringen ihre Tools als `mcp.<server>.<tool>` Fähigkeiten ein. Einmal konfigurieren, im Store sichtbar.",
"mcpEmpty": "Keine MCP-Server konfiguriert. + klicken zum Hinzufügen.",
"n8nHeader": "N8N-ENDPUNKTE",
"n8nHint": "Aktive n8n-Workflows erscheinen als `n8n.<endpoint>.<slug>` Capabilities. Einmal konfigurieren, im Store sichtbar.",
"n8nHint": "Aktive n8n-Workflows erscheinen als `n8n.<endpoint>.<slug>` Fähigkeiten. Einmal konfigurieren, im Store sichtbar.",
"n8nEmpty": "Keine n8n-Endpunkte konfiguriert. + klicken zum Hinzufügen.",
"hubUnreachable": "Hub nicht erreichbar",

View file

@ -29,6 +29,8 @@
"buttonOpen": "Open",
"buttonExplain": "Explain",
"buttonReadDocs": "Read docs",
"buttonCopy": "Copy to clipboard",
"buttonCopied": "Copied",
"settingsTitle": "Hub endpoint",
"settingsHost": "Host",

View file

@ -242,6 +242,18 @@ abstract class AppLocalizations {
/// **'Read docs'**
String get buttonReadDocs;
/// No description provided for @buttonCopy.
///
/// In en, this message translates to:
/// **'Copy to clipboard'**
String get buttonCopy;
/// No description provided for @buttonCopied.
///
/// In en, this message translates to:
/// **'Copied'**
String get buttonCopied;
/// No description provided for @settingsTitle.
///
/// In en, this message translates to:

View file

@ -80,6 +80,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get buttonReadDocs => 'Doku öffnen';
@override
String get buttonCopy => 'In Zwischenablage kopieren';
@override
String get buttonCopied => 'Kopiert';
@override
String get settingsTitle => 'Hub-Endpunkt';
@ -193,7 +199,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get addMcpIntro =>
'Tools, die der Server via `tools/list` anbietet, erscheinen im Store als `mcp.<name>.<tool>`-Capabilities.';
'Tools, die der Server via `tools/list` anbietet, erscheinen im Store als `mcp.<name>.<tool>`-Fähigkeiten.';
@override
String get addMcpSuggestionsLabel => 'Vorschläge — zum Vorausfüllen klicken';
@ -227,7 +233,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get addN8nIntro =>
'Aktive Workflows erscheinen im Store als `n8n.<name>.<workflow_slug>`-Capabilities.';
'Aktive Workflows erscheinen im Store als `n8n.<name>.<workflow_slug>`-Fähigkeiten.';
@override
String get addN8nNameLabel => 'Name (keine Punkte)';
@ -304,7 +310,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get storeSearchHint =>
'Module suchen — Name, Tagline, Tag, Capability';
'Module suchen — Name, Tagline, Tag, Fähigkeit';
@override
String get storeAskHint =>
@ -502,7 +508,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get storeSectionTags => 'Tags';
@override
String get storeSectionRequiredCapabilities => 'Erforderliche Capabilities';
String get storeSectionRequiredCapabilities => 'Erforderliche Fähigkeiten';
@override
String get storeSectionRequiredHostServices => 'Erforderliche Host-Dienste';
@ -605,7 +611,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get storeRecommendedBody =>
'Kuratierte MCP-Server über HTTPS erreichbar. Kein Subprozess, kein API-Key, kein Node. Föderierte Capabilities erscheinen sofort im Store.';
'Kuratierte MCP-Server über HTTPS erreichbar. Kein Subprozess, kein API-Key, kein Node. Föderierte Fähigkeiten erscheinen sofort im Store.';
@override
String get storeRecommendedAddTooltip => 'Hinzufügen und entdecken';
@ -626,15 +632,15 @@ class AppLocalizationsDe extends AppLocalizations {
String _temp0 = intl.Intl.pluralLogic(
n,
locale: localeName,
other: 'föderierte Capabilities',
one: 'föderierte Capability',
other: 'föderierte Fähigkeiten',
one: 'föderierte Fähigkeit',
);
return '$name hinzugefügt — $n $_temp0.';
}
@override
String storeProviderAddedZeroToast(String name) {
return '$name ist eingetragen, aber der Server hat noch keine Capabilities geliefert. Öffne Einstellungen → MCP-Clients, um Discovery erneut zu versuchen oder den Server zu prüfen.';
return '$name ist eingetragen, aber der Server hat noch keine Fähigkeiten geliefert. Öffne Einstellungen → MCP-Clients, um Discovery erneut zu versuchen oder den Server zu prüfen.';
}
@override
@ -669,7 +675,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String auditClearedToast(int n, String channel) {
return '$n Ereignisse auf Kanal „$channel\" gelöscht. Chain mit Marker neu geseedet.';
return '$n Ereignisse auf Kanal „$channel\" gelöscht. Kette mit Marker neu geseedet.';
}
@override
@ -723,7 +729,7 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
String get auditHashChainVerified => 'Hash-Chain verifiziert';
String get auditHashChainVerified => 'Hash-Kette verifiziert';
@override
String get auditDetailHeader => 'DETAIL';
@ -802,7 +808,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get modulesActivityUninstalled => 'deinstalliert';
@override
String get modulesCapabilitiesLabel => 'Capabilities';
String get modulesCapabilitiesLabel => 'Fähigkeiten';
@override
String get modulesUninstallTitle => 'Modul deinstallieren?';
@ -831,7 +837,7 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
String get moduleSheetCapabilities => 'Capabilities';
String get moduleSheetCapabilities => 'Fähigkeiten';
@override
String get moduleSheetPermissions => 'Deklarierte Berechtigungen';
@ -951,7 +957,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get approvalsHistoryEmptyHint =>
'Entschiedene Freigaben (freigegeben / abgelehnt / abgelaufen) erscheinen hier.\nDas Audit-Log enthält den vollständigen hash-chained Datensatz.';
'Entschiedene Freigaben (freigegeben / abgelehnt / abgelaufen) erscheinen hier.\nDas Audit-Log enthält den vollständigen hash-verketteten Datensatz.';
@override
String get approvalsDialogDecided => 'entschieden';
@ -992,7 +998,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String doctorSummaryCapabilities(int n) {
return '$n Capabilities';
return '$n Fähigkeiten';
}
@override
@ -1000,7 +1006,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String doctorSummaryChain(int verified, int total) {
return '$verified/$total Chain';
return '$verified/$total Kette';
}
@override
@ -1008,7 +1014,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String doctorModulesPanelSummary(int n, int m) {
return '$n Module · $m Capabilities';
return '$n Module · $m Fähigkeiten';
}
@override
@ -1060,7 +1066,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get doctorDaemonControlSection => 'Daemon-Steuerung';
@override
String get doctorChainIntact => 'Hash-Chain intakt';
String get doctorChainIntact => 'Hash-Kette intakt';
@override
String doctorChainIntactDetail(int n) {
@ -1193,7 +1199,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get mcpHint =>
'Externe MCP-Server bringen ihre Tools als `mcp.<server>.<tool>` Capabilities ein. Einmal konfigurieren, im Store sichtbar.';
'Externe MCP-Server bringen ihre Tools als `mcp.<server>.<tool>` Fähigkeiten ein. Einmal konfigurieren, im Store sichtbar.';
@override
String get mcpEmpty =>
@ -1204,7 +1210,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get n8nHint =>
'Aktive n8n-Workflows erscheinen als `n8n.<endpoint>.<slug>` Capabilities. Einmal konfigurieren, im Store sichtbar.';
'Aktive n8n-Workflows erscheinen als `n8n.<endpoint>.<slug>` Fähigkeiten. Einmal konfigurieren, im Store sichtbar.';
@override
String get n8nEmpty =>

View file

@ -80,6 +80,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get buttonReadDocs => 'Read docs';
@override
String get buttonCopy => 'Copy to clipboard';
@override
String get buttonCopied => 'Copied';
@override
String get settingsTitle => 'Hub endpoint';