feat(studio): first-run UX, recovery affordances, l10n, bundled fonts

- Connection-aware Welcome: when the hub is down, show a hero with a
  primary "Start hub" CTA + install fallback instead of a dead,
  all-unchecked onboarding checklist (the first-run cliff).
- Actionable binary-not-found (file picker + install link, not a
  "set FAI_BIN" dead end) and a connect-failure banner after
  repeated failed health polls.
- Localize six hardcoded English error/toast clusters (DE+EN ARB).
- Bundle Inter + JetBrains Mono as assets; drop the runtime
  google_fonts fetch (air-gap / KRITIS safe, no font-swap flash).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-06-11 23:52:18 +02:00
parent 7511867774
commit 5313266cc4
25 changed files with 1231 additions and 234 deletions

View file

@ -1523,5 +1523,79 @@
"placeholders": {
"error": {"type": "String"}
}
}
},
"welcomeHubDownTitle": "The hub isn't running yet",
"welcomeHubDownBody": "Studio talks to a local hub — the engine that loads modules and runs flows. It isn't reachable right now. Start it to begin.",
"welcomeHubDownStart": "Start hub",
"welcomeHubDownStarting": "Starting…",
"welcomeHubDownDocsLink": "Can't start it? Read the getting-started guide",
"welcomeHubDownEndpoint": "Endpoint: {endpoint}",
"@welcomeHubDownEndpoint": {
"placeholders": {
"endpoint": {"type": "String"}
}
},
"daemonStartRequested": "Daemon start requested. Reconnecting…",
"daemonStartFailed": "Could not start daemon: {detail}",
"@daemonStartFailed": {
"placeholders": {
"detail": {"type": "String"}
}
},
"faiBinaryNotFound": "Could not find the `fai` program on this machine.",
"faiBinaryNotFoundHint": "Install the F∆I platform, or point Studio at an existing `fai` binary.",
"faiBinaryLocateButton": "Locate `fai` binary…",
"faiBinaryInstallDocsButton": "Open install guide",
"faiBinaryPickerTitle": "Select the `fai` binary",
"faiBinarySetOk": "Using `fai` binary at {path}",
"@faiBinarySetOk": {
"placeholders": {
"path": {"type": "String"}
}
},
"sysAiFixParse": "The provider sent a response in an unexpected format. Providers that aren't OpenAI-compatible may need a translation proxy.",
"sysAiFixUnknown": "Unexpected failure ({kind}). See the System AI documentation.",
"@sysAiFixUnknown": {
"placeholders": {
"kind": {"type": "String"}
}
},
"sysAiFixDisabled": "Configure System AI in Settings (Cmd+,) → System AI panel.",
"sysAiFixEnvMissing": "The API-key environment variable is empty. Set it in your shell, then restart the daemon.",
"sysAiFixNetwork": "The hub can't reach the configured endpoint. Is your provider running?",
"sysAiFixHttp": "The provider returned a non-2xx status. Verify the endpoint, model name, and API key.",
"sysAiFixEmptyResponse": "The provider answered with no content. Try a different model or rephrase the prompt.",
"channelSwitchOk": "Switched active channel to \"{name}\". Daemon restarted.",
"@channelSwitchOk": {
"placeholders": {
"name": {"type": "String"}
}
},
"channelSwitchFailed": "Channel switch failed: {detail}",
"@channelSwitchFailed": {
"placeholders": {
"detail": {"type": "String"}
}
},
"providerDescOllama": "Local `ollama serve`. Models stay on your machine. No API key needed.",
"providerDescOpenai": "OpenAI hosted API. Requires an API key. Data leaves your machine.",
"providerDescLmstudio": "Local LM Studio server. Models stay on your machine. No API key needed.",
"providerDescVllm": "Self-hosted vLLM or any other OpenAI-compatible server. Endpoint required.",
"providerDescCustom": "Anything else that speaks the OpenAI-compatible chat-completions wire (LiteLLM proxy, internal mirror, …).",
"welcomeDocLoadFailedBody": "Could not load the bundled doc \"{slug}\" ({locale}).\nTried: {attempted}\nUnderlying: {underlying}\nThis usually means the Studio build on disk predates the doc bundle. Rebuild Studio or open the online copy.",
"@welcomeDocLoadFailedBody": {
"placeholders": {
"slug": {"type": "String"},
"locale": {"type": "String"},
"attempted": {"type": "String"},
"underlying": {"type": "String"}
}
},
"hubUnreachableBanner": "Can't reach {endpoint} — open Settings",
"@hubUnreachableBanner": {
"placeholders": {
"endpoint": {"type": "String"}
}
},
"hubUnreachableOpenSettings": "Open Settings"
}