feat(studio): channel switcher, autostart toggle, Store redesign (v0.18.0)

UI parity for operators who never touch a CLI, plus a Store
that reads more like an app store.

Settings dialog:
- Channel rows gain a popup menu: Connect / Make active /
  Enable autostart / Disable autostart. "Connect" still
  re-points Studio's wire; "Make active" actually writes
  ~/.fai/current-channel via `fai channel switch`.
- Inline output panel surfaces the spawned binary's stdout
  on success or stderr on failure, so operators see what
  happened without opening a terminal.

Store page rewrite:
- Big top search bar with a clear button. Live filter on
  every keystroke.
- Horizontal category strip auto-populated from the index;
  segmented status row (All / Published / Alpha / Planned),
  Installed-only chip, result count.
- Grid of cards that reflows to fit the viewport — replaces
  the previous single-column list. Each card shows
  category-aware icon, version, status, tagline preview, and
  a one-click Install (or Details for installed / planned).
- Per-module detail sheet renders the full bilingual
  description with a DE/EN toggle, separate Required-
  capabilities + Required-host-services sections, repo link,
  Read-docs button. Install + Uninstall live at the bottom.
- StoreItem and HubService.searchStore now carry the German
  tagline + description so the locale toggle has something
  to switch to.

SystemActions extended with `faiChannelSwitch`,
`faiDaemonEnable`, `faiDaemonDisable` so Settings can spawn
the right CLI without each call site reimplementing the
`fai` resolution rules.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-05-07 18:52:48 +02:00
parent 73e394b39f
commit 93926741b1
6 changed files with 1053 additions and 270 deletions

View file

@ -365,7 +365,9 @@ class HubService {
(e) => StoreItem(
name: e.name,
taglineEn: e.taglineEn,
taglineDe: e.taglineDe,
descriptionEn: e.descriptionEn,
descriptionDe: e.descriptionDe,
category: e.category,
tags: e.tags,
requiresCapabilities: e.requiresCapabilities,
@ -1028,7 +1030,13 @@ class CuratedSnapshot {
class StoreItem {
final String name;
final String taglineEn;
/// German tagline. Falls back to [taglineEn] in the UI when
/// the index entry does not ship a localized one.
final String taglineDe;
final String descriptionEn;
/// German long-form description. Falls back to [descriptionEn]
/// when missing.
final String descriptionDe;
final String category;
final List<String> tags;
final List<String> requiresCapabilities;
@ -1043,7 +1051,9 @@ class StoreItem {
const StoreItem({
required this.name,
required this.taglineEn,
required this.taglineDe,
required this.descriptionEn,
required this.descriptionDe,
required this.category,
required this.tags,
required this.requiresCapabilities,