Some checks failed
Security / Security check (push) Failing after 2s
Three operator-visible improvements per Stefan's review of the v0.50.0 editor. 1. Pencil icon on Flows page now opens Studio's own flow editor (route push) preloaded with the selected flow, replacing the previous "open in OS default editor" (SystemActions.openInOs) behaviour. The editor's AppBar gains a back arrow when reached via route push; reaching the editor via the nav rail leaves it bare. Tooltip string updated in EN + DE. 2. New FlowEditorPage `initialFlowName` parameter. When set, the page loads that flow on first frame (via post-frame callback so the BuildContext is mounted before _openByName runs). When null (the nav-rail path), the editor opens to its empty state as before. 3. Sidebar (_Sidebar) is now collapsed-by-default: shows just icons in a 72px-wide rail with per-destination tooltips. Hovering the rail expands it to 220px (the old width) with brand-mark + labels + the full footer row (theme toggle, language toggle, clock, settings). Collapsed footer shows the settings icon only. Brand- mark (FaiDeltaMark) stays visible in both states so the live/idle status dot is always glanceable. Side-effect: SystemActions import in flows.dart is no longer needed (the pencil no longer shells out) — removed. widget_test.dart: dropped the per-destination Text-presence asserts since labels are now Tooltips when collapsed. Hover-expand testing triggers RenderFlex-overflow mid- animation in widget tests (the AnimatedContainer's width transitions through a constraint slimmer than the Row's intrinsic min). The booting-without-throwing assertion remains; per-destination presence stays in the per-page test suites. Both arb files updated with the new strings (navFlowEditor already existed; added flowEditorBackTooltip + retouched flowsOpenInEditorTooltip). Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2261 lines
55 KiB
Dart
2261 lines
55 KiB
Dart
// ignore: unused_import
|
||
import 'package:intl/intl.dart' as intl;
|
||
import 'app_localizations.dart';
|
||
|
||
// ignore_for_file: type=lint
|
||
|
||
/// The translations for English (`en`).
|
||
class AppLocalizationsEn extends AppLocalizations {
|
||
AppLocalizationsEn([String locale = 'en']) : super(locale);
|
||
|
||
@override
|
||
String get appTitle => 'F∆I Studio';
|
||
|
||
@override
|
||
String get navWelcome => 'Welcome';
|
||
|
||
@override
|
||
String get welcomeHeroTitle => 'F∆I Platform';
|
||
|
||
@override
|
||
String get welcomeHeroSubtitle =>
|
||
'Deterministic workflow engine for AI-assisted document processing in regulated environments.';
|
||
|
||
@override
|
||
String get welcomePillarHubTitle => 'Hub';
|
||
|
||
@override
|
||
String get welcomePillarHubBody =>
|
||
'One Rust binary that loads modules and runs flows. SQLite for state. No Docker, no message broker, no background services to babysit.';
|
||
|
||
@override
|
||
String get welcomePillarModuleTitle => 'Module';
|
||
|
||
@override
|
||
String get welcomePillarModuleBody =>
|
||
'A small WebAssembly component that does one thing. Declares the network endpoints, files, and environment variables it touches; the hub enforces the list.';
|
||
|
||
@override
|
||
String get welcomePillarFlowTitle => 'Flow';
|
||
|
||
@override
|
||
String get welcomePillarFlowBody =>
|
||
'A YAML file that chains modules together. Inputs go in, modules run in declared order, outputs come out — every step recorded in a hash-chained audit log.';
|
||
|
||
@override
|
||
String get welcomeTrustHeader => 'TRUST POSTURE';
|
||
|
||
@override
|
||
String get welcomeTrustSandboxTitle => 'Sandbox by default';
|
||
|
||
@override
|
||
String get welcomeTrustSandboxBody =>
|
||
'Every module ships with an explicit permission list — network endpoints, files, environment variables. The hub enforces it; nothing reaches outside the sandbox without operator approval.';
|
||
|
||
@override
|
||
String get welcomeTrustAuditTitle => 'Tamper-evident audit log';
|
||
|
||
@override
|
||
String get welcomeTrustAuditBody =>
|
||
'Flow runs, install / uninstall actions, approval decisions — all of them write into a hash-chained audit log. The Doctor page verifies the chain end-to-end on every load.';
|
||
|
||
@override
|
||
String get welcomeTrustAirgapTitle => 'Air-gap ready';
|
||
|
||
@override
|
||
String get welcomeTrustAirgapBody =>
|
||
'The whole hub fits in a single binary that runs on Linux, macOS and Windows. Once a module is installed, the flow it powers runs without further network access — perfect for regulated environments.';
|
||
|
||
@override
|
||
String get welcomeDocsHeader => 'DOCUMENTATION';
|
||
|
||
@override
|
||
String get welcomeDocsBody =>
|
||
'Operator-facing explainers, rendered inline. No browser, no external link — air-gap-friendly.';
|
||
|
||
@override
|
||
String get welcomeDocArchitectureTitle => 'Architecture in one paragraph';
|
||
|
||
@override
|
||
String get welcomeDocArchitectureBlurb =>
|
||
'Hub, Module, Flow — and how the three fit together.';
|
||
|
||
@override
|
||
String get welcomeDocSecurityTitle => 'Sandbox model';
|
||
|
||
@override
|
||
String get welcomeDocSecurityBlurb =>
|
||
'What a module declares, what the operator caps, what the hub enforces.';
|
||
|
||
@override
|
||
String get welcomeDocAuditTitle => 'Tamper-evident audit log';
|
||
|
||
@override
|
||
String get welcomeDocAuditBlurb =>
|
||
'How the hash chain works and why WORM-1 is enough for KRITIS.';
|
||
|
||
@override
|
||
String get welcomeDocFlowsTitle => 'Flow composition';
|
||
|
||
@override
|
||
String get welcomeDocFlowsBlurb =>
|
||
'YAML basics, templating reference, the extract→summarize example.';
|
||
|
||
@override
|
||
String get welcomeDocClose => 'Close';
|
||
|
||
@override
|
||
String welcomeDocFailedToLoad(String error) {
|
||
return 'Could not load documentation: $error';
|
||
}
|
||
|
||
@override
|
||
String get welcomeChecklistHeader => 'GETTING STARTED';
|
||
|
||
@override
|
||
String get welcomeChecklistBody =>
|
||
'Four steps to a working hub. Live state — refresh after changing anything.';
|
||
|
||
@override
|
||
String get welcomeChecklistAi => 'Configure System AI';
|
||
|
||
@override
|
||
String get welcomeChecklistAiHint =>
|
||
'Settings → System AI. Required for the AI search and inline failure explanations.';
|
||
|
||
@override
|
||
String get welcomeChecklistMcp => 'Add a public capability source';
|
||
|
||
@override
|
||
String get welcomeChecklistMcpHint =>
|
||
'Store → Today hero, or Settings → MCP Clients. DeepWiki and Semgrep are one click each.';
|
||
|
||
@override
|
||
String get welcomeChecklistModule => 'Install a text module';
|
||
|
||
@override
|
||
String get welcomeChecklistModuleHint =>
|
||
'Store → search \"text\" → Install. Try text.extract for PDFs and DOCX.';
|
||
|
||
@override
|
||
String get welcomeChecklistFlow => 'Run a saved flow';
|
||
|
||
@override
|
||
String get welcomeChecklistFlowHint =>
|
||
'Flows → pick one → Run. The bundled extract-summarize.yaml is a good first run.';
|
||
|
||
@override
|
||
String get welcomeChecklistAllDone => 'All four steps complete.';
|
||
|
||
@override
|
||
String get welcomeChecklistDismiss => 'Hide checklist';
|
||
|
||
@override
|
||
String get welcomeChecklistRefresh => 'Refresh';
|
||
|
||
@override
|
||
String get welcomeChecklistRefreshing => 'Checking…';
|
||
|
||
@override
|
||
String get welcomeChecklistDone => 'Done';
|
||
|
||
@override
|
||
String get welcomeChecklistTodo => 'Pending';
|
||
|
||
@override
|
||
String get navDoctor => 'Doctor';
|
||
|
||
@override
|
||
String get navModules => 'Modules';
|
||
|
||
@override
|
||
String get navStore => 'Store';
|
||
|
||
@override
|
||
String get navFlows => 'Flows';
|
||
|
||
@override
|
||
String get navAudit => 'Audit';
|
||
|
||
@override
|
||
String get navApprovals => 'Approvals';
|
||
|
||
@override
|
||
String get connectionConnected => 'connected';
|
||
|
||
@override
|
||
String get connectionUnreachable => 'unreachable';
|
||
|
||
@override
|
||
String get connectionConnecting => 'connecting…';
|
||
|
||
@override
|
||
String get connectionStartHub => 'Start hub';
|
||
|
||
@override
|
||
String get buttonCancel => 'Cancel';
|
||
|
||
@override
|
||
String get buttonSave => 'Save';
|
||
|
||
@override
|
||
String get buttonClose => 'Close';
|
||
|
||
@override
|
||
String get buttonRetry => 'Retry';
|
||
|
||
@override
|
||
String get buttonRefresh => 'Refresh';
|
||
|
||
@override
|
||
String get buttonInstall => 'Install';
|
||
|
||
@override
|
||
String get buttonUninstall => 'Uninstall';
|
||
|
||
@override
|
||
String get buttonDetails => 'Details';
|
||
|
||
@override
|
||
String get buttonAdd => 'Add';
|
||
|
||
@override
|
||
String get buttonRemove => 'Remove';
|
||
|
||
@override
|
||
String get buttonOpen => 'Open';
|
||
|
||
@override
|
||
String get buttonExplain => 'Explain';
|
||
|
||
@override
|
||
String get buttonReadDocs => 'Read docs';
|
||
|
||
@override
|
||
String get buttonCopy => 'Copy to clipboard';
|
||
|
||
@override
|
||
String get buttonCopied => 'Copied';
|
||
|
||
@override
|
||
String get buttonShowDetails => 'Show details';
|
||
|
||
@override
|
||
String get buttonHideDetails => 'Hide details';
|
||
|
||
@override
|
||
String get storeInstallableOnly => 'Installable only';
|
||
|
||
@override
|
||
String get storePillComingSoon => 'Coming soon';
|
||
|
||
@override
|
||
String storeNotInstallableInline(String status) {
|
||
return 'This module is on the roadmap (status: $status). It has no installable bundle yet — follow the repository for updates.';
|
||
}
|
||
|
||
@override
|
||
String get mcpSuggestionDeepwikiDesc =>
|
||
'Public HTTPS — GitHub repo documentation search (AI-powered). No Node, no API key.';
|
||
|
||
@override
|
||
String get mcpSuggestionSemgrepDesc =>
|
||
'Public HTTPS — security scanning for code vulnerabilities. No Node, no API key.';
|
||
|
||
@override
|
||
String get mcpSuggestionFilesystemDesc =>
|
||
'Anthropic — read / write files in /tmp. Edit the path before saving.';
|
||
|
||
@override
|
||
String get mcpSuggestionFetchDesc =>
|
||
'Anthropic — fetch arbitrary HTTP(S) URLs as markdown.';
|
||
|
||
@override
|
||
String get mcpSuggestionGithubDesc =>
|
||
'Anthropic — issues, PRs, repo files. Needs a GitHub PAT in the env var.';
|
||
|
||
@override
|
||
String get mcpSuggestionPuppeteerDesc =>
|
||
'Anthropic — headless browser automation for screenshots / scraping.';
|
||
|
||
@override
|
||
String get mcpSuggestionPostgresDesc =>
|
||
'Anthropic — read-only SQL queries against a Postgres database. Edit the URL.';
|
||
|
||
@override
|
||
String get mcpSuggestionSqliteDesc =>
|
||
'Anthropic — query a local SQLite file. Edit the path.';
|
||
|
||
@override
|
||
String get mcpSuggestionBraveSearchDesc =>
|
||
'Anthropic — web search via Brave. API key required.';
|
||
|
||
@override
|
||
String get mcpSuggestionMemoryDesc =>
|
||
'Anthropic — persistent knowledge graph for an agent\'s memory.';
|
||
|
||
@override
|
||
String get mcpSuggestionTimeDesc =>
|
||
'Anthropic — current time + timezone conversion.';
|
||
|
||
@override
|
||
String get mcpServerEnLanguageBadge =>
|
||
'Server text in English — Studio shows it as supplied.';
|
||
|
||
@override
|
||
String get themePluginHeader => 'Theme plugin';
|
||
|
||
@override
|
||
String get themePluginNone => 'Built-in (no plugin)';
|
||
|
||
@override
|
||
String get themePluginHint =>
|
||
'Pick an installed studio.theme.* plugin to re-skin the whole app. Install one from the Store first.';
|
||
|
||
@override
|
||
String get themePluginEmpty =>
|
||
'No theme plugins installed. Open the Store and pick one in the studio-plugin category.';
|
||
|
||
@override
|
||
String get settingsTitle => 'Hub endpoint';
|
||
|
||
@override
|
||
String get settingsHost => 'Host';
|
||
|
||
@override
|
||
String get settingsPort => 'Port';
|
||
|
||
@override
|
||
String get settingsTls => 'TLS';
|
||
|
||
@override
|
||
String get settingsSaveAndConnect => 'Save & connect';
|
||
|
||
@override
|
||
String get settingsLanguage => 'Language';
|
||
|
||
@override
|
||
String get settingsLanguageHint =>
|
||
'App language. Affects every page; bilingual store entries also flip via this toggle.';
|
||
|
||
@override
|
||
String get settingsHubEndpointHint =>
|
||
'Where should Studio connect? Default is the local hub at 127.0.0.1:50051.';
|
||
|
||
@override
|
||
String get settingsTlsSubtitle => 'https/grpc-secure';
|
||
|
||
@override
|
||
String get settingsPortError => 'port must be 1–65535';
|
||
|
||
@override
|
||
String get channelsBlurb =>
|
||
'Switch hub channel (writes ~/.fai/current-channel and restarts the daemon). Connect just changes Studio\'s wire.';
|
||
|
||
@override
|
||
String get channelsActionsTooltip => 'Channel actions';
|
||
|
||
@override
|
||
String get systemAiHeader => 'SYSTEM AI';
|
||
|
||
@override
|
||
String get systemAiEnabled => 'enabled';
|
||
|
||
@override
|
||
String get systemAiOff => 'off';
|
||
|
||
@override
|
||
String get systemAiEdit => 'Edit…';
|
||
|
||
@override
|
||
String get systemAiConfigure => 'Configure…';
|
||
|
||
@override
|
||
String get systemAiOffBlurb =>
|
||
'Off — failure explanations on the audit page are disabled. Click Configure… to pick a provider (Ollama / OpenAI / LM Studio / vLLM / Custom). Operator config is rewritten in place; no daemon restart needed.';
|
||
|
||
@override
|
||
String mcpServersCount(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'servers',
|
||
one: 'server',
|
||
);
|
||
return '$n $_temp0';
|
||
}
|
||
|
||
@override
|
||
String get mcpRediscoverTooltip => 'Re-run discovery';
|
||
|
||
@override
|
||
String get mcpAddTooltip => 'Add MCP server';
|
||
|
||
@override
|
||
String get mcpRemoveTooltip => 'Remove server';
|
||
|
||
@override
|
||
String mcpToolsCount(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'tools',
|
||
one: 'tool',
|
||
);
|
||
return '$n $_temp0';
|
||
}
|
||
|
||
@override
|
||
String n8nEndpointsCount(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'endpoints',
|
||
one: 'endpoint',
|
||
);
|
||
return '$n $_temp0';
|
||
}
|
||
|
||
@override
|
||
String n8nWorkflowsCount(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'workflows',
|
||
one: 'workflow',
|
||
);
|
||
return '$n $_temp0';
|
||
}
|
||
|
||
@override
|
||
String get n8nRediscoverTooltip => 'Re-run discovery';
|
||
|
||
@override
|
||
String get n8nAddTooltip => 'Add n8n endpoint';
|
||
|
||
@override
|
||
String get n8nRemoveTooltip => 'Remove endpoint';
|
||
|
||
@override
|
||
String get addMcpTitle => 'Add MCP server';
|
||
|
||
@override
|
||
String get addMcpIntro =>
|
||
'Tools the server advertises via `tools/list` appear in the Store as `mcp.<name>.<tool>` capabilities.';
|
||
|
||
@override
|
||
String get addMcpSuggestionsLabel => 'Suggested servers — click to pre-fill';
|
||
|
||
@override
|
||
String get addMcpNameLabel => 'Name (no dots)';
|
||
|
||
@override
|
||
String get addMcpNameHint => 'filesystem';
|
||
|
||
@override
|
||
String get addMcpEndpointLabel => 'Endpoint';
|
||
|
||
@override
|
||
String get addMcpEndpointHint => 'http://127.0.0.1:3001/mcp';
|
||
|
||
@override
|
||
String get addMcpApiKeyLabel => 'API key env var (optional)';
|
||
|
||
@override
|
||
String get addMcpApiKeyHint => 'MCP_FILESYSTEM_TOKEN';
|
||
|
||
@override
|
||
String get addMcpNotesLabel => 'Notes (optional)';
|
||
|
||
@override
|
||
String get addMcpAddButton => 'Add + discover';
|
||
|
||
@override
|
||
String get addN8nTitle => 'Add n8n endpoint';
|
||
|
||
@override
|
||
String get addN8nIntro =>
|
||
'Active workflows surface in the Store as `n8n.<name>.<workflow_slug>` capabilities.';
|
||
|
||
@override
|
||
String get addN8nNameLabel => 'Name (no dots)';
|
||
|
||
@override
|
||
String get addN8nNameHint => 'ops';
|
||
|
||
@override
|
||
String get addN8nBaseUrlLabel => 'Base URL (without /api/v1)';
|
||
|
||
@override
|
||
String get addN8nBaseUrlHint => 'https://n8n.example.com';
|
||
|
||
@override
|
||
String get addN8nApiKeyLabel => 'API key env var (optional)';
|
||
|
||
@override
|
||
String get addN8nApiKeyHint => 'N8N_OPS_KEY';
|
||
|
||
@override
|
||
String get addN8nNotesLabel => 'Notes (optional)';
|
||
|
||
@override
|
||
String get addN8nAddButton => 'Add + discover';
|
||
|
||
@override
|
||
String addedMcpToast(String name) {
|
||
return 'Added MCP server \"$name\".';
|
||
}
|
||
|
||
@override
|
||
String addedN8nToast(String name) {
|
||
return 'Added n8n endpoint \"$name\".';
|
||
}
|
||
|
||
@override
|
||
String addFailedToast(String error) {
|
||
return 'Add failed: $error';
|
||
}
|
||
|
||
@override
|
||
String removeFailedToast(String error) {
|
||
return 'Remove failed: $error';
|
||
}
|
||
|
||
@override
|
||
String refreshFailedToast(String error) {
|
||
return 'Refresh failed: $error';
|
||
}
|
||
|
||
@override
|
||
String get channelsHeader => 'CHANNELS';
|
||
|
||
@override
|
||
String get channelsActive => 'active';
|
||
|
||
@override
|
||
String get channelsRunning => 'running';
|
||
|
||
@override
|
||
String get channelsStopped => 'stopped';
|
||
|
||
@override
|
||
String get channelsConnect => 'Connect Studio to this channel';
|
||
|
||
@override
|
||
String get channelsSwitch => 'Make this the active channel';
|
||
|
||
@override
|
||
String get channelsEnableAutostart => 'Enable autostart at login';
|
||
|
||
@override
|
||
String get channelsDisableAutostart => 'Disable autostart';
|
||
|
||
@override
|
||
String get storeSearchHint =>
|
||
'Search modules — name, tagline, tag, capability…';
|
||
|
||
@override
|
||
String get storeAskHint =>
|
||
'What do you need? Try \"a module that reads tabular data\" or just type a keyword.';
|
||
|
||
@override
|
||
String get storeAskSubmit => 'Search';
|
||
|
||
@override
|
||
String get storeAskAi => 'Ask the AI';
|
||
|
||
@override
|
||
String get storeAskClear => 'Clear question';
|
||
|
||
@override
|
||
String get storeAskAiThinking => 'Looking through modules…';
|
||
|
||
@override
|
||
String get storeAskAiUnavailable =>
|
||
'Configure a System AI in Settings to ask questions in plain language.';
|
||
|
||
@override
|
||
String get storeAskNoMatches =>
|
||
'No matches yet — refine the question or browse the grid below.';
|
||
|
||
@override
|
||
String get storeAskAnswerLabel => 'AI suggestion';
|
||
|
||
@override
|
||
String get storeAskAnswerWhy => 'Why this matches';
|
||
|
||
@override
|
||
String get storeAskMatchPill => 'match';
|
||
|
||
@override
|
||
String get storeFilterLabel => 'Filter';
|
||
|
||
@override
|
||
String storeFilterActiveCount(int n) {
|
||
return '$n active';
|
||
}
|
||
|
||
@override
|
||
String get storeFilterReset => 'Reset filters';
|
||
|
||
@override
|
||
String get storeFilterApply => 'Done';
|
||
|
||
@override
|
||
String get storeFilterStatusGroup => 'Status';
|
||
|
||
@override
|
||
String get storeFilterSourceGroup => 'Source';
|
||
|
||
@override
|
||
String get storeFilterInstalledGroup => 'Show only installed';
|
||
|
||
@override
|
||
String get storeFeatured => 'FEATURED';
|
||
|
||
@override
|
||
String get storeFeaturedHint => 'curated picks · click for details';
|
||
|
||
@override
|
||
String get storeNoMatches => 'No matches';
|
||
|
||
@override
|
||
String get storeNoMatchesHint =>
|
||
'Adjust the filters or clear the search to see all entries.';
|
||
|
||
@override
|
||
String get storeClearFilters => 'Clear filters';
|
||
|
||
@override
|
||
String get storeStatusAll => 'All';
|
||
|
||
@override
|
||
String get storeStatusPublished => 'stable';
|
||
|
||
@override
|
||
String get storeStatusAlpha => 'experimental';
|
||
|
||
@override
|
||
String get storeStatusPlanned => 'coming soon';
|
||
|
||
@override
|
||
String get storeInstalledOnly => 'Installed';
|
||
|
||
@override
|
||
String get storeCategoryLabel => 'Category';
|
||
|
||
@override
|
||
String get storeCategoryAll => 'All categories';
|
||
|
||
@override
|
||
String get storeCategoryBridge => 'Connectors';
|
||
|
||
@override
|
||
String get storeCategoryDebug => 'Sample modules';
|
||
|
||
@override
|
||
String get storeCategoryText => 'Text';
|
||
|
||
@override
|
||
String get storeCategoryLlm => 'AI models';
|
||
|
||
@override
|
||
String get storeCategorySystem => 'System';
|
||
|
||
@override
|
||
String get storeCategoryAuth => 'Authentication';
|
||
|
||
@override
|
||
String get storeCategoryStorage => 'Storage';
|
||
|
||
@override
|
||
String get storeCategoryChannel => 'Channels';
|
||
|
||
@override
|
||
String get storeCategoryWeb => 'Web';
|
||
|
||
@override
|
||
String get storeCategoryOrchestrator => 'Orchestration';
|
||
|
||
@override
|
||
String storeNResults(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 's',
|
||
one: '',
|
||
);
|
||
return '$n result$_temp0';
|
||
}
|
||
|
||
@override
|
||
String get storeReloadTooltip => 'Reload';
|
||
|
||
@override
|
||
String get storeTodayBadge => 'TODAY';
|
||
|
||
@override
|
||
String get storeTodayDeck => 'From the F∆I editor';
|
||
|
||
@override
|
||
String get storeTodayDismissTooltip => 'Hide for this session';
|
||
|
||
@override
|
||
String get storePillInstalled => 'installed';
|
||
|
||
@override
|
||
String get storePillUpdate => 'update';
|
||
|
||
@override
|
||
String storeUpdateButton(String version) {
|
||
return 'Update to $version';
|
||
}
|
||
|
||
@override
|
||
String storeUpdateTooltip(String installed, String best) {
|
||
return 'Installed $installed — store has v$best';
|
||
}
|
||
|
||
@override
|
||
String get storeNoTagline => '(no tagline)';
|
||
|
||
@override
|
||
String get storeUninstallTitle => 'Uninstall module?';
|
||
|
||
@override
|
||
String storeUninstallBody(String name) {
|
||
return 'Removes $name from this hub. Flows that reference it will fail at the next run.';
|
||
}
|
||
|
||
@override
|
||
String storeInstalledToast(String name, String version) {
|
||
return '$name v$version installed.';
|
||
}
|
||
|
||
@override
|
||
String storeInstallFailedToast(String error) {
|
||
return 'Install failed: $error';
|
||
}
|
||
|
||
@override
|
||
String storeUninstalledToast(String name, String version) {
|
||
return '$name v$version uninstalled.';
|
||
}
|
||
|
||
@override
|
||
String storeUninstallFailedToast(String error) {
|
||
return 'Uninstall failed: $error';
|
||
}
|
||
|
||
@override
|
||
String storeOpenBrowserFailed(String error) {
|
||
return 'Could not open browser: $error';
|
||
}
|
||
|
||
@override
|
||
String get storeSectionTags => 'Tags';
|
||
|
||
@override
|
||
String get storeSectionRequiredCapabilities => 'Required capabilities';
|
||
|
||
@override
|
||
String get storeSectionRequiredHostServices => 'Required host services';
|
||
|
||
@override
|
||
String get storeSectionScreenshots => 'Screenshots';
|
||
|
||
@override
|
||
String get storeSectionDocumentation => 'Documentation';
|
||
|
||
@override
|
||
String get storeSectionSource => 'Source';
|
||
|
||
@override
|
||
String get storeSectionPermissions => 'Declared permissions';
|
||
|
||
@override
|
||
String get storeSectionDirectory => 'Module directory';
|
||
|
||
@override
|
||
String get storeSectionPermissionsNone => '(none — pure-computation module)';
|
||
|
||
@override
|
||
String get storeInstallingButton => 'Installing…';
|
||
|
||
@override
|
||
String get storeNotInstallable => 'Not installable';
|
||
|
||
@override
|
||
String storeNotInstallableTooltip(String status) {
|
||
return 'Status \"$status\" — install path not yet wired.';
|
||
}
|
||
|
||
@override
|
||
String storeInstallProgressTitle(String name) {
|
||
return 'Installing $name';
|
||
}
|
||
|
||
@override
|
||
String get storeInstallProgressBody => 'Fetching, verifying, unpacking…';
|
||
|
||
@override
|
||
String get storeLoadDocs => 'Load documentation';
|
||
|
||
@override
|
||
String get storeDocsHint =>
|
||
'README rendered inline; uses the hub\'s registry token when needed.';
|
||
|
||
@override
|
||
String get storeDocsFetching => 'Fetching README…';
|
||
|
||
@override
|
||
String get storeOpenRepoButton => 'Open repository in browser';
|
||
|
||
@override
|
||
String get storeDocsErrorNotFound =>
|
||
'This module is not in the hub\'s store index.';
|
||
|
||
@override
|
||
String get storeDocsErrorNoDocs =>
|
||
'No repository URL is recorded for this module.';
|
||
|
||
@override
|
||
String get storeDocsErrorAuth =>
|
||
'The registry requires authentication to fetch this README. Set FAI_REGISTRY_TOKEN in the daemon\'s environment and restart the hub.';
|
||
|
||
@override
|
||
String storeDocsErrorNetwork(String detail) {
|
||
return 'Network error while fetching the README. $detail';
|
||
}
|
||
|
||
@override
|
||
String get storeDocsErrorParse => 'README is not valid UTF-8.';
|
||
|
||
@override
|
||
String get storeDocsErrorGeneric => 'Could not load README.';
|
||
|
||
@override
|
||
String get storeSourceLabel => 'Source';
|
||
|
||
@override
|
||
String get storeSourceAll => 'All sources';
|
||
|
||
@override
|
||
String get storeSourceNative => 'Native';
|
||
|
||
@override
|
||
String get storeSourceMcp => 'MCP';
|
||
|
||
@override
|
||
String get storeSourceN8n => 'n8n';
|
||
|
||
@override
|
||
String get storeProvenanceNative => 'native';
|
||
|
||
@override
|
||
String storeProvenanceMcp(String provider) {
|
||
return 'mcp · $provider';
|
||
}
|
||
|
||
@override
|
||
String storeProvenanceN8n(String provider) {
|
||
return 'n8n · $provider';
|
||
}
|
||
|
||
@override
|
||
String get storeRecommendedTitle => 'Add a public source — one click';
|
||
|
||
@override
|
||
String get storeRecommendedBody =>
|
||
'Curated MCP servers reachable over HTTPS. No subprocess, no API key, no Node. Federated capabilities appear in the store immediately.';
|
||
|
||
@override
|
||
String get storeRecommendedAddTooltip => 'Add and discover';
|
||
|
||
@override
|
||
String get storeRecommendedAdding => 'Adding…';
|
||
|
||
@override
|
||
String get storeRecommendedDeepWikiTagline =>
|
||
'GitHub repo documentation search (AI-powered)';
|
||
|
||
@override
|
||
String get storeRecommendedSemgrepTagline =>
|
||
'Security scanning for code vulnerabilities';
|
||
|
||
@override
|
||
String storeProviderAddedToast(String name, int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'capabilities',
|
||
one: 'capability',
|
||
);
|
||
return 'Added $name — $n federated $_temp0.';
|
||
}
|
||
|
||
@override
|
||
String storeProviderAddedZeroToast(String name) {
|
||
return '$name registered, but the server returned no capabilities yet. Open Settings → MCP Clients to retry discovery or check the server.';
|
||
}
|
||
|
||
@override
|
||
String storeProviderAddFailed(String name, String error) {
|
||
return 'Could not add $name: $error';
|
||
}
|
||
|
||
@override
|
||
String get auditTitle => 'Audit';
|
||
|
||
@override
|
||
String get auditNoEvents => 'No events yet';
|
||
|
||
@override
|
||
String get auditNoEventsHint =>
|
||
'Run a flow to populate the audit stream.\nEvents appear here within seconds.';
|
||
|
||
@override
|
||
String get auditFilterAll => 'all';
|
||
|
||
@override
|
||
String get auditFilterFlow => 'flow';
|
||
|
||
@override
|
||
String get auditFilterStep => 'step';
|
||
|
||
@override
|
||
String get auditFilterModule => 'module';
|
||
|
||
@override
|
||
String get auditClearLogTooltip => 'Clear log (local/dev channels only)';
|
||
|
||
@override
|
||
String auditClearedToast(int n, String channel) {
|
||
return 'Cleared $n events on channel \"$channel\". Chain reseeded with marker.';
|
||
}
|
||
|
||
@override
|
||
String auditClearFailed(String detail) {
|
||
return 'Clear failed: $detail';
|
||
}
|
||
|
||
@override
|
||
String get auditExplain => 'Explain';
|
||
|
||
@override
|
||
String get auditReask => 'Re-ask';
|
||
|
||
@override
|
||
String get auditAsking => 'Asking…';
|
||
|
||
@override
|
||
String get auditSystemAi => 'SYSTEM AI';
|
||
|
||
@override
|
||
String get auditCachedPill => 'cached';
|
||
|
||
@override
|
||
String auditOriginalLatency(int ms) {
|
||
return 'orig $ms ms';
|
||
}
|
||
|
||
@override
|
||
String auditLatency(int ms) {
|
||
return '$ms ms';
|
||
}
|
||
|
||
@override
|
||
String get auditRegenerateTooltip =>
|
||
'Regenerate — skip cache, ask the model again';
|
||
|
||
@override
|
||
String auditLiveStatus(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'events',
|
||
one: 'event',
|
||
);
|
||
return 'live · polling 2s · $n $_temp0';
|
||
}
|
||
|
||
@override
|
||
String auditDisconnected(String error) {
|
||
return 'disconnected · $error';
|
||
}
|
||
|
||
@override
|
||
String get auditHashChainVerified => 'hash chain verified';
|
||
|
||
@override
|
||
String get auditDetailHeader => 'DETAIL';
|
||
|
||
@override
|
||
String get auditAskingFull => 'Asking the configured System AI…';
|
||
|
||
@override
|
||
String get auditFixLabel => 'FIX';
|
||
|
||
@override
|
||
String auditCachedTooltipKnown(String at, String hits) {
|
||
return 'Served from the local cache. Originally generated on $at$hits.';
|
||
}
|
||
|
||
@override
|
||
String auditCachedTooltipUnknown(String hits) {
|
||
return 'Served from the local cache. Originally generated at unknown time$hits.';
|
||
}
|
||
|
||
@override
|
||
String auditCachedTooltipHits(int n) {
|
||
return ' · $n hits';
|
||
}
|
||
|
||
@override
|
||
String get auditConfigureSystemAi =>
|
||
'Configure System AI in Settings (Cmd+,)';
|
||
|
||
@override
|
||
String get auditClearDialogTitle => 'Clear audit log?';
|
||
|
||
@override
|
||
String get auditClearDialogBody =>
|
||
'Wipes every audit event on the active channel and seeds a fresh chain.reset marker carrying reviewer + reason. Refused on beta / production. Irreversible.';
|
||
|
||
@override
|
||
String get auditClearReviewerLabel => 'Reviewer';
|
||
|
||
@override
|
||
String get auditClearReasonLabel => 'Reason (recorded in chain.reset marker)';
|
||
|
||
@override
|
||
String get auditClearReasonHelper => 'Required — auditors will read this.';
|
||
|
||
@override
|
||
String get auditClearLogButton => 'Clear log';
|
||
|
||
@override
|
||
String get modulesTitle => 'Modules';
|
||
|
||
@override
|
||
String get modulesNoneTitle => 'No modules yet';
|
||
|
||
@override
|
||
String get modulesNoneHint =>
|
||
'Run `fai install <capability-name>` or check ~/.fai/modules/.';
|
||
|
||
@override
|
||
String get modulesReloadTooltip => 'Reload';
|
||
|
||
@override
|
||
String get modulesRecentActivity => 'RECENT ACTIVITY';
|
||
|
||
@override
|
||
String modulesRecentActivityHint(int n) {
|
||
return 'last $n install/uninstall events from the audit log';
|
||
}
|
||
|
||
@override
|
||
String get modulesActivityInstalled => 'installed';
|
||
|
||
@override
|
||
String get modulesActivityUninstalled => 'uninstalled';
|
||
|
||
@override
|
||
String get modulesCapabilitiesLabel => 'Capabilities';
|
||
|
||
@override
|
||
String get modulesUninstallTitle => 'Uninstall module?';
|
||
|
||
@override
|
||
String modulesUninstallBody(String name, String version) {
|
||
return 'Removes $name v$version from this hub. Flows that reference it will fail at the next run. A `module.uninstalled` audit event is recorded.';
|
||
}
|
||
|
||
@override
|
||
String modulesUninstalledToast(String name, String version) {
|
||
return 'Uninstalled $name v$version.';
|
||
}
|
||
|
||
@override
|
||
String modulesUninstallFailed(String error) {
|
||
return 'Uninstall failed: $error';
|
||
}
|
||
|
||
@override
|
||
String get modulesUninstalling => 'Uninstalling…';
|
||
|
||
@override
|
||
String moduleSheetFailedToLoad(String error) {
|
||
return 'Failed to load: $error';
|
||
}
|
||
|
||
@override
|
||
String get moduleSheetCapabilities => 'Capabilities';
|
||
|
||
@override
|
||
String get moduleSheetPermissions => 'Declared permissions';
|
||
|
||
@override
|
||
String get moduleSheetNoPermissions => '(none — pure-computation module)';
|
||
|
||
@override
|
||
String get flowsTitle => 'Flows';
|
||
|
||
@override
|
||
String get flowsReloadTooltip => 'Reload';
|
||
|
||
@override
|
||
String get flowsNoneTitle => 'No saved flows';
|
||
|
||
@override
|
||
String get flowsNoneHint =>
|
||
'Save a flow with `fai admin flows save <name> <flow.yaml>` and it shows up here.';
|
||
|
||
@override
|
||
String flowsRunDialogTitle(String name) {
|
||
return 'Run $name';
|
||
}
|
||
|
||
@override
|
||
String get flowsRunDialogBody =>
|
||
'One field per declared input. Pick a file for bytes-shaped inputs; type plain text for the rest. The Run button enables when every required input is set.';
|
||
|
||
@override
|
||
String get flowsLoadingDefinition => 'Loading inputs…';
|
||
|
||
@override
|
||
String get flowsDefinitionFailed => 'Could not load flow definition';
|
||
|
||
@override
|
||
String get flowsNoInputs =>
|
||
'This flow declares no inputs. Click Run to start.';
|
||
|
||
@override
|
||
String get flowsTypeBytesPick => 'Choose file…';
|
||
|
||
@override
|
||
String get flowsTypeBytesChange => 'Change file';
|
||
|
||
@override
|
||
String get flowsTypeBytesNoFile => 'No file selected';
|
||
|
||
@override
|
||
String flowsTypeBytesSize(int n) {
|
||
return '$n bytes';
|
||
}
|
||
|
||
@override
|
||
String get flowsRunErrorTitle => 'Flow run failed';
|
||
|
||
@override
|
||
String flowsFileReadFailed(String path, String error) {
|
||
return 'Could not read \"$path\": $error';
|
||
}
|
||
|
||
@override
|
||
String get flowsMissingModulesLabel => 'Needs:';
|
||
|
||
@override
|
||
String get flowsRunDisabledTooltip => 'Install the missing modules first.';
|
||
|
||
@override
|
||
String flowsInstallAllButton(int n) {
|
||
return 'Install all ($n)';
|
||
}
|
||
|
||
@override
|
||
String get flowsInstallPillTooltip => 'Click to install';
|
||
|
||
@override
|
||
String get flowsInstallDepsTitle => 'Install dependencies';
|
||
|
||
@override
|
||
String get flowsInstallDepsBody =>
|
||
'Walking through the missing modules in order. Existing installs are left alone.';
|
||
|
||
@override
|
||
String get flowsInstallStatusPending => 'Pending';
|
||
|
||
@override
|
||
String get flowsInstallStatusInstalling => 'Installing…';
|
||
|
||
@override
|
||
String flowsInstallStatusDone(String version) {
|
||
return 'Installed v$version';
|
||
}
|
||
|
||
@override
|
||
String get flowsInstallStatusFailed => 'Failed';
|
||
|
||
@override
|
||
String flowsInstallStatusFailedDetail(String error) {
|
||
return '$error';
|
||
}
|
||
|
||
@override
|
||
String get flowsInstallDepsAllDone => 'All dependencies installed.';
|
||
|
||
@override
|
||
String flowsInstallDepsAnyFailed(int ok, int failed) {
|
||
return '$ok installed, $failed failed.';
|
||
}
|
||
|
||
@override
|
||
String get flowsInstallAuthWallButton => 'Add registry token';
|
||
|
||
@override
|
||
String get flowsOpenInEditorTooltip => 'Open in Studio\'s flow editor';
|
||
|
||
@override
|
||
String flowsOpenInEditorFailed(String error) {
|
||
return 'Could not open file: $error';
|
||
}
|
||
|
||
@override
|
||
String get registryCredentialsHeader => 'REGISTRY CREDENTIALS';
|
||
|
||
@override
|
||
String get registryCredentialsBlurb =>
|
||
'Token used to download .fai modules from a registry behind a signin wall (Forgejo, GitHub-private). Stored at ~/.fai/registry-token, mode 0600. The FAI_REGISTRY_TOKEN env var still wins when set.';
|
||
|
||
@override
|
||
String registryTokenStatusConfigured(int chars) {
|
||
return 'Configured ($chars chars)';
|
||
}
|
||
|
||
@override
|
||
String get registryTokenStatusNotSet => 'Not set';
|
||
|
||
@override
|
||
String get registryTokenFieldLabel => 'Token';
|
||
|
||
@override
|
||
String get registryTokenFieldHint =>
|
||
'Paste your Forgejo or GitHub personal access token';
|
||
|
||
@override
|
||
String get registryTokenSaveButton => 'Save';
|
||
|
||
@override
|
||
String get registryTokenClearButton => 'Clear';
|
||
|
||
@override
|
||
String get registryTokenStorageHint =>
|
||
'Saved locally to ~/.fai/registry-token. Never sent to a remote service.';
|
||
|
||
@override
|
||
String get registryTokenSavedToast => 'Registry token saved.';
|
||
|
||
@override
|
||
String get registryTokenClearedToast => 'Registry token cleared.';
|
||
|
||
@override
|
||
String registryTokenSaveFailedToast(String error) {
|
||
return 'Could not save: $error';
|
||
}
|
||
|
||
@override
|
||
String get hubAuthTokenHeader => 'HUB AUTHENTICATION';
|
||
|
||
@override
|
||
String get hubAuthTokenBlurb =>
|
||
'Bearer token used to authenticate Studio against a hub that has auth.tokens: configured (RBAC Level 2). Stored at ~/.fai/hub-auth-token, mode 0600. Studio sends it as Authorization: Bearer on every gRPC call.';
|
||
|
||
@override
|
||
String hubAuthTokenStatusConfigured(int chars) {
|
||
return 'Configured ($chars chars)';
|
||
}
|
||
|
||
@override
|
||
String get hubAuthTokenStatusNotSet => 'Not set (anonymous)';
|
||
|
||
@override
|
||
String get hubAuthTokenFieldLabel => 'Token';
|
||
|
||
@override
|
||
String get hubAuthTokenFieldHint =>
|
||
'Paste a token from the hub operator\'s auth.tokens entry';
|
||
|
||
@override
|
||
String get hubAuthTokenSaveButton => 'Save';
|
||
|
||
@override
|
||
String get hubAuthTokenClearButton => 'Clear';
|
||
|
||
@override
|
||
String get hubAuthTokenStorageHint =>
|
||
'Saved locally to ~/.fai/hub-auth-token. Studio reconnects after save so the new token takes effect immediately.';
|
||
|
||
@override
|
||
String get hubAuthTokenSavedToast => 'Hub auth token saved.';
|
||
|
||
@override
|
||
String get hubAuthTokenClearedToast => 'Hub auth token cleared.';
|
||
|
||
@override
|
||
String hubAuthTokenSaveFailedToast(String error) {
|
||
return 'Could not save: $error';
|
||
}
|
||
|
||
@override
|
||
String get storeAdvisoryVersionTooltip =>
|
||
'Federated upstream service — version is advisory; the upstream may break compatibility without bumping it.';
|
||
|
||
@override
|
||
String get uninstallVersionPickerTitle => 'Pick a version to uninstall';
|
||
|
||
@override
|
||
String uninstallVersionPickerBody(String module) {
|
||
return 'Several versions of `$module` are installed side-by-side. Select the exact version to remove — the others stay in place.';
|
||
}
|
||
|
||
@override
|
||
String get uninstallVersionPickerContinue => 'Continue';
|
||
|
||
@override
|
||
String get defaultScopeHeader => 'DEFAULT SCOPE';
|
||
|
||
@override
|
||
String get defaultScopeBlurb =>
|
||
'Ordered list of publisher segments the hub consults when a flow names a capability without an explicit `<provider>/` prefix. First match wins — promote the segments your team uses most to the top. Catalog-known publishers appear as chips; arbitrary entries are accepted for private segments.';
|
||
|
||
@override
|
||
String get defaultScopeAddLabel => 'Add publisher segment';
|
||
|
||
@override
|
||
String get defaultScopeAddHint => 'e.g. acme.team';
|
||
|
||
@override
|
||
String get defaultScopeAddButton => 'Add';
|
||
|
||
@override
|
||
String get defaultScopeSuggestions => 'Catalog suggestions:';
|
||
|
||
@override
|
||
String get defaultScopeSavedToast => 'Default scope updated.';
|
||
|
||
@override
|
||
String get defaultScopeLoadFailed => 'Could not load default scope.';
|
||
|
||
@override
|
||
String get defaultScopeNonEmptyError =>
|
||
'Default scope cannot be empty — keep at least one segment.';
|
||
|
||
@override
|
||
String get maintenanceHeader => 'HUB MAINTENANCE';
|
||
|
||
@override
|
||
String get maintenanceResetBlurb =>
|
||
'Wipe operator state and start over with a clean hub. Stops every running daemon, atomically backs ~/.fai/ up, then recreates it with the binary, channel state, MCP / n8n / system-AI config, and registry token preserved. Restore by moving the backup directory back.';
|
||
|
||
@override
|
||
String get maintenanceKeepModulesTitle => 'Keep installed modules';
|
||
|
||
@override
|
||
String get maintenanceKeepModulesSubtitle =>
|
||
'Default behaviour wipes ~/.fai/modules/ so a fresh install can verify the module-fetch path end-to-end.';
|
||
|
||
@override
|
||
String get maintenanceKeepDataTitle => 'Keep audit log + saved flows';
|
||
|
||
@override
|
||
String get maintenanceKeepDataSubtitle =>
|
||
'Default behaviour wipes ~/.fai/data/ so the audit chain restarts from a fresh genesis and the bundled sample flows re-import on first daemon start.';
|
||
|
||
@override
|
||
String get maintenanceResetButton => 'Reset hub state';
|
||
|
||
@override
|
||
String get maintenanceResetInProgress => 'Resetting…';
|
||
|
||
@override
|
||
String get maintenanceResetConfirmTitle => 'Reset hub state?';
|
||
|
||
@override
|
||
String get maintenanceResetConfirmBody =>
|
||
'This stops every daemon, moves ~/.fai/ to a timestamped backup, and starts fresh. The backup is fully restorable. Continue?';
|
||
|
||
@override
|
||
String get maintenanceResetConfirmButton => 'Reset';
|
||
|
||
@override
|
||
String get welcomeChecklistAllSetTitle => 'You\'re set up.';
|
||
|
||
@override
|
||
String get welcomeChecklistAllSetBody => 'Three threads to pull on next:';
|
||
|
||
@override
|
||
String get welcomeChecklistNextAuditTitle => 'Read the audit log';
|
||
|
||
@override
|
||
String get welcomeChecklistNextAuditBody =>
|
||
'Every install, flow run, and approval lives in the hash-chained log. Open the Audit tab to see what your hub has been up to.';
|
||
|
||
@override
|
||
String get welcomeChecklistNextAuditButton => 'Open Audit';
|
||
|
||
@override
|
||
String get welcomeChecklistNextTodayTitle => 'Set up the daily Today story';
|
||
|
||
@override
|
||
String get welcomeChecklistNextTodayBody =>
|
||
'Run tools/today/propose.sh on a cron and the editorial card on the store auto-fills with operator-curated narratives.';
|
||
|
||
@override
|
||
String get welcomeChecklistNextTodayButton => 'Open Today docs';
|
||
|
||
@override
|
||
String get welcomeChecklistNextModuleTitle => 'Build your own module';
|
||
|
||
@override
|
||
String get welcomeChecklistNextModuleBody =>
|
||
'Generate a Rust+WASM scaffold with `fai new module <name>`. The module-sdk handles the WIT plumbing; you write a typed invoke.';
|
||
|
||
@override
|
||
String get welcomeChecklistNextModuleButton => 'Read the docs';
|
||
|
||
@override
|
||
String get auditGroupToday => 'TODAY';
|
||
|
||
@override
|
||
String get auditGroupYesterday => 'YESTERDAY';
|
||
|
||
@override
|
||
String get auditGroupThisWeek => 'EARLIER THIS WEEK';
|
||
|
||
@override
|
||
String get auditGroupOlder => 'OLDER';
|
||
|
||
@override
|
||
String get auditEventViewFlowRun => 'View flow run';
|
||
|
||
@override
|
||
String auditFlowRunDialogTitle(String flow) {
|
||
return 'Flow run · $flow';
|
||
}
|
||
|
||
@override
|
||
String auditFlowRunDialogSubtitle(int n, String execution) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'events',
|
||
one: 'event',
|
||
);
|
||
return '$n $_temp0 for execution $execution';
|
||
}
|
||
|
||
@override
|
||
String get approvalsSelectAll => 'Select all';
|
||
|
||
@override
|
||
String get approvalsClearSelection => 'Clear';
|
||
|
||
@override
|
||
String approvalsBatchSelected(int n) {
|
||
return '$n selected';
|
||
}
|
||
|
||
@override
|
||
String get approvalsBatchApprove => 'Approve all';
|
||
|
||
@override
|
||
String get approvalsBatchReject => 'Reject all';
|
||
|
||
@override
|
||
String approvalsBatchApproveDoneToast(int n) {
|
||
return '$n approvals approved.';
|
||
}
|
||
|
||
@override
|
||
String approvalsBatchRejectDoneToast(int n) {
|
||
return '$n approvals rejected.';
|
||
}
|
||
|
||
@override
|
||
String approvalsBatchPartialFailure(int ok, int failed) {
|
||
return '$ok done, $failed failed.';
|
||
}
|
||
|
||
@override
|
||
String get flowsRunButton => 'Run';
|
||
|
||
@override
|
||
String flowsRunningTitle(String name) {
|
||
return 'Running $name';
|
||
}
|
||
|
||
@override
|
||
String flowsResultTitle(String name) {
|
||
return '$name — result';
|
||
}
|
||
|
||
@override
|
||
String flowsErrorTitle(String name) {
|
||
return '$name — failed';
|
||
}
|
||
|
||
@override
|
||
String get flowsRunning => 'Flow running…';
|
||
|
||
@override
|
||
String get flowsNoOutputs => 'Flow completed with no declared outputs.';
|
||
|
||
@override
|
||
String get errGeneric => 'Something went wrong.';
|
||
|
||
@override
|
||
String get errInvalidArgument => 'Invalid input.';
|
||
|
||
@override
|
||
String get errInvalidArgumentHint => 'Check the form values and try again.';
|
||
|
||
@override
|
||
String get errNotFound => 'Not found.';
|
||
|
||
@override
|
||
String get errNotFoundHint =>
|
||
'The item you asked for isn\'t here — it may have been removed or never existed.';
|
||
|
||
@override
|
||
String get errAlreadyExists => 'Already exists.';
|
||
|
||
@override
|
||
String get errPermissionDenied => 'Permission denied.';
|
||
|
||
@override
|
||
String get errPermissionDeniedHint =>
|
||
'The hub\'s operator policy blocks this action. Check `~/.fai/config.yaml`.';
|
||
|
||
@override
|
||
String get errFailedPrecondition => 'Setup issue blocked this.';
|
||
|
||
@override
|
||
String get errFailedPreconditionHint =>
|
||
'The hub couldn\'t proceed because something it needs isn\'t ready. The detail below explains what.';
|
||
|
||
@override
|
||
String get errInternal => 'Unexpected internal error.';
|
||
|
||
@override
|
||
String get errInternalHint =>
|
||
'Check the hub logs at `~/.fai/logs/`. If this repeats, please report it.';
|
||
|
||
@override
|
||
String get errUnavailable => 'Hub not reachable.';
|
||
|
||
@override
|
||
String get errUnavailableHint =>
|
||
'Make sure `fai serve` (or `fai daemon start`) is running and the daemon endpoint matches Studio\'s settings.';
|
||
|
||
@override
|
||
String get errUnauthenticated => 'Authentication required.';
|
||
|
||
@override
|
||
String get errUnauthenticatedHint =>
|
||
'Set `FAI_REGISTRY_TOKEN` or sign in through Settings before retrying.';
|
||
|
||
@override
|
||
String get errCopyDetail => 'Copy detail';
|
||
|
||
@override
|
||
String get errDetailCopied => 'Detail copied to clipboard.';
|
||
|
||
@override
|
||
String get flowsOutputUnknown => 'Output payload variant not recognised.';
|
||
|
||
@override
|
||
String get flowsOutputBytesUnknownMime => 'binary';
|
||
|
||
@override
|
||
String get flowsOutputSaveAs => 'Save as…';
|
||
|
||
@override
|
||
String get flowsOutputSaveAsTitle => 'Save flow output';
|
||
|
||
@override
|
||
String flowsOutputSavedAt(String path) {
|
||
return 'Saved to $path';
|
||
}
|
||
|
||
@override
|
||
String flowsOutputSaveFailed(String error) {
|
||
return 'Save failed: $error';
|
||
}
|
||
|
||
@override
|
||
String get flowsOutputOpen => 'Open';
|
||
|
||
@override
|
||
String flowsOutputOpenFailed(String error) {
|
||
return 'Open failed: $error';
|
||
}
|
||
|
||
@override
|
||
String get approvalsTitle => 'Approvals';
|
||
|
||
@override
|
||
String get approvalsTabPending => 'Pending';
|
||
|
||
@override
|
||
String get approvalsTabHistory => 'History';
|
||
|
||
@override
|
||
String get approvalsInboxZero => 'Inbox zero';
|
||
|
||
@override
|
||
String get approvalsInboxHint =>
|
||
'All caught up. New `system.approval@^0` steps land here automatically.';
|
||
|
||
@override
|
||
String get approvalsReloadTooltip => 'Reload';
|
||
|
||
@override
|
||
String get approvalsPillPending => 'pending';
|
||
|
||
@override
|
||
String get approvalsPillApproved => 'approved';
|
||
|
||
@override
|
||
String get approvalsPillRejected => 'rejected';
|
||
|
||
@override
|
||
String get approvalsPillExpired => 'expired';
|
||
|
||
@override
|
||
String get approvalsPayloadPreview => 'PAYLOAD PREVIEW';
|
||
|
||
@override
|
||
String get approvalsApproveButton => 'Approve';
|
||
|
||
@override
|
||
String get approvalsRejectButton => 'Reject';
|
||
|
||
@override
|
||
String get approvalsRejectDialogTitle => 'Reject approval';
|
||
|
||
@override
|
||
String get approvalsRejectReasonLabel => 'Reason (recorded in audit log)';
|
||
|
||
@override
|
||
String approvalsApprovedToast(String flow, String step) {
|
||
return 'approved · $flow › $step';
|
||
}
|
||
|
||
@override
|
||
String approvalsRejectedToast(String flow, String step) {
|
||
return 'rejected · $flow › $step';
|
||
}
|
||
|
||
@override
|
||
String approvalsApproveFailed(String error) {
|
||
return 'approve failed: $error';
|
||
}
|
||
|
||
@override
|
||
String approvalsRejectFailed(String error) {
|
||
return 'reject failed: $error';
|
||
}
|
||
|
||
@override
|
||
String get approvalsHistoryEmpty => 'No history yet';
|
||
|
||
@override
|
||
String get approvalsHistoryEmptyHint =>
|
||
'Decided approvals (approved / rejected / expired) appear here.\nThe audit log keeps the full hash-chained record.';
|
||
|
||
@override
|
||
String get approvalsDialogDecided => 'decided';
|
||
|
||
@override
|
||
String get approvalsDialogCreated => 'created';
|
||
|
||
@override
|
||
String get approvalsDialogReason => 'reason';
|
||
|
||
@override
|
||
String get approvalsDialogPrompt => 'PROMPT';
|
||
|
||
@override
|
||
String approvalsExpiresIn(int minutes) {
|
||
return '${minutes}m';
|
||
}
|
||
|
||
@override
|
||
String approvalsExpiresInHours(int hours) {
|
||
return '${hours}h';
|
||
}
|
||
|
||
@override
|
||
String get doctorTitle => 'Doctor';
|
||
|
||
@override
|
||
String get doctorSummaryModules => 'Modules';
|
||
|
||
@override
|
||
String get doctorSummaryApprovals => 'Approvals';
|
||
|
||
@override
|
||
String get doctorSummaryAudit => 'Audit';
|
||
|
||
@override
|
||
String get doctorSummaryServices => 'Services';
|
||
|
||
@override
|
||
String doctorSummaryCapabilities(int n) {
|
||
return '$n capabilities';
|
||
}
|
||
|
||
@override
|
||
String get doctorSummaryPending => 'pending';
|
||
|
||
@override
|
||
String doctorSummaryChain(int verified, int total) {
|
||
return '$verified/$total chain';
|
||
}
|
||
|
||
@override
|
||
String get doctorSummaryDeclared => 'declared';
|
||
|
||
@override
|
||
String doctorModulesPanelSummary(int n, int m) {
|
||
return '$n modules · $m capabilities';
|
||
}
|
||
|
||
@override
|
||
String get doctorApprovalsNone => 'No pending approvals';
|
||
|
||
@override
|
||
String doctorApprovalsCount(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: '$n approvals awaiting review',
|
||
one: '1 approval awaiting review',
|
||
);
|
||
return '$_temp0';
|
||
}
|
||
|
||
@override
|
||
String get doctorApprovalsAttentionPill => 'attention';
|
||
|
||
@override
|
||
String get doctorServicesEmpty => 'No host services declared';
|
||
|
||
@override
|
||
String get doctorServicesEmptyHint =>
|
||
'add to ~/.fai/config.yaml under services:';
|
||
|
||
@override
|
||
String get doctorPillLoaded => 'loaded';
|
||
|
||
@override
|
||
String get doctorPillEmpty => 'empty';
|
||
|
||
@override
|
||
String get doctorRecheckTooltip => 'Re-check';
|
||
|
||
@override
|
||
String get doctorEventLogSection => 'Event log';
|
||
|
||
@override
|
||
String get doctorModulesApprovalsSection => 'Modules & approvals';
|
||
|
||
@override
|
||
String get doctorHostServicesSection => 'Host services';
|
||
|
||
@override
|
||
String get doctorDaemonFilesSection => 'Daemon files';
|
||
|
||
@override
|
||
String get doctorDaemonControlSection => 'Daemon control';
|
||
|
||
@override
|
||
String get doctorChainIntact => 'Hash chain intact';
|
||
|
||
@override
|
||
String doctorChainIntactDetail(int n) {
|
||
return '$n events · prev_event_sha256 verified end-to-end';
|
||
}
|
||
|
||
@override
|
||
String get doctorChainTampered => 'Tampering detected';
|
||
|
||
@override
|
||
String doctorChainTamperedDetail(int verified, int total, String id) {
|
||
return '$verified of $total verified before mismatch at $id';
|
||
}
|
||
|
||
@override
|
||
String get doctorChainPillOk => 'WORM-1';
|
||
|
||
@override
|
||
String get doctorChainPillTamper => 'TAMPER';
|
||
|
||
@override
|
||
String get doctorVerifyNow => 'Verify now';
|
||
|
||
@override
|
||
String get doctorRestart => 'Restart';
|
||
|
||
@override
|
||
String get doctorStart => 'Start';
|
||
|
||
@override
|
||
String get doctorStop => 'Stop';
|
||
|
||
@override
|
||
String get doctorStatusAction => 'Status';
|
||
|
||
@override
|
||
String doctorRunningOn(String name, String endpoint) {
|
||
return 'Running on $name: $endpoint';
|
||
}
|
||
|
||
@override
|
||
String doctorStoppedOn(String name) {
|
||
return '$name daemon stopped';
|
||
}
|
||
|
||
@override
|
||
String get doctorStatusUnknown => 'Daemon status: …';
|
||
|
||
@override
|
||
String get doctorPillRunning => 'running';
|
||
|
||
@override
|
||
String get doctorPillStopped => 'stopped';
|
||
|
||
@override
|
||
String get doctorDaemonControlHint =>
|
||
'Studio shells out to the platform binary — mirrors `fai daemon …` exactly so the CLI and UI stay in lockstep.';
|
||
|
||
@override
|
||
String get doctorDaemonControlWorking => 'Working…';
|
||
|
||
@override
|
||
String get doctorPathLog => 'Log';
|
||
|
||
@override
|
||
String get doctorPathConfig => 'Config';
|
||
|
||
@override
|
||
String get doctorPathDb => 'Audit DB';
|
||
|
||
@override
|
||
String get doctorPathModules => 'Modules dir';
|
||
|
||
@override
|
||
String get doctorPathFlows => 'Flows dir';
|
||
|
||
@override
|
||
String get doctorPathPid => 'PID file';
|
||
|
||
@override
|
||
String get doctorPathsEmpty =>
|
||
'Daemon did not report file paths. Update the running hub via `fai daemon restart`.';
|
||
|
||
@override
|
||
String doctorOpenError(String detail) {
|
||
return 'Could not open: $detail';
|
||
}
|
||
|
||
@override
|
||
String doctorUpdateAvailable(String version) {
|
||
return 'Update available — $version';
|
||
}
|
||
|
||
@override
|
||
String doctorUpdateBody(String channel, String version) {
|
||
return 'Channel $channel now offers $version. Apply via the button — Studio shells out to `fai update apply --channel $channel`.';
|
||
}
|
||
|
||
@override
|
||
String get doctorUpdateUnreachable => 'Release host unreachable';
|
||
|
||
@override
|
||
String doctorUpdateUnreachableBody(String channel) {
|
||
return 'Could not contact the release feed for $channel.';
|
||
}
|
||
|
||
@override
|
||
String doctorReleaseNotes(String url) {
|
||
return 'notes: $url';
|
||
}
|
||
|
||
@override
|
||
String get doctorPillNew => 'new';
|
||
|
||
@override
|
||
String get doctorPillOffline => 'offline';
|
||
|
||
@override
|
||
String get doctorApplyUpdate => 'Apply update';
|
||
|
||
@override
|
||
String get doctorApplying => 'Applying…';
|
||
|
||
@override
|
||
String get doctorApplyDone =>
|
||
'Update applied. Restart Studio to see the new daemon version.';
|
||
|
||
@override
|
||
String get mcpHeader => 'MCP CLIENTS';
|
||
|
||
@override
|
||
String get mcpHint =>
|
||
'External MCP servers federate their tools as `mcp.<server>.<tool>` capabilities. Configure once, see them in the Store.';
|
||
|
||
@override
|
||
String get mcpEmpty => 'No MCP servers configured. Click + to add one.';
|
||
|
||
@override
|
||
String get n8nHeader => 'N8N ENDPOINTS';
|
||
|
||
@override
|
||
String get n8nHint =>
|
||
'Active n8n workflows surface as `n8n.<endpoint>.<slug>` capabilities. Configure once, see them in the Store.';
|
||
|
||
@override
|
||
String get n8nEmpty => 'No n8n endpoints configured. Click + to add one.';
|
||
|
||
@override
|
||
String get hubUnreachable => 'Hub unreachable';
|
||
|
||
@override
|
||
String get hubUnreachableHint => 'Start the hub with `fai serve`.';
|
||
|
||
@override
|
||
String get languageEnglish => 'English';
|
||
|
||
@override
|
||
String get languageGerman => 'Deutsch';
|
||
|
||
@override
|
||
String get searchHint => 'Jump anywhere — modules, store, flows, pages…';
|
||
|
||
@override
|
||
String get searchIndexing => 'Indexing…';
|
||
|
||
@override
|
||
String get searchNoMatches => 'No matches.';
|
||
|
||
@override
|
||
String get searchHintNavigate => '↑↓ navigate';
|
||
|
||
@override
|
||
String get searchHintOpen => 'enter open';
|
||
|
||
@override
|
||
String get searchHintClose => 'esc close';
|
||
|
||
@override
|
||
String get searchGroupPages => 'Pages';
|
||
|
||
@override
|
||
String get searchGroupModules => 'Modules';
|
||
|
||
@override
|
||
String get searchGroupStore => 'Store';
|
||
|
||
@override
|
||
String get searchGroupFlows => 'Flows';
|
||
|
||
@override
|
||
String get searchSettingsLabel => 'Settings';
|
||
|
||
@override
|
||
String searchPageHint(int n) {
|
||
return 'page · ⌘$n';
|
||
}
|
||
|
||
@override
|
||
String get searchSettingsHint => 'hub endpoint, channels, system AI · ⌘;';
|
||
|
||
@override
|
||
String searchInstalledModuleHint(String version) {
|
||
return 'installed module · v$version';
|
||
}
|
||
|
||
@override
|
||
String get searchStoreUncategorized => 'uncategorized';
|
||
|
||
@override
|
||
String searchStoreHintWithTagline(String tagline) {
|
||
return 'store · $tagline';
|
||
}
|
||
|
||
@override
|
||
String searchStoreHintWithCategory(String category) {
|
||
return 'store · $category';
|
||
}
|
||
|
||
@override
|
||
String get searchSavedFlowHint => 'saved flow';
|
||
|
||
@override
|
||
String get systemAiTitle => 'System AI';
|
||
|
||
@override
|
||
String get systemAiIntro =>
|
||
'The hub-internal LLM the platform itself uses for inline failure explanations and operator help. Off by default; configure here. See docs/architecture/system-ai.md for what crosses the wire.';
|
||
|
||
@override
|
||
String get systemAiProviderLabel => 'Provider';
|
||
|
||
@override
|
||
String get systemAiEndpointLabel => 'Endpoint URL (up to /v1)';
|
||
|
||
@override
|
||
String get systemAiApiKeyRequired => 'API key env var (required)';
|
||
|
||
@override
|
||
String get systemAiApiKeyOptional => 'API key env var (optional)';
|
||
|
||
@override
|
||
String systemAiApiKeyDisclaimer(String name) {
|
||
return 'Studio never reads or stores the key value — only its env-var name. The hub reads `\$$name` at request time.';
|
||
}
|
||
|
||
@override
|
||
String get systemAiTestConnection => 'Test connection';
|
||
|
||
@override
|
||
String get systemAiTesting => 'Testing…';
|
||
|
||
@override
|
||
String get systemAiSave => 'Save';
|
||
|
||
@override
|
||
String get systemAiPrivacyHeader => 'PRIVACY MODE';
|
||
|
||
@override
|
||
String get systemAiPrivacyOffLabel => 'Off';
|
||
|
||
@override
|
||
String get systemAiPrivacyOffDesc =>
|
||
'Feature disabled. No requests leave the hub.';
|
||
|
||
@override
|
||
String get systemAiPrivacyRedactedLabel => 'Redacted';
|
||
|
||
@override
|
||
String get systemAiPrivacyRedactedDesc =>
|
||
'Only event_type / error / module names. KRITIS-friendly default.';
|
||
|
||
@override
|
||
String get systemAiPrivacyFullLabel => 'Full';
|
||
|
||
@override
|
||
String get systemAiPrivacyFullDesc =>
|
||
'Includes the audit detail JSON (hash-only — no raw payloads).';
|
||
|
||
@override
|
||
String get systemAiConnectionOk => 'Connection ok';
|
||
|
||
@override
|
||
String get systemAiConnectionFailed => 'Connection failed';
|
||
|
||
@override
|
||
String systemAiReplyPrefix(String text) {
|
||
return 'Reply: $text';
|
||
}
|
||
|
||
@override
|
||
String get systemAiModelLabel => 'Model (required)';
|
||
|
||
@override
|
||
String get systemAiModelHintFallback =>
|
||
'model identifier the provider expects';
|
||
|
||
@override
|
||
String get systemAiModelHelperBase =>
|
||
'Required: pick from the list (Refresh) or type one.';
|
||
|
||
@override
|
||
String get systemAiModelHelperOllama =>
|
||
'Required: pick from the list (Refresh) or type one. Use Pull to download from Ollama.';
|
||
|
||
@override
|
||
String get systemAiRefresh => 'Refresh';
|
||
|
||
@override
|
||
String get systemAiPull => 'Pull';
|
||
|
||
@override
|
||
String get systemAiPulling => 'Pulling…';
|
||
|
||
@override
|
||
String systemAiCouldNotListModels(String error) {
|
||
return 'Could not list models: $error';
|
||
}
|
||
|
||
@override
|
||
String get systemAiNoModelsOllama =>
|
||
'No models on the Ollama server yet. Type one above and click Pull.';
|
||
|
||
@override
|
||
String get systemAiNoModelsGeneric => 'Provider returned no models.';
|
||
|
||
@override
|
||
String get systemAiSuitabilityRecommended => 'recommended';
|
||
|
||
@override
|
||
String get systemAiSuitabilityBalanced => 'balanced';
|
||
|
||
@override
|
||
String get systemAiSuitabilitySmall => 'small — quality may be limited';
|
||
|
||
@override
|
||
String get systemAiSuitabilityLarge => 'large — slower';
|
||
|
||
@override
|
||
String get systemAiSuitabilityHuge => 'huge — likely too slow on a laptop';
|
||
|
||
@override
|
||
String get systemAiSuitabilityUnknown => 'size unknown';
|
||
|
||
@override
|
||
String systemAiHwDetected(String summary) {
|
||
return 'Detected: $summary';
|
||
}
|
||
|
||
@override
|
||
String systemAiHwReviewed(String date) {
|
||
return ' · curation reviewed $date';
|
||
}
|
||
|
||
@override
|
||
String systemAiLegendRecommended(String tier) {
|
||
return 'recommended for this hardware$tier';
|
||
}
|
||
|
||
@override
|
||
String get systemAiLegendBalanced => 'balanced (4-8B)';
|
||
|
||
@override
|
||
String get systemAiLegendSmallLarge =>
|
||
'small <3B / large 9-15B / above hardware';
|
||
|
||
@override
|
||
String get systemAiLegendHuge => 'huge >15B';
|
||
|
||
@override
|
||
String get systemAiLegendUnknown => 'size unknown';
|
||
|
||
@override
|
||
String systemAiCacheRow(int n) {
|
||
String _temp0 = intl.Intl.pluralLogic(
|
||
n,
|
||
locale: localeName,
|
||
other: 'explanations',
|
||
one: 'explanation',
|
||
);
|
||
return 'Cache: $n cached $_temp0. Identical prompts hit the cache; switching model or privacy mode flushes automatically.';
|
||
}
|
||
|
||
@override
|
||
String get systemAiCacheClear => 'Clear';
|
||
|
||
@override
|
||
String systemAiCacheClearedToast(int n) {
|
||
return 'Cleared $n cached System-AI explanations.';
|
||
}
|
||
|
||
@override
|
||
String systemAiCacheClearFailed(String error) {
|
||
return 'Cache clear failed: $error';
|
||
}
|
||
|
||
@override
|
||
String get systemAiPullEmptyError =>
|
||
'Type a model id (e.g. gemma3:4b) first.';
|
||
|
||
@override
|
||
String systemAiPullFailedError(String error) {
|
||
return 'Pull failed: $error';
|
||
}
|
||
|
||
@override
|
||
String storeProvenanceTemporal(String provider) {
|
||
return 'temporal · $provider';
|
||
}
|
||
|
||
@override
|
||
String get storeSourceTemporal => 'Temporal';
|
||
|
||
@override
|
||
String get navFlowEditor => 'Editor';
|
||
|
||
@override
|
||
String get flowEditorBackTooltip => 'Back';
|
||
|
||
@override
|
||
String get flowEditorNew => 'New flow';
|
||
|
||
@override
|
||
String get flowEditorSave => 'Save';
|
||
|
||
@override
|
||
String get flowEditorRun => 'Run';
|
||
|
||
@override
|
||
String get flowEditorRefresh => 'Refresh file list';
|
||
|
||
@override
|
||
String get flowEditorRunOutput => 'RUN OUTPUT';
|
||
|
||
@override
|
||
String get flowEditorEmptyTitle => 'No flow open';
|
||
|
||
@override
|
||
String get flowEditorEmptyBody =>
|
||
'Pick one from the left, or click New flow to start a fresh one.';
|
||
|
||
@override
|
||
String get flowEditorListEmptyTitle => 'No saved flows';
|
||
|
||
@override
|
||
String get flowEditorListEmptyBody =>
|
||
'Click New flow to scaffold one from a template.';
|
||
|
||
@override
|
||
String get flowEditorDiscardTitle => 'Discard unsaved changes?';
|
||
|
||
@override
|
||
String get flowEditorDiscardBody =>
|
||
'Switching files will throw away the edits you haven\'t saved.';
|
||
|
||
@override
|
||
String get flowEditorDiscardKeep => 'Keep editing';
|
||
|
||
@override
|
||
String get flowEditorDiscardThrow => 'Throw away';
|
||
|
||
@override
|
||
String get flowEditorNewDialogTitle => 'New flow';
|
||
|
||
@override
|
||
String get flowEditorNewDialogLabel => 'Flow name';
|
||
|
||
@override
|
||
String get flowEditorNewDialogHelper =>
|
||
'lowercase letters, digits, hyphen, underscore';
|
||
|
||
@override
|
||
String get flowEditorNewDialogCancel => 'Cancel';
|
||
|
||
@override
|
||
String get flowEditorNewDialogCreate => 'Create';
|
||
|
||
@override
|
||
String flowEditorNewTemplateComment(String name) {
|
||
return 'Generated by Studio\'s Flow editor. Edit, save, run.';
|
||
}
|
||
|
||
@override
|
||
String flowEditorAlreadyExists(String name) {
|
||
return 'A flow named \"$name\" already exists.';
|
||
}
|
||
}
|